mirror of
https://github.com/sct/overseerr.git
synced 2025-12-31 18:20:13 +01:00
feat(add watchlist): adding midding functionality from overserr
feat(add watchlist): adding missing functionality from overserr
This commit is contained in:
@@ -11,6 +11,7 @@ export interface TmdbTitleCardProps {
|
||||
tvdbId?: number;
|
||||
type: 'movie' | 'tv';
|
||||
canExpand?: boolean;
|
||||
isAddedToWatchlist?: boolean;
|
||||
}
|
||||
|
||||
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
||||
@@ -23,6 +24,7 @@ const TmdbTitleCard = ({
|
||||
tvdbId,
|
||||
type,
|
||||
canExpand,
|
||||
isAddedToWatchlist = false,
|
||||
}: TmdbTitleCardProps) => {
|
||||
const { hasPermission } = useUser();
|
||||
|
||||
@@ -56,7 +58,11 @@ const TmdbTitleCard = ({
|
||||
|
||||
return isMovie(title) ? (
|
||||
<TitleCard
|
||||
key={title.id}
|
||||
id={title.id}
|
||||
isAddedToWatchlist={
|
||||
title.mediaInfo?.watchlists?.length || isAddedToWatchlist
|
||||
}
|
||||
image={title.posterPath}
|
||||
status={title.mediaInfo?.status}
|
||||
summary={title.overview}
|
||||
@@ -68,7 +74,11 @@ const TmdbTitleCard = ({
|
||||
/>
|
||||
) : (
|
||||
<TitleCard
|
||||
key={title.id}
|
||||
id={title.id}
|
||||
isAddedToWatchlist={
|
||||
title.mediaInfo?.watchlists?.length || isAddedToWatchlist
|
||||
}
|
||||
image={title.posterPath}
|
||||
status={title.mediaInfo?.status}
|
||||
summary={title.overview}
|
||||
|
||||
@@ -10,12 +10,21 @@ import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import { withProperties } from '@app/utils/typeHelpers';
|
||||
import { Transition } from '@headlessui/react';
|
||||
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
MinusCircleIcon,
|
||||
StarIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { MediaStatus } from '@server/constants/media';
|
||||
import type { Watchlist } from '@server/entity/Watchlist';
|
||||
import type { MediaType } from '@server/models/Search';
|
||||
import axios from 'axios';
|
||||
import Link from 'next/link';
|
||||
import type React from 'react';
|
||||
import { Fragment, useCallback, useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import { mutate } from 'swr';
|
||||
|
||||
interface TitleCardProps {
|
||||
id: number;
|
||||
@@ -28,8 +37,19 @@ interface TitleCardProps {
|
||||
status?: MediaStatus;
|
||||
canExpand?: boolean;
|
||||
inProgress?: boolean;
|
||||
isAddedToWatchlist?: number | boolean;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
addToWatchList: 'Add to watchlist',
|
||||
watchlistSuccess:
|
||||
'<strong>{title}</strong> added to watchlist successfully!',
|
||||
watchlistDeleted:
|
||||
'<strong>{title}</strong> Removed from watchlist successfully!',
|
||||
watchlistCancel: 'watchlist for <strong>{title}</strong> canceled.',
|
||||
watchlistError: 'Something went wrong try again.',
|
||||
});
|
||||
|
||||
const TitleCard = ({
|
||||
id,
|
||||
image,
|
||||
@@ -38,6 +58,7 @@ const TitleCard = ({
|
||||
title,
|
||||
status,
|
||||
mediaType,
|
||||
isAddedToWatchlist = false,
|
||||
inProgress = false,
|
||||
canExpand = false,
|
||||
}: TitleCardProps) => {
|
||||
@@ -48,6 +69,10 @@ const TitleCard = ({
|
||||
const [currentStatus, setCurrentStatus] = useState(status);
|
||||
const [showDetail, setShowDetail] = useState(false);
|
||||
const [showRequestModal, setShowRequestModal] = useState(false);
|
||||
const { addToast } = useToasts();
|
||||
const [toggleWatchlist, setToggleWatchlist] = useState<boolean>(
|
||||
!isAddedToWatchlist
|
||||
);
|
||||
|
||||
// Just to get the year from the date
|
||||
if (year) {
|
||||
@@ -68,6 +93,65 @@ const TitleCard = ({
|
||||
[]
|
||||
);
|
||||
|
||||
const onClickWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const response = await axios.post<Watchlist>('/api/v1/watchlist', {
|
||||
tmdbId: id,
|
||||
mediaType,
|
||||
title,
|
||||
});
|
||||
mutate('/api/v1/discover/watchlist');
|
||||
if (response.data) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(messages.watchlistSuccess, {
|
||||
title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'success', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
addToast(intl.formatMessage(messages.watchlistError), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
setToggleWatchlist((prevState) => !prevState);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickDeleteWatchlistBtn = async (): Promise<void> => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const response = await axios.delete<Watchlist>('/api/v1/watchlist/' + id);
|
||||
|
||||
if (response.status === 204) {
|
||||
addToast(
|
||||
<span>
|
||||
{intl.formatMessage(messages.watchlistDeleted, {
|
||||
title,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
})}
|
||||
</span>,
|
||||
{ appearance: 'info', autoDismiss: true }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
addToast(intl.formatMessage(messages.watchlistError), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
mutate('/api/v1/discover/watchlist');
|
||||
setToggleWatchlist((prevState) => !prevState);
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = useCallback(() => setShowRequestModal(false), []);
|
||||
|
||||
const showRequestButton = hasPermission(
|
||||
@@ -141,6 +225,28 @@ const TitleCard = ({
|
||||
: intl.formatMessage(globalMessages.tvshow)}
|
||||
</div>
|
||||
</div>
|
||||
{showDetail && (
|
||||
<>
|
||||
{toggleWatchlist ? (
|
||||
<Button
|
||||
buttonType={'ghost'}
|
||||
className="z-40"
|
||||
buttonSize={'sm'}
|
||||
onClick={onClickWatchlistBtn}
|
||||
>
|
||||
<StarIcon className={'h-3 text-amber-300'} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className="z-40"
|
||||
buttonSize={'sm'}
|
||||
onClick={onClickDeleteWatchlistBtn}
|
||||
>
|
||||
<MinusCircleIcon className={'h-3'} />
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{currentStatus && currentStatus !== MediaStatus.UNKNOWN && (
|
||||
<div className="pointer-events-none z-40 flex items-center">
|
||||
<StatusBadgeMini
|
||||
|
||||
Reference in New Issue
Block a user