From 6e00a3384cdaebb406ab24a5dbfd2e8302fa0310 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Jan 2025 18:39:45 +0100 Subject: [PATCH 01/41] feat: add mark season as watched for videos --- src/components/Video/Video.js | 16 ++++++++++++++-- .../MetaDetails/VideosList/VideosList.js | 18 ++++++++++++++++++ src/routes/Player/SideDrawer/SideDrawer.tsx | 17 +++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/components/Video/Video.js b/src/components/Video/Video.js index efa1a6847..0bcb569a5 100644 --- a/src/components/Video/Video.js +++ b/src/components/Video/Video.js @@ -11,7 +11,7 @@ const useBinaryState = require('stremio/common/useBinaryState'); const VideoPlaceholder = require('./VideoPlaceholder'); const styles = require('./styles'); -const Video = ({ className, id, title, thumbnail, episode, released, upcoming, watched, progress, scheduled, deepLinks, onMarkVideoAsWatched, ...props }) => { +const Video = ({ className, id, title, thumbnail, season, episode, released, upcoming, watched, progress, scheduled, seasonWatched, deepLinks, onMarkVideoAsWatched, onMarkSeasonAsWatched, ...props }) => { const routeFocused = useRouteFocused(); const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false); const popupLabelOnMouseUp = React.useCallback((event) => { @@ -50,6 +50,12 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w closeMenu(); onMarkVideoAsWatched({ id, released }, watched); }, [id, released, watched]); + const toggleWatchedSeasonOnClick = React.useCallback((event) => { + event.preventDefault(); + event.stopPropagation(); + closeMenu(); + onMarkSeasonAsWatched(season, seasonWatched); + }, [season, seasonWatched, onMarkSeasonAsWatched]); const videoButtonOnClick = React.useCallback(() => { if (deepLinks) { if (typeof deepLinks.player === 'string') { @@ -142,9 +148,12 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w + ); - }, [watched, toggleWatchedOnClick]); + }, [watched, seasonWatched, toggleWatchedOnClick]); React.useEffect(() => { if (!routeFocused) { closeMenu(); @@ -182,17 +191,20 @@ Video.propTypes = { id: PropTypes.string, title: PropTypes.string, thumbnail: PropTypes.string, + season: PropTypes.number, episode: PropTypes.number, released: PropTypes.instanceOf(Date), upcoming: PropTypes.bool, watched: PropTypes.bool, progress: PropTypes.number, scheduled: PropTypes.bool, + seasonWatched: PropTypes.bool, deepLinks: PropTypes.shape({ metaDetailsStreams: PropTypes.string, player: PropTypes.string }), onMarkVideoAsWatched: PropTypes.func, + onMarkSeasonAsWatched: PropTypes.func, }; module.exports = Video; diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 999a1a1ae..557b3df1f 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -56,6 +56,11 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, return a.episode - b.episode; }); }, [videos, selectedSeason]); + + const seasonWatched = React.useMemo(() => { + return videosForSeason.every((video) => video.watched); + }, [videosForSeason]); + const [search, setSearch] = React.useState(''); const searchInputOnChange = React.useCallback((event) => { setSearch(event.currentTarget.value); @@ -71,6 +76,16 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, }); }; + const onMarkSeasonAsWatched = (season, watched) => { + core.transport.dispatch({ + action: 'MetaDetails', + args: { + action: 'MarkSeasonAsWatched', + args: [season, !watched] + } + }); + }; + return (
{ @@ -135,6 +150,7 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, id={video.id} title={video.title} thumbnail={video.thumbnail} + season={video.season} episode={video.episode} released={video.released} upcoming={video.upcoming} @@ -142,7 +158,9 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, progress={video.progress} deepLinks={video.deepLinks} scheduled={video.scheduled} + seasonWatched={seasonWatched} onMarkVideoAsWatched={onMarkVideoAsWatched} + onMarkSeasonAsWatched={onMarkSeasonAsWatched} /> )) } diff --git a/src/routes/Player/SideDrawer/SideDrawer.tsx b/src/routes/Player/SideDrawer/SideDrawer.tsx index 9ed713879..332ead719 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.tsx +++ b/src/routes/Player/SideDrawer/SideDrawer.tsx @@ -47,6 +47,10 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa setSeason(parseInt(event.value)); }, []); + const seasonWatched = React.useMemo(() => { + return videos.every((video) => video.watched); + }, [videos]); + const onMarkVideoAsWatched = useCallback((video: Video, watched: boolean) => { core.transport.dispatch({ action: 'Player', @@ -57,6 +61,16 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa }); }, []); + const onMarkSeasonAsWatched = (season: number, watched: boolean) => { + core.transport.dispatch({ + action: 'MetaDetails', + args: { + action: 'MarkSeasonAsWatched', + args: [season, !watched] + } + }); + }; + const onMouseDown = (event: React.MouseEvent) => { event.stopPropagation(); }; @@ -95,14 +109,17 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa id={video.id} title={video.title} thumbnail={video.thumbnail} + season={video.season} episode={video.episode} released={video.released} upcoming={video.upcoming} watched={video.watched} + seasonWatched={seasonWatched} progress={video.progress} deepLinks={video.deepLinks} scheduled={video.scheduled} onMarkVideoAsWatched={onMarkVideoAsWatched} + onMarkSeasonAsWatched={onMarkSeasonAsWatched} /> ))}
From c08bed630d0a87cbf02f8e07a1b8c98474c01c9c Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 15 Feb 2025 07:27:24 +0100 Subject: [PATCH 02/41] fix(Player): use Player action for MarkSeasonAsWatched Co-authored-by: Timothy Z. --- src/routes/Player/SideDrawer/SideDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Player/SideDrawer/SideDrawer.tsx b/src/routes/Player/SideDrawer/SideDrawer.tsx index 332ead719..cb94e24e5 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.tsx +++ b/src/routes/Player/SideDrawer/SideDrawer.tsx @@ -63,7 +63,7 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa const onMarkSeasonAsWatched = (season: number, watched: boolean) => { core.transport.dispatch({ - action: 'MetaDetails', + action: 'Player', args: { action: 'MarkSeasonAsWatched', args: [season, !watched] From a4ee4db1b879fd2bcbce87add19cc33168b42722 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 19 Feb 2025 11:05:09 +0100 Subject: [PATCH 03/41] feat: add quit on close setting for shell --- src/App/App.js | 12 ++++++++++-- src/routes/Settings/Settings.js | 14 ++++++++++++++ .../Settings/useProfileSettingsInputs.js | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/App/App.js b/src/App/App.js index 6dc2d6e0b..fa7e7a3e3 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -6,7 +6,7 @@ const { useTranslation } = require('react-i18next'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); -const { FileDropProvider, PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common'); +const { FileDropProvider, PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender, useShell } = require('stremio/common'); const ServicesToaster = require('./ServicesToaster'); const DeepLinkHandler = require('./DeepLinkHandler'); const SearchParamsHandler = require('./SearchParamsHandler'); @@ -20,6 +20,7 @@ const RouterWithProtectedRoutes = withCoreSuspender(withProtectedRoutes(Router)) const App = () => { const { i18n } = useTranslation(); + const shell = useShell(); const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); @@ -104,6 +105,9 @@ const App = () => { if (args && args.settings && typeof args.settings.interfaceLanguage === 'string') { i18n.changeLanguage(args.settings.interfaceLanguage); } + if (args?.settings) { + shell.send('update_settings', args.settings); + } break; } } @@ -112,6 +116,10 @@ const App = () => { if (state && state.profile && state.profile.settings && typeof state.profile.settings.interfaceLanguage === 'string') { i18n.changeLanguage(state.profile.settings.interfaceLanguage); } + + if (state?.profile?.settings) { + shell.send('update_settings', state.profile.settings); + } }; const onWindowFocus = () => { services.core.transport.dispatch({ @@ -146,7 +154,7 @@ const App = () => { services.core.transport .getState('ctx') .then(onCtxState) - .catch((e) => console.error(e)); + .catch(console.error); } return () => { if (services.core.active) { diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 6ad15163a..312ad9e49 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -41,6 +41,7 @@ const Settings = () => { seekTimeDurationSelect, seekShortTimeDurationSelect, escExitFullscreenToggle, + quitOnCloseToggle, playInExternalPlayerSelect, nextVideoPopupDurationSelect, bingeWatchingToggle, @@ -322,6 +323,19 @@ const Settings = () => { {...interfaceLanguageSelect} /> + { + shell.active && +
+
+
{ t('SETTINGS_QUIT_ON_CLOSE') }
+
+ +
+ }
{ t('SETTINGS_NAV_PLAYER') }
diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index d36b169f9..c193c6eaf 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -31,6 +31,23 @@ const useProfileSettingsInputs = (profile) => { }); } }), [profile.settings]); + + const quitOnCloseToggle = React.useMemo(() => ({ + checked: profile.settings.quitOnClose, + onClick: () => { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + quitOnClose: !profile.settings.quitOnClose + } + } + }); + } + }), [profile.settings]); + const subtitlesLanguageSelect = React.useMemo(() => ({ options: Object.keys(languageNames).map((code) => ({ value: code, @@ -316,6 +333,7 @@ const useProfileSettingsInputs = (profile) => { audioLanguageSelect, surroundSoundToggle, escExitFullscreenToggle, + quitOnCloseToggle, seekTimeDurationSelect, seekShortTimeDurationSelect, playInExternalPlayerSelect, From 31121aab21098408726c6c54946a2a0a7b95b903 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 19 Feb 2025 11:11:40 +0100 Subject: [PATCH 04/41] refactor(App): use dash for shell update settings message --- src/App/App.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App/App.js b/src/App/App.js index fa7e7a3e3..92240aa0d 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -106,7 +106,7 @@ const App = () => { i18n.changeLanguage(args.settings.interfaceLanguage); } if (args?.settings) { - shell.send('update_settings', args.settings); + shell.send('update-settings', args.settings); } break; } @@ -118,7 +118,7 @@ const App = () => { } if (state?.profile?.settings) { - shell.send('update_settings', state.profile.settings); + shell.send('update-settings', state.profile.settings); } }; const onWindowFocus = () => { From 39e37b5875de0e54394c0d1d6be91dd21018a799 Mon Sep 17 00:00:00 2001 From: Botzy Date: Wed, 19 Feb 2025 18:22:10 +0200 Subject: [PATCH 05/41] refactor(Calendar): rename placeholder image with more generic name and fix import --- ...placeholder.png => not_loggedin_placeholder.png} | Bin src/routes/Calendar/Placeholder/Placeholder.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename images/{calendar_placeholder.png => not_loggedin_placeholder.png} (100%) diff --git a/images/calendar_placeholder.png b/images/not_loggedin_placeholder.png similarity index 100% rename from images/calendar_placeholder.png rename to images/not_loggedin_placeholder.png diff --git a/src/routes/Calendar/Placeholder/Placeholder.tsx b/src/routes/Calendar/Placeholder/Placeholder.tsx index 4b48ba3f2..1a8a412b8 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.tsx +++ b/src/routes/Calendar/Placeholder/Placeholder.tsx @@ -16,7 +16,7 @@ const Placeholder = () => {
{'
From 64310c863f5cee3a0b1ee76fc03953df49eb774a Mon Sep 17 00:00:00 2001 From: Botzy Date: Thu, 20 Feb 2025 14:30:35 +0200 Subject: [PATCH 06/41] feat(Placeholder): added Placeholder component --- .../Library/Placeholder/Placeholder.less | 102 ++++++++++++++++++ .../Library/Placeholder/Placeholder.tsx | 32 ++++++ src/routes/Library/Placeholder/index.ts | 5 + 3 files changed, 139 insertions(+) create mode 100644 src/routes/Library/Placeholder/Placeholder.less create mode 100644 src/routes/Library/Placeholder/Placeholder.tsx create mode 100644 src/routes/Library/Placeholder/index.ts diff --git a/src/routes/Library/Placeholder/Placeholder.less b/src/routes/Library/Placeholder/Placeholder.less new file mode 100644 index 000000000..74908db66 --- /dev/null +++ b/src/routes/Library/Placeholder/Placeholder.less @@ -0,0 +1,102 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +@import (reference) '~stremio/common/screen-sizes.less'; + +.placeholder { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + overflow-y: auto; + + .title { + display: flex; + flex: 1; + align-items: flex-end; + font-size: 1.75rem; + font-weight: 400; + text-align: center; + color: var(--primary-foreground-color); + margin-bottom: 1rem; + opacity: 0.5; + } + + .image-container { + display: flex; + flex: 1; + align-items: center; + padding: 1.5rem 0; + + .image { + height: 100%; + max-height: 14rem; + object-fit: contain; + } + } + + .button-container { + display: flex; + flex: 1; + align-items: flex-start; + margin: 1rem 0; + + .button { + flex: none; + justify-content: center; + height: 4rem; + line-height: 4rem; + padding: 0 5rem; + font-size: 1.1rem; + color: var(--primary-foreground-color); + text-align: center; + border-radius: 3.5rem; + background-color: var(--overlay-color); + + &:hover { + outline: var(--focus-outline-size) solid var(--primary-foreground-color); + background-color: transparent; + } + } + } +} + +@media only screen and (max-width: @xsmall) { + .placeholder { + padding: 1rem 2rem; + + .title { + flex: 0.5; + } + + .image-container { + flex: 2; + padding: 1rem; + + .image { + max-height: 10rem; + } + } + + .button-container { + margin: 1rem 0 0; + } + } +} + +@media only screen and (max-width: @minimum) { + .placeholder { + + .image-container { + flex: 1; + } + + .button-container { + .button { + width: 100%; + } + } + } +} \ No newline at end of file diff --git a/src/routes/Library/Placeholder/Placeholder.tsx b/src/routes/Library/Placeholder/Placeholder.tsx new file mode 100644 index 000000000..d6332d2e6 --- /dev/null +++ b/src/routes/Library/Placeholder/Placeholder.tsx @@ -0,0 +1,32 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button, Image } from 'stremio/components'; +import styles from './Placeholder.less'; + +const Placeholder = () => { + const { t } = useTranslation(); + + return ( +
+
+ {t('LIBRARY_NOT_LOGGED_IN')} +
+
+ {' +
+
+ +
+
+ ); +}; + +export default Placeholder; diff --git a/src/routes/Library/Placeholder/index.ts b/src/routes/Library/Placeholder/index.ts new file mode 100644 index 000000000..b068f608e --- /dev/null +++ b/src/routes/Library/Placeholder/index.ts @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import Placeholder from './Placeholder'; + +export default Placeholder; From 13c9ef986a2ca1c22a4916cb238fa0b8f82a338b Mon Sep 17 00:00:00 2001 From: Botzy Date: Thu, 20 Feb 2025 14:31:20 +0200 Subject: [PATCH 07/41] refactor(Library): reuse Placeholder component and remove unused styles --- src/routes/Library/Library.js | 15 +++------------ src/routes/Library/styles.less | 32 -------------------------------- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index 2871a9b3f..e1cea5995 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -5,7 +5,8 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const NotFound = require('stremio/routes/NotFound'); const { useProfile, useNotifications, routesRegexp, useOnScrollToBottom, withCoreSuspender } = require('stremio/common'); -const { Button, DelayedRenderer, Chips, Image, MainNavBars, Multiselect, LibItem } = require('stremio/components'); +const { DelayedRenderer, Chips, Image, MainNavBars, Multiselect, LibItem } = require('stremio/components'); +const { default: Placeholder } = require('./Placeholder'); const useLibrary = require('./useLibrary'); const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); @@ -76,17 +77,7 @@ const Library = ({ model, urlParams, queryParams }) => { } { model === 'library' && profile.auth === null ? -
- {' -
Library is only available for logged in users!
- -
+ : library.selected === null ? diff --git a/src/routes/Library/styles.less b/src/routes/Library/styles.less index 2bdbc13ec..76a16940e 100644 --- a/src/routes/Library/styles.less +++ b/src/routes/Library/styles.less @@ -66,38 +66,6 @@ padding: 4rem; } - &.no-user-message-container { - .login-button-container { - flex: none; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - width: 20rem; - height: 3.5rem; - border-radius: 3.5rem; - padding: 0.5rem 1rem; - margin-bottom: 1rem; - background-color: var(--secondary-accent-color); - - &:hover { - outline: var(--focus-outline-size) solid var(--secondary-accent-color); - background-color: transparent; - } - - .label { - flex-grow: 0; - flex-shrink: 1; - flex-basis: auto; - max-height: 4.8em; - font-size: 1.2rem; - font-weight: 700; - color: var(--primary-foreground-color); - text-align: center; - } - } - } - .image { flex: none; width: 12rem; From e2c3e719ffd77eab4f98bbd1df682094ecddc302 Mon Sep 17 00:00:00 2001 From: Botzy Date: Thu, 20 Feb 2025 17:36:40 +0200 Subject: [PATCH 08/41] fix(Checkbox): remove transition on outline --- src/components/Checkbox/Checkbox.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Checkbox/Checkbox.less b/src/components/Checkbox/Checkbox.less index 718a7b129..a84244ce9 100644 --- a/src/components/Checkbox/Checkbox.less +++ b/src/components/Checkbox/Checkbox.less @@ -42,7 +42,7 @@ margin: 0 1rem 0 0.3rem; align-items: center; justify-content: center; - transition: all 0.2s ease-in-out; + transition: background-color 0.2s ease-in-out; cursor: pointer; outline: none; user-select: none; From 7b0c013dc08710e36760d3866a7072de5fb88bfd Mon Sep 17 00:00:00 2001 From: Botzy Date: Fri, 21 Feb 2025 13:41:59 +0200 Subject: [PATCH 09/41] fix(Placeholder): improve styles on mobile --- .../Calendar/Placeholder/Placeholder.less | 107 +++++++++++++----- .../Calendar/Placeholder/Placeholder.tsx | 20 ++-- 2 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/routes/Calendar/Placeholder/Placeholder.less b/src/routes/Calendar/Placeholder/Placeholder.less index a509ff79e..a37bd257d 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.less +++ b/src/routes/Calendar/Placeholder/Placeholder.less @@ -13,7 +13,9 @@ overflow-y: auto; .title { - flex: none; + display: flex; + flex: 4; + align-items: flex-end; font-size: 1.75rem; font-weight: 400; text-align: center; @@ -22,19 +24,26 @@ opacity: 0.5; } - .image { - flex: none; - height: 14rem; - margin: 1.5rem 0; + .image-container { + display: flex; + flex: 4; + align-items: center; + padding: 1.5rem 0; + + .image { + height: 100%; + max-height: 14rem; + object-fit: contain; + } } .overview { - flex: none; + flex: 2; display: flex; flex-direction: row; align-items: center; gap: 4rem; - margin-bottom: 3rem; + margin-bottom: 1rem; .point { display: flex; @@ -61,21 +70,57 @@ } } - .button { - flex: none; - justify-content: center; - height: 4rem; - line-height: 4rem; - padding: 0 5rem; - font-size: 1.1rem; - color: var(--primary-foreground-color); - text-align: center; - border-radius: 3.5rem; - background-color: var(--overlay-color); + .button-container { + display: flex; + flex: 2; + align-items: flex-start; + margin: 1rem 0; + + .button { + display: flex; + justify-content: center; + height: 4rem; + line-height: 4rem; + padding: 0 5rem; + font-size: 1.1rem; + color: var(--primary-foreground-color); + text-align: center; + border-radius: 3.5rem; + background-color: var(--overlay-color); - &:hover { - outline: var(--focus-outline-size) solid var(--primary-foreground-color); - background-color: transparent; + &:hover { + outline: var(--focus-outline-size) solid var(--primary-foreground-color); + background-color: transparent; + } + } + } +} + +@media only screen and (max-width: @xsmall) { + .placeholder { + padding: 1rem 2rem; + + .title { + flex: 1; + margin-bottom: 0; + } + + .image-container { + flex: 3; + padding: 1rem; + + .image { + max-height: 10rem; + } + } + + .overview { + flex: 2; + } + + .button-container { + flex: 2; + margin: 1rem 0 0; } } } @@ -83,17 +128,27 @@ @media only screen and (max-width: @minimum) { .placeholder { padding: 1rem 2rem; - - .image { - height: 10rem; + .title { + flex: 2; } .overview { flex-direction: column; + flex: 2; + gap: 1rem; + + .point { + .text { + font-size: 1rem; + } + } } - .button { - width: 100%; + .button-container { + flex: 3; + .button { + width: 100%; + } } } } \ No newline at end of file diff --git a/src/routes/Calendar/Placeholder/Placeholder.tsx b/src/routes/Calendar/Placeholder/Placeholder.tsx index 4b48ba3f2..c84e7a1b8 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.tsx +++ b/src/routes/Calendar/Placeholder/Placeholder.tsx @@ -14,11 +14,13 @@ const Placeholder = () => {
{t('CALENDAR_NOT_LOGGED_IN')}
- {' +
+ {' +
@@ -33,9 +35,11 @@ const Placeholder = () => {
- +
+ +
); }; From 97832d42a8bb1ea4c50a04c3edbb359ba8cf3a20 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 21 Feb 2025 12:48:55 +0100 Subject: [PATCH 10/41] chore: update stremio-core-web --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd094aa9c..210c66388 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.5", + "@stremio/stremio-core-web": "0.49.0", "@stremio/stremio-icons": "5.4.1", "@stremio/stremio-video": "0.0.48", "a-color-picker": "1.2.1", @@ -3371,9 +3371,9 @@ "integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==" }, "node_modules/@stremio/stremio-core-web": { - "version": "0.48.5", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.5.tgz", - "integrity": "sha512-oDTNBrv8zZi1VGbeV+1Bm6CliI2rF23ERdJpz+gv8EnbFjRIo78WIsoS0yO0EOg8HHXYsFytPq5+c0+YlxmBlA==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.0.tgz", + "integrity": "sha512-oxJRVAE6z6Eh1B0qomdz6L2CVaTkwt70kDNC1TmHyGNo+Hhp2RaMlygqBKvBLXyHUXi82R67Mc11gT/JqlmaMw==", "license": "MIT", "dependencies": { "@babel/runtime": "7.24.1" diff --git a/package.json b/package.json index e777dd091..6339eab06 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.5", + "@stremio/stremio-core-web": "0.49.0", "@stremio/stremio-icons": "5.4.1", "@stremio/stremio-video": "0.0.48", "a-color-picker": "1.2.1", From 755f0625bfd994125b50f1b6b36d31639c87b8b6 Mon Sep 17 00:00:00 2001 From: Botzy Date: Fri, 21 Feb 2025 13:53:11 +0200 Subject: [PATCH 11/41] fix(Placeholder): rename placeholder image --- ..._loggedin_placeholder.png => media_carousel.png} | Bin src/routes/Library/Placeholder/Placeholder.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename images/{not_loggedin_placeholder.png => media_carousel.png} (100%) diff --git a/images/not_loggedin_placeholder.png b/images/media_carousel.png similarity index 100% rename from images/not_loggedin_placeholder.png rename to images/media_carousel.png diff --git a/src/routes/Library/Placeholder/Placeholder.tsx b/src/routes/Library/Placeholder/Placeholder.tsx index d6332d2e6..d619611d8 100644 --- a/src/routes/Library/Placeholder/Placeholder.tsx +++ b/src/routes/Library/Placeholder/Placeholder.tsx @@ -16,7 +16,7 @@ const Placeholder = () => {
{'
From a050dd8d7679d8bb0106383f8739199c77ea1c32 Mon Sep 17 00:00:00 2001 From: Botzy Date: Fri, 21 Feb 2025 13:58:48 +0200 Subject: [PATCH 12/41] fix(Calendar): update placeholder image name --- src/routes/Calendar/Placeholder/Placeholder.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Calendar/Placeholder/Placeholder.tsx b/src/routes/Calendar/Placeholder/Placeholder.tsx index 1a8a412b8..3ec983f89 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.tsx +++ b/src/routes/Calendar/Placeholder/Placeholder.tsx @@ -16,7 +16,7 @@ const Placeholder = () => { {'
From a19ef95723d98000d21bc0f446b446a3564978ee Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 21 Feb 2025 13:02:53 +0100 Subject: [PATCH 13/41] chore: update stremio-core-web --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 642d0ea73..dadfc5ec8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.5", + "@stremio/stremio-core-web": "0.49.0", "@stremio/stremio-icons": "5.4.1", "@stremio/stremio-video": "0.0.53", "a-color-picker": "1.2.1", @@ -3371,9 +3371,9 @@ "integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==" }, "node_modules/@stremio/stremio-core-web": { - "version": "0.48.5", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.5.tgz", - "integrity": "sha512-oDTNBrv8zZi1VGbeV+1Bm6CliI2rF23ERdJpz+gv8EnbFjRIo78WIsoS0yO0EOg8HHXYsFytPq5+c0+YlxmBlA==", + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.49.0.tgz", + "integrity": "sha512-oxJRVAE6z6Eh1B0qomdz6L2CVaTkwt70kDNC1TmHyGNo+Hhp2RaMlygqBKvBLXyHUXi82R67Mc11gT/JqlmaMw==", "license": "MIT", "dependencies": { "@babel/runtime": "7.24.1" diff --git a/package.json b/package.json index 9ccb0cee8..04a96a75e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.5", + "@stremio/stremio-core-web": "0.49.0", "@stremio/stremio-icons": "5.4.1", "@stremio/stremio-video": "0.0.53", "a-color-picker": "1.2.1", From 039676afb5b375a8e2c36d18526caea441f6b476 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 24 Feb 2025 14:19:13 +0200 Subject: [PATCH 14/41] fix(MainNavBars): limit the content scroll area to not include navbar --- src/components/MainNavBars/MainNavBars.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/MainNavBars/MainNavBars.less b/src/components/MainNavBars/MainNavBars.less index a5495bc69..4eff455a5 100644 --- a/src/components/MainNavBars/MainNavBars.less +++ b/src/components/MainNavBars/MainNavBars.less @@ -29,8 +29,7 @@ .nav-content-container { position: absolute; - padding-top: calc(var(--horizontal-nav-bar-size) + var(--safe-area-inset-top)); - top: 0; + top: calc(var(--horizontal-nav-bar-size) + var(--safe-area-inset-top)); right: 0; bottom: 0; left: var(--vertical-nav-bar-size); From c5c7805d911c01f93ee94e60037ebb230e96aee6 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 24 Feb 2025 14:20:23 +0200 Subject: [PATCH 15/41] fix(Placeholder): improve styles and add scroll on mobile instead of squeezing in components --- .../Calendar/Placeholder/Placeholder.less | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/routes/Calendar/Placeholder/Placeholder.less b/src/routes/Calendar/Placeholder/Placeholder.less index a37bd257d..456bcfd21 100644 --- a/src/routes/Calendar/Placeholder/Placeholder.less +++ b/src/routes/Calendar/Placeholder/Placeholder.less @@ -8,14 +8,11 @@ flex-direction: column; align-items: center; justify-content: center; - height: 100%; + min-height: 100%; width: 100%; overflow-y: auto; .title { - display: flex; - flex: 4; - align-items: flex-end; font-size: 1.75rem; font-weight: 400; text-align: center; @@ -25,9 +22,6 @@ } .image-container { - display: flex; - flex: 4; - align-items: center; padding: 1.5rem 0; .image { @@ -38,7 +32,6 @@ } .overview { - flex: 2; display: flex; flex-direction: row; align-items: center; @@ -71,9 +64,6 @@ } .button-container { - display: flex; - flex: 2; - align-items: flex-start; margin: 1rem 0; .button { @@ -101,12 +91,10 @@ padding: 1rem 2rem; .title { - flex: 1; margin-bottom: 0; } .image-container { - flex: 3; padding: 1rem; .image { @@ -114,12 +102,7 @@ } } - .overview { - flex: 2; - } - .button-container { - flex: 2; margin: 1rem 0 0; } } @@ -128,13 +111,9 @@ @media only screen and (max-width: @minimum) { .placeholder { padding: 1rem 2rem; - .title { - flex: 2; - } .overview { flex-direction: column; - flex: 2; gap: 1rem; .point { @@ -145,7 +124,6 @@ } .button-container { - flex: 3; .button { width: 100%; } From bf37815d14c90eaeb7bdde52ccb32ad366329db0 Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 24 Feb 2025 14:51:09 +0200 Subject: [PATCH 16/41] fix(MainNavBars): limit content scroll on mobile to not go under bottom nav bar --- src/components/MainNavBars/MainNavBars.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MainNavBars/MainNavBars.less b/src/components/MainNavBars/MainNavBars.less index 4eff455a5..ca816a72f 100644 --- a/src/components/MainNavBars/MainNavBars.less +++ b/src/components/MainNavBars/MainNavBars.less @@ -42,7 +42,7 @@ .main-nav-bars-container { .nav-content-container { left: 0; - padding-bottom: var(--vertical-nav-bar-size); + bottom: var(--vertical-nav-bar-size); } .vertical-nav-bar { From 3bef434f42069a17c6960e37adb4264f0e2a926d Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 24 Feb 2025 14:35:39 +0100 Subject: [PATCH 17/41] refactor: update quit on close logic --- src/App/App.js | 24 +++++++++++---- src/common/useShell.ts | 68 ++++++++++++++++++++++++++++++++++++------ src/types/global.d.ts | 7 ++++- 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/App/App.js b/src/App/App.js index 92240aa0d..b6031be6e 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -21,6 +21,7 @@ const RouterWithProtectedRoutes = withCoreSuspender(withProtectedRoutes(Router)) const App = () => { const { i18n } = useTranslation(); const shell = useShell(); + const [windowHidden, setWindowHidden] = React.useState(false); const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); @@ -98,6 +99,17 @@ const App = () => { services.chromecast.off('stateChanged', onChromecastStateChange); }; }, []); + + // Handle shell window visibility changed event + React.useEffect(() => { + const onWindowVisibilityChanged = (state) => { + setWindowHidden(state.visible === false && state.visibility === 0); + }; + + shell.on('win-visibility-changed', onWindowVisibilityChanged); + return () => shell.off('win-visibility-changed', onWindowVisibilityChanged); + }, []); + React.useEffect(() => { const onCoreEvent = ({ event, args }) => { switch (event) { @@ -105,9 +117,11 @@ const App = () => { if (args && args.settings && typeof args.settings.interfaceLanguage === 'string') { i18n.changeLanguage(args.settings.interfaceLanguage); } - if (args?.settings) { - shell.send('update-settings', args.settings); + + if (args?.settings?.quitOnClose && windowHidden) { + shell.send('quit'); } + break; } } @@ -117,8 +131,8 @@ const App = () => { i18n.changeLanguage(state.profile.settings.interfaceLanguage); } - if (state?.profile?.settings) { - shell.send('update-settings', state.profile.settings); + if (state?.profile?.settings?.quitOnClose && windowHidden) { + shell.send('quit'); } }; const onWindowFocus = () => { @@ -162,7 +176,7 @@ const App = () => { services.core.transport.off('CoreEvent', onCoreEvent); } }; - }, [initialized]); + }, [initialized, windowHidden]); return ( diff --git a/src/common/useShell.ts b/src/common/useShell.ts index 5e61bfe84..f4700d779 100644 --- a/src/common/useShell.ts +++ b/src/common/useShell.ts @@ -1,21 +1,71 @@ +import { useEffect } from 'react'; +import EventEmitter from 'eventemitter3'; + +const SHELL_EVENT_OBJECT = 'transport'; +const transport = globalThis?.qt?.webChannelTransport; +const events = new EventEmitter(); + +enum ShellEventType { + SIGNAL = 1, + INVOKE_METHOD = 6, +} + +type ShellEvent = { + id: number; + type: ShellEventType; + object: string; + args: string[]; +}; + + + const createId = () => Math.floor(Math.random() * 9999) + 1; const useShell = () => { - const transport = globalThis?.qt?.webChannelTransport; - const send = (method: string, ...args: (string | number)[]) => { - transport?.send(JSON.stringify({ - id: createId(), - type: 6, - object: 'transport', - method: 'onEvent', - args: [method, ...args], - })); + try { + transport?.send(JSON.stringify({ + id: createId(), + type: ShellEventType.INVOKE_METHOD, + object: SHELL_EVENT_OBJECT, + method: 'onEvent', + args: [method, ...args], + })); + } catch(e) { + console.error('Shell', 'Failed to send event', e); + } }; + const on = (name: string, listener: (arg: any) => void) => { + events.on(name, listener); + }; + + const off = (name: string, listener: (arg: any) => void) => { + events.off(name, listener); + }; + + useEffect(() => { + if (!transport) return; + + transport.onmessage = ({ data }) => { + try { + const { type, args } = JSON.parse(data) as ShellEvent; + + if (type === ShellEventType.SIGNAL) { + const [methodName, methodArg] = args; + events.emit(methodName, methodArg); + } + } catch (e) { + console.error('Shell', 'Failed to handle event', e); + } + }; + }, []); + return { active: !!transport, send, + on, + off, }; }; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 5effeffd4..7a50d7432 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,7 +1,12 @@ /* eslint-disable no-var */ +type QtTransportMessage = { + data: string; +}; + interface QtTransport { send: (message: string) => void, + onmessage: (message: QtTransportMessage) => void, } interface Qt { @@ -12,4 +17,4 @@ declare global { var qt: Qt | undefined; } -export { }; +export {} \ No newline at end of file From 4b56ac44c2bb4052c867cb4bbf489e73cd12e978 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 24 Feb 2025 14:40:32 +0100 Subject: [PATCH 18/41] style: code format --- src/App/App.js | 2 +- src/common/useShell.ts | 20 +++++++++----------- src/types/global.d.ts | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/App/App.js b/src/App/App.js index b6031be6e..d3a1ce188 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -100,7 +100,7 @@ const App = () => { }; }, []); - // Handle shell window visibility changed event + // Handle shell window visibility changed event React.useEffect(() => { const onWindowVisibilityChanged = (state) => { setWindowHidden(state.visible === false && state.visibility === 0); diff --git a/src/common/useShell.ts b/src/common/useShell.ts index f4700d779..7baab60bc 100644 --- a/src/common/useShell.ts +++ b/src/common/useShell.ts @@ -17,11 +17,17 @@ type ShellEvent = { args: string[]; }; - - const createId = () => Math.floor(Math.random() * 9999) + 1; const useShell = () => { + const on = (name: string, listener: (arg: any) => void) => { + events.on(name, listener); + }; + + const off = (name: string, listener: (arg: any) => void) => { + events.off(name, listener); + }; + const send = (method: string, ...args: (string | number)[]) => { try { transport?.send(JSON.stringify({ @@ -31,19 +37,11 @@ const useShell = () => { method: 'onEvent', args: [method, ...args], })); - } catch(e) { + } catch (e) { console.error('Shell', 'Failed to send event', e); } }; - const on = (name: string, listener: (arg: any) => void) => { - events.on(name, listener); - }; - - const off = (name: string, listener: (arg: any) => void) => { - events.off(name, listener); - }; - useEffect(() => { if (!transport) return; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 7a50d7432..d1d601d5e 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -17,4 +17,4 @@ declare global { var qt: Qt | undefined; } -export {} \ No newline at end of file +export {}; From 3890001085dadbf07f6702082e0107c9e15afc2a Mon Sep 17 00:00:00 2001 From: Botzy Date: Mon, 24 Feb 2025 16:24:55 +0200 Subject: [PATCH 19/41] fix(Library): align guest Placeholder with Calendar Placeholder layout and styles on mobile --- src/routes/Library/Library.js | 87 ++++++++++--------- .../Library/Placeholder/Placeholder.less | 64 ++++++++++---- .../Library/Placeholder/Placeholder.tsx | 17 +++- 3 files changed, 108 insertions(+), 60 deletions(-) diff --git a/src/routes/Library/Library.js b/src/routes/Library/Library.js index e1cea5995..8e19f123b 100644 --- a/src/routes/Library/Library.js +++ b/src/routes/Library/Library.js @@ -59,55 +59,58 @@ const Library = ({ model, urlParams, queryParams }) => { }, [hasNextPage, loadNextPage]); const onScroll = useOnScrollToBottom(onScrollToBottom, SCROLL_TO_BOTTOM_TRESHOLD); React.useLayoutEffect(() => { - if (profile.auth !== null && library.selected && library.selected.request.page === 1 && library.catalog.length !== 0 ) { + if (profile.auth !== null && library.selected && library.selected.request.page === 1 && library.catalog.length !== 0) { scrollContainerRef.current.scrollTop = 0; } }, [profile.auth, library.selected]); return ( -
- { - model === 'continue_watching' || profile.auth !== null ? -
- - -
- : - null - } - { - model === 'library' && profile.auth === null ? - - : - library.selected === null ? - -
- {' -
{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!
-
-
- : - library.catalog.length === 0 ? -
- {' -
Empty {model === 'library' ? 'Library' : 'Continue Watching'}
+ { + profile.auth === null ? + + :
+ { + model === 'continue_watching' ? +
+ +
: -
- {library.catalog.map((libItem, index) => ( - - ))} -
- } -
+ null + } + { + model === 'library' ? + library.selected === null ? + +
+ {' +
{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!
+
+
+ : + library.catalog.length === 0 ? +
+ {' +
Empty {model === 'library' ? 'Library' : 'Continue Watching'}
+
+ : +
+ {library.catalog.map((libItem, index) => ( + + ))} +
+ : null + } +
+ } ); }; diff --git a/src/routes/Library/Placeholder/Placeholder.less b/src/routes/Library/Placeholder/Placeholder.less index 74908db66..55de0356a 100644 --- a/src/routes/Library/Placeholder/Placeholder.less +++ b/src/routes/Library/Placeholder/Placeholder.less @@ -8,14 +8,11 @@ flex-direction: column; align-items: center; justify-content: center; - height: 100%; + min-height: 100%; width: 100%; overflow-y: auto; .title { - display: flex; - flex: 1; - align-items: flex-end; font-size: 1.75rem; font-weight: 400; text-align: center; @@ -25,9 +22,6 @@ } .image-container { - display: flex; - flex: 1; - align-items: center; padding: 1.5rem 0; .image { @@ -37,14 +31,43 @@ } } - .button-container { + .overview { display: flex; - flex: 1; - align-items: flex-start; - margin: 1rem 0; + flex-direction: row; + align-items: center; + gap: 4rem; + margin-bottom: 1rem; + .point { + display: flex; + flex-direction: row; + align-items: center; + gap: 1.5rem; + width: 18rem; + + .icon { + flex: none; + height: 3.25rem; + width: 3.25rem; + color: var(--primary-foreground-color); + opacity: 0.3; + } + + .text { + flex: auto; + font-size: 1.1rem; + font-size: 500; + color: var(--primary-foreground-color); + opacity: 0.9; + } + } + } + + .button-container { + margin: 1rem 0; + .button { - flex: none; + display: flex; justify-content: center; height: 4rem; line-height: 4rem; @@ -54,7 +77,7 @@ text-align: center; border-radius: 3.5rem; background-color: var(--overlay-color); - + &:hover { outline: var(--focus-outline-size) solid var(--primary-foreground-color); background-color: transparent; @@ -68,11 +91,10 @@ padding: 1rem 2rem; .title { - flex: 0.5; + margin-bottom: 0; } .image-container { - flex: 2; padding: 1rem; .image { @@ -88,9 +110,17 @@ @media only screen and (max-width: @minimum) { .placeholder { + padding: 1rem 2rem; - .image-container { - flex: 1; + .overview { + flex-direction: column; + gap: 1rem; + + .point { + .text { + font-size: 1rem; + } + } } .button-container { diff --git a/src/routes/Library/Placeholder/Placeholder.tsx b/src/routes/Library/Placeholder/Placeholder.tsx index d619611d8..d854a2d54 100644 --- a/src/routes/Library/Placeholder/Placeholder.tsx +++ b/src/routes/Library/Placeholder/Placeholder.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import Icon from '@stremio/stremio-icons/react'; import { Button, Image } from 'stremio/components'; import styles from './Placeholder.less'; @@ -16,10 +17,24 @@ const Placeholder = () => {
{'
+
+
+ +
+ {t('NOT_LOGGED_IN_CLOUD')} +
+
+
+ +
+ {t('NOT_LOGGED_IN_RECOMMENDATIONS')} +
+
+
: From 2b5df908271d23f8b690c3298c812afe44fc6b30 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 25 Feb 2025 11:40:34 +0200 Subject: [PATCH 24/41] feat(useWindowSize): added hook for screen size --- src/common/index.js | 2 ++ src/common/useWindowSize.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/common/useWindowSize.js diff --git a/src/common/index.js b/src/common/index.js index 4acf8b056..ab3723cd0 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -24,6 +24,7 @@ const { default: useShell } = require('./useShell'); const useStreamingServer = require('./useStreamingServer'); const useTorrent = require('./useTorrent'); const useTranslate = require('./useTranslate'); +const useWindowSize = require('./useWindowSize'); module.exports = { FileDropProvider, @@ -55,4 +56,5 @@ module.exports = { useStreamingServer, useTorrent, useTranslate, + useWindowSize, }; diff --git a/src/common/useWindowSize.js b/src/common/useWindowSize.js new file mode 100644 index 000000000..9f7285261 --- /dev/null +++ b/src/common/useWindowSize.js @@ -0,0 +1,18 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +const { useState, useEffect } = require('react'); + +const useWindowSize = () => { + const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight }); + + useEffect(() => { + const handleResize = () => setSize({ width: window.innerWidth, height: window.innerHeight }); + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return size; +}; + +module.exports = useWindowSize; From b563ea1d10e24bc21f35af69cfec4fb98fed382e Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 25 Feb 2025 11:45:48 +0200 Subject: [PATCH 25/41] fix(BottomSheet): hide BottomSheet when screen is resized --- src/components/BottomSheet/BottomSheet.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/BottomSheet/BottomSheet.tsx b/src/components/BottomSheet/BottomSheet.tsx index 7ebfb79d8..28e2f631a 100644 --- a/src/components/BottomSheet/BottomSheet.tsx +++ b/src/components/BottomSheet/BottomSheet.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createPortal } from 'react-dom'; import classNames from 'classnames'; import useBinaryState from 'stremio/common/useBinaryState'; +import useWindowSize from 'stremio/common/useWindowSize'; import styles from './BottomSheet.less'; const CLOSE_THRESHOLD = 100; @@ -17,6 +18,7 @@ type Props = { const BottomSheet = ({ children, title, show, onClose }: Props) => { const containerRef = useRef(null); + const { width: windowWidth, height: windowHeight } = useWindowSize(); const [startOffset, setStartOffset] = useState(0); const [offset, setOffset] = useState(0); @@ -58,6 +60,10 @@ const BottomSheet = ({ children, title, show, onClose }: Props) => { !opened && onClose(); }, [opened]); + useEffect(() => { + opened && close(); + }, [windowWidth, windowHeight]); + return opened && createPortal((
From 9d17c82562aac8586f7b12441f85ffd01e7f300e Mon Sep 17 00:00:00 2001 From: Botsy Date: Tue, 25 Feb 2025 12:24:30 +0200 Subject: [PATCH 26/41] Update src/routes/Intro/styles.less Co-authored-by: Timothy Z. --- src/routes/Intro/styles.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index b42a0b144..935b0fad1 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -291,7 +291,6 @@ } } - @media only screen and (max-width: @minimum) { .intro-container { .content-container { From e10c0312334ed550e4a8ea88fee9019222a473ea Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 25 Feb 2025 12:43:57 +0200 Subject: [PATCH 27/41] fix(Calendar): styles --- src/routes/Calendar/Calendar.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Calendar/Calendar.less b/src/routes/Calendar/Calendar.less index 4763353f1..352514da4 100644 --- a/src/routes/Calendar/Calendar.less +++ b/src/routes/Calendar/Calendar.less @@ -13,7 +13,7 @@ gap: 0.5rem; width: 100%; height: 100%; - padding: 0 0 calc(1.5rem + var(--safe-area-inset-bottom)) 2rem; + padding: 0 0 2rem; .main { flex: auto; @@ -36,7 +36,7 @@ @media only screen and (max-width: @small) and (orientation: landscape) { .calendar { .content { - padding: 0 0 calc(1.5rem + var(--safe-area-inset-bottom)) 1rem; + padding: 0 0 1rem; } } } From fdda35841233c9bc8b72186dd2eb13a990e6f750 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 25 Feb 2025 12:56:40 +0200 Subject: [PATCH 28/41] refactor(Calendar): simplify the styles align the padding to other routes values --- src/routes/Calendar/Calendar.less | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/routes/Calendar/Calendar.less b/src/routes/Calendar/Calendar.less index 352514da4..63168360d 100644 --- a/src/routes/Calendar/Calendar.less +++ b/src/routes/Calendar/Calendar.less @@ -13,7 +13,7 @@ gap: 0.5rem; width: 100%; height: 100%; - padding: 0 0 2rem; + padding: 0 0 1.5rem 1.5rem; .main { flex: auto; @@ -31,12 +31,4 @@ padding: 0; } } -} - -@media only screen and (max-width: @small) and (orientation: landscape) { - .calendar { - .content { - padding: 0 0 1rem; - } - } -} +} \ No newline at end of file From 8733af871b2a694e92d676c127664d7c3ce34b94 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 25 Feb 2025 13:57:50 +0200 Subject: [PATCH 29/41] fix(BottomSheet): close BottomSheet on orientation change --- src/common/index.js | 4 +- src/common/useOrientation.ts | 63 ++++++++++++++++++++++ src/common/useWindowSize.js | 18 ------- src/components/BottomSheet/BottomSheet.tsx | 6 +-- 4 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 src/common/useOrientation.ts delete mode 100644 src/common/useWindowSize.js diff --git a/src/common/index.js b/src/common/index.js index ab3723cd0..82f7a6a0c 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -24,7 +24,7 @@ const { default: useShell } = require('./useShell'); const useStreamingServer = require('./useStreamingServer'); const useTorrent = require('./useTorrent'); const useTranslate = require('./useTranslate'); -const useWindowSize = require('./useWindowSize'); +const { default: useOrientation } = require('./useOrientation'); module.exports = { FileDropProvider, @@ -56,5 +56,5 @@ module.exports = { useStreamingServer, useTorrent, useTranslate, - useWindowSize, + useOrientation, }; diff --git a/src/common/useOrientation.ts b/src/common/useOrientation.ts new file mode 100644 index 000000000..b92c7b394 --- /dev/null +++ b/src/common/useOrientation.ts @@ -0,0 +1,63 @@ +// Copyright (C) 2017-2025 Smart code 203358507 + +import { useState, useEffect } from 'react'; + +type DeviceOrientationData = { + alpha: number | null; + beta: number | null; + gamma: number | null; + absolute: boolean | null; + permissionGranted: boolean; +}; + +const useOrientation = () => { + const [orientation, setOrientation] = useState({ + alpha: null, + beta: null, + gamma: null, + absolute: null, + permissionGranted: false, + }); + + const requestPermission = async () => { + if ( + typeof DeviceOrientationEvent !== 'undefined' && + (DeviceOrientationEvent as any).requestPermission + ) { + try { + const permissionState = await (DeviceOrientationEvent as any).requestPermission(); + if (permissionState === 'granted') { + setOrientation((prev) => ({ ...prev, permissionGranted: true })); + } + } catch (error) { + console.error('Error requesting DeviceOrientation permission:', error); + } + } else { + setOrientation((prev) => ({ ...prev, permissionGranted: true })); + } + }; + + useEffect(() => { + const handleOrientationChange = (event: DeviceOrientationEvent) => { + setOrientation((prev) => ({ + ...prev, + alpha: event.alpha ?? null, + beta: event.beta ?? null, + gamma: event.gamma ?? null, + absolute: event.absolute ?? null, + })); + }; + + if (orientation.permissionGranted && window.DeviceOrientationEvent) { + window.addEventListener('deviceorientation', handleOrientationChange); + } + + return () => { + window.removeEventListener('deviceorientation', handleOrientationChange); + }; + }, [orientation.permissionGranted]); + + return { ...orientation, requestPermission }; +}; + +export default useOrientation; diff --git a/src/common/useWindowSize.js b/src/common/useWindowSize.js deleted file mode 100644 index 9f7285261..000000000 --- a/src/common/useWindowSize.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2017-2025 Smart code 203358507 - -const { useState, useEffect } = require('react'); - -const useWindowSize = () => { - const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight }); - - useEffect(() => { - const handleResize = () => setSize({ width: window.innerWidth, height: window.innerHeight }); - - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - return size; -}; - -module.exports = useWindowSize; diff --git a/src/components/BottomSheet/BottomSheet.tsx b/src/components/BottomSheet/BottomSheet.tsx index 28e2f631a..d7dfe7130 100644 --- a/src/components/BottomSheet/BottomSheet.tsx +++ b/src/components/BottomSheet/BottomSheet.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createPortal } from 'react-dom'; import classNames from 'classnames'; import useBinaryState from 'stremio/common/useBinaryState'; -import useWindowSize from 'stremio/common/useWindowSize'; +import useOrientation from 'stremio/common/useOrientation'; import styles from './BottomSheet.less'; const CLOSE_THRESHOLD = 100; @@ -18,7 +18,7 @@ type Props = { const BottomSheet = ({ children, title, show, onClose }: Props) => { const containerRef = useRef(null); - const { width: windowWidth, height: windowHeight } = useWindowSize(); + const orientation = useOrientation(); const [startOffset, setStartOffset] = useState(0); const [offset, setOffset] = useState(0); @@ -62,7 +62,7 @@ const BottomSheet = ({ children, title, show, onClose }: Props) => { useEffect(() => { opened && close(); - }, [windowWidth, windowHeight]); + }, [orientation]); return opened && createPortal((
From dc5c94b461c2d40f9891bbe0f6cc225b3b67965e Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 25 Feb 2025 14:54:32 +0200 Subject: [PATCH 30/41] refactor(useOrientation): refactor hook to not ask for permissions --- src/common/useOrientation.ts | 61 ++++++++++-------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/src/common/useOrientation.ts b/src/common/useOrientation.ts index b92c7b394..ec0eac293 100644 --- a/src/common/useOrientation.ts +++ b/src/common/useOrientation.ts @@ -1,63 +1,34 @@ // Copyright (C) 2017-2025 Smart code 203358507 -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; -type DeviceOrientationData = { - alpha: number | null; - beta: number | null; - gamma: number | null; - absolute: boolean | null; - permissionGranted: boolean; -}; +type DeviceOrientation = 'landscape' | 'portrait'; const useOrientation = () => { - const [orientation, setOrientation] = useState({ - alpha: null, - beta: null, - gamma: null, - absolute: null, - permissionGranted: false, - }); + const [windowHeight, setWindowHeight] = useState(window.innerHeight); + const [windowWidth, setWindowWidth] = useState(window.innerWidth); - const requestPermission = async () => { - if ( - typeof DeviceOrientationEvent !== 'undefined' && - (DeviceOrientationEvent as any).requestPermission - ) { - try { - const permissionState = await (DeviceOrientationEvent as any).requestPermission(); - if (permissionState === 'granted') { - setOrientation((prev) => ({ ...prev, permissionGranted: true })); - } - } catch (error) { - console.error('Error requesting DeviceOrientation permission:', error); - } + const orientation: DeviceOrientation = useMemo(() => { + if (windowHeight > windowWidth) { + return 'portrait'; } else { - setOrientation((prev) => ({ ...prev, permissionGranted: true })); + return 'landscape'; } - }; + }, [windowWidth, windowHeight]); useEffect(() => { - const handleOrientationChange = (event: DeviceOrientationEvent) => { - setOrientation((prev) => ({ - ...prev, - alpha: event.alpha ?? null, - beta: event.beta ?? null, - gamma: event.gamma ?? null, - absolute: event.absolute ?? null, - })); + const handleResize = () => { + setWindowHeight(window.innerHeight); + setWindowWidth(window.innerWidth); }; - if (orientation.permissionGranted && window.DeviceOrientationEvent) { - window.addEventListener('deviceorientation', handleOrientationChange); - } - + window.addEventListener('resize', handleResize); return () => { - window.removeEventListener('deviceorientation', handleOrientationChange); + window.removeEventListener('resize', handleResize); }; - }, [orientation.permissionGranted]); + }, [window.innerWidth, window.innerHeight]); - return { ...orientation, requestPermission }; + return { orientation }; }; export default useOrientation; From a2131393195445ce492d3af268351f06770c5818 Mon Sep 17 00:00:00 2001 From: Botzy Date: Tue, 25 Feb 2025 15:04:27 +0200 Subject: [PATCH 31/41] fix(useOrientation): fix hook return value --- src/common/useOrientation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/useOrientation.ts b/src/common/useOrientation.ts index ec0eac293..add8d1d40 100644 --- a/src/common/useOrientation.ts +++ b/src/common/useOrientation.ts @@ -28,7 +28,7 @@ const useOrientation = () => { }; }, [window.innerWidth, window.innerHeight]); - return { orientation }; + return orientation; }; export default useOrientation; From 7220635d79575a201826e5b5e8523238b58d8903 Mon Sep 17 00:00:00 2001 From: Ivelin Megdanov Date: Wed, 26 Feb 2025 12:05:17 +0200 Subject: [PATCH 32/41] Show catalog names while loading --- src/routes/Search/Search.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index 4b052ed46..abfa99740 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -4,7 +4,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); const debounce = require('lodash.debounce'); -const { useTranslation } = require('react-i18next'); +const useTranslate = require('stremio/common/useTranslate'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { withCoreSuspender, getVisibleChildrenRange } = require('stremio/common'); const { Image, MainNavBars, MetaItem, MetaRow } = require('stremio/components'); @@ -14,7 +14,7 @@ const styles = require('./styles'); const THRESHOLD = 100; const Search = ({ queryParams }) => { - const { t } = useTranslation(); + const t = useTranslate(); const [search, loadSearchRows] = useSearch(queryParams); const query = React.useMemo(() => { return search.selected !== null ? @@ -52,24 +52,24 @@ const Search = ({ queryParams }) => { query === null ?
-
{t('SEARCH_ANYTHING')}
+
{t.string('SEARCH_ANYTHING')}
-
{t('SEARCH_CATEGORIES')}
+
{t.string('SEARCH_CATEGORIES')}
-
{t('SEARCH_PERSONS')}
+
{t.string('SEARCH_PERSONS')}
-
{t('SEARCH_PROTOCOLS')}
+
{t.string('SEARCH_PROTOCOLS')}
-
{t('SEARCH_TYPES')}
+
{t.string('SEARCH_TYPES')}
@@ -81,7 +81,7 @@ const Search = ({ queryParams }) => { src={require('/images/empty.png')} alt={' '} /> -
{ t('STREMIO_TV_SEARCH_NO_ADDONS') }
+
{ t.string('STREMIO_TV_SEARCH_NO_ADDONS') }
: search.catalogs.map((catalog, index) => { @@ -110,11 +110,13 @@ const Search = ({ queryParams }) => { return null; } default: { + const loadingTitle = `${catalog.addon.manifest.name}: ${t.catalogTitle(catalog)}`; return ( ); } From 3d56023ffd662f9eb905eb3ea3ee2355f2ae0f38 Mon Sep 17 00:00:00 2001 From: Ivelin Megdanov Date: Wed, 26 Feb 2025 12:25:47 +0200 Subject: [PATCH 33/41] Added addon name to catalogTitle function --- src/common/useTranslate.js | 3 ++- src/routes/Search/Search.js | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/useTranslate.js b/src/common/useTranslate.js index 7214a4a1e..342b27dcf 100644 --- a/src/common/useTranslate.js +++ b/src/common/useTranslate.js @@ -21,10 +21,11 @@ const useTranslate = () => { if (addon && id && name) { const partialKey = `${addon.manifest.id.split('.').join('_')}_${id}`; const translatedName = stringWithPrefix(partialKey, 'CATALOG_', name); + const addonName = addon.manifest.name; if (type && withType) { const translatedType = stringWithPrefix(type, 'TYPE_'); - return `${translatedName} - ${translatedType}`; + return `${addonName}: ${translatedName} - ${translatedType}`; } return translatedName; diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index abfa99740..58e6e834b 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -110,13 +110,12 @@ const Search = ({ queryParams }) => { return null; } default: { - const loadingTitle = `${catalog.addon.manifest.name}: ${t.catalogTitle(catalog)}`; return ( ); } From 6420b5e0c95f82df3414e9949f100724cf1cd3fc Mon Sep 17 00:00:00 2001 From: Ivelin Megdanov Date: Wed, 26 Feb 2025 12:30:59 +0200 Subject: [PATCH 34/41] Added the loading title to the Board --- src/routes/Board/Board.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/routes/Board/Board.js b/src/routes/Board/Board.js index 9e721edf2..13acb4a86 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 { useTranslation } = require('react-i18next'); +const useTranslate = require('stremio/common/useTranslate'); const { useStreamingServer, useNotifications, withCoreSuspender, getVisibleChildrenRange, useProfile } = require('stremio/common'); const { ContinueWatchingItem, EventModal, MainNavBars, MetaItem, MetaRow } = require('stremio/components'); const useBoard = require('./useBoard'); @@ -14,7 +14,7 @@ const { default: StreamingServerWarning } = require('./StreamingServerWarning'); const THRESHOLD = 5; const Board = () => { - const { t } = useTranslation(); + const t = useTranslate(); const streamingServer = useStreamingServer(); const continueWatchingPreview = useContinueWatchingPreview(); const [board, loadBoardRows] = useBoard(); @@ -55,7 +55,7 @@ const Board = () => { continueWatchingPreview.items.length > 0 ? { key={index} className={classnames(styles['board-row'], styles['board-row-poster'], 'animation-fade-in')} catalog={catalog} + title={t.catalogTitle(catalog)} /> ); } From fef0a57ac719b721fca2571a1c0760257569ed91 Mon Sep 17 00:00:00 2001 From: Ivelin Megdanov Date: Thu, 27 Feb 2025 13:41:30 +0200 Subject: [PATCH 35/41] Removed addon name from catalog title --- src/common/useTranslate.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/useTranslate.js b/src/common/useTranslate.js index 342b27dcf..7214a4a1e 100644 --- a/src/common/useTranslate.js +++ b/src/common/useTranslate.js @@ -21,11 +21,10 @@ const useTranslate = () => { if (addon && id && name) { const partialKey = `${addon.manifest.id.split('.').join('_')}_${id}`; const translatedName = stringWithPrefix(partialKey, 'CATALOG_', name); - const addonName = addon.manifest.name; if (type && withType) { const translatedType = stringWithPrefix(type, 'TYPE_'); - return `${addonName}: ${translatedName} - ${translatedType}`; + return `${translatedName} - ${translatedType}`; } return translatedName; From faba70434286a98ca5635c05df77319754038fa4 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 27 Feb 2025 15:42:24 +0100 Subject: [PATCH 36/41] chore: v5.0.0-beta.19 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index dadfc5ec8..e862ca626 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stremio", - "version": "5.0.0-beta.18", + "version": "5.0.0-beta.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stremio", - "version": "5.0.0-beta.18", + "version": "5.0.0-beta.19", "license": "gpl-2.0", "dependencies": { "@babel/runtime": "7.26.0", diff --git a/package.json b/package.json index 04a96a75e..c0b375ab4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.18", + "version": "5.0.0-beta.19", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", From 72053246cbda476c601131e79d0e666e194868a8 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 27 Feb 2025 22:55:23 +0100 Subject: [PATCH 37/41] fix(useShell): use chrome.webview instead of qt webChannelTransport --- src/common/useShell.ts | 10 ++++++---- src/types/global.d.ts | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/common/useShell.ts b/src/common/useShell.ts index 7baab60bc..1a7bcb6ee 100644 --- a/src/common/useShell.ts +++ b/src/common/useShell.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import EventEmitter from 'eventemitter3'; const SHELL_EVENT_OBJECT = 'transport'; -const transport = globalThis?.qt?.webChannelTransport; +const transport = globalThis?.chrome?.webview; const events = new EventEmitter(); enum ShellEventType { @@ -30,7 +30,7 @@ const useShell = () => { const send = (method: string, ...args: (string | number)[]) => { try { - transport?.send(JSON.stringify({ + transport?.postMessage(JSON.stringify({ id: createId(), type: ShellEventType.INVOKE_METHOD, object: SHELL_EVENT_OBJECT, @@ -45,10 +45,9 @@ const useShell = () => { useEffect(() => { if (!transport) return; - transport.onmessage = ({ data }) => { + const onMessage = ({ data }: { data: string }) => { try { const { type, args } = JSON.parse(data) as ShellEvent; - if (type === ShellEventType.SIGNAL) { const [methodName, methodArg] = args; events.emit(methodName, methodArg); @@ -57,6 +56,9 @@ const useShell = () => { console.error('Shell', 'Failed to handle event', e); } }; + + transport.addEventListener('message', onMessage); + return () => transport.removeEventListener('message', onMessage); }, []); return { diff --git a/src/types/global.d.ts b/src/types/global.d.ts index d1d601d5e..3849b8914 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -13,8 +13,19 @@ interface Qt { webChannelTransport: QtTransport, } +interface ChromeWebView { + addEventListener: (type: 'message', listenenr: (event: any) => void) => void, + removeEventListener: (type: 'message', listenenr: (event: any) => void) => void, + postMessage: (message: string) => void, +} + +interface Chrome { + webview: ChromeWebView, +} + declare global { var qt: Qt | undefined; + var chrome: Chrome | undefined; } export {}; From 7ea8b18c59a24701e0acd6bc4745b62e9c3812f2 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 27 Feb 2025 23:13:16 +0100 Subject: [PATCH 38/41] fix(Settings): quit on close setting label --- src/routes/Settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 312ad9e49..f238c1d02 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -341,7 +341,7 @@ const Settings = () => {
{ t('SETTINGS_NAV_PLAYER') }
-
{t('SETTINGS_SECTION_SUBTITLES')}
+
{t('SETTINGS_CLOSE_WINDOW')}
From 24d11b4cf942957a602d68f4667eadd509bfed49 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 27 Feb 2025 23:15:44 +0100 Subject: [PATCH 39/41] chore: v5.0.0-beta.20 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e862ca626..0824f882f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stremio", - "version": "5.0.0-beta.19", + "version": "5.0.0-beta.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stremio", - "version": "5.0.0-beta.19", + "version": "5.0.0-beta.20", "license": "gpl-2.0", "dependencies": { "@babel/runtime": "7.26.0", diff --git a/package.json b/package.json index c0b375ab4..4cab1781f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.19", + "version": "5.0.0-beta.20", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", From 28dbdaa20dd48e1cb669c3132d9b947a3c1b6fe5 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 28 Feb 2025 09:06:04 +0200 Subject: [PATCH 40/41] feat: MetaDetails selects appropriate season: - For non-watched series it choses 1st season - For watched series it uses the LibraryItem video to choose the same season Signed-off-by: Lachezar Lechev --- src/routes/MetaDetails/VideosList/VideosList.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 557b3df1f..00ee07924 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -20,6 +20,7 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, : []; }, [metaItem]); + // Orders season from 1 to X and 0 (special season) at the end const seasons = React.useMemo(() => { return videos .map(({ season }) => season) @@ -36,17 +37,27 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, return season; } + if (libraryItem?.state.video_id && videos) { + const video = videos?.find((video) => video.id === libraryItem.state.video_id); + + if (video && video.season && seasons.includes(video.season)) { + return video.season; + } + } + const nonSpecialSeasons = seasons.filter((season) => season !== 0); if (nonSpecialSeasons.length > 0) { - return nonSpecialSeasons[nonSpecialSeasons.length - 1]; + // default to 1st season + return nonSpecialSeasons[0]; } if (seasons.length > 0) { - return seasons[seasons.length - 1]; + // default to 1st season + return seasons[0]; } return null; - }, [seasons, season]); + }, [seasons, season, videos, libraryItem]); const videosForSeason = React.useMemo(() => { return videos .filter((video) => { From dba40169793aa6eaee8e0c55178bd4d53394ced9 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 28 Feb 2025 11:01:02 +0200 Subject: [PATCH 41/41] chore: VideoList - clean up Signed-off-by: Lachezar Lechev --- src/routes/MetaDetails/VideosList/VideosList.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/routes/MetaDetails/VideosList/VideosList.js b/src/routes/MetaDetails/VideosList/VideosList.js index 00ee07924..58614c9d9 100644 --- a/src/routes/MetaDetails/VideosList/VideosList.js +++ b/src/routes/MetaDetails/VideosList/VideosList.js @@ -20,7 +20,6 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, : []; }, [metaItem]); - // Orders season from 1 to X and 0 (special season) at the end const seasons = React.useMemo(() => { return videos .map(({ season }) => season) @@ -37,22 +36,18 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, return season; } - if (libraryItem?.state.video_id && videos) { - const video = videos?.find((video) => video.id === libraryItem.state.video_id); + const video = videos?.find((video) => video.id === libraryItem?.state.video_id); - if (video && video.season && seasons.includes(video.season)) { - return video.season; - } + if (video && video.season && seasons.includes(video.season)) { + return video.season; } const nonSpecialSeasons = seasons.filter((season) => season !== 0); if (nonSpecialSeasons.length > 0) { - // default to 1st season return nonSpecialSeasons[0]; } if (seasons.length > 0) { - // default to 1st season return seasons[0]; }