diff --git a/tranga-website/src/App.tsx b/tranga-website/src/App.tsx index 95b42ad..5801ebe 100644 --- a/tranga-website/src/App.tsx +++ b/tranga-website/src/App.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react' import { ApiConfig } from './api/http-client.ts' import MangaProvider from './contexts/MangaContext.tsx' import MangaList from './Components/Mangas/MangaList.tsx' -import Search from './Search.tsx' +import {Search} from './Search.tsx' import MangaConnectorProvider from './contexts/MangaConnectorContext.tsx' export default function App() { diff --git a/tranga-website/src/Components/Mangas/MangaCard.tsx b/tranga-website/src/Components/Mangas/MangaCard.tsx index 8e7405a..c38d5b0 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.tsx +++ b/tranga-website/src/Components/Mangas/MangaCard.tsx @@ -6,51 +6,30 @@ import { Skeleton, Typography, } from '@mui/joy' -import { EventHandler, ReactNode, useContext, useEffect, useState } from 'react' +import { EventHandler, ReactNode, useContext } 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 default function MangaCard({ - mangaDetail, - key, - onClick, -}: { - mangaDetail?: Manga | MinimalManga - key?: string - onClick?: EventHandler -}): ReactNode { - const Api = useContext(ApiContext) - - const [manga, setManga] = useState( - mangaDetail - ) - - useEffect(() => { - if (!key) return - Api.mangaDetail(key).then((data) => { - if (data.ok) { - setManga(data.data) - } - }) - }, [Api, key]) +export default function MangaCard(props: MangaCardProps): ReactNode { + const Api = useContext(ApiContext); return ( ( + badgeContent={props.manga?.mangaConnectorIds.map((id) => ( ))} className={'manga-card-badge'} > - + - + - {manga?.name ?? ( + {props.manga?.name ?? ( {stringWithRandomLength()} )} @@ -60,6 +39,11 @@ export default function MangaCard({ ) } +export interface MangaCardProps { + manga?: Manga | MinimalManga + onClick?: EventHandler +} + const stringWithRandomLength = (): string => { return 'wow' } diff --git a/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx b/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx index 6683efa..2553fb1 100644 --- a/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx +++ b/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx @@ -17,7 +17,6 @@ export default function MangaConnectorIcon({ ); useEffect(() => { - console.log(mangaConnector, mangaConnectorName); if (mangaConnector) { setConnector(mangaConnector) return; diff --git a/tranga-website/src/Components/Mangas/MangaList.tsx b/tranga-website/src/Components/Mangas/MangaList.tsx index bed23d0..6926653 100644 --- a/tranga-website/src/Components/Mangas/MangaList.tsx +++ b/tranga-website/src/Components/Mangas/MangaList.tsx @@ -1,6 +1,6 @@ import { Stack } from '@mui/joy' import './MangaList.css' -import { ReactNode, useContext, useEffect, useState } from 'react' +import {Dispatch, ReactNode, useContext, useEffect, useState} from 'react' import { Manga, MangaReleaseStatus, @@ -29,7 +29,7 @@ export default function MangaList({ - {children} - {manga.map((m) => ( - <> - - - + {props.children} + {props.manga.map((m) => ( + { if(props.mangaOnClick) props.mangaOnClick(m); } } /> ))} ) } + +export interface MangaCardListProps { + manga: (Manga | MinimalManga)[] + children?: ReactNode + mangaOnClick?: Dispatch +} diff --git a/tranga-website/src/MangaDetail.tsx b/tranga-website/src/MangaDetail.tsx new file mode 100644 index 0000000..47aa719 --- /dev/null +++ b/tranga-website/src/MangaDetail.tsx @@ -0,0 +1,59 @@ +import {Manga} from "./api/data-contracts.ts"; +import {Dispatch, ReactNode, useContext, useEffect, useState} from "react"; +import {Card, CardCover, Chip, Modal, ModalDialog, Stack, Typography, useTheme} from "@mui/joy"; +import ModalClose from "@mui/joy/ModalClose"; +import {ApiContext} from "./contexts/ApiContext.tsx"; +import {MangaContext} from "./contexts/MangaContext.tsx"; +import './Components/Mangas/MangaCard.css' +import MarkdownPreview from "@uiw/react-markdown-preview"; + +export default function MangaDetail (props: MangaDetailProps) : ReactNode { + const Api = useContext(ApiContext); + const Manga = useContext(MangaContext); + + const [manga, setManga] = useState(props.manga) + + useEffect(() => { + if (!props.open) return; + if (!props.mangaKey) return; + if (props.manga != undefined) return; + Manga.GetManga(props.mangaKey).then(setManga); + }, [Api, props]); + + const theme = useTheme(); + + return ( + props.setOpen(false)}> + + +
+ {manga?.name} + + + + + + + + {manga?.tags.map(tag => {tag})} + {manga?.authors.map(author => {author.name} )} + {manga?.links.map(link => {link.provider})} + + + + + {props.actions} + +
+
+
+ ); +} + +export interface MangaDetailProps { + manga?: Manga; + mangaKey?: string; + open: boolean; + setOpen: Dispatch; + actions?: ReactNode[]; +} \ No newline at end of file diff --git a/tranga-website/src/Search.tsx b/tranga-website/src/Search.tsx index 66055c2..34a8eb2 100644 --- a/tranga-website/src/Search.tsx +++ b/tranga-website/src/Search.tsx @@ -16,60 +16,64 @@ import MangaConnectorIcon from './Components/Mangas/MangaConnectorIcon.tsx' import TInput from './Components/Inputs/TInput.tsx' import { ApiContext } from './contexts/ApiContext.tsx' import { MangaCardList } from './Components/Mangas/MangaList.tsx' -import { MangaConnector, MinimalManga } from './api/data-contracts.ts' +import {MangaConnector, MinimalManga} from './api/data-contracts.ts' +import MangaDetail from "./MangaDetail.tsx"; -export default function Search({ - open, - setOpen, -}: { - open: boolean - setOpen: Dispatch -}): ReactNode { +export function Search(props: SearchModalProps): ReactNode { const Api = useContext(ApiContext) const MangaConnectors = useContext(MangaConnectorContext) - const [selectedConnector, setSelectedConnector] = useState() - - const [searchResults, setSearchResults] = useState([]) - - const startSearch = ( - value: string | number | readonly string[] | undefined - ): Promise => { - if (typeof value != 'string') return Promise.reject() - setSearchResults([]) - if (isUrl(value)) { - return Api.searchUrlCreate(value) - .then((result) => { - if (result.ok) { - setSearchResults([result.data]) - return Promise.resolve() - } else return Promise.reject() - }) - .catch(Promise.reject) - } else { - if (!selectedConnector) return Promise.reject() - return Api.searchDetail(selectedConnector?.key, value) - .then((result) => { - if (result.ok) { - setSearchResults(result.data) - return Promise.resolve() - } else return Promise.reject() - }) - .catch(Promise.reject) - } - } - useEffect(() => { - if (open){ + if (props.open) { setSelectedConnector(undefined); setSearchResults([]); } }, [open]); + const [selectedConnector, setSelectedConnector] = useState() + const [searchResults, setSearchResults] = useState([]) + + const startSearch = async ( + value: string | number | readonly string[] | undefined + ): Promise => { + if (typeof value != 'string') return Promise.reject() + setSearchResults([]) + if (isUrl(value)) { + try { + let result = await Api.searchUrlCreate(value); + if (result.ok) { + setSearchResults([result.data]) + return Promise.resolve() + } else return Promise.reject() + } catch (reason) { + return await Promise.reject(reason); + } + } else { + if (!selectedConnector) return Promise.reject() + try { + let result2 = await Api.searchDetail(selectedConnector?.key, value); + if (result2.ok) { + setSearchResults(result2.data) + return Promise.resolve() + } else return Promise.reject() + } catch (reason1) { + return await Promise.reject(reason1); + } + } + } + + const [selectedManga, setSelectedManga] = useState(undefined); + const [mangaDetailOpen, setMangaDetailOpen] = useState(false); + + function openMangaDetail(manga: MinimalManga) { + setSelectedManga(manga); + setMangaDetailOpen(true); + } + return ( - setOpen(false)}> - - + props.setOpen(false)}> + + {MangaConnectors.map((c) => ( setSelectedConnector(c)} > @@ -91,7 +96,7 @@ export default function Search({ @@ -114,12 +119,18 @@ export default function Search({ /> - + + ) } +export interface SearchModalProps { + open: boolean; + setOpen: Dispatch; +} + function isUrl(str: string): boolean { try { new URL(str) @@ -127,4 +138,4 @@ function isUrl(str: string): boolean { } catch { return false } -} +} \ No newline at end of file