mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
WebUI Improvements and fixes (#9613)
* Show toast instead of text for success and errors * Show correct times * Start playing next hour when current hour ends * Fix refreshing camera image * Fix timeline
This commit is contained in:
parent
3df8b5829c
commit
f4310862aa
311
web/package-lock.json
generated
311
web/package-lock.json
generated
@ -34,6 +34,7 @@
|
|||||||
"immer": "^10.0.3",
|
"immer": "^10.0.3",
|
||||||
"lucide-react": "^0.294.0",
|
"lucide-react": "^0.294.0",
|
||||||
"monaco-yaml": "^5.1.0",
|
"monaco-yaml": "^5.1.0",
|
||||||
|
"next-themes": "^0.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-apexcharts": "^1.4.1",
|
"react-apexcharts": "^1.4.1",
|
||||||
"react-day-picker": "^8.9.1",
|
"react-day-picker": "^8.9.1",
|
||||||
@ -43,6 +44,7 @@
|
|||||||
"react-router-dom": "^6.20.1",
|
"react-router-dom": "^6.20.1",
|
||||||
"react-use-websocket": "^4.5.0",
|
"react-use-websocket": "^4.5.0",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
|
"sonner": "^1.4.0",
|
||||||
"sort-by": "^1.2.0",
|
"sort-by": "^1.2.0",
|
||||||
"strftime": "^0.10.2",
|
"strftime": "^0.10.2",
|
||||||
"swr": "^2.2.4",
|
"swr": "^2.2.4",
|
||||||
@ -804,6 +806,156 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@next/env": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -2191,6 +2343,15 @@
|
|||||||
"integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==",
|
"integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@swc/types": {
|
"node_modules/@swc/types": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
|
||||||
@ -3211,6 +3372,18 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"streamsearch": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cac": {
|
"node_modules/cac": {
|
||||||
"version": "6.7.14",
|
"version": "6.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||||
@ -3238,10 +3411,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001566",
|
"version": "1.0.30001583",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz",
|
||||||
"integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
|
"integrity": "sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -4635,6 +4807,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
@ -5804,6 +5982,90 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/next": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@next/env": "14.1.0",
|
||||||
|
"@swc/helpers": "0.5.2",
|
||||||
|
"busboy": "1.6.0",
|
||||||
|
"caniuse-lite": "^1.0.30001579",
|
||||||
|
"graceful-fs": "^4.2.11",
|
||||||
|
"postcss": "8.4.31",
|
||||||
|
"styled-jsx": "5.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"next": "dist/bin/next"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@next/swc-darwin-arm64": "14.1.0",
|
||||||
|
"@next/swc-darwin-x64": "14.1.0",
|
||||||
|
"@next/swc-linux-arm64-gnu": "14.1.0",
|
||||||
|
"@next/swc-linux-arm64-musl": "14.1.0",
|
||||||
|
"@next/swc-linux-x64-gnu": "14.1.0",
|
||||||
|
"@next/swc-linux-x64-musl": "14.1.0",
|
||||||
|
"@next/swc-win32-arm64-msvc": "14.1.0",
|
||||||
|
"@next/swc-win32-ia32-msvc": "14.1.0",
|
||||||
|
"@next/swc-win32-x64-msvc": "14.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@opentelemetry/api": "^1.1.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"sass": "^1.3.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@opentelemetry/api": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/next-themes": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"next": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-dom": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/next/node_modules/postcss": {
|
||||||
|
"version": "8.4.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
|
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.6",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.14",
|
"version": "2.0.14",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||||
@ -7112,6 +7374,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sonner": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-nvkTsIuOmi9e5Wz5If8ldasJjZNVfwiXYijBi2dbijvTQnQppvMcXTFNxL/NUFWlI2yJ1JX7TREDsg+gYm9WyA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sort-by": {
|
"node_modules/sort-by": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/sort-by/-/sort-by-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/sort-by/-/sort-by-1.2.0.tgz",
|
||||||
@ -7158,6 +7429,15 @@
|
|||||||
"integrity": "sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==",
|
"integrity": "sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strftime": {
|
"node_modules/strftime": {
|
||||||
"version": "0.10.2",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.2.tgz",
|
||||||
@ -7255,6 +7535,29 @@
|
|||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/styled-jsx": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"client-only": "0.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@babel/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"babel-plugin-macros": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sucrase": {
|
"node_modules/sucrase": {
|
||||||
"version": "3.34.0",
|
"version": "3.34.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"immer": "^10.0.3",
|
"immer": "^10.0.3",
|
||||||
"lucide-react": "^0.294.0",
|
"lucide-react": "^0.294.0",
|
||||||
"monaco-yaml": "^5.1.0",
|
"monaco-yaml": "^5.1.0",
|
||||||
|
"next-themes": "^0.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-apexcharts": "^1.4.1",
|
"react-apexcharts": "^1.4.1",
|
||||||
"react-day-picker": "^8.9.1",
|
"react-day-picker": "^8.9.1",
|
||||||
@ -48,6 +49,7 @@
|
|||||||
"react-router-dom": "^6.20.1",
|
"react-router-dom": "^6.20.1",
|
||||||
"react-use-websocket": "^4.5.0",
|
"react-use-websocket": "^4.5.0",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
|
"sonner": "^1.4.0",
|
||||||
"sort-by": "^1.2.0",
|
"sort-by": "^1.2.0",
|
||||||
"strftime": "^0.10.2",
|
"strftime": "^0.10.2",
|
||||||
"swr": "^2.2.4",
|
"swr": "^2.2.4",
|
||||||
|
@ -24,6 +24,9 @@ export default function DynamicCameraImage({
|
|||||||
aspect,
|
aspect,
|
||||||
}: DynamicCameraImageProps) {
|
}: DynamicCameraImageProps) {
|
||||||
const [key, setKey] = useState(Date.now());
|
const [key, setKey] = useState(Date.now());
|
||||||
|
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
const [activeObjects, setActiveObjects] = useState<string[]>([]);
|
const [activeObjects, setActiveObjects] = useState<string[]>([]);
|
||||||
const hasActiveObjects = useMemo(
|
const hasActiveObjects = useMemo(
|
||||||
() => activeObjects.length > 0,
|
() => activeObjects.length > 0,
|
||||||
@ -58,6 +61,8 @@ export default function DynamicCameraImage({
|
|||||||
if (eventIndex == -1) {
|
if (eventIndex == -1) {
|
||||||
const newActiveObjects = [...activeObjects, event.after.id];
|
const newActiveObjects = [...activeObjects, event.after.id];
|
||||||
setActiveObjects(newActiveObjects);
|
setActiveObjects(newActiveObjects);
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
setKey(Date.now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,12 +74,13 @@ export default function DynamicCameraImage({
|
|||||||
? INTERVAL_ACTIVE_MS
|
? INTERVAL_ACTIVE_MS
|
||||||
: INTERVAL_INACTIVE_MS;
|
: INTERVAL_INACTIVE_MS;
|
||||||
|
|
||||||
setTimeout(
|
const tId = setTimeout(
|
||||||
() => {
|
() => {
|
||||||
setKey(Date.now());
|
setKey(Date.now());
|
||||||
},
|
},
|
||||||
loadTime > loadInterval ? 1 : loadInterval
|
loadTime > loadInterval ? 1 : loadInterval
|
||||||
);
|
);
|
||||||
|
setTimeoutId(tId);
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -228,6 +228,7 @@ export default function DynamicVideoPlayer({
|
|||||||
player.on("timeupdate", () => {
|
player.on("timeupdate", () => {
|
||||||
controller.updateProgress(player.currentTime() || 0);
|
controller.updateProgress(player.currentTime() || 0);
|
||||||
});
|
});
|
||||||
|
player.on("ended", () => controller.fireClipEndEvent());
|
||||||
|
|
||||||
if (onControllerReady) {
|
if (onControllerReady) {
|
||||||
onControllerReady(controller);
|
onControllerReady(controller);
|
||||||
@ -284,6 +285,7 @@ export class DynamicVideoController {
|
|||||||
// playback
|
// playback
|
||||||
private recordings: Recording[] = [];
|
private recordings: Recording[] = [];
|
||||||
private onPlaybackTimestamp: ((time: number) => void) | undefined = undefined;
|
private onPlaybackTimestamp: ((time: number) => void) | undefined = undefined;
|
||||||
|
private onClipEnded: (() => void) | undefined = undefined;
|
||||||
private annotationOffset: number;
|
private annotationOffset: number;
|
||||||
private timeToStart: number | undefined = undefined;
|
private timeToStart: number | undefined = undefined;
|
||||||
|
|
||||||
@ -393,6 +395,16 @@ export class DynamicVideoController {
|
|||||||
this.onPlaybackTimestamp = listener;
|
this.onPlaybackTimestamp = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClipEndedEvent(listener: () => void) {
|
||||||
|
this.onClipEnded = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireClipEndEvent() {
|
||||||
|
if (this.onClipEnded) {
|
||||||
|
this.onClipEnded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scrubToTimestamp(time: number) {
|
scrubToTimestamp(time: number) {
|
||||||
if (this.playerMode != "scrubbing") {
|
if (this.playerMode != "scrubbing") {
|
||||||
this.playerMode = "scrubbing";
|
this.playerMode = "scrubbing";
|
||||||
|
32
web/src/components/ui/sonner.tsx
Normal file
32
web/src/components/ui/sonner.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { Toaster as Sonner } from "sonner";
|
||||||
|
|
||||||
|
type ToasterProps = React.ComponentProps<typeof Sonner>;
|
||||||
|
|
||||||
|
const Toaster = ({ ...props }: ToasterProps) => {
|
||||||
|
const { theme = "system" } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sonner
|
||||||
|
theme={theme as ToasterProps["theme"]}
|
||||||
|
className="toaster group"
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
toast:
|
||||||
|
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||||
|
description: "group-[.toast]:text-muted-foreground",
|
||||||
|
actionButton:
|
||||||
|
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||||
|
cancelButton:
|
||||||
|
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||||
|
success:
|
||||||
|
"group toast group-[.toaster]:bg-success group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||||
|
error: "group toast group-[.toaster]:bg-danger group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Toaster };
|
@ -9,6 +9,8 @@ import { Button } from "@/components/ui/button";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import { useTheme } from "@/context/theme-provider";
|
import { useTheme } from "@/context/theme-provider";
|
||||||
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
type SaveOptions = "saveonly" | "restart";
|
type SaveOptions = "saveonly" | "restart";
|
||||||
|
|
||||||
@ -18,7 +20,6 @@ function ConfigEditor() {
|
|||||||
const { data: config } = useSWR<string>("config/raw");
|
const { data: config } = useSWR<string>("config/raw");
|
||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const [success, setSuccess] = useState<string | undefined>();
|
|
||||||
const [error, setError] = useState<string | undefined>();
|
const [error, setError] = useState<string | undefined>();
|
||||||
|
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
|
||||||
@ -42,11 +43,11 @@ function ConfigEditor() {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setError("");
|
setError("");
|
||||||
setSuccess(response.data.message);
|
toast.success(response.data.message, { position: "top-center" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setSuccess("");
|
toast.error("Error saving config", { position: "top-center" });
|
||||||
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
setError(error.response.data.message);
|
setError(error.response.data.message);
|
||||||
@ -150,7 +151,6 @@ function ConfigEditor() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{success && <div className="max-h-20 text-success">{success}</div>}
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-4 overflow-scroll text-danger whitespace-pre-wrap">
|
<div className="p-4 overflow-scroll text-danger whitespace-pre-wrap">
|
||||||
{error}
|
{error}
|
||||||
@ -158,6 +158,7 @@ function ConfigEditor() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div ref={configRef} className="h-full mt-2" />
|
<div ref={configRef} className="h-full mt-2" />
|
||||||
|
<Toaster />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,13 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { DateRange } from "react-day-picker";
|
import { DateRange } from "react-day-picker";
|
||||||
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
type ExportItem = {
|
type ExportItem = {
|
||||||
@ -55,7 +57,6 @@ function Export() {
|
|||||||
// Export States
|
// Export States
|
||||||
const [camera, setCamera] = useState<string | undefined>();
|
const [camera, setCamera] = useState<string | undefined>();
|
||||||
const [playback, setPlayback] = useState<string | undefined>();
|
const [playback, setPlayback] = useState<string | undefined>();
|
||||||
const [message, setMessage] = useState({ text: "", error: false });
|
|
||||||
|
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
currentDate.setHours(0, 0, 0, 0);
|
currentDate.setHours(0, 0, 0, 0);
|
||||||
@ -70,23 +71,21 @@ function Export() {
|
|||||||
const [deleteClip, setDeleteClip] = useState<string | undefined>();
|
const [deleteClip, setDeleteClip] = useState<string | undefined>();
|
||||||
|
|
||||||
const onHandleExport = () => {
|
const onHandleExport = () => {
|
||||||
if (camera == "select") {
|
if (!camera) {
|
||||||
setMessage({ text: "A camera needs to be selected.", error: true });
|
toast.error("A camera needs to be selected.", { position: "top-center" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playback == "select") {
|
if (!playback) {
|
||||||
setMessage({
|
toast.error("A playback factor needs to be selected.", {
|
||||||
text: "A playback factor needs to be selected.",
|
position: "top-center",
|
||||||
error: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!date?.from || !startTime || !endTime) {
|
if (!date?.from || !startTime || !endTime) {
|
||||||
setMessage({
|
toast.error("A start and end time needs to be selected", {
|
||||||
text: "A start and end time needs to be selected",
|
position: "top-center",
|
||||||
error: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -106,9 +105,8 @@ function Export() {
|
|||||||
const end = endDate.getTime() / 1000;
|
const end = endDate.getTime() / 1000;
|
||||||
|
|
||||||
if (end <= start) {
|
if (end <= start) {
|
||||||
setMessage({
|
toast.error("The end time must be after the start time.", {
|
||||||
text: "The end time must be after the start time.",
|
position: "top-center",
|
||||||
error: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -117,24 +115,23 @@ function Export() {
|
|||||||
.post(`export/${camera}/start/${start}/end/${end}`, { playback })
|
.post(`export/${camera}/start/${start}/end/${end}`, { playback })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
setMessage({
|
toast.success(
|
||||||
text: "Successfully started export. View the file in the /exports folder.",
|
"Successfully started export. View the file in the /exports folder.",
|
||||||
error: false,
|
{ position: "top-center" }
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutate();
|
mutate();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (error.response?.data?.message) {
|
if (error.response?.data?.message) {
|
||||||
setMessage({
|
toast.error(
|
||||||
text: `Failed to start export: ${error.response.data.message}`,
|
`Failed to start export: ${error.response.data.message}`,
|
||||||
error: true,
|
{ position: "top-center" }
|
||||||
});
|
);
|
||||||
} else {
|
} else {
|
||||||
setMessage({
|
toast.error(`Failed to start export: ${error.message}`, {
|
||||||
text: `Failed to start export: ${error.message}`,
|
position: "top-center",
|
||||||
error: true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -156,16 +153,7 @@ function Export() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading as="h2">Export</Heading>
|
<Heading as="h2">Export</Heading>
|
||||||
|
<Toaster />
|
||||||
{message.text && (
|
|
||||||
<div
|
|
||||||
className={`max-h-20 ${
|
|
||||||
message.error ? "text-danger" : "text-success"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{message.text}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<AlertDialog
|
<AlertDialog
|
||||||
open={deleteClip != undefined}
|
open={deleteClip != undefined}
|
||||||
|
@ -284,8 +284,13 @@ export function getRangeForTimestamp(timestamp: number) {
|
|||||||
date.setHours(date.getHours() + 1);
|
date.setHours(date.getHours() + 1);
|
||||||
|
|
||||||
// ensure not to go past current time
|
// ensure not to go past current time
|
||||||
const end = Math.min(new Date().getTime() / 1000, date.getTime() / 1000);
|
return { start, end: endOfHourOrCurrentTime(date.getTime() / 1000) };
|
||||||
return { start, end };
|
}
|
||||||
|
|
||||||
|
export function endOfHourOrCurrentTime(timestamp: number) {
|
||||||
|
const now = new Date();
|
||||||
|
now.setMilliseconds(0);
|
||||||
|
return Math.min(timestamp, now.getTime() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCurrentHour(timestamp: number) {
|
export function isCurrentHour(timestamp: number) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { endOfHourOrCurrentTime } from "./dateUtil";
|
||||||
|
|
||||||
// group history cards by 120 seconds of activity
|
// group history cards by 120 seconds of activity
|
||||||
const GROUP_SECONDS = 120;
|
const GROUP_SECONDS = 120;
|
||||||
|
|
||||||
@ -169,7 +171,7 @@ export function getTimelineHoursForDay(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = startDay.getTime() / 1000;
|
end = endOfHourOrCurrentTime(startDay.getTime() / 1000);
|
||||||
const hour = Object.values(day).find((cards) => {
|
const hour = Object.values(day).find((cards) => {
|
||||||
const card = Object.values(cards)[0];
|
const card = Object.values(cards)[0];
|
||||||
if (card == undefined || card.time < start || card.time > end) {
|
if (card == undefined || card.time < start || card.time > end) {
|
||||||
|
@ -34,9 +34,6 @@ export default function DesktopTimelineView({
|
|||||||
const controllerRef = useRef<DynamicVideoController | undefined>(undefined);
|
const controllerRef = useRef<DynamicVideoController | undefined>(undefined);
|
||||||
const initialScrollRef = useRef<HTMLDivElement | null>(null);
|
const initialScrollRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const [selectedPlayback, setSelectedPlayback] = useState(initialPlayback);
|
|
||||||
const [timelineTime, setTimelineTime] = useState(0);
|
|
||||||
|
|
||||||
// handle scrolling to initial timeline item
|
// handle scrolling to initial timeline item
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialScrollRef.current != null) {
|
if (initialScrollRef.current != null) {
|
||||||
@ -50,17 +47,45 @@ export default function DesktopTimelineView({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [timelineTime, setTimelineTime] = useState(0);
|
||||||
const timelineStack = useMemo(
|
const timelineStack = useMemo(
|
||||||
() =>
|
() =>
|
||||||
getTimelineHoursForDay(
|
getTimelineHoursForDay(
|
||||||
selectedPlayback.camera,
|
initialPlayback.camera,
|
||||||
timelineData,
|
timelineData,
|
||||||
cameraPreviews,
|
cameraPreviews,
|
||||||
selectedPlayback.range.start + 60
|
initialPlayback.range.start + 60
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [selectedPlaybackIdx, setSelectedPlaybackIdx] = useState(
|
||||||
|
timelineStack.playbackItems.findIndex((playback) => {
|
||||||
|
return (
|
||||||
|
playback.range.start == initialPlayback.range.start &&
|
||||||
|
playback.range.end == initialPlayback.range.end
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const selectedPlayback = useMemo(
|
||||||
|
() => timelineStack.playbackItems[selectedPlaybackIdx],
|
||||||
|
[selectedPlaybackIdx]
|
||||||
|
);
|
||||||
|
|
||||||
|
// handle moving to next clip
|
||||||
|
useEffect(() => {
|
||||||
|
if (!controllerRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedPlaybackIdx > 0) {
|
||||||
|
controllerRef.current.onClipEndedEvent(() => {
|
||||||
|
console.log("setting to " + (selectedPlaybackIdx - 1));
|
||||||
|
setSelectedPlaybackIdx(selectedPlaybackIdx - 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [controllerRef, selectedPlaybackIdx]);
|
||||||
|
|
||||||
const { data: activity } = useSWR<RecordingActivity>(
|
const { data: activity } = useSWR<RecordingActivity>(
|
||||||
[
|
[
|
||||||
`${initialPlayback.camera}/recording/hourly/activity`,
|
`${initialPlayback.camera}/recording/hourly/activity`,
|
||||||
@ -148,12 +173,14 @@ export default function DesktopTimelineView({
|
|||||||
</div>
|
</div>
|
||||||
<div className="relative mt-4 w-full h-full">
|
<div className="relative mt-4 w-full h-full">
|
||||||
<div className="absolute left-0 top-0 right-0 bottom-0 overflow-auto">
|
<div className="absolute left-0 top-0 right-0 bottom-0 overflow-auto">
|
||||||
{timelineStack.playbackItems.map((timeline) => {
|
{timelineStack.playbackItems.map((timeline, tIdx) => {
|
||||||
const isInitiallySelected =
|
const isInitiallySelected =
|
||||||
initialPlayback.range.start == timeline.range.start;
|
initialPlayback.range.start == timeline.range.start;
|
||||||
const isSelected =
|
const isSelected =
|
||||||
timeline.range.start == selectedPlayback.range.start;
|
timeline.range.start == selectedPlayback.range.start;
|
||||||
const graphData = timelineGraphData[timeline.range.start];
|
const graphData = timelineGraphData[timeline.range.start];
|
||||||
|
const start = new Date(timeline.range.start * 1000);
|
||||||
|
const end = new Date(timeline.range.end * 1000);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -178,10 +205,10 @@ export default function DesktopTimelineView({
|
|||||||
}
|
}
|
||||||
options={{
|
options={{
|
||||||
snap: null,
|
snap: null,
|
||||||
min: new Date(timeline.range.start * 1000),
|
min: start,
|
||||||
max: new Date(timeline.range.end * 1000),
|
max: end,
|
||||||
start: new Date(timeline.range.start * 1000),
|
start: start,
|
||||||
end: new Date(timeline.range.end * 1000),
|
end: end,
|
||||||
zoomable: false,
|
zoomable: false,
|
||||||
height: "120px",
|
height: "120px",
|
||||||
}}
|
}}
|
||||||
@ -220,7 +247,7 @@ export default function DesktopTimelineView({
|
|||||||
startTime={timeline.range.start}
|
startTime={timeline.range.start}
|
||||||
graphData={graphData}
|
graphData={graphData}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedPlayback(timeline);
|
setSelectedPlaybackIdx(tIdx);
|
||||||
|
|
||||||
let startTs;
|
let startTs;
|
||||||
if (timeline.timelineItems.length > 0) {
|
if (timeline.timelineItems.length > 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user