diff --git a/package-lock.json b/package-lock.json index 14b0f2e61..211ba679e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@babel/runtime": "7.16.0", "@sentry/browser": "6.13.3", "@stremio/stremio-colors": "4.0.1", - "@stremio/stremio-core-web": "0.43.0", + "@stremio/stremio-core-web": "0.44.0", "@stremio/stremio-icons": "3.0.5", "@stremio/stremio-video": "0.0.20-rc.4", "a-color-picker": "1.2.1", @@ -22,13 +22,16 @@ "eventemitter3": "4.0.7", "filter-invalid-dom-props": "2.1.0", "lodash.debounce": "4.0.8", + "lodash.intersection": "4.4.0", "lodash.isequal": "4.5.0", "lodash.throttle": "4.1.1", "prop-types": "15.7.2", - "react": "16.12.0", - "react-dom": "16.12.0", - "react-focus-lock": "2.2.1", - "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6" + "react": "18.2.0", + "react-dom": "18.2.0", + "react-focus-lock": "2.9.1", + "react-is": "18.2.0", + "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "url": "0.11.0" }, "devDependencies": { "@babel/core": "7.16.0", @@ -283,12 +286,12 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -326,9 +329,9 @@ } }, "node_modules/@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==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz", + "integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==", "dev": true, "engines": { "node": ">=6.9.0" @@ -400,9 +403,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1722,12 +1725,12 @@ } }, "node_modules/@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", + "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2613,9 +2616,9 @@ "integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw==" }, "node_modules/@stremio/stremio-core-web": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.43.0.tgz", - "integrity": "sha512-EXr2PQpviUS4kF7gRUnTFDshsDh4gh/pp/ACaIgqXe6qZS4YlmPhLtXDxDKdBnulhjABW29GVul/vuuiEtff3w==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.0.tgz", + "integrity": "sha512-zTUS6J1RAj6OiFnSD9gTfP8Va4MsgAz9QAynP1T1rO3nVJLCy8PolJf0qZWl/KERoeWuL1IE3X8Zjk+VPMkkeA==", "dependencies": { "@babel/runtime": "7.15.4" } @@ -3970,26 +3973,31 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", - "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", + "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001271", - "electron-to-chromium": "^1.3.878", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001359", + "electron-to-chromium": "^1.4.172", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.4" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/bser": { @@ -4105,14 +4113,20 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001274", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", - "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "version": "1.0.30001363", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", + "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/chalk": { "version": "2.4.2", @@ -4600,12 +4614,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz", - "integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==", + "version": "3.23.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.3.tgz", + "integrity": "sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw==", "dev": true, "dependencies": { - "browserslist": "^4.17.5", + "browserslist": "^4.21.0", "semver": "7.0.0" }, "funding": { @@ -5259,9 +5273,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.885", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", - "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", + "version": "1.4.180", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.180.tgz", + "integrity": "sha512-7at5ash3FD9U5gPa3/wPr6OdiZd/zBjvDZaaHBpcqFOFUhZiWnb7stkqk8xUFL9H9nk7Yok5vCCNK8wyC/+f8A==", "dev": true }, "node_modules/emittery": { @@ -6334,9 +6348,20 @@ "dev": true }, "node_modules/focus-lock": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.8.tgz", - "integrity": "sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og==" + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.2.tgz", + "integrity": "sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==", + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/focus-lock/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/follow-redirects": { "version": "1.14.8", @@ -9595,6 +9620,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "node_modules/lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -10032,9 +10062,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "node_modules/normalize-path": { @@ -11293,6 +11323,11 @@ "react-is": "^16.8.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -11417,63 +11452,65 @@ } }, "node_modules/react": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", - "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "loose-envify": "^1.1.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-clientside-effect": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz", - "integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", + "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==", "dependencies": { "@babel/runtime": "^7.12.13" }, "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0" + "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-dom": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", - "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^16.0.0" + "react": "^18.2.0" } }, "node_modules/react-focus-lock": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz", - "integrity": "sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.1.tgz", + "integrity": "sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==", "dependencies": { "@babel/runtime": "^7.0.0", - "focus-lock": "^0.6.6", + "focus-lock": "^0.11.2", "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.2", - "use-callback-ref": "^1.2.1", - "use-sidecar": "^1.0.1" + "react-clientside-effect": "^1.2.6", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" }, "peerDependencies": { - "react": "^16.8.0" + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/readable-stream": { "version": "3.6.0", @@ -11801,12 +11838,11 @@ } }, "node_modules/scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "node_modules/schema-utils": { @@ -12896,6 +12932,32 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", + "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -12924,15 +12986,18 @@ } }, "node_modules/use-callback-ref": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", - "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", + "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "dependencies": { + "tslib": "^2.0.0" + }, "engines": { - "node": ">=8.5.0" + "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0", - "react": "^16.8.0 || ^17.0.0" + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -12940,21 +13005,37 @@ } } }, + "node_modules/use-callback-ref/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/use-sidecar": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz", - "integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", "dependencies": { "detect-node-es": "^1.1.0", - "tslib": "^1.9.3" + "tslib": "^2.0.0" }, "engines": { - "node": ">=8.5.0" + "node": ">=10" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, + "node_modules/use-sidecar/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -14023,12 +14104,12 @@ } }, "@babel/helper-module-imports": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", - "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "requires": { - "@babel/types": "^7.16.0" + "@babel/types": "^7.18.6" } }, "@babel/helper-module-transforms": { @@ -14057,9 +14138,9 @@ } }, "@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==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz", + "integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -14113,9 +14194,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true }, "@babel/helper-validator-option": { @@ -15003,12 +15084,12 @@ } }, "@babel/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", - "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "version": "7.18.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz", + "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, @@ -15684,9 +15765,9 @@ "integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw==" }, "@stremio/stremio-core-web": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.43.0.tgz", - "integrity": "sha512-EXr2PQpviUS4kF7gRUnTFDshsDh4gh/pp/ACaIgqXe6qZS4YlmPhLtXDxDKdBnulhjABW29GVul/vuuiEtff3w==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.0.tgz", + "integrity": "sha512-zTUS6J1RAj6OiFnSD9gTfP8Va4MsgAz9QAynP1T1rO3nVJLCy8PolJf0qZWl/KERoeWuL1IE3X8Zjk+VPMkkeA==", "requires": { "@babel/runtime": "7.15.4" }, @@ -16829,16 +16910,15 @@ "dev": true }, "browserslist": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", - "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz", + "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001271", - "electron-to-chromium": "^1.3.878", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001359", + "electron-to-chromium": "^1.4.172", + "node-releases": "^2.0.5", + "update-browserslist-db": "^1.0.4" } }, "bser": { @@ -16930,9 +17010,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001274", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", - "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "version": "1.0.30001363", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", + "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==", "dev": true }, "chalk": { @@ -17312,12 +17392,12 @@ } }, "core-js-compat": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz", - "integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==", + "version": "3.23.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.3.tgz", + "integrity": "sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw==", "dev": true, "requires": { - "browserslist": "^4.17.5", + "browserslist": "^4.21.0", "semver": "7.0.0" }, "dependencies": { @@ -17820,9 +17900,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.885", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz", - "integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==", + "version": "1.4.180", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.180.tgz", + "integrity": "sha512-7at5ash3FD9U5gPa3/wPr6OdiZd/zBjvDZaaHBpcqFOFUhZiWnb7stkqk8xUFL9H9nk7Yok5vCCNK8wyC/+f8A==", "dev": true }, "emittery": { @@ -18646,9 +18726,19 @@ "dev": true }, "focus-lock": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.8.tgz", - "integrity": "sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og==" + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.2.tgz", + "integrity": "sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==", + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } }, "follow-redirects": { "version": "1.14.8", @@ -21030,6 +21120,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -21372,9 +21467,9 @@ "dev": true }, "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "normalize-path": { @@ -22249,6 +22344,13 @@ "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } } }, "proxy-addr": { @@ -22340,51 +22442,47 @@ } }, "react": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", - "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "loose-envify": "^1.1.0" } }, "react-clientside-effect": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz", - "integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", + "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==", "requires": { "@babel/runtime": "^7.12.13" } }, "react-dom": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", - "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" + "scheduler": "^0.23.0" } }, "react-focus-lock": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz", - "integrity": "sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.1.tgz", + "integrity": "sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==", "requires": { "@babel/runtime": "^7.0.0", - "focus-lock": "^0.6.6", + "focus-lock": "^0.11.2", "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.2", - "use-callback-ref": "^1.2.1", - "use-sidecar": "^1.0.1" + "react-clientside-effect": "^1.2.6", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" } }, "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "readable-stream": { "version": "3.6.0", @@ -22632,12 +22730,11 @@ } }, "scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "schema-utils": { @@ -23497,6 +23594,16 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "update-browserslist-db": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", + "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -23524,18 +23631,34 @@ } }, "use-callback-ref": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", - "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", - "requires": {} + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", + "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } }, "use-sidecar": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz", - "integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", "requires": { "detect-node-es": "^1.1.0", - "tslib": "^1.9.3" + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, "util-deprecate": { diff --git a/package.json b/package.json index 73a234313..ab8b42ff1 100755 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@babel/runtime": "7.16.0", "@sentry/browser": "6.13.3", "@stremio/stremio-colors": "4.0.1", - "@stremio/stremio-core-web": "0.43.0", + "@stremio/stremio-core-web": "0.44.0", "@stremio/stremio-icons": "3.0.5", "@stremio/stremio-video": "0.0.20-rc.4", "a-color-picker": "1.2.1", @@ -25,13 +25,16 @@ "eventemitter3": "4.0.7", "filter-invalid-dom-props": "2.1.0", "lodash.debounce": "4.0.8", + "lodash.intersection": "4.4.0", "lodash.isequal": "4.5.0", "lodash.throttle": "4.1.1", "prop-types": "15.7.2", - "react": "16.12.0", - "react-dom": "16.12.0", - "react-focus-lock": "2.2.1", - "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6" + "react": "18.2.0", + "react-dom": "18.2.0", + "react-is": "18.2.0", + "react-focus-lock": "2.9.1", + "spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", + "url": "0.11.0" }, "devDependencies": { "@babel/core": "7.16.0", diff --git a/src/App/App.js b/src/App/App.js index a07435d2b..1c39635a2 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -5,24 +5,21 @@ const React = require('react'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); -const { ToastProvider, sanitizeLocationPath, CONSTANTS } = require('stremio/common'); +const { ToastProvider, CONSTANTS } = require('stremio/common'); const CoreEventsToaster = require('./CoreEventsToaster'); const ErrorDialog = require('./ErrorDialog'); const routerViewsConfig = require('./routerViewsConfig'); const styles = require('./styles'); -window.core_imports = { - app_version: process.env.VERSION, - shell_version: null, - sanitize_location_path: sanitizeLocationPath -}; - const App = () => { const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); const services = React.useMemo(() => ({ - core: new Core(), + core: new Core({ + appVersion: process.env.VERSION, + shellVersion: null + }), shell: new Shell(), chromecast: new Chromecast(), keyboardShortcuts: new KeyboardShortcuts() @@ -93,6 +90,12 @@ const App = () => { action: 'PullAddonsFromAPI' } }); + services.core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'PullUserFromAPI' + } + }); } }, [initialized]); return ( diff --git a/src/App/styles.less b/src/App/styles.less index e9a01575a..1d24672ab 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -5,6 +5,7 @@ @import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; :global { + @import (once, less) '~stremio/common/animations.less'; @import (once, less) '~stremio-router/styles.css'; } diff --git a/src/common/AddonDetailsModal/AddonDetailsModal.js b/src/common/AddonDetailsModal/AddonDetailsModal.js index 25a4acd54..2c417a142 100644 --- a/src/common/AddonDetailsModal/AddonDetailsModal.js +++ b/src/common/AddonDetailsModal/AddonDetailsModal.js @@ -3,6 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const ModalDialog = require('stremio/common/ModalDialog'); +const { withCoreSuspender } = require('stremio/common/CoreSuspender'); const { useServices } = require('stremio/services'); const AddonDetailsWithRemoteAndLocalAddon = withRemoteAndLocalAddon(require('./AddonDetails')); const useAddonDetails = require('./useAddonDetails'); @@ -144,4 +145,18 @@ AddonDetailsModal.propTypes = { onCloseRequest: PropTypes.func }; -module.exports = AddonDetailsModal; +const AddonDetailsModalFallback = ({ onCloseRequest }) => ( + +
+ Loading addon manifest +
+
+); + +AddonDetailsModalFallback.propTypes = AddonDetailsModal.propTypes; + +module.exports = withCoreSuspender(AddonDetailsModal, AddonDetailsModalFallback); diff --git a/src/common/AddonDetailsModal/useAddonDetails.js b/src/common/AddonDetailsModal/useAddonDetails.js index b0bf251d3..e13ac1007 100644 --- a/src/common/AddonDetailsModal/useAddonDetails.js +++ b/src/common/AddonDetailsModal/useAddonDetails.js @@ -3,12 +3,6 @@ const React = require('react'); const useModelState = require('stremio/common/useModelState'); -const init = () => ({ - selected: null, - localAddon: null, - remoteAddon: null -}); - const useAddonDetails = (transportUrl) => { const action = React.useMemo(() => { if (typeof transportUrl === 'string') { @@ -27,7 +21,7 @@ const useAddonDetails = (transportUrl) => { }; } }, [transportUrl]); - return useModelState({ model: 'addon_details', action, init }); + return useModelState({ model: 'addon_details', action }); }; module.exports = useAddonDetails; diff --git a/src/common/CoreSuspender.js b/src/common/CoreSuspender.js new file mode 100644 index 000000000..94ec7475f --- /dev/null +++ b/src/common/CoreSuspender.js @@ -0,0 +1,78 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const React = require('react'); +const { useServices } = require('stremio/services'); + +const CoreSuspenderContext = React.createContext(null); + +CoreSuspenderContext.displayName = 'CoreSuspenderContext'; + +function wrapPromise(promise) { + let status = 'pending'; + let result; + const suspender = promise.then( + (resp) => { + status = 'success'; + result = resp; + }, + (error) => { + status = 'error'; + result = error; + } + ); + return { + read() { + if (status === 'pending') { + throw suspender; + } else if (status === 'error') { + throw result; + } else if (status === 'success') { + return result; + } + } + }; +} + +const useCoreSuspender = () => { + return React.useContext(CoreSuspenderContext); +}; + +const withCoreSuspender = (Component, Fallback = () => { }) => { + return function withCoreSuspender(props) { + const { core } = useServices(); + const parentSuspender = useCoreSuspender(); + const [render, setRender] = React.useState(parentSuspender === null); + const statesRef = React.useRef({}); + const streamsRef = React.useRef({}); + const getState = React.useCallback((model) => { + if (!statesRef.current[model]) { + statesRef.current[model] = wrapPromise(core.transport.getState(model)); + } + + return statesRef.current[model].read(); + }, []); + const decodeStream = React.useCallback((stream) => { + if (!streamsRef.current[stream]) { + streamsRef.current[stream] = wrapPromise(core.transport.decodeStream(stream)); + } + + return streamsRef.current[stream].read(); + }, []); + const suspender = React.useMemo(() => ({ getState, decodeStream }), []); + React.useLayoutEffect(() => { + if (!render) { + setRender(true); + } + }, []); + return render ? + }> + + + + + : + null; + }; +}; + +module.exports = { withCoreSuspender, useCoreSuspender }; diff --git a/src/common/DelayedRenderer/DelayedRenderer.js b/src/common/DelayedRenderer/DelayedRenderer.js new file mode 100644 index 000000000..eb4bea81f --- /dev/null +++ b/src/common/DelayedRenderer/DelayedRenderer.js @@ -0,0 +1,23 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const React = require('react'); +const PropTypes = require('prop-types'); + +const DelayedRenderer = ({ children, delay }) => { + const [render, setRender] = React.useState(false); + React.useEffect(() => { + const timeout = setTimeout(() => { + setRender(true); + }, delay); + return () => { + clearTimeout(timeout); + }; + }, []); + return render ? children : null; +}; + +DelayedRenderer.propTypes = { + children: PropTypes.node +}; + +module.exports = DelayedRenderer; diff --git a/src/common/DelayedRenderer/index.js b/src/common/DelayedRenderer/index.js new file mode 100644 index 000000000..97d3811dd --- /dev/null +++ b/src/common/DelayedRenderer/index.js @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +const DelayedRenderer = require('./DelayedRenderer'); + +module.exports = DelayedRenderer; diff --git a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js index 3a7b3a73c..7e511ee0d 100644 --- a/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js +++ b/src/common/NavBar/HorizontalNavBar/NavMenu/NavMenu.js @@ -3,22 +3,14 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const Icon = require('@stremio/stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); -const { useServices } = require('stremio/services'); -const Button = require('stremio/common/Button'); const Popup = require('stremio/common/Popup'); const useBinaryState = require('stremio/common/useBinaryState'); -const useFullscreen = require('stremio/common/useFullscreen'); -const useProfile = require('stremio/common/useProfile'); -const styles = require('./styles'); +const NavMenuContent = require('./NavMenuContent'); const NavMenu = (props) => { - const { core } = useServices(); const routeFocused = useRouteFocused(); - const profile = useProfile(); const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); - const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen(); const popupLabelOnClick = React.useCallback((event) => { if (!event.nativeEvent.togglePopupPrevented) { toggleMenu(); @@ -27,14 +19,6 @@ const NavMenu = (props) => { const popupMenuOnClick = React.useCallback((event) => { event.nativeEvent.togglePopupPrevented = true; }, []); - const logoutButtonOnClick = React.useCallback(() => { - core.transport.dispatch({ - action: 'Ctx', - args: { - action: 'Logout' - } - }); - }, []); const renderLabel = React.useMemo(() => ({ ref, className, children }) => ( props.renderLabel({ ref, @@ -44,65 +28,8 @@ const NavMenu = (props) => { }) ), [menuOpen, popupLabelOnClick, props.renderLabel]); const renderMenu = React.useCallback(() => ( -
-
-
-
-
{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}
-
- -
-
- -
-
- - - - - -
-
- - - -
-
- ), [profile, fullscreen]); + + ), []); React.useEffect(() => { if (!routeFocused) { closeMenu(); @@ -111,6 +38,7 @@ const NavMenu = (props) => { return ( { + const { core } = useServices(); + const profile = useProfile(); + const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen(); + const logoutButtonOnClick = React.useCallback(() => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'Logout' + } + }); + }, []); + return ( +
+
+
+
+
{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}
+
+ +
+
+ +
+
+ + + + + +
+
+ + + +
+
+ ); +}; + +NavMenuContent.propTypes = { + onClick: PropTypes.func +}; + +const NavMenuContentFallback = () => ( +
+); + +module.exports = withCoreSuspender(NavMenuContent, NavMenuContentFallback); diff --git a/src/common/StreamingServerWarning/StreamingServerWarning.js b/src/common/StreamingServerWarning/StreamingServerWarning.js index 3ffff03c4..b0775b2e6 100644 --- a/src/common/StreamingServerWarning/StreamingServerWarning.js +++ b/src/common/StreamingServerWarning/StreamingServerWarning.js @@ -6,6 +6,7 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const Button = require('stremio/common/Button'); const useProfile = require('stremio/common/useProfile'); +const { withCoreSuspender } = require('stremio/common/CoreSuspender'); const styles = require('./styles'); const StreamingServerWarning = ({ className }) => { @@ -39,6 +40,12 @@ const StreamingServerWarning = ({ className }) => { } }); }, [profile.settings]); + + if (!isNaN(profile.settings.streamingServerWarningDismissed.getTime()) && + profile.settings.streamingServerWarningDismissed.getTime() > Date.now()) { + return null; + } + return (
Streaming server is not available.
@@ -56,4 +63,4 @@ StreamingServerWarning.propTypes = { className: PropTypes.string }; -module.exports = StreamingServerWarning; +module.exports = withCoreSuspender(StreamingServerWarning); diff --git a/src/common/animations.less b/src/common/animations.less new file mode 100644 index 000000000..9173a625b --- /dev/null +++ b/src/common/animations.less @@ -0,0 +1,22 @@ +// Copyright (C) 2017-2022 Smart code 203358507 + +:global(.animation-fade-in) { + :local { + animation-name: fade-in; + } + + animation-timing-function: ease-in-out; + animation-duration: 100ms; +} + +@keyframes fade-in { + 0% { + opacity: 0.6; + transform: translateY(0.2vh); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/src/common/index.js b/src/common/index.js index 1ee08108c..54fac3a83 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -4,6 +4,7 @@ const AddonDetailsModal = require('./AddonDetailsModal'); const Button = require('./Button'); const Checkbox = require('./Checkbox'); const ColorInput = require('./ColorInput'); +const DelayedRenderer = require('./DelayedRenderer'); const Image = require('./Image'); const LibItem = require('./LibItem'); const MainNavBars = require('./MainNavBars'); @@ -24,15 +25,13 @@ const TextInput = require('./TextInput'); const { ToastProvider, useToast } = require('./Toast'); const comparatorWithPriorities = require('./comparatorWithPriorities'); const CONSTANTS = require('./CONSTANTS'); +const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender'); const getVisibleChildrenRange = require('./getVisibleChildrenRange'); const languageNames = require('./languageNames'); const routesRegexp = require('./routesRegexp'); -const sanitizeLocationPath = require('./sanitizeLocationPath'); const useAnimationFrame = require('./useAnimationFrame'); const useBinaryState = require('./useBinaryState'); -const useDeepEqualEffect = require('./useDeepEqualEffect'); const useDeepEqualMemo = require('./useDeepEqualMemo'); -const useDeepEqualState = require('./useDeepEqualState'); const useFullscreen = require('./useFullscreen'); const useLiveRef = require('./useLiveRef'); const useModelState = require('./useModelState'); @@ -45,6 +44,7 @@ module.exports = { Button, Checkbox, ColorInput, + DelayedRenderer, Image, LibItem, MainNavBars, @@ -67,15 +67,14 @@ module.exports = { useToast, comparatorWithPriorities, CONSTANTS, + withCoreSuspender, + useCoreSuspender, getVisibleChildrenRange, languageNames, routesRegexp, - sanitizeLocationPath, useAnimationFrame, useBinaryState, - useDeepEqualEffect, useDeepEqualMemo, - useDeepEqualState, useFullscreen, useLiveRef, useModelState, diff --git a/src/common/routesRegexp.js b/src/common/routesRegexp.js index d15bc7d46..21ad98662 100644 --- a/src/common/routesRegexp.js +++ b/src/common/routesRegexp.js @@ -26,7 +26,7 @@ const routesRegexp = { urlParamsNames: [] }, metadetails: { - regexp: /^\/metadetails\/([^/]*)\/([^/]*)(?:\/([^/]*))?$/, + regexp: /^\/(?:metadetails|detail)\/([^/]*)\/([^/]*)(?:\/([^/]*))?$/, urlParamsNames: ['type', 'id', 'videoId'] }, addons: { diff --git a/src/common/sanitizeLocationPath.js b/src/common/sanitizeLocationPath.js deleted file mode 100644 index e45f1e15b..000000000 --- a/src/common/sanitizeLocationPath.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2017-2022 Smart code 203358507 - -const UrlUtils = require('url'); -const routesRegexp = require('stremio/common/routesRegexp'); - -const sanitizeLocationPath = (path) => { - const { href, pathname, search } = UrlUtils.parse(path); - if (typeof pathname === 'string') { - const matches = pathname.match(routesRegexp.player.regexp); - if (matches) { - if (typeof matches[2] === 'string') { - return `/player/***/***/${matches[3]}/${matches[4]}/${matches[5]}/${matches[6]}${typeof search === 'string' ? search : ''}`; - } else { - return `/player/***${typeof search === 'string' ? search : ''}`; - } - } - } - - return href; -}; - -module.exports = sanitizeLocationPath; diff --git a/src/common/useDeepEqualEffect.js b/src/common/useDeepEqualEffect.js deleted file mode 100644 index 095576356..000000000 --- a/src/common/useDeepEqualEffect.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2017-2022 Smart code 203358507 - -const React = require('react'); -const useDeepEqualMemo = require('stremio/common/useDeepEqualMemo'); - -const useDeepEqualEffect = (cb, deps) => { - React.useEffect(cb, [useDeepEqualMemo(() => ({}), deps)]); -}; - -module.exports = useDeepEqualEffect; diff --git a/src/common/useDeepEqualState.js b/src/common/useDeepEqualState.js deleted file mode 100644 index edd762b46..000000000 --- a/src/common/useDeepEqualState.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2017-2022 Smart code 203358507 - -const React = require('react'); -const isEqual = require('lodash.isequal'); - -const useDeepEqualState = (initialState) => { - return React.useReducer( - (prevState, nextState) => { - return isEqual(prevState, nextState) ? - prevState - : - nextState; - }, - undefined, - () => { - return typeof initialState === 'function' ? - initialState() - : - initialState; - } - ); -}; - -module.exports = useDeepEqualState; diff --git a/src/common/useModelState.js b/src/common/useModelState.js index 75e2ab634..a2a630237 100644 --- a/src/common/useModelState.js +++ b/src/common/useModelState.js @@ -3,16 +3,19 @@ const React = require('react'); const throttle = require('lodash.throttle'); const isEqual = require('lodash.isequal'); +const intersection = require('lodash.intersection'); +const { useCoreSuspender } = require('stremio/common/CoreSuspender'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); -const useModelState = ({ init, action, ...args }) => { +const useModelState = ({ action, ...args }) => { const { core } = useServices(); const routeFocused = useRouteFocused(); const mountedRef = React.useRef(false); - const [model, timeout, map] = React.useMemo(() => { - return [args.model, args.timeout, args.map]; + const [model, timeout, map, deps] = React.useMemo(() => { + return [args.model, args.timeout, args.map, args.deps]; }, []); + const { getState } = useCoreSuspender(); const [state, setState] = React.useReducer( (prevState, nextState) => { return Object.keys(prevState).reduce((result, key) => { @@ -22,35 +25,41 @@ const useModelState = ({ init, action, ...args }) => { }, undefined, () => { - return typeof init === 'function' ? - init() - : - init; + if (typeof map === 'function') { + return map(getState(model)); + } else { + return getState(model); + } } ); - React.useLayoutEffect(() => { + React.useInsertionEffect(() => { if (action) { core.transport.dispatch(action, model); } }, [action]); - React.useLayoutEffect(() => { + React.useInsertionEffect(() => { return () => { core.transport.dispatch({ action: 'Unload' }, model); }; }, []); - React.useLayoutEffect(() => { - const onNewStateThrottled = throttle(() => { - const state = core.transport.getState(model); + React.useInsertionEffect(() => { + const onNewState = async (models) => { + if (models.indexOf(model) === -1 && (!Array.isArray(deps) || intersection(deps, models).length === 0)) { + return; + } + + const state = await core.transport.getState(model); if (typeof map === 'function') { setState(map(state)); } else { setState(state); } - }, timeout); + }; + const onNewStateThrottled = throttle(onNewState, timeout); if (routeFocused) { core.transport.on('NewState', onNewStateThrottled); if (mountedRef.current) { - onNewStateThrottled.call(); + onNewState([model]); } } return () => { @@ -58,7 +67,7 @@ const useModelState = ({ init, action, ...args }) => { core.transport.off('NewState', onNewStateThrottled); }; }, [routeFocused]); - React.useLayoutEffect(() => { + React.useInsertionEffect(() => { mountedRef.current = true; }, []); return state; diff --git a/src/common/useProfile.js b/src/common/useProfile.js index cbd60b36c..371a4df12 100644 --- a/src/common/useProfile.js +++ b/src/common/useProfile.js @@ -1,7 +1,5 @@ // Copyright (C) 2017-2022 Smart code 203358507 -const React = require('react'); -const { useServices } = require('stremio/services'); const useModelState = require('stremio/common/useModelState'); const map = (ctx) => ({ @@ -18,12 +16,7 @@ const map = (ctx) => ({ }); const useProfile = () => { - const { core } = useServices(); - const init = React.useCallback(() => { - const ctx = core.transport.getState('ctx'); - return map(ctx); - }, []); - return useModelState({ model: 'ctx', init, map }); + return useModelState({ model: 'ctx', map }); }; module.exports = useProfile; diff --git a/src/common/useStreamingServer.js b/src/common/useStreamingServer.js index a8cf71596..00dd0cee1 100644 --- a/src/common/useStreamingServer.js +++ b/src/common/useStreamingServer.js @@ -1,15 +1,9 @@ // Copyright (C) 2017-2022 Smart code 203358507 -const React = require('react'); -const { useServices } = require('stremio/services'); const useModelState = require('stremio/common/useModelState'); const useStreamingServer = () => { - const { core } = useServices(); - const init = React.useCallback(() => { - return core.transport.getState('streaming_server'); - }, []); - return useModelState({ model: 'streaming_server', init }); + return useModelState({ model: 'streaming_server' }); }; module.exports = useStreamingServer; diff --git a/src/index.js b/src/index.js index c7af67f8b..d716b0b2e 100755 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,8 @@ if (browser?.platform?.type === 'desktop') { } const React = require('react'); -const ReactDOM = require('react-dom'); +const ReactDOM = require('react-dom/client'); const App = require('./App'); -ReactDOM.render(, document.getElementById('app')); +const root = ReactDOM.createRoot(document.getElementById('app')); +root.render(); diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 1a889ae50..aa513daaf 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -2,8 +2,9 @@ const React = require('react'); const PropTypes = require('prop-types'); +const classnames = require('classnames'); const Icon = require('@stremio/stremio-icons/dom'); -const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState } = require('stremio/common'); +const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState, withCoreSuspender } = require('stremio/common'); const Addon = require('./Addon'); const useInstalledAddons = require('./useInstalledAddons'); const useRemoteAddons = require('./useRemoteAddons'); @@ -118,7 +119,7 @@ const Addons = ({ urlParams, queryParams }) => { .map((addon, index) => ( { .map((addon, index) => ( ( + +); + +module.exports = withCoreSuspender(Addons, AddonsFallback); diff --git a/src/routes/Addons/useInstalledAddons.js b/src/routes/Addons/useInstalledAddons.js index 8be193982..fc649c772 100644 --- a/src/routes/Addons/useInstalledAddons.js +++ b/src/routes/Addons/useInstalledAddons.js @@ -1,14 +1,9 @@ // Copyright (C) 2017-2022 Smart code 203358507 const React = require('react'); -const { useServices } = require('stremio/services'); const { useModelState } = require('stremio/common'); const useInstalledAddons = (urlParams) => { - const { core } = useServices(); - const init = React.useMemo(() => { - return core.transport.getState('installed_addons'); - }, []); const action = React.useMemo(() => { if (typeof urlParams.transportUrl !== 'string' && typeof urlParams.catalogId !== 'string') { return { @@ -28,7 +23,7 @@ const useInstalledAddons = (urlParams) => { }; } }, [urlParams]); - return useModelState({ model: 'installed_addons', action, init }); + return useModelState({ model: 'installed_addons', action }); }; module.exports = useInstalledAddons; diff --git a/src/routes/Addons/useRemoteAddons.js b/src/routes/Addons/useRemoteAddons.js index f6bc023d1..b3f74516e 100644 --- a/src/routes/Addons/useRemoteAddons.js +++ b/src/routes/Addons/useRemoteAddons.js @@ -3,15 +3,6 @@ const React = require('react'); const { useModelState } = require('stremio/common'); -const init = () => ({ - selected: null, - selectable: { - catalogs: [], - types: [] - }, - catalog: null, -}); - const useRemoteAddons = (urlParams) => { const action = React.useMemo(() => { if (typeof urlParams.type === 'string' && typeof urlParams.transportUrl === 'string' && typeof urlParams.catalogId === 'string') { @@ -38,7 +29,7 @@ const useRemoteAddons = (urlParams) => { }; } }, [urlParams]); - return useModelState({ model: 'remote_addons', action, init }); + return useModelState({ model: 'remote_addons', action, deps: ['ctx'] }); }; module.exports = useRemoteAddons; diff --git a/src/routes/Board/Board.js b/src/routes/Board/Board.js index 0aca6964b..d01666448 100644 --- a/src/routes/Board/Board.js +++ b/src/routes/Board/Board.js @@ -3,7 +3,7 @@ const React = require('react'); const classnames = require('classnames'); const debounce = require('lodash.debounce'); -const { MainNavBars, MetaRow, LibItem, MetaItem, StreamingServerWarning, useProfile, useStreamingServer, getVisibleChildrenRange } = require('stremio/common'); +const { MainNavBars, MetaRow, LibItem, MetaItem, StreamingServerWarning, useStreamingServer, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useBoard = require('./useBoard'); const useContinueWatchingPreview = require('./useContinueWatchingPreview'); const styles = require('./styles'); @@ -11,7 +11,6 @@ const styles = require('./styles'); const THRESHOLD = 5; const Board = () => { - const profile = useProfile(); const streamingServer = useStreamingServer(); const continueWatchingPreview = useContinueWatchingPreview(); const [board, loadBoardRows] = useBoard(); @@ -42,7 +41,7 @@ const Board = () => { { continueWatchingPreview.libraryItems.length > 0 ? { return ( { return ( { return ( @@ -91,8 +90,7 @@ const Board = () => {
{ - streamingServer.settings !== null && streamingServer.settings.type === 'Err' && - (isNaN(profile.settings.streamingServerWarningDismissed.getTime()) || profile.settings.streamingServerWarningDismissed.getTime() < Date.now()) ? + streamingServer.settings !== null && streamingServer.settings.type === 'Err' ? : null @@ -101,4 +99,10 @@ const Board = () => { ); }; -module.exports = Board; +const BoardFallback = () => ( +
+ +
+); + +module.exports = withCoreSuspender(Board, BoardFallback); diff --git a/src/routes/Board/useBoard.js b/src/routes/Board/useBoard.js index 1233b9a10..6e3540ab6 100644 --- a/src/routes/Board/useBoard.js +++ b/src/routes/Board/useBoard.js @@ -4,11 +4,6 @@ const React = require('react'); const { useServices } = require('stremio/services'); const { useModelState } = require('stremio/common'); -const init = () => ({ - selected: null, - catalogs: [] -}); - const useBoard = () => { const { core } = useServices(); const action = React.useMemo(() => ({ @@ -27,7 +22,7 @@ const useBoard = () => { } }, 'board'); }, []); - const board = useModelState({ model: 'board', timeout: 1500, action, init }); + const board = useModelState({ model: 'board', timeout: 1500, action }); return [board, loadRange]; }; diff --git a/src/routes/Board/useContinueWatchingPreview.js b/src/routes/Board/useContinueWatchingPreview.js index a505ef737..f7f623587 100644 --- a/src/routes/Board/useContinueWatchingPreview.js +++ b/src/routes/Board/useContinueWatchingPreview.js @@ -1,15 +1,9 @@ // Copyright (C) 2017-2022 Smart code 203358507 -const React = require('react'); -const { useServices } = require('stremio/services'); const { useModelState } = require('stremio/common'); const useContinueWatchingPreview = () => { - const { core } = useServices(); - const init = React.useMemo(() => { - return core.transport.getState('continue_watching_preview'); - }, []); - return useModelState({ model: 'continue_watching_preview', init }); + return useModelState({ model: 'continue_watching_preview' }); }; module.exports = useContinueWatchingPreview; diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index d2e2ce462..688eb072a 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -5,7 +5,7 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const Icon = require('@stremio/stremio-icons/dom'); const { useServices } = require('stremio/services'); -const { AddonDetailsModal, Button, MainNavBars, MetaItem, Image, MetaPreview, Multiselect, ModalDialog, CONSTANTS, useBinaryState, useOnScrollToBottom } = require('stremio/common'); +const { AddonDetailsModal, DelayedRenderer, Button, MainNavBars, MetaItem, Image, MetaPreview, Multiselect, ModalDialog, CONSTANTS, useBinaryState, useOnScrollToBottom, withCoreSuspender } = require('stremio/common'); const useDiscover = require('./useDiscover'); const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); @@ -75,37 +75,26 @@ const Discover = ({ urlParams, queryParams }) => { closeAddonModal(); setSelectedMetaItemIndex(0); }, [discover.selected]); - const metaItemsContainerRef = React.useRef(); - React.useEffect(() => { - if (discover.catalog?.content.type === 'Loading') { - metaItemsContainerRef.current.scrollTop = 0; - } - }, [discover.catalog]); return (
- { - discover.defaultRequest ? -
- {selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => ( - - ))} - -
- : - null - } +
+ {selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => ( + + ))} + +
{ discover.catalog !== null && !discover.catalog.installed ?
@@ -119,10 +108,12 @@ const Discover = ({ urlParams, queryParams }) => { } { discover.catalog === null ? -
- {' -
No catalog selected!
-
+ +
+ {' +
No catalog selected!
+
+
: discover.catalog.content.type === 'Err' ?
@@ -131,7 +122,7 @@ const Discover = ({ urlParams, queryParams }) => {
: discover.catalog.content.type === 'Loading' ? -
+
{Array(CONSTANTS.CATALOG_PAGE_SIZE).fill(null).map((_, index) => (
@@ -142,7 +133,7 @@ const Discover = ({ urlParams, queryParams }) => { ))}
: -
+
{discover.catalog.content.content.map((metaItem, index) => ( { }
{ - inputsModalOpen && discover.defaultRequest ? + inputsModalOpen ? {selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => ( ( + +); + +module.exports = withCoreSuspender(Discover, DiscoverFallback); diff --git a/src/routes/Discover/useDiscover.js b/src/routes/Discover/useDiscover.js index 895b98910..c1555646c 100644 --- a/src/routes/Discover/useDiscover.js +++ b/src/routes/Discover/useDiscover.js @@ -5,18 +5,6 @@ const UrlUtils = require('url'); const { useServices } = require('stremio/services'); const { useModelState } = require('stremio/common'); -const init = () => ({ - selected: null, - selectable: { - types: [], - catalogs: [], - extra: [], - nextPage: false - }, - catalog: null, - defaultRequest: null, -}); - const map = (discover) => ({ ...discover, catalog: discover.catalog !== null && discover.catalog.content.type === 'Ready' ? @@ -67,25 +55,20 @@ const useDiscover = (urlParams, queryParams) => { }; } } else { - const discover = core.transport.getState('discover'); - if (discover.defaultRequest !== null) { - return { - action: 'Load', - args: { - model: 'CatalogWithFilters', - args: { - request: discover.defaultRequest - } - } - }; - } + return { + action: 'Load', + args: { + model: 'CatalogWithFilters', + args: null + } + }; } return { action: 'Unload' }; }, [urlParams, queryParams]); - const discover = useModelState({ model: 'discover', action, map, init }); + const discover = useModelState({ model: 'discover', action, map, deps: ['ctx'] }); return [discover, loadNextPage]; }; diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index a97a113f8..4e2fb74cd 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -186,7 +186,6 @@ const Intro = ({ queryParams }) => { tos: state.termsAccepted, privacy: state.privacyPolicyAccepted, marketing: state.marketingAccepted, - time: new Date(), from: 'web' } } diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index b350ce170..c2790824e 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -5,7 +5,7 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const Icon = require('@stremio/stremio-icons/dom'); const NotFound = require('stremio/routes/NotFound'); -const { Button, Multiselect, MainNavBars, LibItem, Image, ModalDialog, PaginationInput, useProfile, routesRegexp, useBinaryState } = require('stremio/common'); +const { Button, DelayedRenderer, Multiselect, MainNavBars, LibItem, Image, ModalDialog, PaginationInput, useProfile, routesRegexp, useBinaryState, withCoreSuspender } = require('stremio/common'); const useLibrary = require('./useLibrary'); const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); @@ -85,14 +85,16 @@ const Library = ({ model, urlParams, queryParams }) => {
: library.selected === null ? -
- {' -
{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!
-
+ +
+ {' +
{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!
+
+
: library.catalog.length === 0 ?
@@ -104,7 +106,7 @@ const Library = ({ model, urlParams, queryParams }) => {
Empty {model === 'library' ? 'Library' : 'Continue Watching'}
: -
+
{library.catalog.map((libItem, index) => ( ))} @@ -132,4 +134,10 @@ Library.propTypes = { queryParams: PropTypes.instanceOf(URLSearchParams) }; -module.exports = withModel(Library); +const LibraryFallback = ({ model }) => ( + +); + +LibraryFallback.propTypes = Library.propTypes; + +module.exports = withModel(withCoreSuspender(Library, LibraryFallback)); diff --git a/src/routes/Library/useLibrary.js b/src/routes/Library/useLibrary.js index cff44771d..d882d889b 100644 --- a/src/routes/Library/useLibrary.js +++ b/src/routes/Library/useLibrary.js @@ -3,17 +3,6 @@ const React = require('react'); const { useModelState } = require('stremio/common'); -const init = () => ({ - selected: null, - selectable: { - types: [], - sorts: [], - prevPage: null, - nextPage: null - }, - catalog: [] -}); - const useLibrary = (model, urlParams, queryParams) => { const action = React.useMemo(() => ({ action: 'Load', @@ -28,7 +17,7 @@ const useLibrary = (model, urlParams, queryParams) => { } } }), [urlParams, queryParams]); - return useModelState({ model, action, init }); + return useModelState({ model, action }); }; module.exports = useLibrary; diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index 602bdd3c8..144ef6c6c 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -2,8 +2,9 @@ const React = require('react'); const PropTypes = require('prop-types'); +const classnames = require('classnames'); const { useServices } = require('stremio/services'); -const { VerticalNavBar, HorizontalNavBar, MetaPreview, ModalDialog, Image } = require('stremio/common'); +const { VerticalNavBar, HorizontalNavBar, MetaPreview, ModalDialog, Image, DelayedRenderer, withCoreSuspender } = require('stremio/common'); const StreamsList = require('./StreamsList'); const VideosList = require('./VideosList'); const useMetaDetails = require('./useMetaDetails'); @@ -86,10 +87,12 @@ const MetaDetails = ({ urlParams, queryParams }) => { } { metaPath === null ? -
- {' -
No meta was selected!
-
+ +
+ {' +
No meta was selected!
+
+
: metaDetails.metaItem === null ?
@@ -122,7 +125,7 @@ const MetaDetails = ({ urlParams, queryParams }) => { null } ( +
+ +
+); + +module.exports = withCoreSuspender(MetaDetails, MetaDetailsFallback); diff --git a/src/routes/MetaDetails/VideosList/Video/Video.js b/src/routes/MetaDetails/VideosList/Video/Video.js index bc9210346..a8113be4e 100644 --- a/src/routes/MetaDetails/VideosList/Video/Video.js +++ b/src/routes/MetaDetails/VideosList/Video/Video.js @@ -88,7 +88,7 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w { released instanceof Date && !isNaN(released.getTime()) ?
- {released.toLocaleString(undefined, { year: '2-digit', month: 'short', day: 'numeric' })} + {released.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
: scheduled ? diff --git a/src/routes/MetaDetails/useMetaDetails.js b/src/routes/MetaDetails/useMetaDetails.js index fb9f270c5..f2c10d10f 100644 --- a/src/routes/MetaDetails/useMetaDetails.js +++ b/src/routes/MetaDetails/useMetaDetails.js @@ -3,14 +3,6 @@ const React = require('react'); const { useModelState } = require('stremio/common'); -const init = () => ({ - selected: null, - metaItem: null, - streams: [], - metaExtensions: [], - title: null -}); - const map = (metaDetails) => ({ ...metaDetails, metaItem: metaDetails.metaItem !== null && metaDetails.metaItem.content.type === 'Ready' ? @@ -74,7 +66,7 @@ const useMetaDetails = (urlParams) => { }; } }, [urlParams]); - return useModelState({ model: 'meta_details', action, map, init }); + return useModelState({ model: 'meta_details', action, map }); }; module.exports = useMetaDetails; diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 43d99f2b4..b16334c49 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -6,7 +6,7 @@ const classnames = require('classnames'); const debounce = require('lodash.debounce'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); -const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useStreamingServer } = require('stremio/common'); +const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender } = require('stremio/common'); const Icon = require('@stremio/stremio-icons/dom'); const BufferingLoader = require('./BufferingLoader'); const ControlBar = require('./ControlBar'); @@ -18,14 +18,14 @@ const useSettings = require('./useSettings'); const styles = require('./styles'); const Player = ({ urlParams, queryParams }) => { - const { core, chromecast, shell } = useServices(); + const { chromecast, shell } = useServices(); const [forceTranscoding, maxAudioChannels] = React.useMemo(() => { return [ queryParams.has('forceTranscoding'), queryParams.has('maxAudioChannels') ? parseInt(queryParams.get('maxAudioChannels'), 10) : null ]; }, [queryParams]); - const [player, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams); + const [player, timeChanged, pausedChanged, ended, pushToLibrary] = usePlayer(urlParams); const [settings, updateSettings] = useSettings(); const streamingServer = useStreamingServer(); const routeFocused = useRouteFocused(); @@ -42,6 +42,7 @@ const Player = ({ urlParams, queryParams }) => { const [videoState, setVideoState] = React.useReducer( (videoState, nextVideoState) => ({ ...videoState, ...nextVideoState }), { + manifest: null, stream: null, paused: null, time: null, @@ -75,6 +76,7 @@ const Player = ({ urlParams, queryParams }) => { } }, []); const onImplementationChanged = React.useCallback((manifest) => { + setVideoState({ manifest }); manifest.props.forEach((propName) => { dispatch({ type: 'observeProp', propName }); }); @@ -93,16 +95,8 @@ const Player = ({ urlParams, queryParams }) => { setVideoState({ [propName]: propValue }); }, []); const onEnded = React.useCallback(() => { + ended(); pushToLibrary(); - if (player.libraryItem !== null) { - core.transport.dispatch({ - action: 'Ctx', - args: { - action: 'RewindLibraryItem', - args: player.libraryItem._id - } - }); - } if (player.nextVideo !== null) { window.location.replace( typeof player.nextVideo.deepLinks.player === 'string' ? @@ -224,7 +218,8 @@ const Player = ({ urlParams, queryParams }) => { setError(null); if (player.selected === null) { dispatch({ type: 'command', commandName: 'unload' }); - } else if (streamingServer.baseUrl !== null && streamingServer.baseUrl.type !== 'Loading' && player.metaItem !== null && player.metaItem.type !== 'Loading') { + } else if (streamingServer.baseUrl !== null && streamingServer.baseUrl.type !== 'Loading' && + (player.selected.metaRequest === null || (player.metaItem !== null && player.metaItem.type !== 'Loading'))) { dispatch({ type: 'command', commandName: 'load', @@ -302,10 +297,17 @@ const Player = ({ urlParams, queryParams }) => { dispatch({ type: 'setProp', propName: 'extraSubtitlesOutlineColor', propValue: settings.subtitlesOutlineColor }); }, [settings.subtitlesOutlineColor]); React.useEffect(() => { - if (videoState.time !== null && !isNaN(videoState.time) && videoState.duration !== null && !isNaN(videoState.duration)) { - updateLibraryItemState(videoState.time, videoState.duration); + if (videoState.time !== null && !isNaN(videoState.time) && + videoState.duration !== null && !isNaN(videoState.duration) && + videoState.manifest !== null && typeof videoState.manifest.name === 'string') { + timeChanged(videoState.time, videoState.duration, videoState.manifest.name); } - }, [videoState.time, videoState.duration]); + }, [videoState.time, videoState.duration, videoState.manifest]); + React.useEffect(() => { + if (videoState.paused !== null) { + pausedChanged(videoState.paused); + } + }, [videoState.paused]); React.useEffect(() => { if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) && (!Array.isArray(videoState.extraSubtitlesTracks) || videoState.extraSubtitlesTracks.length === 0) && @@ -568,4 +570,8 @@ Player.propTypes = { queryParams: PropTypes.instanceOf(URLSearchParams) }; -module.exports = Player; +const PlayerFallback = () => ( +
+); + +module.exports = withCoreSuspender(Player, PlayerFallback); diff --git a/src/routes/Player/usePlayer.js b/src/routes/Player/usePlayer.js index 0e728b51f..9acd16396 100644 --- a/src/routes/Player/usePlayer.js +++ b/src/routes/Player/usePlayer.js @@ -2,18 +2,7 @@ const React = require('react'); const { useServices } = require('stremio/services'); -const { useModelState } = require('stremio/common'); - -const init = () => ({ - selected: null, - metaItem: null, - subtitles: [], - nextVideo: null, - seriesInfo: null, - libraryItem: null, - title: null, - addon: null, -}); +const { useModelState, useCoreSuspender } = require('stremio/common'); const map = (player) => ({ ...player, @@ -45,8 +34,9 @@ const map = (player) => ({ const usePlayer = (urlParams) => { const { core } = useServices(); + const { decodeStream } = useCoreSuspender(); + const stream = decodeStream(urlParams.stream); const action = React.useMemo(() => { - const stream = core.transport.decodeStream(urlParams.stream); if (stream !== null) { return { action: 'Load', @@ -96,12 +86,12 @@ const usePlayer = (urlParams) => { }; } }, [urlParams]); - const updateLibraryItemState = React.useCallback((time, duration) => { + const timeChanged = React.useCallback((time, duration, device) => { core.transport.dispatch({ action: 'Player', args: { - action: 'UpdateLibraryItemState', - args: { time, duration } + action: 'TimeChanged', + args: { time, duration, device } } }, 'player'); }, []); @@ -113,8 +103,25 @@ const usePlayer = (urlParams) => { } }, 'player'); }, []); - const player = useModelState({ model: 'player', action, init, map }); - return [player, updateLibraryItemState, pushToLibrary]; + const ended = React.useCallback(() => { + core.transport.dispatch({ + action: 'Player', + args: { + action: 'Ended' + } + }, 'player'); + }, []); + const pausedChanged = React.useCallback((paused) => { + core.transport.dispatch({ + action: 'Player', + args: { + action: 'PausedChanged', + args: { paused } + } + }, 'player'); + }, []); + const player = useModelState({ model: 'player', action, map }); + return [player, timeChanged, pausedChanged, ended, pushToLibrary]; }; module.exports = usePlayer; diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index 47fb15cba..bea2e7606 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -5,7 +5,7 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const debounce = require('lodash.debounce'); const Icon = require('@stremio/stremio-icons/dom'); -const { Image, MainNavBars, MetaRow, MetaItem, useDeepEqualMemo, getVisibleChildrenRange } = require('stremio/common'); +const { Image, MainNavBars, MetaRow, MetaItem, useDeepEqualMemo, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const useSearch = require('./useSearch'); const styles = require('./styles'); @@ -47,7 +47,7 @@ const Search = ({ queryParams }) => {
{ query === null ? -
+
Search for movies, series, YouTube and TV channels
@@ -74,7 +74,7 @@ const Search = ({ queryParams }) => { return ( { return ( { return ( @@ -115,4 +115,10 @@ Search.propTypes = { queryParams: PropTypes.instanceOf(URLSearchParams) }; -module.exports = Search; +const SearchFallback = ({ queryParams }) => ( + +); + +SearchFallback.propTypes = Search.propTypes; + +module.exports = withCoreSuspender(Search, SearchFallback); diff --git a/src/routes/Search/useSearch.js b/src/routes/Search/useSearch.js index d1ff8e79c..40a46fb76 100644 --- a/src/routes/Search/useSearch.js +++ b/src/routes/Search/useSearch.js @@ -4,37 +4,33 @@ const React = require('react'); const { useModelState } = require('stremio/common'); const { useServices } = require('stremio/services'); -const init = () => ({ - selected: null, - catalogs: [] -}); - const useSearch = (queryParams) => { const { core } = useServices(); - React.useEffect(() => { - let timerId = setTimeout(emitSearchEvent, 500); - function emitSearchEvent() { - timerId = null; - const state = core.transport.getState('search'); - if (state.selected !== null) { - const [, query] = state.selected.extra.find(([name]) => name === 'search'); - const responses = state.catalogs.filter((catalog) => catalog.content?.type === 'Ready'); - core.transport.analytics({ - event: 'Search', - args: { - query, - responsesCount: responses.length - } - }); - } - } - return () => { - if (timerId !== null) { - clearTimeout(timerId); - emitSearchEvent(); - } - }; - }, [queryParams.get('search')]); + // TODO: refactor this to be in stremio-core-web + // React.useEffect(() => { + // let timerId = setTimeout(emitSearchEvent, 500); + // function emitSearchEvent() { + // timerId = null; + // const state = core.transport.getState('search'); + // if (state.selected !== null) { + // const [, query] = state.selected.extra.find(([name]) => name === 'search'); + // const responses = state.catalogs.filter((catalog) => catalog.content?.type === 'Ready'); + // core.transport.analytics({ + // event: 'Search', + // args: { + // query, + // responsesCount: responses.length + // } + // }); + // } + // } + // return () => { + // if (timerId !== null) { + // clearTimeout(timerId); + // emitSearchEvent(); + // } + // }; + // }, [queryParams.get('search')]); const action = React.useMemo(() => { if (queryParams.has('search') && queryParams.get('search').length > 0) { return { @@ -63,7 +59,7 @@ const useSearch = (queryParams) => { } }, 'search'); }, []); - const search = useModelState({ model: 'search', action, init }); + const search = useModelState({ model: 'search', action }); return [search, loadRange]; }; diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index bffa9da66..44550794e 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -6,7 +6,7 @@ const throttle = require('lodash.throttle'); const Icon = require('@stremio/stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); -const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, useStreamingServer, useBinaryState } = require('stremio/common'); +const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, useStreamingServer, useBinaryState, withCoreSuspender } = require('stremio/common'); const useProfileSettingsInputs = require('./useProfileSettingsInputs'); const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs'); const styles = require('./styles'); @@ -131,7 +131,7 @@ const Settings = () => { }, [routeFocused]); return ( -
+