not using calendar

This commit is contained in:
darkhan 2026-05-05 13:26:13 +05:00
parent e2177938d1
commit a9ad4cefc8
4 changed files with 112 additions and 7 deletions

View file

@ -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;

View file

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

View 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;

View file

@ -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[],