Skip to content
Open
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
7 changes: 4 additions & 3 deletions client/src/components/AddToListModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useUser } from 'src/api/user';
import { Modal } from 'src/components/Modal';
import { formatEpisodeNumber, formatSeasonNumber, listName } from 'src/utils';
import { AddListButton } from 'src/components/AddOrEditListButton';
import { originalAndTranslatedTitle } from 'src/mediaItem';

export const AddToListModal: FunctionComponent<{
mediaItemId: number;
Expand Down Expand Up @@ -54,10 +55,10 @@ export const AddToListModal: FunctionComponent<{
<Trans>
Add &quot;
{episode
? `${mediaItem.title} ${formatEpisodeNumber(episode)}`
? `${originalAndTranslatedTitle(mediaItem)} ${formatEpisodeNumber(episode)}`
: season
? `${mediaItem.title} ${formatSeasonNumber(season)}`
: mediaItem.title}
? `${originalAndTranslatedTitle(mediaItem)} ${formatSeasonNumber(season)}`
: originalAndTranslatedTitle(mediaItem)}
&quot; to list
</Trans>
</div>
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/GridItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { removeFromWatchlist } from 'src/api/details';
import { BadgeRating } from 'src/components/StarRating';
import { originalAndTranslatedTitle } from 'src/mediaItem';

import {
formatEpisodeNumber,
Expand Down Expand Up @@ -53,75 +54,75 @@
listTitle: string;
};
appearance?: GridItemAppearanceArgs;
}> = (props) => {
const { mediaItem, season, episode, mediaType } = props;
const {
topBar,
showNextAiring,
showLastAiring,
showMarksAsSeenFirstUnwatchedEpisode,
showMarksAsSeenLastAiredEpisode,
showRating,
showAddToWatchlistAndMarkAsSeenButtons,
showTotalRuntime,
showReleaseDate,
showLastSeenAt,
} = props.appearance || {};

const item = episode || season || mediaItem;

const isOnWatchlist =
(season && season.onWatchlist) ||
(episode && episode.onWatchlist) ||
(!episode && !season && mediaItem.onWatchlist);

const mediaTypeString: Record<MediaType, string> = {
audiobook: t`Audiobook`,
book: t`Book`,
movie: t`Movie`,
tv: t`Tv`,
video_game: t`Video game`,
};

if (season && episode) {
throw new Error('Booth season and episode cannot be provided');
}

if (season && season.tvShowId !== mediaItem.id) {
throw new Error('Season needs to be from the same tv show as mediaItem');
}

if (episode && episode.tvShowId !== mediaItem.id) {
throw new Error('Episode needs to be from the same tv show as mediaItem');
}

return (
<div className="item">
<div className="pb-4">
<Poster
src={mediaItem.posterSmall}
mediaType={mediaType}
itemMediaType={mediaItem.mediaType}
href={
season
? `#/seasons/${mediaItem.id}/${season.seasonNumber}`
: episode
? `#/episode/${mediaItem.id}/${episode.seasonNumber}/${episode.episodeNumber}`
: `#/details/${mediaItem.id}`
}
>
{topBar && (
<>
{topBar.showOnWatchlistIcon && (
<div className="absolute top-0 left-0 inline-flex mt-1 pointer-events-auto hover:cursor-pointer">
{isOnWatchlist && (
<Item

Check warning on line 119 in client/src/components/GridItem.tsx

View check run for this annotation

codefactor.io / CodeFactor

client/src/components/GridItem.tsx#L57-L119

Very Complex Method
onClick={async (e) => {
e.preventDefault();

if (
await Confirm(
t`Remove "${mediaItem.title}${
t`Remove "${originalAndTranslatedTitle(mediaItem)}${
season
? ' ' + formatSeasonNumber(season)
: episode
Expand Down Expand Up @@ -205,7 +206,7 @@
<div className="overflow-hidden text-lg overflow-ellipsis whitespace-nowrap">
{season && formatSeasonNumber(season) + ' '}
{episode && formatEpisodeNumber(episode) + ' '}
{mediaItem.title}
{originalAndTranslatedTitle(mediaItem)}
</div>

{hasProgress(mediaItem) && (
Expand Down
9 changes: 5 additions & 4 deletions client/src/components/SelectSeenDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Trans } from '@lingui/macro';

import { markAsSeen } from 'src/api/details';
import { SelectLastSeenEpisode } from 'src/components/SelectLastSeenEpisode';
import { originalAndTranslatedTitle } from 'src/mediaItem';

import {
LastSeenAt,
Expand Down Expand Up @@ -74,15 +75,15 @@ export const SelectSeenDateComponent: FunctionComponent<{
<div className="p-2">
<div className="max-w-sm mx-5 my-3 text-3xl font-bold text-center">
{isAudiobook(mediaItem) && (
<Trans>When did you listen to &quot;{mediaItem.title}&quot;?</Trans>
<Trans>When did you listen to &quot;{originalAndTranslatedTitle(mediaItem)}&quot;?</Trans>
)}

{isBook(mediaItem) && (
<Trans>When did you read &quot;{mediaItem.title}&quot;?</Trans>
<Trans>When did you read &quot;{originalAndTranslatedTitle(mediaItem)}&quot;?</Trans>
)}

{isMovie(mediaItem) && (
<Trans>When did you see &quot;{mediaItem.title}&quot;?</Trans>
<Trans>When did you see &quot;{originalAndTranslatedTitle(mediaItem)}&quot;?</Trans>
)}

{isTvShow(mediaItem) && (
Expand All @@ -96,7 +97,7 @@ export const SelectSeenDateComponent: FunctionComponent<{
)}

{isVideoGame(mediaItem) && (
<Trans>When did you play &quot;{mediaItem.title}&quot;?</Trans>
<Trans>When did you play &quot;{originalAndTranslatedTitle(mediaItem)}&quot;?</Trans>
)}
</div>

Expand Down
3 changes: 2 additions & 1 deletion client/src/components/StarRating.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Modal, useOpenModalRef } from 'src/components/Modal';
import { SelectSeenDate } from 'src/components/SelectSeenDate';
import { formatEpisodeNumber, formatSeasonNumber } from 'src/utils';
import { queryClient } from 'src/App';
import { originalAndTranslatedTitle } from 'src/mediaItem';

export const StarRating: FunctionComponent<
| { mediaItem: MediaItemItemsResponse }
Expand Down Expand Up @@ -110,7 +111,7 @@ const StarRatingModal: FunctionComponent<
return (
<div className="flex flex-col items-center justify-center p-3 text-black select-none bottom-full min-w-max w-96">
<div className="pb-2 text-4xl font-bold">
{mediaItem.title}
{originalAndTranslatedTitle(mediaItem)}
{season && <> {formatSeasonNumber(season)}</>}
{episode && <> {formatEpisodeNumber(episode)}</>}
</div>
Expand Down
7 changes: 7 additions & 0 deletions client/src/mediaItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';

import {
MediaItemDetailsResponse,
MediaItemItemsResponse,
TvEpisode,
TvSeason,
} from 'mediatracker-api';
Expand Down Expand Up @@ -71,3 +72,9 @@ export const hasBeenSeenAtLeastOnce = (
?.length > 0
: value.seenHistory?.length > 0;
};

export const originalAndTranslatedTitle = (mediaItem: MediaItemDetailsResponse | MediaItemItemsResponse) => {
return (mediaItem.originalTitle && mediaItem.originalTitle !== mediaItem.title)
? `${mediaItem.originalTitle} (${mediaItem.title})`
: mediaItem.title;
};
3 changes: 2 additions & 1 deletion client/src/pages/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import clsx from 'clsx';
import { Plural, Trans } from '@lingui/macro';
import { parseISO } from 'date-fns';
import { originalAndTranslatedTitle } from 'src/mediaItem';

import {
AudibleCountryCode,
Expand Down Expand Up @@ -194,431 +195,431 @@
);
};

export const DetailsPage: FunctionComponent = () => {
const { mediaItemId } = useParams();
const { mediaItem, isLoading, error } = useDetails(Number(mediaItemId));

if (isLoading) {
return (
<>
<Trans>Loading</Trans>
</>
);
}

if (error) {
return <>{error}</>;
}

return (
<div>
<div className="flex flex-col mt-2 mb-4 md:flex-row">
<div className="self-center w-64 shrink-0 md:self-start">
<Poster
src={mediaItem.poster}
mediaType={mediaItem.mediaType}
itemMediaType={mediaItem.mediaType}
/>
</div>
<div className="md:ml-4">
<div className="mt-2 text-4xl font-bold md:mt-0">
{mediaItem.title}
{originalAndTranslatedTitle(mediaItem)}
</div>

{mediaItem.releaseDate && (
<div>
<span className="font-bold">
<Trans>Release date</Trans>:{' '}
</span>
<span>
{parseISO(mediaItem.releaseDate).toLocaleDateString()}
</span>
</div>
)}

{mediaItem.runtime > 0 && (
<div>
<span className="font-bold">
<Trans>Runtime</Trans>:{' '}
</span>
<span>
<FormatDuration milliseconds={mediaItem.runtime * 60 * 1000} />
</span>
</div>
)}

{mediaItem.totalRuntime > 0 && (
<div>
<span className="font-bold">
<Trans>Total runtime</Trans>:{' '}
</span>
<span>
<FormatDuration
milliseconds={mediaItem.totalRuntime * 60 * 1000}
/>
</span>
</div>
)}

{mediaItem.platform && (
<div>
<span className="font-bold">
<Plural
value={mediaItem.platform.length}
one="Platform"
other="platforms"
/>
:{' '}
</span>
<span>{mediaItem.platform.sort().join(', ')}</span>
</div>
)}

{mediaItem.network && (
<div>
<span className="font-bold">
<Trans>Network</Trans>:{' '}
</span>
<span>{mediaItem.network}</span>
</div>
)}

{mediaItem.status && (
<div>
<span className="font-bold">
<Trans>Status</Trans>:{' '}
</span>
<span>{mediaItem.status}</span>
</div>
)}

{mediaItem.genres && (
<div>
<span className="font-bold">
<Plural
value={mediaItem.genres.length}
one="Genre"
other="Genres"
/>
:{' '}
</span>
{mediaItem.genres.sort().map((genre, index) => (
<span key={genre}>
<span className="italic">{genre}</span>

{index < mediaItem.genres.length - 1 && (
<span className="mx-1 text-gray-600">|</span>
)}
</span>
))}
</div>
)}

{mediaItem.overview && (
<div>
<span className="font-bold">
<Trans>Overview</Trans>:{' '}
</span>
<span className="whitespace-pre-wrap">{mediaItem.overview}</span>
</div>
)}

{mediaItem.language && (
<div>
<span className="font-bold">
<Trans>Language</Trans>:{' '}
</span>
<span>{mediaItem.language}</span>
</div>
)}

{mediaItem.authors && (
<div>
<span className="font-bold">
<Plural
value={mediaItem.authors.length}
one="Author"
other="Authors"
/>
:{' '}
</span>
{mediaItem.authors.sort().join(', ')}
</div>
)}

{mediaItem.narrators && (
<div>
<span className="font-bold">
<Plural
value={mediaItem.narrators.length}
one="Narrator"
other="Narrators"
/>
:{' '}
</span>
{mediaItem.narrators.sort().join(',')}
</div>
)}
{mediaItem.numberOfPages && (
<div>
<span className="font-bold">
<Trans>Number of pages</Trans>:{' '}
</span>
{mediaItem.numberOfPages}
</div>
)}

{isTvShow(mediaItem) && (
<>
<div>
<span className="font-bold">
<Trans>Seasons</Trans>:{' '}
</span>
{mediaItem.numberOfSeasons}
</div>

<div>
<span className="font-bold">
<Trans>Episodes</Trans>:{' '}
</span>
{mediaItem.numberOfEpisodes}
</div>

{mediaItem.unseenEpisodesCount > 0 && (
<div>
<span className="font-bold">
<Trans>Unseen episodes</Trans>:{' '}
</span>
{mediaItem.unseenEpisodesCount}
</div>
)}
</>
)}

<div>
<span className="font-bold">
<Trans>Source</Trans>:{' '}
</span>
<span>{mediaItem.source}</span>
</div>

<div className="pt-3">
<ExternalLinks mediaItem={mediaItem} />
</div>
</div>
</div>

{canMetadataBeUpdated(mediaItem) && (
<div className="pt-3">
<UpdateMetadataButton mediaItem={mediaItem} />
</div>
)}

<div className="mt-3">
{isOnWatchlist(mediaItem) ? (
<RemoveFromWatchlistButton mediaItem={mediaItem} />
) : (
<AddToWatchlistButton mediaItem={mediaItem} />
)}
</div>

<div className="mt-3">
<AddToListButtonWithModal mediaItemId={mediaItem.id} />
</div>

<div className="mt-3">
{(hasBeenReleased(mediaItem) || !hasReleaseDate(mediaItem)) && (
<>
<AddToSeenHistoryButton mediaItem={mediaItem} />

{hasBeenSeenAtLeastOnce(mediaItem) && (
<div className="mt-3">
<RemoveFromSeenHistoryButton mediaItem={mediaItem} />
</div>
)}
</>
)}
</div>

<div className="mt-3"></div>

{mediaItem.mediaType === 'tv' && (
<Link
to={`/seasons/${mediaItem.id}`}
className="mt-3 text-green-600 dark:text-green-400 btn"
>
<Trans>Episodes page</Trans>
</Link>
)}

{(hasBeenReleased(mediaItem) || !hasReleaseDate(mediaItem)) &&
!isTvShow(mediaItem) && (
<>
{!hasProgress(mediaItem) && (
<div
className="mt-3 text-sm btn"
onClick={async () => {
addToProgress({
mediaItemId: mediaItem.id,
progress: 0,
});
}}
>
{isMovie(mediaItem) && <Trans>I am watching it</Trans>}
{isBook(mediaItem) && <Trans>I am reading it</Trans>}
{isAudiobook(mediaItem) && <Trans>I am listening it</Trans>}
{isVideoGame(mediaItem) && <Trans>I am playing it</Trans>}
</div>
)}

{hasProgress(mediaItem) && (
<>
<div
className="mt-3 text-sm btn"
onClick={async () => {
addToProgress({
mediaItemId: mediaItem.id,
progress: 1,
});
}}
>
{isMovie(mediaItem) && <Trans>I finished watching it</Trans>}
{isBook(mediaItem) && <Trans>I finished reading it</Trans>}
{isAudiobook(mediaItem) && (
<Trans>I finished listening it</Trans>
)}
{isVideoGame(mediaItem) && (
<Trans>I finished playing it</Trans>
)}
</div>

<div className="mt-3">
<Trans>Progress</Trans>:{' '}
{Math.round(mediaItem.progress * 100)}%
</div>
</>
)}

<div className="mt-3">
<SetProgressButton mediaItem={mediaItem} />
</div>
</>
)}

{mediaItem.upcomingEpisode && (
<>
<div className="mt-3 font-bold">
<Trans>Next episode</Trans>{' '}
{mediaItem.upcomingEpisode.releaseDate && (
<RelativeTime
to={parseISO(mediaItem.upcomingEpisode.releaseDate)}
/>
)}
: {formatEpisodeNumber(mediaItem.upcomingEpisode)}{' '}
{mediaItem.upcomingEpisode.title}
</div>
</>
)}
{mediaItem.firstUnwatchedEpisode && (
<div className="flex mt-3 font-bold">
<Trans>First unwatched episode</Trans>:{' '}
{formatEpisodeNumber(mediaItem.firstUnwatchedEpisode)}{' '}
{mediaItem.firstUnwatchedEpisode.title}
<MarkAsSeenFirstUnwatchedEpisode mediaItem={mediaItem} />
</div>
)}
{mediaItem.lastSeenAt > 0 && (
<div className="mt-3">
{isAudiobook(mediaItem) && (
<Trans>
Last listened at {new Date(mediaItem.lastSeenAt).toLocaleString()}
</Trans>
)}

{isBook(mediaItem) && (
<Trans>
Last read at {new Date(mediaItem.lastSeenAt).toLocaleString()}
</Trans>
)}

{(isMovie(mediaItem) || isTvShow(mediaItem)) && (
<Trans>
Last seen at {new Date(mediaItem.lastSeenAt).toLocaleString()}
</Trans>
)}

{isVideoGame(mediaItem) && (
<Trans>
Last played at {new Date(mediaItem.lastSeenAt).toLocaleString()}
</Trans>
)}
</div>
)}
{mediaItem.seenHistory?.length > 0 && (
<div className="mt-3">
<div>
{isAudiobook(mediaItem) && (
<Plural
value={mediaItem.seenHistory.length}
one="Listened 1 time"
other="Listened # times"
/>
)}

{isBook(mediaItem) && (
<Plural
value={mediaItem.seenHistory.length}
one="Read 1 time"
other="Read # times"
/>
)}

{(isMovie(mediaItem) || isTvShow(mediaItem)) && (
<Plural
value={mediaItem.seenHistory.length}
one="Seen 1 time"
other="Seen # times"
/>
)}

{isVideoGame(mediaItem) && (
<Plural
value={mediaItem.seenHistory.length}
one="Played 1 time"
other="Played # times"
/>
)}
</div>
<Link to={`/seen-history/${mediaItem.id}`} className="underline">
{isAudiobook(mediaItem) && <Trans>Listened history</Trans>}

{isBook(mediaItem) && <Trans>Read history</Trans>}

{(isMovie(mediaItem) || isTvShow(mediaItem)) && (
<Trans>Seen history</Trans>
)}

{isVideoGame(mediaItem) && <Trans>Played history</Trans>}
</Link>
</div>
)}

{(isMovie(mediaItem) || isTvShow(mediaItem)) && (
<div className="pt-3">
<WhereToWatchComponent mediaItem={mediaItem} />
</div>
)}

{/* Rating */}
{(hasBeenReleased(mediaItem) || !hasReleaseDate(mediaItem)) && (
<RatingAndReview
userRating={mediaItem.userRating}
mediaItem={mediaItem}
/>
)}
</div>
);
};

Check notice on line 622 in client/src/pages/Details.tsx

View check run for this annotation

codefactor.io / CodeFactor

client/src/pages/Details.tsx#L198-L622

Complex Method

export const AddToWatchlistButton: FunctionComponent<{
mediaItem: MediaItemItemsResponse;
Expand Down
3 changes: 2 additions & 1 deletion client/src/pages/EpisodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useDetails } from 'src/api/details';
import {
findEpisodeBySeasonAndEpisodeNumber,
hasBeenSeenAtLeastOnce,
originalAndTranslatedTitle,
} from 'src/mediaItem';
import {
formatEpisodeNumber,
Expand Down Expand Up @@ -45,7 +46,7 @@ export const EpisodePage: FunctionComponent = () => {
return (
<div>
<Link className="text-2xl font-bold" to={`/details/${mediaItem.id}`}>
{mediaItem.title} {formatEpisodeNumber(episode)}
{originalAndTranslatedTitle(mediaItem)} {formatEpisodeNumber(episode)}
</Link>
{episode.description && (
<div className="pt-2">
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/SeasonsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { parseISO } from 'date-fns';
import { useDetails } from 'src/api/details';
import { Poster } from 'src/components/Poster';
import { BadgeRating } from 'src/components/StarRating';
import { hasBeenSeenAtLeastOnce, useSelectedSeason } from 'src/mediaItem';
import { hasBeenSeenAtLeastOnce, useSelectedSeason, originalAndTranslatedTitle } from 'src/mediaItem';
import {
hasBeenReleased,
hasReleaseDate,
Expand Down Expand Up @@ -157,7 +157,7 @@ export const SeasonsPage: FunctionComponent = () => {
<div className="sm:w-full">
<div className="flex w-full">
<Link className="text-2xl font-bold" to={`/details/${mediaItem.id}`}>
{mediaItem.title}
{originalAndTranslatedTitle(mediaItem)}
</Link>
</div>
<div className="flex flex-row flex-wrap w-full">
Expand Down