Rebuild with custom components

This commit is contained in:
2025-09-03 20:48:50 +02:00
parent 63618d15f4
commit 22e3ec7929
54 changed files with 8423 additions and 11407 deletions

View File

@@ -1,180 +1,65 @@
import {
Badge,
Box,
Card,
CardContent,
CardCover,
Chip,
Link,
Modal,
ModalDialog,
Stack,
Tooltip,
Typography,
} from "@mui/joy";
import { Manga, MinimalManga } from "../../apiClient/data-contracts.ts";
import {
Dispatch,
ReactNode,
SetStateAction,
useContext,
useEffect,
useState,
} from "react";
import "./MangaCard.css";
import MangaConnectorBadge from "./MangaConnectorBadge.tsx";
import ModalClose from "@mui/joy/ModalClose";
import { ApiContext } from "../../apiClient/ApiContext.tsx";
import MarkdownPreview from "@uiw/react-markdown-preview";
import { MangaContext } from "../../App.tsx";
import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx";
Badge,
Card,
CardContent,
CardCover,
Skeleton,
Typography,
} from '@mui/joy'
import { EventHandler, ReactNode, useContext, useEffect, useState } from 'react'
import './MangaCard.css'
import MangaConnectorIcon from './MangaConnectorIcon.tsx'
import { Manga, MinimalManga } from '../../api/data-contracts.ts'
import { ApiContext } from '../../contexts/ApiContext.tsx'
export function MangaCard({
manga,
children,
export default function MangaCard({
mangaDetail,
key,
onClick,
}: {
manga: MinimalManga | undefined;
children?: ReactNode;
}) {
const [open, setOpen] = useState(false);
mangaDetail?: Manga | MinimalManga
key?: string
onClick?: EventHandler<any>
}): ReactNode {
const Api = useContext(ApiContext)
if (manga === undefined) return PlaceHolderCard();
const [manga, setManga] = useState<Manga | MinimalManga | undefined>(
mangaDetail
)
return (
<MangaConnectorBadge manga={manga}>
<Card className={"manga-card"} onClick={() => setOpen(true)}>
<CardCover className={"manga-cover"}>
<MangaCover mangaId={manga?.key} />
</CardCover>
<CardCover className={"manga-cover-blur"} />
<CardContent className={"manga-content"}>
<Typography level={"title-lg"}>{manga?.name}</Typography>
</CardContent>
</Card>
<MangaModal minimalManga={manga} open={open} setOpen={setOpen}>
{children}
</MangaModal>
</MangaConnectorBadge>
);
}
useEffect(() => {
if (!key) return
Api.mangaDetail(key).then((data) => {
if (data.ok) {
setManga(data.data)
}
})
}, [Api, key])
export function MangaModal({
minimalManga,
open,
setOpen,
children,
}: {
minimalManga: MinimalManga;
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
children?: ReactNode;
}) {
const { getManga } = useContext(MangaContext);
const [manga, setManga] = useState<Manga>();
useEffect(() => {
getManga(minimalManga.key).then(setManga);
}, [getManga, minimalManga]);
return (
<Modal open={open} onClose={() => setOpen(false)} className={"manga-modal"}>
<ModalDialog style={{ width: "100%" }}>
<ModalClose />
<Tooltip
title={
<Stack spacing={1}>
{manga?.altTitles?.map((title) => (
<Chip>{title.title}</Chip>
))}
</Stack>
}
return (
<Badge
badgeContent={manga?.mangaConnectorIds.map((id) => (
<MangaConnectorIcon key={id.mangaConnectorName} />
))}
className={'manga-card-badge'}
>
<Typography level={"h4"} width={"fit-content"}>
{manga?.name ?? minimalManga.name}
</Typography>
</Tooltip>
<Stack direction={"row"} spacing={2}>
<Box key={"Cover"} className={"manga-card"}>
<MangaCover mangaId={minimalManga.key} />
</Box>
<Stack
key={"Description"}
direction={"column"}
sx={{ width: "calc(100% - 230px)" }}
>
<Stack
key={"Tags"}
direction={"row"}
flexWrap={"wrap"}
useFlexGap
spacing={0.5}
>
{manga?.mangaConnectorIdsIds?.map((idid) => (
<MangaConnectorLinkFromId
key={idid}
MangaConnectorIdId={idid}
/>
))}
{manga?.mangaTags?.map((tag) => (
<Chip key={tag.tag}>{tag.tag}</Chip>
))}
{manga?.links?.map((link) => (
<Chip key={link.key}>
<Link href={link.linkUrl}>{link.linkProvider}</Link>
</Chip>
))}
</Stack>
<Box sx={{ flexGrow: 1 }}>
<MarkdownPreview
source={manga?.description ?? "Loading..."}
style={{
background: "transparent",
maxHeight: "50vh",
overflowY: "auto",
}}
/>
</Box>
<Stack
sx={{ justifySelf: "flex-end", alignSelf: "flex-end" }}
spacing={2}
direction={"row"}
>
{manga ? children : null}
</Stack>
</Stack>
</Stack>
</ModalDialog>
</Modal>
);
<Card className={'manga-card'} onClick={onClick}>
<CardCover className={'manga-card-cover'}>
<img src={'/blahaj.png'} />
</CardCover>
<CardCover className={'manga-card-cover-blur'} />
<CardContent className={'manga-card-content'}>
<Typography level={'h4'}>
{manga?.name ?? (
<Skeleton>{stringWithRandomLength()}</Skeleton>
)}
</Typography>
</CardContent>
</Card>
</Badge>
)
}
function PlaceHolderCard() {
return (
<Badge>
<Card className={"manga-card"}>
<CardCover className={"manga-cover"}>
<img src={"/blahaj.png"} />
</CardCover>
<CardCover className={"manga-cover-blur"} />
</Card>
</Badge>
);
}
function MangaCover({ mangaId }: { mangaId?: string }) {
const api = useContext(ApiContext);
const uri = mangaId
? `${api.baseUrl}/v2/Manga/${mangaId}/Cover`
: "blahaj.png";
return (
<img
src={uri}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
borderRadius: "var(--CardCover-radius)",
}}
/>
);
const stringWithRandomLength = (): string => {
return 'wow'
}