fix: fetch localized person details from TMDb (#1243)

* fix: fetch localized person details from TMDb

* feat: include DOB, hometown, and alternate names on person detail pages

* fix: remove unnecessary ternary operator

* fix(ui): don't display AKA when empty
pull/1260/head
TheCatLady 3 years ago committed by GitHub
parent aee43cecfd
commit 1d7a938ef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -306,6 +306,7 @@ export interface TmdbKeyword {
export interface TmdbPersonDetail {
id: number;
name: string;
birthday: string;
deathday: string;
known_for_department: string;
also_known_as?: string[];

@ -8,6 +8,7 @@ import Media from '../entity/Media';
export interface PersonDetail {
id: number;
name: string;
birthday: string;
deathday: string;
knownForDepartment: string;
alsoKnownAs?: string[];
@ -64,6 +65,7 @@ export interface CombinedCredit {
export const mapPersonDetails = (person: TmdbPersonDetail): PersonDetail => ({
id: person.id,
name: person.name,
birthday: person.birthday,
deathday: person.deathday,
knownForDepartment: person.known_for_department,
alsoKnownAs: person.also_known_as,

@ -16,10 +16,13 @@ import PageTitle from '../Common/PageTitle';
import TitleCard from '../TitleCard';
const messages = defineMessages({
birthdate: 'Born {birthdate}',
lifespan: '{birthdate} {deathdate}',
alsoknownas: 'Also Known As: {names}',
namedelimiter: ', ',
appearsin: 'Appearances',
crewmember: 'Crew',
ascharacter: 'as {character}',
nobiography: 'No biography available.',
});
const PersonDetails: React.FC = () => {
@ -27,7 +30,7 @@ const PersonDetails: React.FC = () => {
const { locale } = useContext(LanguageContext);
const router = useRouter();
const { data, error } = useSWR<PersonDetail>(
`/api/v1/person/${router.query.personId}`
`/api/v1/person/${router.query.personId}?language=${locale}`
);
const [showBio, setShowBio] = useState(false);
@ -82,6 +85,41 @@ const PersonDetails: React.FC = () => {
return <Error statusCode={404} />;
}
const personAttributes: string[] = [];
if (data.birthday) {
if (data.deathday) {
personAttributes.push(
intl.formatMessage(messages.lifespan, {
birthdate: intl.formatDate(data.birthday, {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
deathdate: intl.formatDate(data.deathday, {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
})
);
} else {
personAttributes.push(
intl.formatMessage(messages.birthdate, {
birthdate: intl.formatDate(data.birthday, {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
})
);
}
}
if (data.placeOfBirth) {
personAttributes.push(data.placeOfBirth);
}
const isLoading = !combinedCredits && !errorCombinedCredits;
const cast = (sortedCast ?? []).length > 0 && (
@ -179,9 +217,13 @@ const PersonDetails: React.FC = () => {
/>
</div>
)}
<div className="relative z-10 flex flex-col items-center mt-4 mb-8 md:flex-row md:items-start">
<div
className={`relative z-10 flex flex-col items-center mt-4 mb-8 lg:flex-row ${
data.biography ? 'lg:items-start' : ''
}`}
>
{data.profilePath && (
<div className="relative flex-shrink-0 mb-6 mr-0 overflow-hidden rounded-full w-36 h-36 md:w-44 md:h-44 md:mb-0 md:mr-6 ring-1 ring-gray-700">
<div className="relative flex-shrink-0 mb-6 mr-0 overflow-hidden rounded-full w-36 h-36 lg:w-44 lg:h-44 lg:mb-0 lg:mr-6 ring-1 ring-gray-700">
<CachedImage
src={`https://image.tmdb.org/t/p/w600_and_h900_bestv2${data.profilePath}`}
alt=""
@ -190,30 +232,40 @@ const PersonDetails: React.FC = () => {
/>
</div>
)}
<div className="text-center text-gray-300 md:text-left">
<h1 className="mb-4 text-3xl text-white md:text-4xl">{data.name}</h1>
<div className="relative">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
<div
className="outline-none group ring-0"
onClick={() => setShowBio((show) => !show)}
role="button"
tabIndex={-1}
>
<TruncateMarkup
lines={showBio ? 200 : 6}
ellipsis={
<Ellipsis className="relative inline-block ml-2 -top-0.5 opacity-70 group-hover:opacity-100 transition duration-300" />
}
<div className="text-center text-gray-300 lg:text-left">
<h1 className="text-3xl text-white lg:text-4xl">{data.name}</h1>
<div className="mt-1 mb-2 space-y-1 text-xs text-white sm:text-sm lg:text-base">
<div>{personAttributes.join(' | ')}</div>
{(data.alsoKnownAs ?? []).length > 0 && (
<div>
{intl.formatMessage(messages.alsoknownas, {
names: (data.alsoKnownAs ?? []).join(
intl.formatMessage(messages.namedelimiter)
),
})}
</div>
)}
</div>
{data.biography && (
<div className="relative text-left">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
<div
className="outline-none group ring-0"
onClick={() => setShowBio((show) => !show)}
role="button"
tabIndex={-1}
>
<div>
{data.biography
? data.biography
: intl.formatMessage(messages.nobiography)}
</div>
</TruncateMarkup>
<TruncateMarkup
lines={showBio ? 200 : 6}
ellipsis={
<Ellipsis className="relative inline-block ml-2 -top-0.5 opacity-70 group-hover:opacity-100 transition duration-300" />
}
>
<p className="pt-2 text-sm lg:text-base">{data.biography}</p>
</TruncateMarkup>
</div>
</div>
</div>
)}
</div>
</div>
{data.knownForDepartment === 'Acting' ? [cast, crew] : [crew, cast]}

@ -142,10 +142,13 @@
"components.PermissionEdit.viewrequestsDescription": "Grant permission to view other users' requests.",
"components.PermissionEdit.vote": "Vote",
"components.PermissionEdit.voteDescription": "Grant permission to vote on requests (voting not yet implemented).",
"components.PersonDetails.alsoknownas": "Also Known As: {names}",
"components.PersonDetails.appearsin": "Appearances",
"components.PersonDetails.ascharacter": "as {character}",
"components.PersonDetails.birthdate": "Born {birthdate}",
"components.PersonDetails.crewmember": "Crew",
"components.PersonDetails.nobiography": "No biography available.",
"components.PersonDetails.lifespan": "{birthdate} {deathdate}",
"components.PersonDetails.namedelimiter": ", ",
"components.PlexLoginButton.loading": "Loading…",
"components.PlexLoginButton.signingin": "Signing in…",
"components.PlexLoginButton.signinwithplex": "Sign In",

Loading…
Cancel
Save