Skip to content
Draft
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
1 change: 1 addition & 0 deletions server/constants/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum DiscoverSliderType {
TMDB_NETWORK,
TMDB_MOVIE_STREAMING_SERVICES,
TMDB_TV_STREAMING_SERVICES,
EXTERNAL_WATCHLIST,
}

export const defaultSliders: Partial<DiscoverSlider>[] = [
Expand Down
73 changes: 45 additions & 28 deletions src/components/Discover/CreateSlider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Button from '@app/components/Common/Button';
import Tooltip from '@app/components/Common/Tooltip';
import { sliderTitles } from '@app/components/Discover/constants';
import ExternalWatchlistSlider from '@app/components/Discover/ExternalWatchlistSlider';
import MediaSlider from '@app/components/MediaSlider';
import { WatchProviderSelector } from '@app/components/Selector';
import { encodeURIExtraParams } from '@app/hooks/useDiscover';
Expand Down Expand Up @@ -30,6 +31,8 @@ const messages = defineMessages({
providetmdbsearch: 'Provide a search query',
providetmdbstudio: 'Provide TMDB Studio ID',
providetmdbnetwork: 'Provide TMDB Network ID',
provideapiendpoint:
'Provide API endpoint with pagination, eg. http://example.com/api',
addsuccess: 'Created new slider and saved discover customization settings.',
addfail: 'Failed to create new slider.',
editsuccess: 'Edited slider and saved discover customization settings.',
Expand Down Expand Up @@ -291,6 +294,13 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
params: 'watchRegion=$regionValue&watchProviders=$providersValue',
titlePlaceholderText: intl.formatMessage(messages.slidernameplaceholder),
},
{
type: DiscoverSliderType.EXTERNAL_WATCHLIST,
title: intl.formatMessage(sliderTitles.externalwatchlist),
dataUrl: '',
titlePlaceholderText: intl.formatMessage(messages.slidernameplaceholder),
dataPlaceholderText: intl.formatMessage(messages.provideapiendpoint),
},
];

return (
Expand Down Expand Up @@ -527,37 +537,44 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
</div>
)}
</div>

