From 4d23e79f65e25ff95ab12af521c47abaa8b90a2f Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Fri, 11 Jun 2021 14:49:57 +0200 Subject: [PATCH] Add TypeScript support for React components --- .eslintrc.js | 29 +- babel.config.js | 9 +- package-lock.json | 576 ++++++++++++++++++ package.json | 7 + ...rComponent.js => AlphaPickerComponent.tsx} | 10 +- src/components/layoutManager.js | 4 + .../pages/{SearchPage.js => SearchPage.tsx} | 16 +- ...archResults.js => LiveTVSearchResults.tsx} | 72 ++- .../{SearchFields.js => SearchFields.tsx} | 13 +- .../{SearchResults.js => SearchResults.tsx} | 112 ++-- ...archResultsRow.js => SearchResultsRow.tsx} | 12 +- ...chSuggestions.js => SearchSuggestions.tsx} | 14 +- src/global.d.ts | 5 + tsconfig.json | 21 + webpack.common.js | 10 +- webpack.dev.js | 7 +- 16 files changed, 810 insertions(+), 107 deletions(-) rename src/components/alphaPicker/{AlphaPickerComponent.js => AlphaPickerComponent.tsx} (66%) rename src/components/pages/{SearchPage.js => SearchPage.tsx} (68%) rename src/components/search/{LiveTVSearchResults.js => LiveTVSearchResults.tsx} (76%) rename src/components/search/{SearchFields.js => SearchFields.tsx} (88%) rename src/components/search/{SearchResults.js => SearchResults.tsx} (79%) rename src/components/search/{SearchResultsRow.js => SearchResultsRow.tsx} (79%) rename src/components/search/{SearchSuggestions.js => SearchSuggestions.tsx} (82%) create mode 100644 src/global.d.ts create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index f461a6ceba..a42ab3f53c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -61,12 +61,22 @@ module.exports = { settings: { react: { version: 'detect' + }, + 'import/extensions': [ + '.js', + '.ts', + '.jsx', + '.tsx' + ], + 'import/parsers': { + '@typescript-eslint/parser': [ '.ts', '.tsx' ] } }, overrides: [ { files: [ - './src/**/*.js' + './src/**/*.js', + './src/**/*.ts' ], parser: '@babel/eslint-parser', env: { @@ -197,6 +207,23 @@ module.exports = { 'document.querySelector' ] } + }, + { + files: [ + './src/**/*.ts', + './src/**/*.tsx' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/recommended', + 'plugin:eslint-comments/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:jsx-a11y/recommended' + ] } ] }; diff --git a/babel.config.js b/babel.config.js index e31ea23126..fcd9dcb4ac 100644 --- a/babel.config.js +++ b/babel.config.js @@ -12,7 +12,14 @@ module.exports = { corejs: 3 } ], - '@babel/preset-react' + '@babel/preset-react', + [ + '@babel/preset-typescript', + { + isTSX: true, + allExtensions: true + } + ] ], plugins: [ '@babel/plugin-proposal-class-properties', diff --git a/package-lock.json b/package-lock.json index daa6ec16a7..f2840976e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1387,6 +1387,23 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz", + "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true + } + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", @@ -2030,6 +2047,197 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-transform-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.5.tgz", + "integrity": "sha512-cFD5PKp4b8/KkwQ7h71FdPXFvz1RgwTFF9akRZwFldb9G0AHf7CgoPx96c4Q/ZVjh6V81tqQwW5YiHws16OzPg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-typescript": "^7.14.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz", + "integrity": "sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.5.tgz", + "integrity": "sha512-Uq9z2e7ZtcnDMirRqAGLRaLwJn+Lrh388v5ETrR3pALJnElVh2zqQmdbz4W2RUJYohAPh2mtyPUgyMHMzXMncQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5" + } + }, + "@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz", + "integrity": "sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", + "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "dev": true + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/traverse": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.5.tgz", + "integrity": "sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz", @@ -2189,6 +2397,31 @@ } } }, + "@babel/preset-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz", + "integrity": "sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-transform-typescript": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + } + } + }, "@babel/runtime": { "version": "7.13.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", @@ -2198,6 +2431,16 @@ "regenerator-runtime": "^0.13.4" } }, + "@babel/runtime-corejs3": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.5.tgz", + "integrity": "sha512-cBbwXj3F2xjnQJ0ERaFRLjxhUSBYsQPXJ7CERz/ecx6q6hzQ99eTflAPFC3ks4q/IG4CWupNVdflc4jlFBJVsg==", + "dev": true, + "requires": { + "core-js-pure": "^3.14.0", + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", @@ -2791,6 +3034,156 @@ } } }, + "@typescript-eslint/eslint-plugin": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", + "integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.26.1", + "@typescript-eslint/scope-manager": "4.26.1", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.21", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", + "integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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 + } + } + }, + "@typescript-eslint/parser": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", + "integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.26.1", + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/typescript-estree": "4.26.1", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", + "integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1" + } + }, + "@typescript-eslint/types": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", + "integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", + "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "@typescript-eslint/visitor-keys": "4.26.1", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "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" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", + "integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.26.1", + "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 + } + } + }, "@uupaa/dynamic-import-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@uupaa/dynamic-import-polyfill/-/dynamic-import-polyfill-1.0.2.tgz", @@ -3080,6 +3473,16 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -3185,6 +3588,12 @@ "integrity": "sha512-tKHdBe8N/Vq2nLAm4YPBVREVZjMux6KrqyPfNQgIbDl0t7HaNSmy8w4OyVHYg/cvyn5BW7o7pVwpjPte89Zhcg==", "dev": true }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3265,6 +3674,18 @@ } } }, + "axe-core": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.2.2.tgz", + "integrity": "sha512-OKRkKM4ojMEZRJ5UNJHmq9tht7cEnRnqKG6KyB/trYws00Xtkv12mHtlJ0SK7cmuNbrU8dPUova3ELTuilfBbw==", + "dev": true + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, "babel-loader": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", @@ -4126,6 +4547,12 @@ } } }, + "core-js-pure": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.14.0.tgz", + "integrity": "sha512-YVh+LN2FgNU0odThzm61BsdkwrbrchumFq3oztnE9vTKC4KS2fvnPmcx8t6jnqAyOTCTF4ZSiuK8Qhh7SNcL4g==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -4538,6 +4965,12 @@ "type": "^1.0.1" } }, + "damerau-levenshtein": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", + "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", + "dev": true + }, "date-fns": { "version": "2.22.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.22.1.tgz", @@ -5513,6 +5946,33 @@ } } }, + "eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + }, + "dependencies": { + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + } + } + }, "eslint-plugin-promise": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", @@ -5617,6 +6077,12 @@ } } }, + "eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", + "dev": true + }, "eslint-rule-composer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", @@ -7573,6 +8039,21 @@ "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", "dev": true }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -15489,6 +15970,78 @@ "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", "dev": true }, + "ts-loader": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.3.tgz", + "integrity": "sha512-sEyWiU3JMHBL55CIeC4iqJQadI0U70A5af0kvgbNLHVNz2ACztQg0j/9x10bjjIht8WfFYLKfn4L6tkZ+pu+8Q==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "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" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -15518,6 +16071,23 @@ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", "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" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -15557,6 +16127,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", + "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "dev": true + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", diff --git a/package.json b/package.json index 4b1b1a7e27..efb97124a5 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "@babel/plugin-transform-modules-umd": "^7.14.5", "@babel/preset-env": "^7.14.5", "@babel/preset-react": "^7.14.5", + "@babel/preset-typescript": "^7.14.5", + "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "^4.26.1", "@uupaa/dynamic-import-polyfill": "^1.0.2", "autoprefixer": "^10.2.6", "babel-loader": "^8.2.2", @@ -26,8 +29,10 @@ "eslint-plugin-compat": "^3.9.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.23.4", + "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-react": "^7.24.0", + "eslint-plugin-react-hooks": "^4.2.0", "expose-loader": "^3.0.0", "file-loader": "^6.2.0", "html-loader": "^2.1.2", @@ -44,6 +49,8 @@ "stylelint-no-browser-hacks": "^1.2.1", "stylelint-order": "^4.1.0", "stylelint-scss": "^3.19.0", + "ts-loader": "^9.2.3", + "typescript": "^4.3.2", "webpack": "^5.39.0", "webpack-cli": "^4.7.2", "webpack-dev-server": "^3.11.2", diff --git a/src/components/alphaPicker/AlphaPickerComponent.js b/src/components/alphaPicker/AlphaPickerComponent.tsx similarity index 66% rename from src/components/alphaPicker/AlphaPickerComponent.js rename to src/components/alphaPicker/AlphaPickerComponent.tsx index ae325c24f1..79e196e978 100644 --- a/src/components/alphaPicker/AlphaPickerComponent.js +++ b/src/components/alphaPicker/AlphaPickerComponent.tsx @@ -1,10 +1,15 @@ import PropTypes from 'prop-types'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import AlphaPicker from './alphaPicker'; +type AlphaPickerProps = { + onAlphaPicked: () => void +}; + // React compatibility wrapper component for alphaPicker.js -const AlphaPickerComponent = ({ onAlphaPicked = () => {} }) => { +// eslint-disable-next-line @typescript-eslint/no-empty-function +const AlphaPickerComponent: FunctionComponent = ({ onAlphaPicked = () => {} }) => { const [ alphaPicker, setAlphaPicker ] = useState(null); const element = useRef(null); @@ -19,6 +24,7 @@ const AlphaPickerComponent = ({ onAlphaPicked = () => {} }) => { return () => { alphaPicker?.destroy(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps -- Disabled for wrapper components }, []); return ( diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 7268b3914b..bd0796b0d2 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -15,6 +15,10 @@ function setLayout(instance, layout, selectedLayout) { } class LayoutManager { + tv = false; + mobile = false; + desktop = false; + setLayout(layout, save) { if (!layout || layout === 'auto') { this.autoLayout(); diff --git a/src/components/pages/SearchPage.js b/src/components/pages/SearchPage.tsx similarity index 68% rename from src/components/pages/SearchPage.js rename to src/components/pages/SearchPage.tsx index 93e2779880..7aa820cc3c 100644 --- a/src/components/pages/SearchPage.js +++ b/src/components/pages/SearchPage.tsx @@ -1,12 +1,18 @@ import PropTypes from 'prop-types'; -import React, { useState } from 'react'; +import React, { FunctionComponent, useState } from 'react'; import SearchFields from '../search/SearchFields'; import SearchResults from '../search/SearchResults'; import SearchSuggestions from '../search/SearchSuggestions'; import LiveTVSearchResults from '../search/LiveTVSearchResults'; -const SearchPage = ({ serverId, parentId, collectionType }) => { +type SearchProps = { + serverId: string, + parentId: string, + collectionType: string +}; + +const SearchPage: FunctionComponent = ({ serverId, parentId, collectionType }: SearchProps) => { const [ query, setQuery ] = useState(null); return ( @@ -14,18 +20,18 @@ const SearchPage = ({ serverId, parentId, collectionType }) => { {!query && } { +const LiveTVSearchResults: FunctionComponent = ({ serverId, parentId, collectionType, query }) => { const [ movies, setMovies ] = useState([]); const [ episodes, setEpisodes ] = useState([]); const [ sports, setSports ] = useState([]); @@ -30,34 +37,32 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => { const [ programs, setPrograms ] = useState([]); const [ channels, setChannels ] = useState([]); - const getDefaultParameters = () => ({ - ParentId: parentId, - searchTerm: query, - Limit: 24, - Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount', - Recursive: true, - EnableTotalRecordCount: false, - ImageTypeLimit: 1, - IncludePeople: false, - IncludeMedia: false, - IncludeGenres: false, - IncludeStudios: false, - IncludeArtists: false - }); - - // FIXME: This query does not support Live TV filters - const fetchItems = (apiClient, params = {}) => apiClient?.getItems( - apiClient?.getCurrentUserId(), - { - ...getDefaultParameters(), - IncludeMedia: true, - ...params - } - ); - - const isLiveTV = () => collectionType === 'livetv'; - useEffect(() => { + const getDefaultParameters = () => ({ + ParentId: parentId, + searchTerm: query, + Limit: 24, + Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount', + Recursive: true, + EnableTotalRecordCount: false, + ImageTypeLimit: 1, + IncludePeople: false, + IncludeMedia: false, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false + }); + + // FIXME: This query does not support Live TV filters + const fetchItems = (apiClient, params = {}) => apiClient?.getItems( + apiClient?.getCurrentUserId(), + { + ...getDefaultParameters(), + IncludeMedia: true, + ...params + } + ); + // Reset state setMovies([]); setEpisodes([]); @@ -67,8 +72,9 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => { setPrograms([]); setChannels([]); - if (query && isLiveTV()) { - const apiClient = ServerConnections.getApiClient(serverId); + if (query && collectionType === 'livetv') { + // TODO: Remove type casting once we're using a properly typed API client + const apiClient = (ServerConnections as any).getApiClient(serverId); // Movies row fetchItems(apiClient, { @@ -128,7 +134,7 @@ const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => { fetchItems(apiClient, { IncludeItemTypes: 'TvChannel' }) .then(result => setChannels(result.Items)); } - }, [ query ]); + }, [collectionType, parentId, query, serverId]); return (
{ 'searchResults', 'padded-bottom-page', 'padded-top', - { 'hide': !query || !isLiveTV() } + { 'hide': !query || !(collectionType === 'livetv') } )} > ({ const normalizeInput = (value = '') => value.trim(); -const SearchFields = ({ onSearch = () => {} }) => { +type SearchFieldsProps = { + onSearch: () => void +}; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const SearchFields: FunctionComponent = ({ onSearch = () => {} }) => { const element = useRef(null); const getSearchInput = () => element?.current?.querySelector('.searchfields-txtSearch'); - const debouncedOnSearch = useMemo(() => debounce(onSearch, 400), []); + const debouncedOnSearch = useMemo(() => debounce(onSearch, 400), [onSearch]); useEffect(() => { getSearchInput()?.addEventListener('input', e => { @@ -47,7 +52,7 @@ const SearchFields = ({ onSearch = () => {} }) => { return () => { debouncedOnSearch.cancel(); }; - }, []); + }, [debouncedOnSearch]); const onAlphaPicked = e => { const value = e.detail.value; diff --git a/src/components/search/SearchResults.js b/src/components/search/SearchResults.tsx similarity index 79% rename from src/components/search/SearchResults.js rename to src/components/search/SearchResults.tsx index 3b6322e201..21a53640ad 100644 --- a/src/components/search/SearchResults.js +++ b/src/components/search/SearchResults.tsx @@ -1,15 +1,22 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; +import React, { FunctionComponent, useEffect, useState } from 'react'; import globalize from '../../scripts/globalize'; import ServerConnections from '../ServerConnections'; import SearchResultsRow from './SearchResultsRow'; +type SearchResultsProps = { + serverId: string; + parentId: string; + collectionType: string; + query: string; +} + /* * React component to display search result rows for global search and non-live tv library search */ -const SearchResults = ({ serverId, parentId, collectionType, query }) => { +const SearchResults: FunctionComponent = ({ serverId, parentId, collectionType, query }) => { const [ movies, setMovies ] = useState([]); const [ shows, setShows ] = useState([]); const [ episodes, setEpisodes ] = useState([]); @@ -26,55 +33,55 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => { const [ books, setBooks ] = useState([]); const [ people, setPeople ] = useState([]); - const getDefaultParameters = () => ({ - ParentId: parentId, - searchTerm: query, - Limit: 24, - Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount', - Recursive: true, - EnableTotalRecordCount: false, - ImageTypeLimit: 1, - IncludePeople: false, - IncludeMedia: false, - IncludeGenres: false, - IncludeStudios: false, - IncludeArtists: false - }); - - const fetchArtists = (apiClient, params = {}) => apiClient?.getArtists( - apiClient?.getCurrentUserId(), - { - ...getDefaultParameters(), - IncludeArtists: true, - ...params - } - ); - - const fetchItems = (apiClient, params = {}) => apiClient?.getItems( - apiClient?.getCurrentUserId(), - { - ...getDefaultParameters(), - IncludeMedia: true, - ...params - } - ); - - const fetchPeople = (apiClient, params = {}) => apiClient?.getPeople( - apiClient?.getCurrentUserId(), - { - ...getDefaultParameters(), - IncludePeople: true, - ...params - } - ); - - const isMovies = () => collectionType === 'movies'; - - const isMusic = () => collectionType === 'music'; - - const isTVShows = () => collectionType === 'tvshows' || collectionType === 'tv'; - useEffect(() => { + const getDefaultParameters = () => ({ + ParentId: parentId, + searchTerm: query, + Limit: 24, + Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount', + Recursive: true, + EnableTotalRecordCount: false, + ImageTypeLimit: 1, + IncludePeople: false, + IncludeMedia: false, + IncludeGenres: false, + IncludeStudios: false, + IncludeArtists: false + }); + + const fetchArtists = (apiClient, params = {}) => apiClient?.getArtists( + apiClient?.getCurrentUserId(), + { + ...getDefaultParameters(), + IncludeArtists: true, + ...params + } + ); + + const fetchItems = (apiClient, params = {}) => apiClient?.getItems( + apiClient?.getCurrentUserId(), + { + ...getDefaultParameters(), + IncludeMedia: true, + ...params + } + ); + + const fetchPeople = (apiClient, params = {}) => apiClient?.getPeople( + apiClient?.getCurrentUserId(), + { + ...getDefaultParameters(), + IncludePeople: true, + ...params + } + ); + + const isMovies = () => collectionType === 'movies'; + + const isMusic = () => collectionType === 'music'; + + const isTVShows = () => collectionType === 'tvshows' || collectionType === 'tv'; + // Reset state setMovies([]); setShows([]); @@ -93,7 +100,8 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => { setPeople([]); if (query) { - const apiClient = ServerConnections.getApiClient(serverId); + // TODO: Remove type casting once we're using a properly typed API client + const apiClient = (ServerConnections as any).getApiClient(serverId); // Movie libraries if (!collectionType || isMovies()) { @@ -160,7 +168,7 @@ const SearchResults = ({ serverId, parentId, collectionType, query }) => { .then(results => setBooks(results.Items)); } } - }, [ query ]); + }, [collectionType, parentId, query, serverId]); return (
({
` }); -const SearchResultsRow = ({ title, items = [], cardOptions = {} }) => { +type SearchResultsRowProps = { + title: string; + items: Array; // TODO: Should be Array once we have a typed API client + cardOptions: Record; +} + +const SearchResultsRow: FunctionComponent = ({ title, items = [], cardOptions = {} }) => { const element = useRef(null); useEffect(() => { @@ -31,7 +37,7 @@ const SearchResultsRow = ({ title, items = [], cardOptions = {} }) => { allowBottomPadding: false, ...cardOptions }); - }, [ items ]); + }, [cardOptions, items]); return (
({ >${name}` }); -const SearchSuggestions = ({ serverId, parentId }) => { +type SearchSuggestionsProps = { + serverId: string; + parentId: string; +} + +const SearchSuggestions: FunctionComponent = ({ serverId, parentId }) => { const [ suggestions, setSuggestions ] = useState([]); useEffect(() => { - const apiClient = ServerConnections.getApiClient(serverId); + // TODO: Remove type casting once we're using a properly typed API client + const apiClient = (ServerConnections as any).getApiClient(serverId); apiClient.getItems(apiClient.getCurrentUserId(), { SortBy: 'IsFavoriteOrLiked,Random', @@ -35,7 +41,7 @@ const SearchSuggestions = ({ serverId, parentId }) => { ParentId: parentId, EnableTotalRecordCount: false }).then(result => setSuggestions(result.Items)); - }, []); + }, [parentId, serverId]); return (