Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/common/getMetaDetailsHref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2017-2026 Smart code 203358507

type DeepLinks = {
metaDetailsVideos?: string | null,
metaDetailsStreams?: string | null,
};

const getMetaDetailsHref = (deepLinks?: DeepLinks | null, videosFirst = false): string | null => {
if (!deepLinks) {
return null;
}

const primaryHref = videosFirst ? deepLinks.metaDetailsVideos : deepLinks.metaDetailsStreams;
const fallbackHref = videosFirst ? deepLinks.metaDetailsStreams : deepLinks.metaDetailsVideos;

return typeof primaryHref === 'string' ? primaryHref : typeof fallbackHref === 'string' ? fallbackHref : null;
};

export default getMetaDetailsHref;
46 changes: 23 additions & 23 deletions src/components/LibItem/LibItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

const React = require('react');
const { useNavigate } = require('react-router');
const { useNavigateWithOrigin } = require('stremio-router');
const { default: toPath } = require('stremio-router/toPath');
const PropTypes = require('prop-types');
const { useCore } = require('stremio/core');
const { default: getMetaDetailsHref } = require('stremio/common/getMetaDetailsHref');
const MetaItem = require('stremio/components/MetaItem');
const { t } = require('i18next');

const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
const LibItem = ({ _id, removable, notifications, watched, detailsVideosFirst, ...props }) => {
const navigate = useNavigate();
const { navigateWithOrigin } = useNavigateWithOrigin();
const core = useCore();
const detailsHref = React.useMemo(() => getMetaDetailsHref(props.deepLinks, detailsVideosFirst), [props.deepLinks, detailsVideosFirst]);
const playerHref = props.deepLinks && typeof props.deepLinks.player === 'string' ? props.deepLinks.player : null;

const newVideos = React.useMemo(() => {
const count = notifications.items?.[_id]?.length ?? 0;
Expand All @@ -27,11 +32,11 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
].filter(({ value }) => {
switch (value) {
case 'play':
return props.deepLinks && typeof props.deepLinks.player === 'string';
return typeof playerHref === 'string';
case 'details':
return props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string');
return typeof detailsHref === 'string';
case 'watched':
return typeof watched !== 'undefined' && props.deepLinks && (typeof props.deepLinks.metaDetailsVideos === 'string' || typeof props.deepLinks.metaDetailsStreams === 'string');
return typeof watched !== 'undefined' && typeof detailsHref === 'string';
case 'dismiss':
return typeof _id === 'string' && props.progress !== null && !isNaN(props.progress) && props.progress > 0;
case 'remove':
Expand All @@ -41,7 +46,7 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
...option,
label: t(option.label)
}));
}, [_id, removable, props.progress, props.deepLinks, watched]);
}, [_id, removable, props.progress, playerHref, detailsHref, watched]);

