mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-05-24 12:32:30 +00:00
not using calendar
This commit is contained in:
parent
e2177938d1
commit
a9ad4cefc8
4 changed files with 112 additions and 7 deletions
|
|
@ -13,7 +13,7 @@ const useBinaryState = require('stremio/common/useBinaryState');
|
|||
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, dataset, optionOnSelect, onDismissClick, onPlayClick, watched, nextEpisodeReleaseDate, ...props }) => {
|
||||
const { t } = useTranslation();
|
||||
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
|
||||
const href = React.useMemo(() => {
|
||||
|
|
@ -61,8 +61,14 @@ const MetaItem = React.memo(({ className, type, name, poster, posterShape, poste
|
|||
const renderMenuLabelContent = React.useCallback(() => (
|
||||
<Icon className={styles['icon']} name={'more-vertical'} />
|
||||
), []);
|
||||
const title = React.useMemo(() => {
|
||||
if (typeof nextEpisodeReleaseDate === 'string' && nextEpisodeReleaseDate.length > 0) {
|
||||
return `${name} (${nextEpisodeReleaseDate})`;
|
||||
}
|
||||
return name;
|
||||
}, [name, nextEpisodeReleaseDate]);
|
||||
return (
|
||||
<Button title={name} href={href} {...filterInvalidDOMProps(props)} className={classnames(className, styles['meta-item-container'], styles['poster-shape-poster'], styles[`poster-shape-${posterShape}`], { 'active': menuOpen })} onClick={metaItemOnClick}>
|
||||
<Button title={title} href={href} {...filterInvalidDOMProps(props)} className={classnames(className, styles['meta-item-container'], styles['poster-shape-poster'], styles[`poster-shape-${posterShape}`], { 'active': menuOpen })} onClick={metaItemOnClick}>
|
||||
<div className={classnames(styles['poster-container'], { 'poster-change-cursor': posterChangeCursor })}>
|
||||
{
|
||||
onDismissClick ?
|
||||
|
|
@ -175,7 +181,8 @@ MetaItem.propTypes = {
|
|||
onDismissClick: PropTypes.func,
|
||||
onPlayClick: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
watched: PropTypes.bool
|
||||
watched: PropTypes.bool,
|
||||
nextEpisodeReleaseDate: PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = MetaItem;
|
||||
|
|
|
|||
|
|
@ -8,20 +8,35 @@ const { useStreamingServer, useNotifications, withCoreSuspender, getVisibleChild
|
|||
const { ContinueWatchingItem, EventModal, MainNavBars, MetaItem, MetaRow } = require('stremio/components');
|
||||
const useBoard = require('./useBoard');
|
||||
const useContinueWatchingPreview = require('./useContinueWatchingPreview');
|
||||
const useMetaDetailsForMetaItem = require('./useMetaDetailsForMetaItem'); // Import the new hook
|
||||
const styles = require('./styles');
|
||||
const { default: StreamingServerWarning } = require('./StreamingServerWarning');
|
||||
|
||||
const THRESHOLD = 5;
|
||||
|
||||
// Helper component to fetch and pass nextEpisodeReleaseDate for a single item
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const ContinueWatchingItemWithDetails = ({ item, notifications }) => {
|
||||
const nextEpisodeReleaseDate = useMetaDetailsForMetaItem(item);
|
||||
return (
|
||||
<ContinueWatchingItem
|
||||
{...item}
|
||||
notifications={notifications}
|
||||
nextEpisodeReleaseDate={nextEpisodeReleaseDate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Board = () => {
|
||||
const t = useTranslate();
|
||||
const streamingServer = useStreamingServer();
|
||||
const continueWatchingPreview = useContinueWatchingPreview();
|
||||
const [board, loadBoardRows] = useBoard();
|
||||
const notifications = useNotifications();
|
||||
const notifications = useNotifications(); // useNotifications returns an object, no need to convert to Map
|
||||
const profile = useProfile();
|
||||
const boardCatalogsOffset = continueWatchingPreview.items.length > 0 ? 1 : 0;
|
||||
const scrollContainerRef = React.useRef();
|
||||
|
||||
const showStreamingServerWarning = React.useMemo(() => {
|
||||
return streamingServer.settings !== null && streamingServer.settings.type === 'Err' && (
|
||||
isNaN(profile.settings.streamingServerWarningDismissed.getTime()) ||
|
||||
|
|
@ -45,6 +60,7 @@ const Board = () => {
|
|||
React.useLayoutEffect(() => {
|
||||
onVisibleRangeChange();
|
||||
}, [board.catalogs, onVisibleRangeChange]);
|
||||
|
||||
return (
|
||||
<div className={styles['board-container']}>
|
||||
<EventModal />
|
||||
|
|
@ -55,9 +71,19 @@ const Board = () => {
|
|||
<MetaRow
|
||||
className={classnames(styles['board-row'], styles['continue-watching-row'], 'animation-fade-in')}
|
||||
title={t.string('BOARD_CONTINUE_WATCHING')}
|
||||
catalog={continueWatchingPreview}
|
||||
itemComponent={ContinueWatchingItem}
|
||||
notifications={notifications}
|
||||
catalog={{
|
||||
...continueWatchingPreview,
|
||||
items: continueWatchingPreview.items.map((item, index) => (
|
||||
<ContinueWatchingItemWithDetails
|
||||
key={item.id || index} // Use item.id for key if available, otherwise index
|
||||
item={item}
|
||||
notifications={notifications}
|
||||
/>
|
||||
)),
|
||||
}}
|
||||
// We are passing a React element as itemComponent, so we don't need itemComponent prop here
|
||||
// The items array now contains the rendered components
|
||||
itemComponent={({ item }) => item} // This is a workaround to render the React elements directly
|
||||
/>
|
||||
:
|
||||
null
|
||||
|
|
|
|||
71
src/routes/Board/useMetaDetailsForMetaItem.js
Normal file
71
src/routes/Board/useMetaDetailsForMetaItem.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
const React = require('react');
|
||||
const { useModelState, useProfile } = require('stremio/common');
|
||||
|
||||
const useMetaDetailsForMetaItem = (metaItemPreview) => {
|
||||
const profile = useProfile();
|
||||
// Create a unique model name for each meta item to avoid state conflicts
|
||||
const modelName = React.useMemo(() => {
|
||||
return metaItemPreview && metaItemPreview.id ? `metaDetails_${metaItemPreview.id}` : null;
|
||||
}, [metaItemPreview?.id]);
|
||||
|
||||
const metaDetails = useModelState({
|
||||
model: modelName,
|
||||
action: React.useMemo(() => {
|
||||
if (!metaItemPreview || !metaItemPreview.id || !metaItemPreview.type) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
action: 'Load',
|
||||
args: {
|
||||
model: 'MetaDetails',
|
||||
args: {
|
||||
type: metaItemPreview.type,
|
||||
id: metaItemPreview.id
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [metaItemPreview?.id, metaItemPreview?.type])
|
||||
});
|
||||
|
||||
const nextEpisodeReleaseDate = React.useMemo(() => {
|
||||
if (metaDetails && metaDetails.content?.type === 'Ready' && Array.isArray(metaDetails.content.content.videos)) {
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0); // Normalize 'now' to start of day
|
||||
|
||||
const upcomingVideos = metaDetails.content.content.videos.filter((video) => {
|
||||
// Check if the video is scheduled and has a release date
|
||||
if (video.scheduled && video.released) {
|
||||
// Parse the release date string (e.g., 'YYYY-MM-DD')
|
||||
const [year, month, day] = video.released.split('-').map(Number);
|
||||
const releaseDate = new Date(year, month - 1, day); // month - 1 because Date months are 0-indexed
|
||||
|
||||
// Only consider episodes that are in the future or today
|
||||
return releaseDate >= now;
|
||||
}
|
||||
return false;
|
||||
}).sort((a, b) => {
|
||||
// Sort by release date to find the soonest upcoming episode
|
||||
const dateA = new Date(a.released);
|
||||
const dateB = new Date(b.released);
|
||||
return dateA.getTime() - dateB.getTime();
|
||||
});
|
||||
|
||||
if (upcomingVideos.length > 0) {
|
||||
const nextVideo = upcomingVideos[0];
|
||||
const [year, month, day] = nextVideo.released.split('-').map(Number);
|
||||
const releaseDate = new Date(year, month - 1, day);
|
||||
// Format the date string using the user's interface language
|
||||
return releaseDate.toLocaleDateString(profile.settings.interfaceLanguage, {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [metaDetails, profile.settings.interfaceLanguage]);
|
||||
|
||||
return nextEpisodeReleaseDate;
|
||||
};
|
||||
|
||||
module.exports = useMetaDetailsForMetaItem;
|
||||
1
src/types/MetaItem.d.ts
vendored
1
src/types/MetaItem.d.ts
vendored
|
|
@ -14,6 +14,7 @@ type MetaItemPreview = {
|
|||
poster: string | null,
|
||||
posterShape: PosterShape,
|
||||
releaseInfo: string | null,
|
||||
nextEpisodeReleaseDate: string | null,
|
||||
runtime: string | null,
|
||||
released: Date | null | undefined,
|
||||
trailerStreams: TrailerStream[],
|
||||
|
|
|
|||
Loading…
Reference in a new issue