feat: watched on discover & details

This commit is contained in:
higorgoulart 2025-10-31 19:52:30 -03:00
parent 536be36005
commit 6833bb719d
6 changed files with 156 additions and 16 deletions

View file

@ -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 (
<MetaItem
{...props}
watched={watched}
playname={selected}
className={classnames({ 'selected': selected })}
options={options}
optionOnSelect={optionOnSelect}
/>
);
};
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;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2023 Smart code 203358507
const DiscItem = require('./DiscItem');
module.exports = DiscItem;

View file

@ -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' ?
<ActionButton
className={styles['action-button']}
icon={watched ? 'eye-off' : 'eye'}
label={watched ? t('CTX_MARK_UNWATCHED') : t('CTX_MARK_WATCHED')}
tooltip={compact}
tabIndex={compact ? -1 : 0}
onClick={toggleWatched}
/>
:
null
}
{
typeof showHref === 'string' && compact ?
<ActionButton
@ -298,6 +311,8 @@ MetaPreview.propTypes = {
trailerStreams: PropTypes.array,
inLibrary: PropTypes.bool,
toggleInLibrary: PropTypes.func,
watched: PropTypes.bool,
toggleWatched: PropTypes.func,
ratingInfo: PropTypes.object,
};

View file

@ -11,6 +11,7 @@ import EventModal from './EventModal';
import HorizontalScroll from './HorizontalScroll';
import Image from './Image';
import LibItem from './LibItem';
import DiscItem from './DiscItem';
import MainNavBars from './MainNavBars';
import MetaItem from './MetaItem';
import MetaPreview from './MetaPreview';
@ -45,6 +46,7 @@ export {
HorizontalScroll,
Image,
LibItem,
DiscItem,
MainNavBars,
MetaItem,
MetaPreview,

View file

@ -7,7 +7,7 @@ const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
const { useServices } = require('stremio/services');
const { CONSTANTS, useBinaryState, useOnScrollToBottom, withCoreSuspender } = require('stremio/common');
const { AddonDetailsModal, Button, DelayedRenderer, Image, MainNavBars, MetaItem, MetaPreview, ModalDialog, MultiselectMenu } = require('stremio/components');
const { AddonDetailsModal, Button, DelayedRenderer, Image, MainNavBars, DiscItem, MetaPreview, ModalDialog, MultiselectMenu } = require('stremio/components');
const useDiscover = require('./useDiscover');
const useSelectableInputs = require('./useSelectableInputs');
const styles = require('./styles');
@ -74,6 +74,32 @@ const Discover = ({ urlParams, queryParams }) => {
}
});
}, [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 }) => {
</div>
:
<div ref={metasContainerRef} className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll} onFocusCapture={metaItemsOnFocusCapture}>
{discover.catalog.content.content.map((metaItem, index) => (
<MetaItem
key={index}
className={classnames({ 'selected': selectedMetaItemIndex === index })}
type={metaItem.type}
name={metaItem.name}
poster={metaItem.poster}
posterShape={metaItem.posterShape}
playname={selectedMetaItemIndex === index}
deepLinks={metaItem.deepLinks}
watched={metaItem.watched}
data-index={index}
onClick={metaItemOnClick}
/>
{discover.catalog.content.content.map((discItem, index) => (
<DiscItem {...discItem} toggleWatched={toggleWatched} selected={selectedMetaItemIndex === index} key={index} data-index={index} onClick={metaItemOnClick} />
))}
</div>
}
@ -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}
/>

View file

@ -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}
/>