From c3b813e8214f032461d9dc7f2d2ac4cb4f5e4377 Mon Sep 17 00:00:00 2001 From: JohnMark Sill Date: Fri, 18 Feb 2022 17:14:16 -0600 Subject: [PATCH] lint: fixes from lint and updates to typescript linting --- web/.eslintrc.js | 17 + web/babel.config.js | 2 +- web/jsconfig.json | 8 + web/package-lock.json | 749 ++++++++++++++++++ web/package.json | 9 +- web/src/components/BubbleButton.tsx | 12 +- .../components/HistoryViewer/HistoryVideo.tsx | 38 +- .../HistoryViewer/HistoryViewer.tsx | 25 +- web/src/components/LiveChip.jsx | 2 +- web/src/components/Timeline/Timeline.tsx | 146 +--- .../components/Timeline/TimelineBlockView.tsx | 4 +- web/src/routes/Camera_V2.jsx | 2 +- web/tsconfig.json | 14 +- 13 files changed, 872 insertions(+), 156 deletions(-) create mode 100644 web/jsconfig.json diff --git a/web/.eslintrc.js b/web/.eslintrc.js index c8b576cab..d184d1840 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -13,6 +13,7 @@ module.exports = { 'prettier', 'preact', 'plugin:import/react', + 'plugin:import/typescript', 'plugin:testing-library/recommended', 'plugin:jest/recommended', ], @@ -137,4 +138,20 @@ module.exports = { }, }, }, + + overrides: [ + { + files: ['*.{ts,tsx}'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + extends: ['plugin:@typescript-eslint/recommended'], + settings: { + 'import/resolver': { + node: { + extensions: ['.ts', '.tsx'], + }, + }, + }, + }, + ], }; diff --git a/web/babel.config.js b/web/babel.config.js index fbdf92e2e..8cd0c50e1 100644 --- a/web/babel.config.js +++ b/web/babel.config.js @@ -1,4 +1,4 @@ module.exports = { - presets: ['@babel/preset-env'], + presets: ['@babel/preset-env', ['@babel/typescript', { jsxPragma: 'h' }]], plugins: [['@babel/plugin-transform-react-jsx', { pragma: 'h' }]], }; diff --git a/web/jsconfig.json b/web/jsconfig.json new file mode 100644 index 000000000..92a1a27a7 --- /dev/null +++ b/web/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "ES2019", + "jsx": "preserve", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment", + } +} diff --git a/web/package-lock.json b/web/package-lock.json index 9810ef583..d29aa01fd 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -350,6 +350,33 @@ "regexpu-core": "^4.7.1" } }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/helper-explode-assignable-expression": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz", @@ -1072,6 +1099,23 @@ } } }, + "@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + } + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz", @@ -2704,6 +2748,206 @@ } } }, + "@babel/plugin-transform-typescript": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", + "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-typescript": "^7.16.7" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/generator": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz", + "integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", + "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", + "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz", + "integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==", + "dev": true + }, + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", @@ -2869,6 +3113,31 @@ "esutils": "^2.0.2" } }, + "@babel/preset-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz", + "integrity": "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-typescript": "^7.16.7" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + } + } + }, "@babel/runtime": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", @@ -4063,6 +4332,102 @@ "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.12.0.tgz", + "integrity": "sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.12.0", + "@typescript-eslint/type-utils": "5.12.0", + "@typescript-eslint/utils": "5.12.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", + "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0" + } + }, + "@typescript-eslint/types": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", + "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", + "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/experimental-utils": { "version": "4.15.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", @@ -4077,6 +4442,164 @@ "eslint-utils": "^2.0.0" } }, + "@typescript-eslint/parser": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.12.0.tgz", + "integrity": "sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.12.0", + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/typescript-estree": "5.12.0", + "debug": "^4.3.2" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", + "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0" + } + }, + "@typescript-eslint/types": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", + "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz", + "integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", + "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/scope-manager": { "version": "4.15.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", @@ -4087,6 +4610,49 @@ "@typescript-eslint/visitor-keys": "4.15.0" } }, + "@typescript-eslint/type-utils": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.12.0.tgz", + "integrity": "sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.12.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/types": { "version": "4.15.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", @@ -4108,6 +4674,189 @@ "tsutils": "^3.17.1" } }, + "@typescript-eslint/utils": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.12.0.tgz", + "integrity": "sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.12.0", + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/typescript-estree": "5.12.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@typescript-eslint/scope-manager": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", + "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0" + } + }, + "@typescript-eslint/types": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", + "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz", + "integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", + "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@typescript-eslint/visitor-keys": { "version": "4.15.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", diff --git a/web/package.json b/web/package.json index e0eaa373a..d31ebab5c 100644 --- a/web/package.json +++ b/web/package.json @@ -2,12 +2,12 @@ "name": "frigate", "private": true, "scripts": { - "start": "cross-env SNOWPACK_PUBLIC_API_HOST=http://localhost:5000 snowpack dev", + "start": "cross-env SNOWPACK_PUBLIC_API_HOST=http://hawkeye:5000 snowpack dev", "start:custom": "snowpack dev", "prebuild": "rimraf build", - "build": "cross-env NODE_ENV=production SNOWPACK_MODE=production SNOWPACK_PUBLIC_API_HOST='' snowpack build", + "build": "cross-env NODE_ENV=production SNOWPACK_MODE=production SNOWPACK_PUBLIC_API_HOST='http://hawkeye:5000' snowpack build", "lint": "npm run lint:cmd -- --fix", - "lint:cmd": "eslint ./ --ext .jsx,.js", + "lint:cmd": "eslint ./ --ext .jsx,.js,.tsx,.ts", "test": "jest" }, "dependencies": { @@ -26,11 +26,14 @@ "@babel/eslint-parser": "^7.12.13", "@babel/plugin-transform-react-jsx": "^7.12.13", "@babel/preset-env": "^7.12.13", + "@babel/preset-typescript": "^7.16.7", "@prefresh/snowpack": "^3.0.1", "@snowpack/plugin-postcss": "^1.1.0", "@testing-library/jest-dom": "^5.11.9", "@testing-library/preact": "^2.0.1", "@testing-library/user-event": "^12.7.1", + "@typescript-eslint/eslint-plugin": "^5.12.0", + "@typescript-eslint/parser": "^5.12.0", "autoprefixer": "^10.2.1", "cross-env": "^7.0.3", "eslint": "^7.19.0", diff --git a/web/src/components/BubbleButton.tsx b/web/src/components/BubbleButton.tsx index f0293bb08..87705eb7f 100644 --- a/web/src/components/BubbleButton.tsx +++ b/web/src/components/BubbleButton.tsx @@ -2,7 +2,7 @@ import { h } from 'preact'; interface BubbleButtonProps { variant?: 'primary' | 'secondary'; - children?: JSX.Element; + children?: preact.JSX.Element; disabled?: boolean; className?: string; onClick?: () => void; @@ -22,12 +22,10 @@ export const BubbleButton = ({ if (disabled) { computedClass += ' text-gray-200 dark:text-gray-200'; - } else { - if (variant === 'primary') { - computedClass += ` ${PRIMARY_CLASS}`; - } else if (variant === 'secondary') { - computedClass += ` ${SECONDARY_CLASS}`; - } + } else if (variant === 'primary') { + computedClass += ` ${PRIMARY_CLASS}`; + } else if (variant === 'secondary') { + computedClass += ` ${SECONDARY_CLASS}`; } const onClickHandler = () => { diff --git a/web/src/components/HistoryViewer/HistoryVideo.tsx b/web/src/components/HistoryViewer/HistoryVideo.tsx index 23aadebd9..b08611b95 100644 --- a/web/src/components/HistoryViewer/HistoryVideo.tsx +++ b/web/src/components/HistoryViewer/HistoryVideo.tsx @@ -1,6 +1,7 @@ import { h } from 'preact'; import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { useApiHost } from '../../api'; +import { isNullOrUndefined } from '../../utils/objectUtils'; interface OnTimeUpdateEvent { timestamp: number; @@ -17,13 +18,11 @@ interface HistoryVideoProps { id: string; isPlaying: boolean; currentTime: number; - onTimeUpdate: (event: OnTimeUpdateEvent) => void; + onTimeUpdate?: (event: OnTimeUpdateEvent) => void; onPause: () => void; onPlay: () => void; } -const isNullOrUndefined = (object: any): boolean => object === null || object === undefined; - export const HistoryVideo = ({ id, isPlaying: videoIsPlaying, @@ -47,7 +46,7 @@ export const HistoryVideo = ({ setVideoHeight(videoHeight); } } - }, [videoRef.current]); + }, [videoRef]); useEffect(() => { initializeVideoContainerHeight(); @@ -56,6 +55,10 @@ export const HistoryVideo = ({ useEffect(() => { const idExists = !isNullOrUndefined(id); if (idExists) { + if (videoRef.current && !videoRef.current.paused) { + videoRef.current = undefined; + } + setVideoProperties({ posterUrl: `${apiHost}/api/events/${id}/snapshot.jpg`, videoUrl: `${apiHost}/vod/event/${id}/index.m3u8`, @@ -64,28 +67,15 @@ export const HistoryVideo = ({ } else { setVideoProperties(undefined); } - }, [id, videoHeight]); + }, [id, videoHeight, videoRef, apiHost]); useEffect(() => { - const playVideo = (video: HTMLMediaElement) => { - console.debug('playVideo: attempt playback'); - video - .play() - .then(() => { - console.debug('playVideo: video started'); - }) - .catch((e) => { - console.error('Fail', { e }); - }); - }; + const playVideo = (video: HTMLMediaElement) => video.play(); const attemptPlayVideo = (video: HTMLMediaElement) => { const videoHasNotLoaded = video.readyState <= 1; - console.debug('playVideo', { videoHasNotLoaded }); if (videoHasNotLoaded) { - console.debug('playVideo: attempt to load video'); video.oncanplay = () => { - console.debug('onLoad: video loaded'); playVideo(video); }; video.load(); @@ -97,7 +87,6 @@ export const HistoryVideo = ({ const video = videoRef.current; const videoExists = !isNullOrUndefined(video); if (videoExists) { - console.log('check should start', { videoIsPlaying }); if (videoIsPlaying) { attemptPlayVideo(video); } else { @@ -122,19 +111,22 @@ export const HistoryVideo = ({ isPlaying: videoIsPlaying, timestamp: target.currentTime, }; - onTimeUpdate(timeUpdateEvent); + + onTimeUpdate && onTimeUpdate(timeUpdateEvent); }, - [videoIsPlaying] + [videoIsPlaying, onTimeUpdate] ); const videoPropertiesIsUndefined = isNullOrUndefined(videoProperties); if (videoPropertiesIsUndefined) { - return
; + return
; } + const { posterUrl, videoUrl, height } = videoProperties; return (
Live diff --git a/web/src/components/Timeline/Timeline.tsx b/web/src/components/Timeline/Timeline.tsx index caa07b155..0f01be9c7 100644 --- a/web/src/components/Timeline/Timeline.tsx +++ b/web/src/components/Timeline/Timeline.tsx @@ -1,6 +1,7 @@ import { Fragment, h } from 'preact'; -import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; -import { longToDate } from '../../utils/dateUtil'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'; +import { getTimelineEventBlocksFromTimelineEvents } from '../../utils/Timeline/timelineEventUtils'; +import { ScrollPermission } from './ScrollPermission'; import { TimelineBlocks } from './TimelineBlocks'; import { TimelineChangeEvent } from './TimelineChangeEvent'; import { DisabledControls, TimelineControls } from './TimelineControls'; @@ -11,7 +12,7 @@ interface TimelineProps { events: TimelineEvent[]; isPlaying: boolean; onChange: (event: TimelineChangeEvent) => void; - onPlayPause: (isPlaying: boolean) => void; + onPlayPause?: (isPlaying: boolean) => void; } export default function Timeline({ events, isPlaying, onChange, onPlayPause }: TimelineProps) { @@ -26,12 +27,36 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T const [timelineOffset, setTimelineOffset] = useState(undefined); const [markerTime, setMarkerTime] = useState(undefined); const [currentEvent, setCurrentEvent] = useState(undefined); - const [scrollTimeout, setScrollTimeout] = useState(undefined); + const [scrollTimeout, setScrollTimeout] = useState(undefined); const [scrollPermission, setScrollPermission] = useState({ allowed: true, resetAfterSeeked: false, }); + const scrollToPosition = useCallback( + (positionX: number) => { + if (timelineContainerRef.current) { + const permission: ScrollPermission = { + allowed: true, + resetAfterSeeked: true, + }; + setScrollPermission(permission); + timelineContainerRef.current.scroll({ + left: positionX, + behavior: 'smooth', + }); + } + }, + [timelineContainerRef] + ); + + const scrollToEvent = useCallback( + (event, offset = 0) => { + scrollToPosition(event.positionX + offset - timelineOffset); + }, + [timelineOffset, scrollToPosition] + ); + useEffect(() => { if (timeline.length > 0 && currentEvent) { const currentIndex = currentEvent.index; @@ -57,86 +82,18 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T } }, [timeline, currentEvent]); - const checkEventForOverlap = (firstEvent: TimelineEvent, secondEvent: TimelineEvent) => { - if (secondEvent.startTime < firstEvent.endTime && secondEvent.startTime > firstEvent.startTime) { - return true; - } - return false; - }; - - const determineOffset = (currentEvent: TimelineEventBlock, previousEvents: TimelineEventBlock[]): number => { - const OFFSET_DISTANCE_IN_PIXELS = 10; - const previousIndex = previousEvents.length - 1; - const previousEvent = previousEvents[previousIndex]; - if (previousEvent) { - const isOverlap = checkEventForOverlap(previousEvent, currentEvent); - if (isOverlap) { - return OFFSET_DISTANCE_IN_PIXELS + determineOffset(currentEvent, previousEvents.slice(0, previousIndex)); - } - } - return 0; - }; - - const buildTimelineView = (events: TimelineEvent[]): TimelineEventBlock[] => { - const firstEvent = events[0]; - const firstEventTime = longToDate(firstEvent.start_time); - return events - .map((e, index) => { - const startTime = longToDate(e.start_time); - const endTime = e.end_time ? longToDate(e.end_time) : new Date(); - const seconds = Math.round(Math.abs(endTime.getTime() - startTime.getTime()) / 1000); - const positionX = Math.round(Math.abs(startTime.getTime() - firstEventTime.getTime()) / 1000 + timelineOffset); - return { - ...e, - startTime, - endTime, - width: seconds, - positionX, - index, - } as TimelineEventBlock; - }) - .reduce((rowMap, current) => { - for (let i = 0; i < rowMap.length; i++) { - const row = rowMap[i] ?? []; - const lastItem = row[row.length - 1]; - if (lastItem) { - const isOverlap = checkEventForOverlap(lastItem, current); - if (isOverlap) { - continue; - } - } - rowMap[i] = [...row, current]; - return rowMap; - } - rowMap.push([current]); - return rowMap; - }, [] as TimelineEventBlock[][]) - .flatMap((rows, rowPosition) => { - rows.forEach((eventBlock) => { - const OFFSET_DISTANCE_IN_PIXELS = 10; - eventBlock.yOffset = OFFSET_DISTANCE_IN_PIXELS * rowPosition; - }); - return rows; - }) - .sort((a, b) => a.startTime.getTime() - b.startTime.getTime()); - }; - useEffect(() => { if (events && events.length > 0 && timelineOffset) { - const timelineEvents = buildTimelineView(events); + const timelineEvents = getTimelineEventBlocksFromTimelineEvents(events, timelineOffset); const lastEventIndex = timelineEvents.length - 1; const recentEvent = timelineEvents[lastEventIndex]; setTimeline(timelineEvents); setMarkerTime(recentEvent.startTime); setCurrentEvent(recentEvent); - onChange({ - timelineEvent: recentEvent, - markerTime: recentEvent.startTime, - seekComplete: true, - }); + scrollToEvent(recentEvent); } - }, [events, timelineOffset]); + }, [events, timelineOffset, scrollToEvent]); useEffect(() => { const timelineIsLoaded = timeline.length > 0; @@ -144,7 +101,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T const lastEvent = timeline[timeline.length - 1]; scrollToEvent(lastEvent); } - }, [timeline]); + }, [timeline, scrollToEvent]); const checkMarkerForEvent = (markerTime: Date) => { const adjustedMarkerTime = new Date(markerTime); @@ -197,32 +154,14 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T } }; - const scrollToPosition = (positionX: number) => { - if (timelineContainerRef.current) { - const permission: ScrollPermission = { - allowed: true, - resetAfterSeeked: true, - }; - setScrollPermission(permission); - timelineContainerRef.current.scroll({ - left: positionX, - behavior: 'smooth', - }); - } - }; - - const scrollToEvent = (event, offset = 0) => { - scrollToPosition(event.positionX + offset - timelineOffset); - }; - - const getCurrentMarkerTime = () => { + const getCurrentMarkerTime = useCallback(() => { if (timelineContainerRef.current && timeline.length > 0) { const scrollPosition = timelineContainerRef.current.scrollLeft; const firstTimelineEvent = timeline[0] as TimelineEventBlock; const firstTimelineEventStartTime = firstTimelineEvent.startTime.getTime(); return new Date(firstTimelineEventStartTime + scrollPosition * 1000); } - }; + }, [timeline, timelineContainerRef]); useEffect(() => { if (timelineContainerRef) { @@ -232,10 +171,13 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T } }, [timelineContainerRef]); - const handleViewEvent = (event: TimelineEventBlock) => { - scrollToEvent(event); - setMarkerTime(getCurrentMarkerTime()); - }; + const handleViewEvent = useCallback( + (event: TimelineEventBlock) => { + scrollToEvent(event); + setMarkerTime(getCurrentMarkerTime()); + }, + [scrollToEvent, getCurrentMarkerTime] + ); const onPlayPauseHandler = (isPlaying: boolean) => { onPlayPause(isPlaying); @@ -260,7 +202,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T if (timelineOffset && timeline.length > 0) { return ; } - }, [timeline, timelineOffset]); + }, [timeline, timelineOffset, handleViewEvent]); return ( @@ -279,7 +221,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T left: 'calc(100% / 2)', borderRight: '2px solid rgba(252, 211, 77)', }} - > + />
diff --git a/web/src/components/Timeline/TimelineBlockView.tsx b/web/src/components/Timeline/TimelineBlockView.tsx index e3a9a7a40..dea13f1a6 100644 --- a/web/src/components/Timeline/TimelineBlockView.tsx +++ b/web/src/components/Timeline/TimelineBlockView.tsx @@ -9,7 +9,7 @@ interface TimelineBlockViewProps { } export const TimelineBlockView = ({ block, onClick }: TimelineBlockViewProps) => { - const onClickHandler = useCallback(() => onClick(block), [block]); + const onClickHandler = useCallback(() => onClick(block), [block, onClick]); return (
left: `${block.positionX}px`, width: `${block.width}px`, }} - >
+ /> ); }; diff --git a/web/src/routes/Camera_V2.jsx b/web/src/routes/Camera_V2.jsx index f517b5299..acb775b6f 100644 --- a/web/src/routes/Camera_V2.jsx +++ b/web/src/routes/Camera_V2.jsx @@ -6,7 +6,7 @@ import { useConfig } from '../api'; import { Tabs, TextTab } from '../components/Tabs'; import { LiveChip } from '../components/LiveChip'; import { DebugCamera } from '../components/DebugCamera'; -import { HistoryViewer } from '../components/HistoryViewer' +import HistoryViewer from '../components/HistoryViewer/HistoryViewer.tsx'; export default function Camera({ camera }) { const { data: config } = useConfig(); diff --git a/web/tsconfig.json b/web/tsconfig.json index 930223342..5c5356777 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,7 +1,15 @@ { "compilerOptions": { - "jsx": "preserve", + "module": "CommonJS", + "target": "ES2019", + "jsx": "react", "jsxFactory": "h", - "jsxFragmentFactory": "Fragment", - } + "lib": [ + "ES2019" + ] + }, + "include": [ + "./src/**/*.tsx", + "./src/**/*.ts" + ] }