const optionOnSelect = React.useCallback((event) => {
if (typeof props.optionOnSelect === 'function') {
Expand All @@ -51,19 +56,15 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
if (!event.nativeEvent.optionSelectPrevented) {
switch (event.value) {
case 'play': {
if (props.deepLinks && typeof props.deepLinks.player === 'string') {
navigate(toPath(props.deepLinks.player));
if (typeof playerHref === 'string') {
navigate(toPath(playerHref));
}

break;
}
case 'details': {
if (props.deepLinks) {
if (typeof props.deepLinks.metaDetailsVideos === 'string') {
navigate(toPath(props.deepLinks.metaDetailsVideos));
} else if (typeof props.deepLinks.metaDetailsStreams === 'string') {
navigate(toPath(props.deepLinks.metaDetailsStreams));
}
if (typeof detailsHref === 'string') {
navigateWithOrigin(detailsHref);
}

break;
Expand Down Expand Up @@ -119,26 +120,24 @@ const LibItem = ({ _id, removable, notifications, watched, ...props }) => {
}
}
}
}, [_id, props.deepLinks, props.optionOnSelect]);
}, [_id, detailsHref, navigate, navigateWithOrigin, playerHref, props.optionOnSelect, watched]);

const onPlayClick = React.useMemo(() => {
if (props.deepLinks && typeof props.deepLinks.player === 'string') {
return (event) => {
event.preventDefault();
navigate(toPath(props.deepLinks.player));
};
const onPlayClick = React.useCallback((event) => {
event.preventDefault();
if (typeof playerHref === 'string') {
navigate(toPath(playerHref));
}
return null;
}, [props.deepLinks]);
}, [navigate, playerHref]);

return (
<MetaItem
{...props}
href={detailsHref}
watched={watched}
newVideos={newVideos}
options={options}
optionOnSelect={optionOnSelect}
onPlayClick={onPlayClick}
onPlayClick={typeof playerHref === 'string' ? onPlayClick : null}
/>
);
};
Expand All @@ -149,6 +148,7 @@ LibItem.propTypes = {
progress: PropTypes.number,
notifications: PropTypes.object,
watched: PropTypes.bool,
detailsVideosFirst: PropTypes.bool,
deepLinks: PropTypes.shape({
metaDetailsVideos: PropTypes.string,
metaDetailsStreams: PropTypes.string,
Expand Down
34 changes: 17 additions & 17 deletions src/components/MetaItem/MetaItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,17 @@ const { default: Button } = require('stremio/components/Button');
const { default: Image } = require('stremio/components/Image');
const Multiselect = require('stremio/components/Multiselect');
const useBinaryState = require('stremio/common/useBinaryState');
const { default: getMetaDetailsHref } = require('stremio/common/getMetaDetailsHref');
const { ICON_FOR_TYPE } = require('stremio/common/CONSTANTS');
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 MetaItem = React.memo(({ className, type, name, poster, posterShape, posterChangeCursor, progress, newVideos, options, deepLinks, href: customHref, dataset, optionOnSelect, onDismissClick, onPlayClick, watched, ...props }) => {
const { t } = useTranslation();
const { navigateWithOrigin } = useNavigateWithOrigin();
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
const href = React.useMemo(() => {
return deepLinks ?
typeof deepLinks.metaDetailsStreams === 'string' ?
deepLinks.metaDetailsStreams
:
typeof deepLinks.metaDetailsVideos === 'string' ?
deepLinks.metaDetailsVideos
:
typeof deepLinks.player === 'string' ?
deepLinks.player
:
null
:
null;
}, [deepLinks]);
return typeof customHref === 'string' ? customHref : getMetaDetailsHref(deepLinks);
}, [customHref, deepLinks]);
const metaItemOnClick = React.useCallback((event) => {
if (event.nativeEvent.selectPrevented) {
event.preventDefault();
Expand All @@ -46,6 +35,16 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste
const menuOnClick = React.useCallback((event) => {
event.nativeEvent.selectPrevented = true;
}, []);
const dismissOnClick = React.useCallback((event) => {
event.preventDefault();
event.stopPropagation();
onDismissClick(event);
}, [onDismissClick]);
const playOnClick = React.useCallback((event) => {
event.preventDefault();
event.stopPropagation();
onPlayClick(event);
}, [onPlayClick]);
const menuOnSelect = React.useCallback((event) => {
if (typeof optionOnSelect === 'function') {
optionOnSelect({
Expand All @@ -71,7 +70,7 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste
<div className={classnames(styles['poster-container'], { 'poster-change-cursor': posterChangeCursor })}>
{
onDismissClick ?
<div title={t('LIBRARY_RESUME_DISMISS')} className={styles['dismiss-icon-layer']} onClick={onDismissClick}>
<div title={t('LIBRARY_RESUME_DISMISS')} className={styles['dismiss-icon-layer']} onClick={dismissOnClick}>
<Icon className={styles['dismiss-icon']} name={'close'} />
<div className={styles['dismiss-icon-backdrop']} />
</div>
Expand All @@ -96,7 +95,7 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste
</div>
{
onPlayClick ?
<div title={t('CONTINUE_WATCHING')} className={styles['play-icon-layer']} onClick={onPlayClick}>
<div title={t('CONTINUE_WATCHING')} className={styles['play-icon-layer']} onClick={playOnClick}>
<Icon className={styles['play-icon']} name={'play'} />
<div className={styles['play-icon-outer']} />
<div className={styles['play-icon-background']} />
Expand Down Expand Up @@ -170,6 +169,7 @@ MetaItem.propTypes = {
progress: PropTypes.number,
newVideos: PropTypes.number,
options: PropTypes.array,
href: PropTypes.string,
deepLinks: PropTypes.shape({
metaDetailsVideos: PropTypes.string,
metaDetailsStreams: PropTypes.string,
Expand Down
8 changes: 7 additions & 1 deletion src/routes/Library/Library.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ const Library = ({ model }) => {
<div ref={scrollContainerRef} className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll}>
{
library.catalog.map((libItem, index) => (
<LibItem {...libItem} notifications={notifications} removable={model === 'library'} key={index} />
<LibItem
{...libItem}
key={index}
notifications={notifications}
removable={model === 'library'}
detailsVideosFirst={model === 'library'}
/>
))
}
</div>
Expand Down