From 84e5f23ffa2e99a351ae08af649662ac0d5fcbf2 Mon Sep 17 00:00:00 2001 From: Botzy Date: Wed, 16 Apr 2025 14:02:04 +0300 Subject: [PATCH 01/47] feat: added react-router dependency --- package-lock.json | 25 +++++++++++++++++++++++++ package.json | 1 + 2 files changed, 26 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7f2e0b955..71e7ca452 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "react-focus-lock": "2.13.2", "react-i18next": "^15.1.3", "react-is": "18.3.1", + "react-router": "6.30.0", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "stremio-translations": "github:Stremio/stremio-translations#a6be0425573917c2e82b66d28968c1a4d444cb96", "url": "0.11.4", @@ -3103,6 +3104,15 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -12465,6 +12475,21 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "dev": true, diff --git a/package.json b/package.json index c12a0b7e8..6d12ee566 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react-focus-lock": "2.13.2", "react-i18next": "^15.1.3", "react-is": "18.3.1", + "react-router": "6.30.0", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "stremio-translations": "github:Stremio/stremio-translations#a6be0425573917c2e82b66d28968c1a4d444cb96", "url": "0.11.4", From f7c1c8267033da7db17209673b8ea732151c053a Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 29 Apr 2025 13:57:00 +0300 Subject: [PATCH 02/47] feat(dependencies): added react-router-dom --- package-lock.json | 18 ++++++++++++++++++ package.json | 1 + 2 files changed, 19 insertions(+) diff --git a/package-lock.json b/package-lock.json index 71e7ca452..581298e4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "react-router": "6.30.0", + "react-router-dom": "6.30.0", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "stremio-translations": "github:Stremio/stremio-translations#a6be0425573917c2e82b66d28968c1a4d444cb96", "url": "0.11.4", @@ -12490,6 +12491,23 @@ "react": ">=16.8" } }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "dev": true, diff --git a/package.json b/package.json index 6d12ee566..e69a33879 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "react-router": "6.30.0", + "react-router-dom": "6.30.0", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", "stremio-translations": "github:Stremio/stremio-translations#a6be0425573917c2e82b66d28968c1a4d444cb96", "url": "0.11.4", From 3c5e75431b457bc8581ab6caa035c940dc2de359 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 29 Apr 2025 15:34:25 +0300 Subject: [PATCH 03/47] feat(Router): added HashRouter to manage routes in app --- src/App/App.js | 3 +- src/common/routerPaths.tsx | 59 ++++++++++++++ src/components/MainNavBars/MainNavBars.tsx | 12 +-- src/router/Route/Route.js | 6 +- src/router/Router/Router.js | 95 ++-------------------- src/router/Router/Routes.tsx | 40 +++++++++ 6 files changed, 115 insertions(+), 100 deletions(-) create mode 100644 src/common/routerPaths.tsx create mode 100644 src/router/Router/Routes.tsx diff --git a/src/App/App.js b/src/App/App.js index 38be271ac..d3c5c3caa 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -12,11 +12,10 @@ const DeepLinkHandler = require('./DeepLinkHandler'); const SearchParamsHandler = require('./SearchParamsHandler'); const { default: UpdaterBanner } = require('./UpdaterBanner'); const ErrorDialog = require('./ErrorDialog'); -const withProtectedRoutes = require('./withProtectedRoutes'); const routerViewsConfig = require('./routerViewsConfig'); const styles = require('./styles'); -const RouterWithProtectedRoutes = withCoreSuspender(withProtectedRoutes(Router)); +const RouterWithProtectedRoutes = withCoreSuspender(Router); const App = () => { const { i18n } = useTranslation(); diff --git a/src/common/routerPaths.tsx b/src/common/routerPaths.tsx new file mode 100644 index 000000000..9cd98a9ec --- /dev/null +++ b/src/common/routerPaths.tsx @@ -0,0 +1,59 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import React from 'react'; +import routes from 'stremio/routes'; + +export const routerPaths = [ + { + path: '/intro', + element: , + }, + { + path: '/discover/:transportUrl?/:type?/:catalogId?', + element: , + }, + { + path: '/library/:type?', + element: , + }, + { + path: '/calendar/:year?/:month?', + element: , + }, + { + path: '/continuewatching/:type?', + element: , + }, + { + path: '/search', + element: , + }, + { + path: '/metadetails/:type?/:id?/:videoId?', + element: , + }, + { + path: '/detail/:type?/:id?/:videoId?', + element: , + }, + { + path: '/addons/:type?/:transportUrl?/:catalogId?', + element: , + }, + { + path: '/settings', + element: , + }, + { + path: '/player/:stream?/:streamTransportUrl?/:metaTransportUrl?/:type?/:id?/:videoId?', + element: , + }, + { + path: '/', + element: , + }, + { + path: '*', + element: , + }, +]; diff --git a/src/components/MainNavBars/MainNavBars.tsx b/src/components/MainNavBars/MainNavBars.tsx index 43d08f5c8..a42273eab 100644 --- a/src/components/MainNavBars/MainNavBars.tsx +++ b/src/components/MainNavBars/MainNavBars.tsx @@ -6,12 +6,12 @@ import { VerticalNavBar, HorizontalNavBar } from 'stremio/components/NavBar'; import styles from './MainNavBars.less'; const TABS = [ - { id: 'board', label: 'Board', icon: 'home', href: '#/' }, - { id: 'discover', label: 'Discover', icon: 'discover', href: '#/discover' }, - { id: 'library', label: 'Library', icon: 'library', href: '#/library' }, - { id: 'calendar', label: 'Calendar', icon: 'calendar', href: '#/calendar' }, - { id: 'addons', label: 'ADDONS', icon: 'addons', href: '#/addons' }, - { id: 'settings', label: 'SETTINGS', icon: 'settings', href: '#/settings' }, + { id: 'board', label: 'Board', icon: 'home', href: '/' }, + { id: 'discover', label: 'Discover', icon: 'discover', href: '/discover' }, + { id: 'library', label: 'Library', icon: 'library', href: '/library' }, + { id: 'calendar', label: 'Calendar', icon: 'calendar', href: '/calendar' }, + { id: 'addons', label: 'ADDONS', icon: 'addons', href: '/addons' }, + { id: 'settings', label: 'SETTINGS', icon: 'settings', href: '/settings' }, ]; type Props = { diff --git a/src/router/Route/Route.js b/src/router/Route/Route.js index c52c3fbf7..ae92115d9 100644 --- a/src/router/Route/Route.js +++ b/src/router/Route/Route.js @@ -4,12 +4,12 @@ const React = require('react'); const PropTypes = require('prop-types'); const { ModalsContainerProvider } = require('../ModalsContainerContext'); -const Route = ({ children }) => { +const Route = ({ component }) => { return (
- {children} + {component}
@@ -17,7 +17,7 @@ const Route = ({ children }) => { }; Route.propTypes = { - children: PropTypes.node + component: PropTypes.node }; module.exports = Route; diff --git a/src/router/Router/Router.js b/src/router/Router/Router.js index db4c9e957..e2fb9d6f5 100644 --- a/src/router/Router/Router.js +++ b/src/router/Router/Router.js @@ -1,107 +1,24 @@ // Copyright (C) 2017-2023 Smart code 203358507 const React = require('react'); -const ReactIs = require('react-is'); +const { HashRouter } = require('react-router-dom'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const UrlUtils = require('url'); -const isEqual = require('lodash.isequal'); -const { RouteFocusedProvider } = require('../RouteFocusedContext'); -const Route = require('../Route'); -const routeConfigForPath = require('./routeConfigForPath'); -const urlParamsForPath = require('./urlParamsForPath'); +const { default: Routes } = require('./Routes'); -const Router = ({ className, onPathNotMatch, onRouteChange, ...props }) => { - const viewsConfig = React.useMemo(() => props.viewsConfig, []); - const [views, setViews] = React.useState(() => { - return Array(viewsConfig.length).fill(null); - }); - React.useLayoutEffect(() => { - const onLocationHashChange = () => { - const { pathname, query } = UrlUtils.parse(window.location.hash.slice(1)); - const queryParams = new URLSearchParams(typeof query === 'string' ? query : ''); - const routeConfig = routeConfigForPath(viewsConfig, typeof pathname === 'string' ? pathname : ''); - if (routeConfig === null) { - if (typeof onPathNotMatch === 'function') { - const component = onPathNotMatch(); - if (ReactIs.isValidElementType(component)) { - setViews((views) => { - return views - .slice(0, viewsConfig.length) - .concat({ - key: '-1', - component - }); - }); - } - } +const Router = ({ className }) => { - return; - } - - const urlParams = urlParamsForPath(routeConfig, typeof pathname === 'string' ? pathname : ''); - const routeViewIndex = viewsConfig.findIndex((vc) => vc.includes(routeConfig)); - const routeIndex = viewsConfig[routeViewIndex].findIndex((rc) => rc === routeConfig); - const handled = typeof onRouteChange === 'function' && onRouteChange(routeConfig, urlParams, queryParams); - if (!handled) { - setViews((views) => { - return views - .slice(0, viewsConfig.length) - .map((view, index) => { - if (index < routeViewIndex) { - return view; - } else if (index === routeViewIndex) { - return { - key: `${routeViewIndex}${routeIndex}`, - component: routeConfig.component, - urlParams: view !== null && isEqual(view.urlParams, urlParams) ? - view.urlParams - : - urlParams, - queryParams: view !== null && isEqual(Array.from(view.queryParams.entries()), Array.from(queryParams.entries())) ? - view.queryParams - : - queryParams - }; - } else { - return null; - } - }); - }); - } - }; - window.addEventListener('hashchange', onLocationHashChange); - onLocationHashChange(); - return () => { - window.removeEventListener('hashchange', onLocationHashChange); - }; - }, [onPathNotMatch, onRouteChange]); return (
- { - views - .filter((view) => view !== null) - .map(({ key, component, urlParams, queryParams }, index, views) => ( - - - {React.createElement(component, { urlParams, queryParams })} - - - )) - } + + +
); }; Router.propTypes = { className: PropTypes.string, - onPathNotMatch: PropTypes.func, - onRouteChange: PropTypes.func, - viewsConfig: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.exact({ - regexp: PropTypes.instanceOf(RegExp).isRequired, - urlParamsNames: PropTypes.arrayOf(PropTypes.string).isRequired, - component: PropTypes.elementType.isRequired - }))).isRequired }; module.exports = Router; diff --git a/src/router/Router/Routes.tsx b/src/router/Router/Routes.tsx new file mode 100644 index 000000000..d33c9166a --- /dev/null +++ b/src/router/Router/Routes.tsx @@ -0,0 +1,40 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import React from 'react'; +import { Routes as RRoutes, Route as RRoute, useLocation, useNavigate } from 'react-router'; +import { routerPaths } from 'stremio/common/routerPaths'; +import Route from '../Route/Route'; +import { useProfile } from 'stremio/common'; + +const Routes = () => { + const location = useLocation(); + const navigate = useNavigate(); + const profile = useProfile(); + const previousAuthRef = React.useRef(profile.auth); + + React.useEffect(() => { + if (previousAuthRef.current !== null && profile.auth === null) { + previousAuthRef.current = profile.auth; + navigate('/intro', { replace: true }); + } + }, [profile]); + + /** + * Replaced onRouteChange with following useEffect: + */ + React.useEffect(() => { + if (profile.auth !== null && location.pathname === '/intro') { + navigate('/', { replace: true }); + } + }, [location, profile.auth, navigate]); + + const routes = routerPaths.map((route) => + } /> + ); + + return + {routes} + ; +}; + +export default Routes; From 90d54017c1e0a9caaa15793bf36aab8c777d6d5b Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 29 Apr 2025 15:35:03 +0300 Subject: [PATCH 04/47] fix: cycle dependency with useRouteFocused --- src/common/useModelState.js | 2 +- src/common/useRouteFocused.ts | 24 +++++++++++++++++++ src/components/ModalDialog/ModalDialog.js | 3 ++- .../HorizontalNavBar/NavMenu/NavMenu.js | 2 +- .../HorizontalNavBar/SearchBar/SearchBar.js | 2 +- .../NavTabButton/NavTabButton.js | 7 +++--- src/components/Popup/Popup.js | 2 +- src/components/SharePrompt/SharePrompt.js | 2 +- src/components/Slider/Slider.js | 2 +- src/components/Video/Video.js | 2 +- .../RouteFocusedContext.js | 9 ------- src/router/RouteFocusedContext/index.js | 9 ------- .../RouteFocusedContext/useRouteFocused.js | 10 -------- src/router/index.js | 2 -- src/routes/Intro/Intro.js | 3 ++- .../PasswordResetModal/PasswordResetModal.js | 2 +- .../MetaDetails/StreamsList/Stream/Stream.js | 2 +- .../Player/ControlBar/SeekBar/SeekBar.js | 2 +- .../ControlBar/VolumeSlider/VolumeSlider.js | 2 +- src/routes/Player/Player.js | 2 +- src/routes/Settings/Settings.js | 4 ++-- 21 files changed, 46 insertions(+), 49 deletions(-) create mode 100644 src/common/useRouteFocused.ts delete mode 100644 src/router/RouteFocusedContext/RouteFocusedContext.js delete mode 100644 src/router/RouteFocusedContext/index.js delete mode 100644 src/router/RouteFocusedContext/useRouteFocused.js diff --git a/src/common/useModelState.js b/src/common/useModelState.js index 42672dc22..8657f052c 100644 --- a/src/common/useModelState.js +++ b/src/common/useModelState.js @@ -5,7 +5,7 @@ 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 { default: useRouteFocused } = require('stremio/common/useRouteFocused'); const { useServices } = require('stremio/services'); const useModelState = ({ action, ...args }) => { diff --git a/src/common/useRouteFocused.ts b/src/common/useRouteFocused.ts new file mode 100644 index 000000000..80037f7d3 --- /dev/null +++ b/src/common/useRouteFocused.ts @@ -0,0 +1,24 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import React from 'react'; + +const useRouteFocused = () => { + const [isFocused, setIsFocused] = React.useState(document.hasFocus()); + + React.useEffect(() => { + const handleFocus = () => setIsFocused(true); + const handleBlur = () => setIsFocused(false); + + window.addEventListener('focus', handleFocus); + window.addEventListener('blur', handleBlur); + + return () => { + window.removeEventListener('focus', handleFocus); + window.removeEventListener('blur', handleBlur); + }; + }, []); + + return isFocused; +}; + +export default useRouteFocused; diff --git a/src/components/ModalDialog/ModalDialog.js b/src/components/ModalDialog/ModalDialog.js index 5e5907c5a..1c6b2cfd0 100644 --- a/src/components/ModalDialog/ModalDialog.js +++ b/src/components/ModalDialog/ModalDialog.js @@ -3,7 +3,8 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const { useRouteFocused, useModalsContainer } = require('stremio-router'); +const { useModalsContainer } = require('stremio-router'); +const { default: useRouteFocused } = require('stremio/common/useRouteFocused'); const { default: Button } = require('stremio/components/Button'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Modal } = require('stremio-router'); diff --git a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenu.js b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenu.js index 8d381f3ca..b5de0ee71 100644 --- a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenu.js +++ b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenu.js @@ -3,7 +3,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const { useRouteFocused } = require('stremio-router'); +const { default: useRouteFocused } = require('stremio/common/useRouteFocused'); const Popup = require('stremio/components/Popup'); const useBinaryState = require('stremio/common/useBinaryState'); const NavMenuContent = require('./NavMenuContent'); diff --git a/src/components/NavBar/HorizontalNavBar/SearchBar/SearchBar.js b/src/components/NavBar/HorizontalNavBar/SearchBar/SearchBar.js index 0bc95cd73..a4bb25c7a 100644 --- a/src/components/NavBar/HorizontalNavBar/SearchBar/SearchBar.js +++ b/src/components/NavBar/HorizontalNavBar/SearchBar/SearchBar.js @@ -6,7 +6,7 @@ const classnames = require('classnames'); const debounce = require('lodash.debounce'); const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); -const { useRouteFocused } = require('stremio-router'); +const { default: useRouteFocused } = require('stremio/common/useRouteFocused'); const Button = require('stremio/components/Button').default; const TextInput = require('stremio/components/TextInput').default; const useTorrent = require('stremio/common/useTorrent'); diff --git a/src/components/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js b/src/components/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js index 65c6a02a9..e59f106f7 100644 --- a/src/components/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js +++ b/src/components/NavBar/VerticalNavBar/NavTabButton/NavTabButton.js @@ -4,8 +4,9 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const { default: Icon } = require('@stremio/stremio-icons/react'); -const { Button, Image } = require('stremio/components'); +const { Image } = require('stremio/components'); const styles = require('./styles'); +const { Link } = require('react-router-dom'); const NavTabButton = ({ className, logo, icon, label, href, selected, onClick }) => { const renderLogoFallback = React.useCallback(() => ( @@ -24,7 +25,7 @@ const NavTabButton = ({ className, logo, icon, label, href, selected, onClick }) }); }; return ( - From 4fbdd6467d388eeab194fb206be866368ec46e19 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 20 May 2025 19:16:08 +0300 Subject: [PATCH 33/47] refactor(withProtectedRoutes): remove unused HOC and improved the redirect logic --- src/App/withProtectedRoutes.js | 31 ------------------------------- src/router/Router/Routes.tsx | 17 +++++++++-------- 2 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 src/App/withProtectedRoutes.js diff --git a/src/App/withProtectedRoutes.js b/src/App/withProtectedRoutes.js deleted file mode 100644 index 9edc4e39c..000000000 --- a/src/App/withProtectedRoutes.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2017-2023 Smart code 203358507 - -const React = require('react'); -const { Intro } = require('stremio/routes'); -const { useProfile } = require('stremio/common'); -const { useNavigate } = require('react-router'); - -const withProtectedRoutes = (Component) => { - const navigate = useNavigate(); - return function withProtectedRoutes(props) { - const profile = useProfile(); - const previousAuthRef = React.useRef(profile.auth); - React.useEffect(() => { - if (previousAuthRef.current !== null && profile.auth === null) { - navigate('/intro'); - } - previousAuthRef.current = profile.auth; - }, [profile]); - const onRouteChange = React.useCallback((routeConfig) => { - if (profile.auth !== null && routeConfig.component === Intro) { - navigate('/', { replace: true }); - return true; - } - }, [profile]); - return ( - - ); - }; -}; - -module.exports = withProtectedRoutes; diff --git a/src/router/Router/Routes.tsx b/src/router/Router/Routes.tsx index d33c9166a..68ecf3666 100644 --- a/src/router/Router/Routes.tsx +++ b/src/router/Router/Routes.tsx @@ -12,21 +12,22 @@ const Routes = () => { const profile = useProfile(); const previousAuthRef = React.useRef(profile.auth); - React.useEffect(() => { - if (previousAuthRef.current !== null && profile.auth === null) { - previousAuthRef.current = profile.auth; - navigate('/intro', { replace: true }); - } - }, [profile]); - /** * Replaced onRouteChange with following useEffect: */ React.useEffect(() => { + // Handle redirect if user logs out + if (previousAuthRef.current !== null && profile.auth === null) { + previousAuthRef.current = profile.auth; + navigate('/intro', { replace: true }); + } + + // Handle redirect if user is logged in on intro screen if (profile.auth !== null && location.pathname === '/intro') { navigate('/', { replace: true }); } - }, [location, profile.auth, navigate]); + previousAuthRef.current = profile.auth; + }, [location, profile.auth, navigate, previousAuthRef.current]); const routes = routerPaths.map((route) => } /> From c43950316d80601b49f6ca0720c0ee91d75b8503 Mon Sep 17 00:00:00 2001 From: Botzy Date: Thu, 22 May 2025 15:33:06 +0300 Subject: [PATCH 34/47] revert(useModelState): revert changes on dispatching actions for different models --- src/common/useModelState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/useModelState.js b/src/common/useModelState.js index 8e2dc9b0f..8f22f2a02 100644 --- a/src/common/useModelState.js +++ b/src/common/useModelState.js @@ -36,7 +36,7 @@ const useModelState = ({ action, ...args }) => { if (action) { core.transport.dispatch(action, model); } - }, []); + }, [action]); React.useLayoutEffect(() => { return () => { core.transport.dispatch({ action: 'Unload' }, model); From 604a1ca67605be76ad27a143049adf2818c30667 Mon Sep 17 00:00:00 2001 From: Botzy Date: Wed, 18 Jun 2025 17:25:54 +0300 Subject: [PATCH 35/47] fix: issue with infinite api calls on meta details --- src/routes/MetaDetails/MetaDetails.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index 10039343a..cde0880a5 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -15,11 +15,16 @@ const useMetaExtensionTabs = require('./useMetaExtensionTabs'); const styles = require('./styles'); const MetaDetails = () => { - const urlParams = useParams(); + const { type, id, videoId } = useParams(); const location = useLocation(); const navigate = useNavigate(); const { t } = useTranslation(); const { core } = useServices(); + const urlParams = React.useMemo(() => ({ + type, + id, + videoId + }), [type, id, videoId]); const metaDetails = useMetaDetails(urlParams); const [season, setSeason] = useSeason(urlParams); const [tabs, metaExtension, clearMetaExtension] = useMetaExtensionTabs(metaDetails.metaExtensions); From 1457d125ab6fcad1dff29e717d559470c0127048 Mon Sep 17 00:00:00 2001 From: Botzy Date: Thu, 19 Jun 2025 14:39:09 +0300 Subject: [PATCH 36/47] fix typo --- src/routes/Player/Player.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index abf4c1cb9..77b9b29b3 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -110,10 +110,10 @@ const Player = () => { const handleNextVideoNavigation = React.useCallback((deepLinks) => { if (deepLinks.player) { isNavigating.current = true; - navigate(deepLinks.player.replace('#', '', { replace: true })); + navigate(deepLinks.player.replace('#', ''), { replace: true }); } else if (deepLinks.metaDetailsStreams) { isNavigating.current = true; - navigate(deepLinks.metaDetailsStreams.replace('#', '', { replace: true })); + navigate(deepLinks.metaDetailsStreams.replace('#', ''), { replace: true }); } }, []); From 6d0136e54503fc67490c2276c050b2e1a3deb87d Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 24 Jun 2025 16:30:07 +0300 Subject: [PATCH 37/47] fix(Discover): flashing on tab switch --- src/routes/Discover/Discover.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index 57f34d160..51fe799d7 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -16,7 +16,12 @@ const styles = require('./styles'); const SCROLL_TO_BOTTOM_THRESHOLD = 400; const Discover = () => { - const urlParams = useParams(); + const { type, transportUrl, catalogId } = useParams(); + const urlParams = React.useMemo(() => ({ + type, + transportUrl, + catalogId + }), [type, transportUrl, catalogId]); const [queryParams] = useSearchParams(); const { t } = useTranslation(); const { core } = useServices(); From bb450045861e7e336c8cf425ca5ec771c762fa46 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 24 Jun 2025 17:58:07 +0300 Subject: [PATCH 38/47] fix: memoize url params --- src/routes/Addons/Addons.js | 7 ++++++- src/routes/Calendar/Calendar.tsx | 6 +++++- src/routes/Library/Library.js | 5 ++++- src/routes/Player/Player.js | 10 +++++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 17f55514a..85e43f7d5 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -18,8 +18,13 @@ const styles = require('./styles'); const { AddonPlaceholder } = require('./AddonPlaceholder'); const Addons = () => { - const urlParams = useParams(); + const { type, transportUrl, catalogId } = useParams(); const [queryParams] = useSearchParams(); + const urlParams = React.useMemo(() => ({ + type, + transportUrl, + catalogId + }), [type, transportUrl, catalogId]); const { t } = useTranslation(); const platform = usePlatform(); const { core } = useServices(); diff --git a/src/routes/Calendar/Calendar.tsx b/src/routes/Calendar/Calendar.tsx index 454fff88d..3e73f66ed 100644 --- a/src/routes/Calendar/Calendar.tsx +++ b/src/routes/Calendar/Calendar.tsx @@ -15,7 +15,11 @@ import styles from './Calendar.less'; import classNames from 'classnames'; const Calendar = () => { - const urlParams = useParams(); + const { year, month } = useParams(); + const urlParams = React.useMemo(() => ({ + year, + month + }), [year, month]); const calendar = useCalendar(urlParams); const profile = useProfile(); diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index 09a5b0842..0c165eff6 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -41,7 +41,10 @@ function withModel(Library, useLocation) { } const Library = ({ model }) => { - const urlParams = useParams(); + const { type } = useParams(); + const urlParams = React.useMemo(() => ({ + type + }), [type]); const [queryParams] = useSearchParams(); const navigate = useNavigate(); const { t } = useTranslation(); diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index af736a283..a9aecabcb 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -31,7 +31,15 @@ const Video = require('./Video'); const { default: Indicator } = require('./Indicator/Indicator'); const Player = () => { - const urlParams = useParams(); + const { stream, streamTransportUrl, metaTransportUrl, type, id, videoId } = useParams(); + const urlParams = React.useMemo(() => ({ + stream, + streamTransportUrl, + metaTransportUrl, + type, + id, + videoId + }), [stream, streamTransportUrl, metaTransportUrl, type, id, videoId]); const [queryParams] = useSearchParams(); const navigate = useNavigate(); const { t } = useTranslation(); From e30b33cbe03a92c6d27f6ade4bfcaf274e3b06e1 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 24 Jun 2025 22:25:43 +0300 Subject: [PATCH 39/47] chore: fix builds --- src/components/Video/Video.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js index 8da7c7657..5d9b130dd 100644 --- a/src/components/Video/Video.js +++ b/src/components/Video/Video.js @@ -4,7 +4,6 @@ const React = require('react'); const { useNavigate } = require('react-router'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const { t } = require('i18next'); const { default: useRouteFocused } = require('stremio/common/useRouteFocused'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Button, Image, Popup } = require('stremio/components'); @@ -12,12 +11,14 @@ const useBinaryState = require('stremio/common/useBinaryState'); const useProfile = require('stremio/common/useProfile'); const VideoPlaceholder = require('./VideoPlaceholder'); const styles = require('./styles'); +const { useTranslation } = require('react-i18next'); const Video = React.forwardRef(({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }, ref) => { const routeFocused = useRouteFocused(); const profile = useProfile(); const navigate = useNavigate(); const { t } = useTranslation(); + const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); const popupLabelOnMouseUp = React.useCallback((event) => { if (!event.nativeEvent.togglePopupPrevented) { From 2776367c2c97758db35b6c05c45df214588d7219 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 24 Jun 2025 22:26:27 +0300 Subject: [PATCH 40/47] chore: remove unnecessary change --- src/components/Video/Video.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js index 5d9b130dd..e8848a395 100644 --- a/src/components/Video/Video.js +++ b/src/components/Video/Video.js @@ -1,6 +1,7 @@ // Copyright (C) 2017-2023 Smart code 203358507 const React = require('react'); +const { useTranslation } = require('react-i18next'); const { useNavigate } = require('react-router'); const PropTypes = require('prop-types'); const classnames = require('classnames'); @@ -11,7 +12,6 @@ const useBinaryState = require('stremio/common/useBinaryState'); const useProfile = require('stremio/common/useProfile'); const VideoPlaceholder = require('./VideoPlaceholder'); const styles = require('./styles'); -const { useTranslation } = require('react-i18next'); const Video = React.forwardRef(({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }, ref) => { const routeFocused = useRouteFocused(); From fc320e00c08c122c668d68c2c00a7a396d24ec0f Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 21 Jul 2025 18:00:42 +0300 Subject: [PATCH 41/47] fix: replace route when going back in streams list or changing season --- src/routes/MetaDetails/StreamsList/StreamsList.js | 2 +- src/routes/MetaDetails/useSeason.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/MetaDetails/StreamsList/StreamsList.js b/src/routes/MetaDetails/StreamsList/StreamsList.js index 45ea707b9..e464113a8 100644 --- a/src/routes/MetaDetails/StreamsList/StreamsList.js +++ b/src/routes/MetaDetails/StreamsList/StreamsList.js @@ -36,7 +36,7 @@ const StreamsList = ({ className, video, type, onEpisodeSearch, ...props }) => { typeof video.season === 'number' ? `?${new URLSearchParams({ 'season': video.season })}` : ''}`; - navigate(navigateTo.replace('#', '')); + navigate(navigateTo.replace('#', ''), { replace: true }); } else { navigate(-1); } diff --git a/src/routes/MetaDetails/useSeason.js b/src/routes/MetaDetails/useSeason.js index 2c99bca2f..2409913ea 100644 --- a/src/routes/MetaDetails/useSeason.js +++ b/src/routes/MetaDetails/useSeason.js @@ -14,7 +14,7 @@ const useSeason = (urlParams) => { const setSeason = React.useCallback((season) => { const nextQueryParams = new URLSearchParams(queryParams); nextQueryParams.set('season', season); - setQueryParams(nextQueryParams); + setQueryParams(nextQueryParams, { replace: true }); }, [urlParams, queryParams]); return [season, setSeason]; }; From 75f0820a10358a6d93e329bd25a0b1229fd294b3 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 21 Jul 2025 19:40:37 +0300 Subject: [PATCH 42/47] fix(MetaItem): horizontal nav - go back to main origin instead using window history --- src/common/useNavigateWithOrigin.ts | 35 +++++++++++++++++++ src/components/MetaItem/MetaItem.js | 3 ++ .../HorizontalNavBar/HorizontalNavBar.js | 11 ++++-- src/routes/Calendar/List/Item/Item.tsx | 3 ++ src/routes/Calendar/Table/Cell/Cell.tsx | 3 ++ src/routes/MetaDetails/MetaDetails.js | 5 ++- 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/common/useNavigateWithOrigin.ts diff --git a/src/common/useNavigateWithOrigin.ts b/src/common/useNavigateWithOrigin.ts new file mode 100644 index 000000000..51c58a8da --- /dev/null +++ b/src/common/useNavigateWithOrigin.ts @@ -0,0 +1,35 @@ +import { useLocation, useNavigate, To, Location } from 'react-router-dom'; + +const ORIGIN_KEY = 'originPath'; + +export function useNavigateWithOrigin() { + const navigate = useNavigate(); + const location = useLocation(); + + function navigateWithOrigin(target: To) { + const origin: Location = location.state?.from || location; + + // Save origin in sessionStorage + sessionStorage.setItem(ORIGIN_KEY, origin.pathname + origin.search); + + // Navigate and propagate origin + navigate(target, { + state: { from: origin }, + }); + } + + function setOriginPath(path?: string) { + const finalPath = path ?? location.pathname + location.search; + sessionStorage.setItem(ORIGIN_KEY, finalPath); + } + + function getStoredOrigin(fallback = '/'): string { + return sessionStorage.getItem(ORIGIN_KEY) || fallback; + } + + return { + navigateWithOrigin, + getStoredOrigin, + setOriginPath, + }; +} diff --git a/src/components/MetaItem/MetaItem.js b/src/components/MetaItem/MetaItem.js index 1e19054c1..eec59cefb 100644 --- a/src/components/MetaItem/MetaItem.js +++ b/src/components/MetaItem/MetaItem.js @@ -6,6 +6,7 @@ const classnames = require('classnames'); const { useTranslation } = require('react-i18next'); const filterInvalidDOMProps = require('filter-invalid-dom-props').default; const { default: Icon } = require('@stremio/stremio-icons/react'); +const { useNavigateWithOrigin } = require('stremio/common/useNavigateWithOrigin'); const { default: Button } = require('stremio/components/Button'); const { default: Image } = require('stremio/components/Image'); const Multiselect = require('stremio/components/Multiselect'); @@ -15,6 +16,7 @@ const styles = require('./styles'); const MetaItem = React.memo(({ className, type, name, poster, posterShape, posterChangeCursor, progress, newVideos, options, deepLinks, dataset, optionOnSelect, onDismissClick, onPlayClick, watched, ...props }) => { const { t } = useTranslation(); + const { setOriginPath } = useNavigateWithOrigin(); const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false); const href = React.useMemo(() => { return deepLinks ? @@ -32,6 +34,7 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste null; }, [deepLinks]); const metaItemOnClick = React.useCallback((event) => { + setOriginPath(); if (event.nativeEvent.selectPrevented) { event.preventDefault(); } else if (typeof props.onClick === 'function') { diff --git a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js index 1b3e9b3da..e1f418ddd 100644 --- a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js +++ b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js @@ -13,10 +13,14 @@ const NavMenu = require('./NavMenu'); const styles = require('./styles'); const { t } = require('i18next'); -const HorizontalNavBar = React.memo(({ className, route, query, title, backButton, searchBar, fullscreenButton, navMenu, ...props }) => { +const HorizontalNavBar = React.memo(({ className, route, query, title, backButton, searchBar, fullscreenButton, navMenu, originPath, ...props }) => { const navigate = useNavigate(); const backButtonOnClick = React.useCallback(() => { - navigate(-1); + if (originPath) { + navigate(originPath); + } else { + navigate(-1); + } }, []); const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen(); const [isIOSPWA] = usePWA(); @@ -84,7 +88,8 @@ HorizontalNavBar.propTypes = { backButton: PropTypes.bool, searchBar: PropTypes.bool, fullscreenButton: PropTypes.bool, - navMenu: PropTypes.bool + navMenu: PropTypes.bool, + originPath: PropTypes.string, }; module.exports = HorizontalNavBar; diff --git a/src/routes/Calendar/List/Item/Item.tsx b/src/routes/Calendar/List/Item/Item.tsx index 7448e96db..9525a4bc8 100644 --- a/src/routes/Calendar/List/Item/Item.tsx +++ b/src/routes/Calendar/List/Item/Item.tsx @@ -3,6 +3,7 @@ import React, { useEffect, useMemo, useRef } from 'react'; import Icon from '@stremio/stremio-icons/react'; import classNames from 'classnames'; +import { useNavigateWithOrigin } from 'stremio/common/useNavigateWithOrigin'; import { Button } from 'stremio/components'; import useCalendarDate from '../../useCalendarDate'; import styles from './Item.less'; @@ -18,6 +19,7 @@ type Props = { const Item = ({ selected, monthInfo, date, items, profile, onClick }: Props) => { const ref = useRef(null); + const { setOriginPath } = useNavigateWithOrigin(); const { toDayMonth } = useCalendarDate(profile); const [active, today] = useMemo(() => [ @@ -26,6 +28,7 @@ const Item = ({ selected, monthInfo, date, items, profile, onClick }: Props) => ], [selected, monthInfo, date]); const onItemClick = () => { + setOriginPath(); onClick && onClick(date); }; diff --git a/src/routes/Calendar/Table/Cell/Cell.tsx b/src/routes/Calendar/Table/Cell/Cell.tsx index 2db1b0992..b36aea2e3 100644 --- a/src/routes/Calendar/Table/Cell/Cell.tsx +++ b/src/routes/Calendar/Table/Cell/Cell.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useMemo, MouseEvent } from 'react'; import Icon from '@stremio/stremio-icons/react'; import classNames from 'classnames'; +import { useNavigateWithOrigin } from 'stremio/common/useNavigateWithOrigin'; import { Button, HorizontalScroll, Image } from 'stremio/components'; import styles from './Cell.less'; @@ -15,6 +16,7 @@ type Props = { }; const Cell = ({ selected, monthInfo, date, items, onClick }: Props) => { + const { setOriginPath } = useNavigateWithOrigin(); const [active, today] = useMemo(() => [ date.day === selected?.day, date.day === monthInfo.today, @@ -25,6 +27,7 @@ const Cell = ({ selected, monthInfo, date, items, onClick }: Props) => { }; const onPosterClick = useCallback((event: MouseEvent) => { + setOriginPath(); event.stopPropagation(); }, []); diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index cde0880a5..962379984 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -6,6 +6,7 @@ const { useTranslation } = require('react-i18next'); const classnames = require('classnames'); const { useServices } = require('stremio/services'); const { withCoreSuspender } = require('stremio/common'); +const { useNavigateWithOrigin } = require('stremio/common/useNavigateWithOrigin'); const { VerticalNavBar, HorizontalNavBar, DelayedRenderer, Image, MetaPreview, ModalDialog } = require('stremio/components'); const StreamsList = require('./StreamsList'); const VideosList = require('./VideosList'); @@ -18,6 +19,7 @@ const MetaDetails = () => { const { type, id, videoId } = useParams(); const location = useLocation(); const navigate = useNavigate(); + const { getStoredOrigin } = useNavigateWithOrigin(); const { t } = useTranslation(); const { core } = useServices(); const urlParams = React.useMemo(() => ({ @@ -101,7 +103,7 @@ const MetaDetails = () => { typeof metaDetails.metaItem.content.content?.background === 'string' && metaDetails.metaItem.content.content.background.length > 0 ), [metaPath, metaDetails]); - + const originPath = React.useMemo(() => getStoredOrigin(), [getStoredOrigin]); return (
{ @@ -122,6 +124,7 @@ const MetaDetails = () => { backButton={true} fullscreenButton={true} navMenu={true} + originPath={originPath} />
{ From 8876cea34b6ad92929380b49719f74b5b1924772 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 22 Jul 2025 19:35:09 +0300 Subject: [PATCH 43/47] fix: season search replace url --- src/routes/MetaDetails/MetaDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index 962379984..8e274be1b 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -92,7 +92,7 @@ const MetaDetails = () => { const searchVideoHash = encodeURIComponent(`${urlParams.id}:${season}:${episode}`); const url = location.pathname; const searchVideoPath = url.replace(encodeURIComponent(urlParams.videoId), searchVideoHash); - navigate(searchVideoPath); + navigate(searchVideoPath, { replace: true }); }, [urlParams, location]); const renderBackgroundImageFallback = React.useCallback(() => null, []); From cf69505ae60c9c4dd883995292bf5457211de802 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 22 Jul 2025 19:53:01 +0300 Subject: [PATCH 44/47] fix: stop replacing routes in meta details video if on mobile --- src/components/Video/Video.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js index e8848a395..d4cc2fbac 100644 --- a/src/components/Video/Video.js +++ b/src/components/Video/Video.js @@ -10,6 +10,7 @@ const { default: Icon } = require('@stremio/stremio-icons/react'); const { Button, Image, Popup } = require('stremio/components'); const useBinaryState = require('stremio/common/useBinaryState'); const useProfile = require('stremio/common/useProfile'); +const { usePlatform } = require('stremio/common/Platform'); const VideoPlaceholder = require('./VideoPlaceholder'); const styles = require('./styles'); @@ -17,6 +18,7 @@ const Video = React.forwardRef(({ className, id, title, thumbnail, season, episo const routeFocused = useRouteFocused(); const profile = useProfile(); const navigate = useNavigate(); + const platform = usePlatform(); const { t } = useTranslation(); const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); @@ -68,7 +70,7 @@ const Video = React.forwardRef(({ className, id, title, thumbnail, season, episo // TODO: remove # from deeplinks in core navigate(deepLinks.player.replace('#', '')); } else if (typeof deepLinks.metaDetailsStreams === 'string') { - navigate(deepLinks.metaDetailsStreams.replace('#', ''), { replace: true }); + navigate(deepLinks.metaDetailsStreams.replace('#', ''), { replace: !platform.isMobile }); } } }, [deepLinks]); From 457e168fcbef27d28f31cd6b2e8474d9697da896 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 18 Aug 2025 17:14:23 +0300 Subject: [PATCH 45/47] fix: issue with opening trailer --- src/routes/Player/Player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index c29a69583..4a6bc476d 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -860,7 +860,7 @@ const Player = () => { metaItem={player.metaItem?.content} seriesInfo={player.seriesInfo} closeSideDrawer={closeSideDrawer} - selected={player.selected?.streamRequest.path.id} + selected={player.selected?.streamRequest?.path?.id} /> { From 9205761faaef51e8e2029eeb36f5c5713784d317 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 25 Aug 2025 19:08:39 +0300 Subject: [PATCH 46/47] fix: use window location replace on next episode for complete component remount --- src/routes/Player/Player.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 207880c93..d60e682b7 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -117,12 +117,13 @@ const Player = () => { }, [settings.subtitlesSize, settings.subtitlesOffset, settings.subtitlesTextColor, settings.subtitlesBackgroundColor, settings.subtitlesOutlineColor]); const handleNextVideoNavigation = React.useCallback((deepLinks) => { + // We use window.location.replace here instead navigate, to ensure complete player component remount if (deepLinks.player) { isNavigating.current = true; - navigate(deepLinks.player.replace('#', ''), { replace: true }); + window.location.replace(deepLinks.player); } else if (deepLinks.metaDetailsStreams) { isNavigating.current = true; - navigate(deepLinks.metaDetailsStreams.replace('#', ''), { replace: true }); + window.location.replace(deepLinks.metaDetailsStreams); } }, []); From b4b8e0ab03f3de5d215ee62caa18a545b8566293 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 26 Aug 2025 23:18:27 +0300 Subject: [PATCH 47/47] fix: race condition with updating player time --- src/routes/Player/Player.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index d60e682b7..4c101729c 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -117,13 +117,12 @@ const Player = () => { }, [settings.subtitlesSize, settings.subtitlesOffset, settings.subtitlesTextColor, settings.subtitlesBackgroundColor, settings.subtitlesOutlineColor]); const handleNextVideoNavigation = React.useCallback((deepLinks) => { - // We use window.location.replace here instead navigate, to ensure complete player component remount if (deepLinks.player) { isNavigating.current = true; - window.location.replace(deepLinks.player); + navigate(deepLinks.player.replace('#', ''), { replace: true }); } else if (deepLinks.metaDetailsStreams) { isNavigating.current = true; - window.location.replace(deepLinks.metaDetailsStreams); + navigate(deepLinks.metaDetailsStreams.replace('#', ''), { replace: true }); } }, []); @@ -408,7 +407,7 @@ const Player = () => { }, [settings.subtitlesOutlineColor]); React.useEffect(() => { - !seeking && timeChanged(video.state.time, video.state.duration, video.state.manifest?.name); + !isNavigating.current && !seeking && timeChanged(video.state.time, video.state.duration, video.state.manifest?.name); }, [video.state.time, video.state.duration, video.state.manifest, seeking]); React.useEffect(() => {