feat(SeasonsBar): Handle episodes options in multiselect menu

This commit is contained in:
Botzy 2025-02-04 20:59:04 +02:00
parent 3bbbe66b33
commit 0b5c14aba4
2 changed files with 60 additions and 25 deletions

View file

@ -9,19 +9,26 @@ const { Button, MultiselectMenu } = require('stremio/components');
const SeasonsBarPlaceholder = require('./SeasonsBarPlaceholder');
const styles = require('./styles');
const SeasonsBar = ({ className, seasons, season, onSelect }) => {
const SeasonsBar = ({ className, seasons, season, episode, onSelect }) => {
const options = React.useMemo(() => {
return seasons.map((season) => ({
value: String(season),
label: season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')
return seasons.map(({ id, episodes }) => ({
value: String(id),
label: id > 0 ? `${t('SEASON')} ${id}` : t('SPECIAL'),
options: episodes.map((episode) => ({
value: String(episode),
label: `${t('EPISODE')} ${episode}`
})),
}));
}, [seasons]);
const selectedSeason = React.useMemo(() => {
return { label: String(season), value: String(season) };
return { label: String(season.id), value: String(season.id), options: season.episodes.map((episode) => ({
value: String(episode),
label: `${t('EPISODE')} ${episode}`
}))};
}, [season]);
const prevNextButtonOnClick = React.useCallback((event) => {
if (typeof onSelect === 'function') {
const seasonIndex = seasons.indexOf(season);
const seasonIndex = seasons.findIndex(({ id }) => id === season.id);
const valueIndex = event.currentTarget.dataset.action === 'next' ?
seasonIndex + 1 < seasons.length ? seasonIndex + 1 : seasons.length - 1
:
@ -35,11 +42,12 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => {
});
}
}, [season, seasons, onSelect]);
const seasonOnSelect = React.useCallback((value) => {
const seasonOnSelect = React.useCallback((level, value) => {
if (typeof onSelect === 'function') {
onSelect({
type: 'select',
value: value,
level,
reactEvent: event.reactEvent,
nativeEvent: event.nativeEvent
});
@ -47,7 +55,7 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => {
}, [onSelect]);
const [prevDisabled, nextDisabled] = React.useMemo(() => {
const currentIndex = seasons.indexOf(season);
const currentIndex = seasons.findIndex(({ id }) => id === season.id);
return [
currentIndex === 0,
currentIndex === seasons.length - 1
@ -63,7 +71,8 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => {
<MultiselectMenu
className={styles['seasons-popup-label-container']}
options={options}
title={season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')}
title={season.id > 0 ? `${t('SEASON')} ${season.id}` : t('SPECIAL')}
subtitle={episode && `${t('EPISODE')} ${episode}`}
selectedOption={selectedSeason}
onSelect={seasonOnSelect}
/>
@ -79,8 +88,15 @@ SeasonsBar.Placeholder = SeasonsBarPlaceholder;
SeasonsBar.propTypes = {
className: PropTypes.string,
seasons: PropTypes.arrayOf(PropTypes.number).isRequired,
season: PropTypes.number.isRequired,
seasons: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number,
episodes: PropTypes.arrayOf(PropTypes.number),
})).isRequired,
season: PropTypes.shape({
id: PropTypes.number,
episodes: PropTypes.arrayOf(PropTypes.number),
}).isRequired,
episode: PropTypes.string,
onSelect: PropTypes.func
};

View file

@ -11,6 +11,7 @@ const styles = require('./styles');
const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect, toggleNotifications }) => {
const { core } = useServices();
const [selectedEpisode, setSelectedEpisode] = React.useState(null);
const showNotificationsToggle = React.useMemo(() => {
return metaItem?.content?.content?.inLibrary && metaItem?.content?.content?.videos?.length;
}, [metaItem]);
@ -21,22 +22,19 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect,
[];
}, [metaItem]);
const seasons = React.useMemo(() => {
return videos
.map(({ season }) => season)
.filter((season, index, seasons) => {
return season !== null &&
!isNaN(season) &&
typeof season === 'number' &&
seasons.indexOf(season) === index;
})
.sort((a, b) => (a || Number.MAX_SAFE_INTEGER) - (b || Number.MAX_SAFE_INTEGER));
return [...new Set(videos.map(({ season }) => season))].map((season) => ({
id: season,
episodes: videos.filter(({ season: s }) => s === season).map(({ episode }) => episode)
}))
.sort((a, b) => (a.id || Number.MAX_SAFE_INTEGER) - (b.id || Number.MAX_SAFE_INTEGER));
}, [videos]);
const selectedSeason = React.useMemo(() => {
if (seasons.includes(season)) {
return season;
const foundSeason = seasons.find(({ id }) => id === season);
if (foundSeason) {
return foundSeason;
}
const nonSpecialSeasons = seasons.filter((season) => season !== 0);
const nonSpecialSeasons = seasons.filter(({ id }) => id !== 0);
if (nonSpecialSeasons.length > 0) {
return nonSpecialSeasons[nonSpecialSeasons.length - 1];
}
@ -50,7 +48,7 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect,
const videosForSeason = React.useMemo(() => {
return videos
.filter((video) => {
return selectedSeason === null || video.season === selectedSeason;
return selectedSeason === null || video.season === selectedSeason.id;
})
.sort((a, b) => {
return a.episode - b.episode;
@ -71,6 +69,26 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect,
});
};
const handleSelect = (event) => {
if (!event.level) {
seasonOnSelect(event);
} else {
setSelectedEpisode(event.value);
const episode = videos.find(({ season: s, episode: e }) => s === season && e.toString() === event.value);
if (episode.deepLinks) {
if (typeof episode.deepLinks.player === 'string') {
window.location = episode.deepLinks.player;
} else if (typeof episode.deepLinks.metaDetailsStreams === 'string') {
window.location.replace(episode.deepLinks.metaDetailsStreams);
}
}
}
};
React.useEffect(() => {
setSelectedEpisode(null);
}, [season]);
return (
<div className={classnames(className, styles['videos-list-container'])}>
{
@ -107,8 +125,9 @@ const VideosList = ({ className, metaItem, libraryItem, season, seasonOnSelect,
<SeasonsBar
className={styles['seasons-bar']}
season={selectedSeason}
episode={selectedEpisode}
seasons={seasons}
onSelect={seasonOnSelect}
onSelect={handleSelect}
/>
:
null