From 6833bb719d3e894e6c511bec96a309cfd2f364c9 Mon Sep 17 00:00:00 2001 From: higorgoulart Date: Fri, 31 Oct 2025 19:52:30 -0300 Subject: [PATCH] feat: watched on discover & details --- src/components/DiscItem/DiscItem.js | 74 +++++++++++++++++++++++ src/components/DiscItem/index.js | 5 ++ src/components/MetaPreview/MetaPreview.js | 17 +++++- src/components/index.ts | 2 + src/routes/Discover/Discover.js | 46 +++++++++----- src/routes/MetaDetails/MetaDetails.js | 28 +++++++++ 6 files changed, 156 insertions(+), 16 deletions(-) create mode 100644 src/components/DiscItem/DiscItem.js create mode 100644 src/components/DiscItem/index.js diff --git a/src/components/DiscItem/DiscItem.js b/src/components/DiscItem/DiscItem.js new file mode 100644 index 000000000..45f6499a6 --- /dev/null +++ b/src/components/DiscItem/DiscItem.js @@ -0,0 +1,74 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const { useServices } = require('stremio/services'); +const PropTypes = require('prop-types'); +const classnames = require('classnames'); +const MetaItem = require('stremio/components/MetaItem'); +const { t } = require('i18next'); + +const DiscItem = ({ id, watched, selected, toggleWatched, ...props }) => { + + const { core } = useServices(); + + const options = React.useMemo(() => { + return [ + { label: watched ? 'CTX_MARK_UNWATCHED' : 'CTX_MARK_WATCHED', value: 'watched' }, + ].filter(({ value }) => { + switch (value) { + case 'watched': + return props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string'); + } + }).map((option) => ({ + ...option, + label: t(option.label) + })); + }, [id, props.deepLinks, watched]); + + const optionOnSelect = React.useCallback((event) => { + if (typeof props.optionOnSelect === 'function') { + props.optionOnSelect(event); + } + + if (!event.nativeEvent.optionSelectPrevented) { + switch (event.value) { + case 'watched': { + if (typeof id === 'string') { + if (typeof toggleWatched === 'function') { + toggleWatched(); + } + } + + break; + } + } + } + }, [id, props.deepLinks, props.optionOnSelect]); + + return ( + + ); +}; + +DiscItem.propTypes = { + id: PropTypes.string, + removable: PropTypes.bool, + watched: PropTypes.bool, + selected: PropTypes.bool, + deepLinks: PropTypes.shape({ + metaDetailsVideos: PropTypes.string, + metaDetailsStreams: PropTypes.string, + player: PropTypes.string + }), + toggleWatched: PropTypes.func, + optionOnSelect: PropTypes.func +}; + +module.exports = DiscItem; diff --git a/src/components/DiscItem/index.js b/src/components/DiscItem/index.js new file mode 100644 index 000000000..f0fe335c2 --- /dev/null +++ b/src/components/DiscItem/index.js @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const DiscItem = require('./DiscItem'); + +module.exports = DiscItem; diff --git a/src/components/MetaPreview/MetaPreview.js b/src/components/MetaPreview/MetaPreview.js index 13717919a..e4ec82ded 100644 --- a/src/components/MetaPreview/MetaPreview.js +++ b/src/components/MetaPreview/MetaPreview.js @@ -25,7 +25,7 @@ const ALLOWED_LINK_REDIRECTS = [ routesRegexp.metadetails.regexp ]; -const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, ratingInfo }, ref) => { +const MetaPreview = React.forwardRef(({ className, compact, name, logo, background, runtime, releaseInfo, released, description, deepLinks, links, trailerStreams, inLibrary, toggleInLibrary, watched, toggleWatched, ratingInfo }, ref) => { const { t } = useTranslation(); const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false); const linksGroups = React.useMemo(() => { @@ -221,6 +221,19 @@ const MetaPreview = React.forwardRef(({ className, compact, name, logo, backgrou : null } + { + typeof toggleWatched === 'function' ? + + : + null + } { typeof showHref === 'string' && compact ? { } }); }, [selectedMetaItem]); + const toggleWatched = React.useCallback(() => { + if (selectedMetaItem === null) { + return; + } + + if (!selectedMetaItem.inLibrary) { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'AddToLibrary', + args: selectedMetaItem + } + }); + } + + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'LibraryItemMarkAsWatched', + args: { + id: selectedMetaItem.id, + is_watched: !selectedMetaItem.watched + } + } + }); + }, [selectedMetaItem]); const metaItemsOnFocusCapture = React.useCallback((event) => { if (event.target.dataset.index !== null && !isNaN(event.target.dataset.index)) { setSelectedMetaItemIndex(parseInt(event.target.dataset.index, 10)); @@ -157,20 +183,8 @@ const Discover = ({ urlParams, queryParams }) => { :
- {discover.catalog.content.content.map((metaItem, index) => ( - + {discover.catalog.content.content.map((discItem, index) => ( + ))}
} @@ -193,6 +207,8 @@ const Discover = ({ urlParams, queryParams }) => { trailerStreams={selectedMetaItem.trailerStreams} inLibrary={selectedMetaItem.inLibrary} toggleInLibrary={selectedMetaItem.inLibrary ? removeFromLibrary : addToLibrary} + watched={selectedMetaItem.watched} + toggleWatched={toggleWatched} metaId={selectedMetaItem.id} like={selectedMetaItem.like} /> diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index fd27478b5..478e59bee 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -64,6 +64,32 @@ const MetaDetails = ({ urlParams, queryParams }) => { } }); }, [metaDetails]); + const toggleWatched = React.useCallback(() => { + if (metaDetails.metaItem.content.content === null || metaDetails.metaItem.content.type !== 'Ready') { + return; + } + + if (!metaDetails.metaItem.content.content.inLibrary) { + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'AddToLibrary', + args: metaDetails.metaItem.content.content + } + }); + } + + core.transport.dispatch({ + action: 'Ctx', + args: { + action: 'LibraryItemMarkAsWatched', + args: { + id: metaDetails.metaItem.content.content.id, + is_watched: !metaDetails.metaItem.content.content.watched + } + } + }); + }, [metaDetails]); const toggleNotifications = React.useCallback(() => { if (metaDetails.libraryItem) { core.transport.dispatch({ @@ -168,6 +194,8 @@ const MetaDetails = ({ urlParams, queryParams }) => { trailerStreams={metaDetails.metaItem.content.content.trailerStreams} inLibrary={metaDetails.metaItem.content.content.inLibrary} toggleInLibrary={metaDetails.metaItem.content.content.inLibrary ? removeFromLibrary : addToLibrary} + watched={metaDetails.metaItem.content.content.watched} + toggleWatched={toggleWatched} metaId={metaDetails.metaItem.content.content.id} ratingInfo={metaDetails.ratingInfo} />