14 changed files with 200 additions and 42 deletions
@ -0,0 +1,56 @@ |
|||||||
|
import { Icon, Icons } from "@/components/Icon"; |
||||||
|
|
||||||
|
function Card(props: { children: React.ReactNode }) { |
||||||
|
return <div className="px-6 py-8">{props.children}</div>; |
||||||
|
} |
||||||
|
|
||||||
|
function Title(props: { children: React.ReactNode }) { |
||||||
|
return ( |
||||||
|
<h3 className="uppercase font-bold text-video-context-type-secondary text-sm pl-1 pb-2 border-b border-opacity-25 border-video-context-border mb-6"> |
||||||
|
{props.children} |
||||||
|
</h3> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
function Section(props: { children: React.ReactNode }) { |
||||||
|
return <div className="my-5">{props.children}</div>; |
||||||
|
} |
||||||
|
|
||||||
|
function Link(props: { onClick?: () => void; children: React.ReactNode }) { |
||||||
|
return ( |
||||||
|
<button |
||||||
|
type="button" |
||||||
|
className="flex justify-between items-center py-2 pl-3 pr-3 -ml-3 rounded hover:bg-video-context-border hover:bg-opacity-10 w-full" |
||||||
|
style={{ width: "calc(100% + 1.5rem)" }} |
||||||
|
onClick={props.onClick} |
||||||
|
> |
||||||
|
{props.children} |
||||||
|
</button> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
function LinkTitle(props: { children: React.ReactNode }) { |
||||||
|
return ( |
||||||
|
<span className="text-video-context-type-main font-medium"> |
||||||
|
{props.children} |
||||||
|
</span> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
function LinkChevron(props: { children?: React.ReactNode }) { |
||||||
|
return ( |
||||||
|
<span className="text-white flex items-center font-medium"> |
||||||
|
{props.children} |
||||||
|
<Icon className="text-xl ml-1" icon={Icons.CHEVRON_RIGHT} /> |
||||||
|
</span> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export const Context = { |
||||||
|
Card, |
||||||
|
Title, |
||||||
|
Section, |
||||||
|
Link, |
||||||
|
LinkTitle, |
||||||
|
LinkChevron, |
||||||
|
}; |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
import { useHistory, useParams } from "react-router-dom"; |
||||||
|
import { useAsync } from "react-use"; |
||||||
|
|
||||||
|
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta"; |
||||||
|
import { decodeTMDBId } from "@/backend/metadata/tmdb"; |
||||||
|
import { MWMediaType } from "@/backend/metadata/types/mw"; |
||||||
|
|
||||||
|
export interface MetaPartProps { |
||||||
|
onGetMeta?: (meta: DetailedMeta, episodeId?: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export function MetaPart(props: MetaPartProps) { |
||||||
|
const params = useParams<{ |
||||||
|
media: string; |
||||||
|
episode?: string; |
||||||
|
season?: string; |
||||||
|
}>(); |
||||||
|
const history = useHistory(); |
||||||
|
|
||||||
|
const { loading, error } = useAsync(async () => { |
||||||
|
const data = decodeTMDBId(params.media); |
||||||
|
if (!data) return; |
||||||
|
|
||||||
|
const meta = await getMetaFromId(data.type, data.id, params.season); |
||||||
|
if (!meta) return; |
||||||
|
|
||||||
|
// replace link with new link if youre not already on the right link
|
||||||
|
let epId = params.episode; |
||||||
|
if (meta.meta.type === MWMediaType.SERIES) { |
||||||
|
let ep = meta.meta.seasonData.episodes.find( |
||||||
|
(v) => v.id === params.episode |
||||||
|
); |
||||||
|
if (!ep) ep = meta.meta.seasonData.episodes[0]; |
||||||
|
epId = ep.id; |
||||||
|
if ( |
||||||
|
params.season !== meta.meta.seasonData.id || |
||||||
|
params.episode !== ep.id |
||||||
|
) { |
||||||
|
history.replace( |
||||||
|
`/media/${params.media}/${meta.meta.seasonData.id}/${ep.id}` |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
props.onGetMeta?.(meta, epId); |
||||||
|
}, []); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="flex items-center justify-center"> |
||||||
|
{loading ? <p>loading meta...</p> : null} |
||||||
|
{error ? <p>failed to load meta!</p> : null} |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
Loading…
Reference in new issue