{activeOption && values.title && values.data && (
<div className="relative py-4">
<MediaSlider
sliderKey={`preview-${values.title}`}
title={values.title}
url={activeOption?.dataUrl.replace(
'$value',
encodeURIExtraParams(values.data)
)}
extraParams={
activeOption.type ===
DiscoverSliderType.TMDB_MOVIE_STREAMING_SERVICES ||
activeOption.type ===
DiscoverSliderType.TMDB_TV_STREAMING_SERVICES
? activeOption.params
?.replace(
'$regionValue',
encodeURIExtraParams(values?.data.split(',')[0])
)
.replace(
'$providersValue',
encodeURIExtraParams(values?.data.split(',')[1])
{activeOption.type === DiscoverSliderType.EXTERNAL_WATCHLIST ? (
<ExternalWatchlistSlider
sliderKey={`custom-slider-${values.title}`}
title={values.title}
url={values.data}
/>
) : (
<MediaSlider
sliderKey={`preview-${values.title}`}
title={values.title}
url={activeOption?.dataUrl.replace(
'$value',
encodeURIExtraParams(values.data)
)}
extraParams={
activeOption.type ===
DiscoverSliderType.TMDB_MOVIE_STREAMING_SERVICES ||
activeOption.type ===
DiscoverSliderType.TMDB_TV_STREAMING_SERVICES
? activeOption.params
?.replace(
'$regionValue',
encodeURIExtraParams(values?.data.split(',')[0])
)
.replace(
'$providersValue',
encodeURIExtraParams(values?.data.split(',')[1])
)
: activeOption.params?.replace(
'$value',
encodeURIExtraParams(values.data)
)
: activeOption.params?.replace(
'$value',
encodeURIExtraParams(values.data)
)
}
onNewTitles={updateResultCount}
/>
}
onNewTitles={updateResultCount}
/>
)}
</div>
)}
</Form>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Discover/DiscoverSliderEdit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ const DiscoverSliderEdit = ({
return intl.formatMessage(sliderTitles.tmdbmoviestreamingservices);
case DiscoverSliderType.TMDB_TV_STREAMING_SERVICES:
return intl.formatMessage(sliderTitles.tmdbtvstreamingservices);
case DiscoverSliderType.EXTERNAL_WATCHLIST:
return intl.formatMessage(sliderTitles.externalwatchlist);
default:
return 'Unknown Slider';
}
Expand Down
77 changes: 77 additions & 0 deletions src/components/Discover/ExternalWatchlistSlider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Slider from '@app/components/Slider';
import TmdbTitleCard from '@app/components/TitleCard/TmdbTitleCard';
import { ArrowRightCircleIcon } from '@heroicons/react/24/outline';
import type { WatchlistItem } from '@server/interfaces/api/discoverInterfaces';
import Link from 'next/link';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';

const messages = defineMessages({
externalwatchlist: 'External Watchlist',
emptywatchlist: 'Media added to the list will appear here.',
});

interface ExternalWatchlistSliderProps {
title: string;
url: string;
linkUrl?: string;
sliderKey: string;
}

const ExternalWatchlistSlider = ({
title,
url,
linkUrl,
sliderKey,
}: ExternalWatchlistSliderProps) => {
const intl = useIntl();

const { data: watchlistItems, error: watchlistError } = useSWR<{
page: number;
totalPages: number;
totalResults: number;
results: WatchlistItem[];
}>(url, { revalidateOnMount: true });

if (
(watchlistItems && watchlistItems.results.length === 0) ||
watchlistError
) {
return null;
}

return (
<>
<div className="slider-header">
{linkUrl ? (
<Link href={linkUrl}>
<a className="slider-title min-w-0 pr-16">
<span className="truncate">{title}</span>
<ArrowRightCircleIcon />
</a>
</Link>
) : (
<div className="slider-title">
<span>{title}</span>
</div>
)}
</div>
<Slider
sliderKey={sliderKey}
isLoading={!watchlistItems}
isEmpty={!!watchlistItems && watchlistItems.results.length === 0}
emptyMessage={intl.formatMessage(messages.emptywatchlist)}
items={watchlistItems?.results.map((item) => (
<TmdbTitleCard
id={item.tmdbId}
key={`watchlist-slider-item-${item.tmdbId}`}
tmdbId={item.tmdbId}
type={item.mediaType}
/>
))}
/>
</>
);
};

export default ExternalWatchlistSlider;
1 change: 1 addition & 0 deletions src/components/Discover/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const sliderTitles = defineMessages({
tmdbsearch: 'TMDB Search',
tmdbmoviestreamingservices: 'TMDB Movie Streaming Services',
tmdbtvstreamingservices: 'TMDB TV Streaming Services',
externalwatchlist: 'External Watchlist',
});

export const QueryFilterOptions = z.object({
Expand Down
10 changes: 10 additions & 0 deletions src/components/Discover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Tooltip from '@app/components/Common/Tooltip';
import { sliderTitles } from '@app/components/Discover/constants';
import CreateSlider from '@app/components/Discover/CreateSlider';
import DiscoverSliderEdit from '@app/components/Discover/DiscoverSliderEdit';
import ExternalWatchlistSlider from '@app/components/Discover/ExternalWatchlistSlider';
import MovieGenreSlider from '@app/components/Discover/MovieGenreSlider';
import NetworkSlider from '@app/components/Discover/NetworkSlider';
import PlexWatchlistSlider from '@app/components/Discover/PlexWatchlistSlider';
Expand Down Expand Up @@ -395,6 +396,15 @@ const Discover = () => {
/>
);
break;
case DiscoverSliderType.EXTERNAL_WATCHLIST:
sliderComponent = (
<ExternalWatchlistSlider
sliderKey={`custom-slider-${slider.id}`}
title={slider.title ?? ''}
url={slider.data ?? ''}
/>
);
break;
}

if (isEditing) {
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"components.Discover.CreateSlider.editsuccess": "Edited slider and saved discover customization settings.",
"components.Discover.CreateSlider.needresults": "You need to have at least 1 result.",
"components.Discover.CreateSlider.nooptions": "No results.",
"components.Discover.CreateSlider.provideapiendpoint": "Provide API endpoint with pagination, eg. http://example.com/api",
"components.Discover.CreateSlider.providetmdbgenreid": "Provide a TMDB Genre ID",
"components.Discover.CreateSlider.providetmdbkeywordid": "Provide a TMDB Keyword ID",
"components.Discover.CreateSlider.providetmdbnetwork": "Provide TMDB Network ID",
Expand Down Expand Up @@ -61,6 +62,8 @@
"components.Discover.DiscoverTvLanguage.languageSeries": "{language} Series",
"components.Discover.DiscoverWatchlist.discoverwatchlist": "Your Plex Watchlist",
"components.Discover.DiscoverWatchlist.watchlist": "Plex Watchlist",
"components.Discover.ExternalWatchlistSlider.emptywatchlist": "Media added to the list will appear here.",
"components.Discover.ExternalWatchlistSlider.externalwatchlist": "External Watchlist",
"components.Discover.FilterSlideover.activefilters": "{count, plural, one {# Active Filter} other {# Active Filters}}",
"components.Discover.FilterSlideover.clearfilters": "Clear Active Filters",
"components.Discover.FilterSlideover.filters": "Filters",
Expand Down Expand Up @@ -92,6 +95,7 @@
"components.Discover.customizediscover": "Customize Discover",
"components.Discover.discover": "Discover",
"components.Discover.emptywatchlist": "Media added to your <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink> will appear here.",
"components.Discover.externalwatchlist": "External Watchlist",
"components.Discover.moviegenres": "Movie Genres",
"components.Discover.networks": "Networks",
"components.Discover.plexwatchlist": "Your Plex Watchlist",
Expand Down