mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-09-10 20:08:19 +02:00
lint and prettier
This commit is contained in:
@@ -8,7 +8,7 @@ import MangaProvider from './contexts/MangaContext.tsx'
|
|||||||
import MangaList from './Components/Mangas/MangaList.tsx'
|
import MangaList from './Components/Mangas/MangaList.tsx'
|
||||||
import { Search } from './Search.tsx'
|
import { Search } from './Search.tsx'
|
||||||
import MangaConnectorProvider from './contexts/MangaConnectorContext.tsx'
|
import MangaConnectorProvider from './contexts/MangaConnectorContext.tsx'
|
||||||
import LibraryProvider from "./contexts/FileLibraryContext.tsx";
|
import LibraryProvider from './contexts/FileLibraryContext.tsx'
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [apiUri, setApiUri] = useState<string>(
|
const [apiUri, setApiUri] = useState<string>(
|
||||||
@@ -35,8 +35,13 @@ export default function App() {
|
|||||||
<Settings setApiUri={setApiUri} />
|
<Settings setApiUri={setApiUri} />
|
||||||
</Header>
|
</Header>
|
||||||
<Sheet className={'app-content'}>
|
<Sheet className={'app-content'}>
|
||||||
<MangaList openSearch={() => setSearchOpen(true)} />
|
<MangaList
|
||||||
<Search open={searchOpen} setOpen={setSearchOpen} />
|
openSearch={() => setSearchOpen(true)}
|
||||||
|
/>
|
||||||
|
<Search
|
||||||
|
open={searchOpen}
|
||||||
|
setOpen={setSearchOpen}
|
||||||
|
/>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
</MangaProvider>
|
</MangaProvider>
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
from {transform: translate(-50%, -50%) rotate(0);}
|
from {
|
||||||
to {transform: translate(-50%, -50%) rotate(360deg);}
|
transform: translate(-50%, -50%) rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translate(-50%, -50%) rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t-loadable[aria-disabled='true'] {
|
||||||
.t-loadable[aria-disabled="true"] {
|
|
||||||
--border-radius: 5px;
|
--border-radius: 5px;
|
||||||
--border-size: 2px;
|
--border-size: 2px;
|
||||||
--border-bg: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
|
--border-bg: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
|
||||||
@@ -42,10 +45,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-loadable[aria-disabled="true"]:has(input)::after {
|
.t-loadable[aria-disabled='true']:has(input)::after {
|
||||||
background: black;
|
background: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-loadable[aria-disabled="true"]:is(button) {
|
.t-loadable[aria-disabled='true']:is(button) {
|
||||||
color: var(--joy-palette-text-primary);
|
color: var(--joy-palette-text-primary);
|
||||||
}
|
}
|
@@ -13,7 +13,7 @@
|
|||||||
.manga-card-cover-blur {
|
.manga-card-cover-blur {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
150deg,
|
150deg,
|
||||||
rgba(245, 169, 184, 0.80) 50%,
|
rgba(245, 169, 184, 0.8) 50%,
|
||||||
rgba(91, 206, 250, 0.3)
|
rgba(91, 206, 250, 0.3)
|
||||||
);
|
);
|
||||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
@@ -13,18 +13,26 @@ import { Manga, MinimalManga } from '../../api/data-contracts.ts'
|
|||||||
import { ApiContext } from '../../contexts/ApiContext.tsx'
|
import { ApiContext } from '../../contexts/ApiContext.tsx'
|
||||||
|
|
||||||
export default function MangaCard(props: MangaCardProps): ReactNode {
|
export default function MangaCard(props: MangaCardProps): ReactNode {
|
||||||
const Api = useContext(ApiContext);
|
const Api = useContext(ApiContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
badgeContent={props.manga?.mangaConnectorIds.map((id) => (
|
badgeContent={props.manga?.mangaConnectorIds.map((id) => (
|
||||||
<MangaConnectorIcon mangaConnectorName={id.mangaConnectorName} />
|
<MangaConnectorIcon
|
||||||
|
mangaConnectorName={id.mangaConnectorName}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
className={'manga-card-badge'}
|
className={'manga-card-badge'}
|
||||||
>
|
>
|
||||||
<Card className={'manga-card'} onClick={props.onClick}>
|
<Card className={'manga-card'} onClick={props.onClick}>
|
||||||
<CardCover className={'manga-card-cover'}>
|
<CardCover className={'manga-card-cover'}>
|
||||||
<img src={props.manga && props.manga.key != "Search" ? `${Api.baseUrl}/v2/Manga/${props.manga?.key}/Cover` : '/blahaj.png'} />
|
<img
|
||||||
|
src={
|
||||||
|
props.manga && props.manga.key != 'Search'
|
||||||
|
? `${Api.baseUrl}/v2/Manga/${props.manga?.key}/Cover`
|
||||||
|
: '/blahaj.png'
|
||||||
|
}
|
||||||
|
/>
|
||||||
</CardCover>
|
</CardCover>
|
||||||
<CardCover className={'manga-card-cover-blur'} />
|
<CardCover className={'manga-card-cover-blur'} />
|
||||||
<CardContent className={'manga-card-content'}>
|
<CardContent className={'manga-card-content'}>
|
||||||
|
@@ -14,20 +14,20 @@ export default function MangaConnectorIcon({
|
|||||||
|
|
||||||
const [connector, setConnector] = useState<MangaConnector | undefined>(
|
const [connector, setConnector] = useState<MangaConnector | undefined>(
|
||||||
mangaConnector
|
mangaConnector
|
||||||
);
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mangaConnector) {
|
if (mangaConnector) {
|
||||||
setConnector(mangaConnector)
|
setConnector(mangaConnector)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (!mangaConnectorName) return;
|
if (!mangaConnectorName) return
|
||||||
Api.mangaConnectorDetail(mangaConnectorName).then((result) => {
|
Api.mangaConnectorDetail(mangaConnectorName).then((result) => {
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
setConnector(result.data)
|
setConnector(result.data)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}, [Api, mangaConnectorName, mangaConnector]);
|
}, [Api, mangaConnectorName, mangaConnector])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={connector?.name ?? 'loading'}>
|
<Tooltip title={connector?.name ?? 'loading'}>
|
||||||
|
@@ -56,7 +56,13 @@ export function MangaCardList(props: MangaCardListProps): ReactNode {
|
|||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
{props.manga.map((m) => (
|
{props.manga.map((m) => (
|
||||||
<MangaCard key={m.key} manga={m} onClick={() => { if(props.mangaOnClick) props.mangaOnClick(m); } } />
|
<MangaCard
|
||||||
|
key={m.key}
|
||||||
|
manga={m}
|
||||||
|
onClick={() => {
|
||||||
|
if (props.mangaOnClick) props.mangaOnClick(m)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
@@ -1,59 +1,135 @@
|
|||||||
import {Manga} from "./api/data-contracts.ts";
|
import { Manga } from './api/data-contracts.ts'
|
||||||
import {Dispatch, ReactNode, useContext, useEffect, useState} from "react";
|
import { Dispatch, ReactNode, useContext, useEffect, useState } from 'react'
|
||||||
import {Card, CardCover, Chip, Modal, ModalDialog, Stack, Typography, useTheme} from "@mui/joy";
|
import {
|
||||||
import ModalClose from "@mui/joy/ModalClose";
|
Card,
|
||||||
import {ApiContext} from "./contexts/ApiContext.tsx";
|
CardCover,
|
||||||
import {MangaContext} from "./contexts/MangaContext.tsx";
|
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 './Components/Mangas/MangaCard.css'
|
||||||
import MarkdownPreview from "@uiw/react-markdown-preview";
|
import MarkdownPreview from '@uiw/react-markdown-preview'
|
||||||
|
|
||||||
export default function MangaDetail(props: MangaDetailProps): ReactNode {
|
export default function MangaDetail(props: MangaDetailProps): ReactNode {
|
||||||
const Api = useContext(ApiContext);
|
const Api = useContext(ApiContext)
|
||||||
const Manga = useContext(MangaContext);
|
const Manga = useContext(MangaContext)
|
||||||
|
|
||||||
const [manga, setManga] = useState<Manga | undefined>(props.manga)
|
const [manga, setManga] = useState<Manga | undefined>(props.manga)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.open) return;
|
if (!props.open) return
|
||||||
if (!props.mangaKey) return;
|
if (!props.mangaKey) return
|
||||||
if (props.manga != undefined) return;
|
if (props.manga != undefined) return
|
||||||
Manga.GetManga(props.mangaKey).then(setManga);
|
Manga.GetManga(props.mangaKey).then(setManga)
|
||||||
}, [Api, props]);
|
}, [Api, Manga, props])
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={props.open} onClose={() => props.setOpen(false)}>
|
<Modal open={props.open} onClose={() => props.setOpen(false)}>
|
||||||
<ModalDialog>
|
<ModalDialog>
|
||||||
<ModalClose />
|
<ModalClose />
|
||||||
<div style={{display: 'flex', flexWrap: 'wrap', flexDirection: 'row'}}>
|
<div
|
||||||
<Typography level={"h3"} sx={{width: '100%'}}>{manga?.name}</Typography>
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography level={'h3'} sx={{ width: '100%' }}>
|
||||||
|
{manga?.name}
|
||||||
|
</Typography>
|
||||||
<Card className={'manga-card'}>
|
<Card className={'manga-card'}>
|
||||||
<CardCover className={'manga-card-cover'}>
|
<CardCover className={'manga-card-cover'}>
|
||||||
<img src={manga ? `${Api.baseUrl}/v2/Manga/${manga.key}/Cover` : '/blahaj.png'} />
|
<img
|
||||||
|
src={
|
||||||
|
manga
|
||||||
|
? `${Api.baseUrl}/v2/Manga/${manga.key}/Cover`
|
||||||
|
: '/blahaj.png'
|
||||||
|
}
|
||||||
|
/>
|
||||||
</CardCover>
|
</CardCover>
|
||||||
</Card>
|
</Card>
|
||||||
<Stack direction={'column'} gap={2} sx={{maxWidth: 'calc(100% - 230px)', margin: '5px'}}>
|
<Stack
|
||||||
|
direction={'column'}
|
||||||
|
gap={2}
|
||||||
|
sx={{ maxWidth: 'calc(100% - 230px)', margin: '5px' }}
|
||||||
|
>
|
||||||
<Stack direction={'row'} gap={0.5} flexWrap={'wrap'}>
|
<Stack direction={'row'} gap={0.5} flexWrap={'wrap'}>
|
||||||
{manga?.tags.map(tag => <Chip key={tag} size={"sm"} sx={{backgroundColor: theme.palette.primary.plainColor}}>{tag}</Chip>)}
|
{manga?.tags.map((tag) => (
|
||||||
{manga?.authors.map(author => <Chip key={author.key} size={'sm'} sx={{backgroundColor: theme.palette.success.plainColor}}>{author.name}</Chip> )}
|
<Chip
|
||||||
{manga?.links.map(link => <Chip key={link.provider} size={"sm"} sx={{backgroundColor: theme.palette.neutral.plainColor}}><a href={link.url}>{link.provider}</a></Chip>)}
|
key={tag}
|
||||||
|
size={'sm'}
|
||||||
|
sx={{
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.primary.plainColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
{manga?.authors.map((author) => (
|
||||||
|
<Chip
|
||||||
|
key={author.key}
|
||||||
|
size={'sm'}
|
||||||
|
sx={{
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.success.plainColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{author.name}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
{manga?.links.map((link) => (
|
||||||
|
<Chip
|
||||||
|
key={link.provider}
|
||||||
|
size={'sm'}
|
||||||
|
sx={{
|
||||||
|
backgroundColor:
|
||||||
|
theme.palette.neutral.plainColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a href={link.url}>{link.provider}</a>
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
<MarkdownPreview source={manga?.description} style={{backgroundColor: "transparent", color: theme.palette.text.primary, overflowY: "auto"}}/>
|
<MarkdownPreview
|
||||||
|
source={manga?.description}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack sx={{flexGrow: 1, flexBasis: 0, margin: '5px 0', alignItems: 'flex-end'}} flexWrap={'nowrap'} gap={1}>
|
<Stack
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
flexBasis: 0,
|
||||||
|
margin: '5px 0',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
}}
|
||||||
|
flexWrap={'nowrap'}
|
||||||
|
gap={1}
|
||||||
|
>
|
||||||
{props.actions}
|
{props.actions}
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MangaDetailProps {
|
export interface MangaDetailProps {
|
||||||
manga?: Manga;
|
manga?: Manga
|
||||||
mangaKey?: string;
|
mangaKey?: string
|
||||||
open: boolean;
|
open: boolean
|
||||||
setOpen: Dispatch<boolean>;
|
setOpen: Dispatch<boolean>
|
||||||
actions?: ReactNode[];
|
actions?: ReactNode[]
|
||||||
}
|
}
|
@@ -1,74 +1,120 @@
|
|||||||
import {Dispatch, ReactNode, useContext, useEffect, useState} from "react";
|
import { Dispatch, ReactNode, useContext, useEffect, useState } from 'react'
|
||||||
import {Box, Card, Checkbox, Drawer, List, ListItem, Option, Select, Stack, Typography} from "@mui/joy";
|
import {
|
||||||
import ModalClose from "@mui/joy/ModalClose";
|
Box,
|
||||||
import {Manga, MangaConnectorId} from "./api/data-contracts.ts";
|
Card,
|
||||||
import {ApiContext} from "./contexts/ApiContext.tsx";
|
Checkbox,
|
||||||
import {MangaContext} from "./contexts/MangaContext.tsx";
|
Drawer,
|
||||||
import {FileLibraryContext} from "./contexts/FileLibraryContext.tsx";
|
List,
|
||||||
import MangaConnectorIcon from "./Components/Mangas/MangaConnectorIcon.tsx";
|
ListItem,
|
||||||
import TButton from "./Components/Inputs/TButton.tsx";
|
Option,
|
||||||
|
Select,
|
||||||
|
Stack,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/joy'
|
||||||
|
import ModalClose from '@mui/joy/ModalClose'
|
||||||
|
import { Manga, MangaConnectorId } from './api/data-contracts.ts'
|
||||||
|
import { ApiContext } from './contexts/ApiContext.tsx'
|
||||||
|
import { MangaContext } from './contexts/MangaContext.tsx'
|
||||||
|
import { FileLibraryContext } from './contexts/FileLibraryContext.tsx'
|
||||||
|
import MangaConnectorIcon from './Components/Mangas/MangaConnectorIcon.tsx'
|
||||||
|
import TButton from './Components/Inputs/TButton.tsx'
|
||||||
|
|
||||||
export default function MangaDownloadDrawer (props: MangaDownloadDrawerProps) : ReactNode{
|
export default function MangaDownloadDrawer(
|
||||||
const Api = useContext(ApiContext);
|
props: MangaDownloadDrawerProps
|
||||||
const Manga = useContext(MangaContext);
|
): ReactNode {
|
||||||
const Libraries = useContext(FileLibraryContext);
|
const Api = useContext(ApiContext)
|
||||||
|
const Manga = useContext(MangaContext)
|
||||||
|
const Libraries = useContext(FileLibraryContext)
|
||||||
|
|
||||||
const [manga, setManga] = useState<Manga | undefined>(props.manga)
|
const [manga, setManga] = useState<Manga | undefined>(props.manga)
|
||||||
const [downloadFromMap, setDownloadFromMap] = useState<Map<MangaConnectorId, boolean>>(new Map())
|
const [downloadFromMap, setDownloadFromMap] = useState<
|
||||||
|
Map<MangaConnectorId, boolean>
|
||||||
|
>(new Map())
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.open) return;
|
if (!props.open) return
|
||||||
if (!props.mangaKey) return;
|
if (!props.mangaKey) return
|
||||||
if (props.manga != undefined) return;
|
if (props.manga != undefined) return
|
||||||
Manga.GetManga(props.mangaKey).then(setManga);
|
Manga.GetManga(props.mangaKey).then(setManga)
|
||||||
}, [Api, props]);
|
}, [Api, Manga, props])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newMap = new Map();
|
const newMap = new Map()
|
||||||
manga?.mangaConnectorIds.forEach(id => {
|
manga?.mangaConnectorIds.forEach((id) => {
|
||||||
newMap.set(id, id.useForDownload);
|
newMap.set(id, id.useForDownload)
|
||||||
})
|
})
|
||||||
setDownloadFromMap(newMap);
|
setDownloadFromMap(newMap)
|
||||||
}, [manga]);
|
}, [manga])
|
||||||
|
|
||||||
const setDownload = (): Promise<void> => {
|
const setDownload = (): Promise<void> => {
|
||||||
if (!manga) return Promise.reject();
|
if (!manga) return Promise.reject()
|
||||||
downloadFromMap.forEach(async (download, id) => {
|
downloadFromMap.forEach(async (download, id) => {
|
||||||
const result = await Api.mangaSetAsDownloadFromCreate(manga?.key, id.mangaConnectorName, download);
|
const result = await Api.mangaSetAsDownloadFromCreate(
|
||||||
if (!result.ok)
|
manga?.key,
|
||||||
return Promise.reject();
|
id.mangaConnectorName,
|
||||||
});
|
download
|
||||||
return Promise.resolve();
|
)
|
||||||
|
if (!result.ok) return Promise.reject()
|
||||||
|
})
|
||||||
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer open={props.open}
|
<Drawer
|
||||||
|
open={props.open}
|
||||||
onClose={() => props.setOpen(false)}
|
onClose={() => props.setOpen(false)}
|
||||||
anchor="left"
|
anchor="left"
|
||||||
size="md">
|
size="md"
|
||||||
|
>
|
||||||
<Card sx={{ flexGrow: 1, margin: '10px' }}>
|
<Card sx={{ flexGrow: 1, margin: '10px' }}>
|
||||||
<ModalClose />
|
<ModalClose />
|
||||||
<Typography level={"h3"}>Download</Typography>
|
<Typography level={'h3'}>Download</Typography>
|
||||||
<Typography level={"h4"}>{manga?.name}</Typography>
|
<Typography level={'h4'}>{manga?.name}</Typography>
|
||||||
<Stack direction={'column'} gap={2} sx={{ flexBasis: 0 }}>
|
<Stack direction={'column'} gap={2} sx={{ flexBasis: 0 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography>Select a Library to Download to:</Typography>
|
<Typography>
|
||||||
<Select placeholder={"Select a Library"}>
|
Select a Library to Download to:
|
||||||
{Libraries.map(l => <Option key={l.key} value={l.key}>{l.libraryName} ({l.basePath})</Option>)}
|
</Typography>
|
||||||
|
<Select placeholder={'Select a Library'}>
|
||||||
|
{Libraries.map((l) => (
|
||||||
|
<Option key={l.key} value={l.key}>
|
||||||
|
{l.libraryName} ({l.basePath})
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography>Select which connectors you want to download this Manga from:</Typography>
|
<Typography>
|
||||||
|
Select which connectors you want to download this
|
||||||
|
Manga from:
|
||||||
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
{manga?.mangaConnectorIds.map(id => (
|
{manga?.mangaConnectorIds.map((id) => (
|
||||||
<ListItem key={id.key}>
|
<ListItem key={id.key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={id.useForDownload}
|
defaultChecked={id.useForDownload}
|
||||||
onChange={(c) => downloadFromMap.set(id, c.target.checked)}
|
onChange={(c) =>
|
||||||
|
downloadFromMap.set(
|
||||||
|
id,
|
||||||
|
c.target.checked
|
||||||
|
)
|
||||||
|
}
|
||||||
label={
|
label={
|
||||||
<div style={{display: 'flex', alignItems: 'center', gap: 5}}>
|
<div
|
||||||
<MangaConnectorIcon mangaConnectorName={id.mangaConnectorName} />
|
style={{
|
||||||
<Typography>{id.mangaConnectorName}</Typography>
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MangaConnectorIcon
|
||||||
|
mangaConnectorName={
|
||||||
|
id.mangaConnectorName
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Typography>
|
||||||
|
{id.mangaConnectorName}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -76,7 +122,9 @@ export default function MangaDownloadDrawer (props: MangaDownloadDrawerProps) :
|
|||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
<TButton completionAction={setDownload}>Download All</TButton>
|
<TButton completionAction={setDownload}>
|
||||||
|
Download All
|
||||||
|
</TButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
@@ -84,8 +132,8 @@ export default function MangaDownloadDrawer (props: MangaDownloadDrawerProps) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MangaDownloadDrawerProps {
|
export interface MangaDownloadDrawerProps {
|
||||||
manga?: Manga;
|
manga?: Manga
|
||||||
mangaKey?: string;
|
mangaKey?: string
|
||||||
open: boolean;
|
open: boolean
|
||||||
setOpen: Dispatch<boolean>;
|
setOpen: Dispatch<boolean>
|
||||||
}
|
}
|
@@ -18,8 +18,8 @@ import TInput from './Components/Inputs/TInput.tsx'
|
|||||||
import { ApiContext } from './contexts/ApiContext.tsx'
|
import { ApiContext } from './contexts/ApiContext.tsx'
|
||||||
import { MangaCardList } from './Components/Mangas/MangaList.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";
|
import MangaDetail from './MangaDetail.tsx'
|
||||||
import MangaDownloadDrawer from "./MangaDownloadDrawer.tsx";
|
import MangaDownloadDrawer from './MangaDownloadDrawer.tsx'
|
||||||
|
|
||||||
export function Search(props: SearchModalProps): ReactNode {
|
export function Search(props: SearchModalProps): ReactNode {
|
||||||
const Api = useContext(ApiContext)
|
const Api = useContext(ApiContext)
|
||||||
@@ -27,10 +27,10 @@ export function Search(props: SearchModalProps): ReactNode {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.open) {
|
if (props.open) {
|
||||||
setSelectedConnector(undefined);
|
setSelectedConnector(undefined)
|
||||||
setSearchResults([]);
|
setSearchResults([])
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [props])
|
||||||
|
|
||||||
const [selectedConnector, setSelectedConnector] = useState<MangaConnector>()
|
const [selectedConnector, setSelectedConnector] = useState<MangaConnector>()
|
||||||
const [searchResults, setSearchResults] = useState<MinimalManga[]>([])
|
const [searchResults, setSearchResults] = useState<MinimalManga[]>([])
|
||||||
@@ -42,40 +42,46 @@ export function Search(props: SearchModalProps): ReactNode {
|
|||||||
setSearchResults([])
|
setSearchResults([])
|
||||||
if (isUrl(value)) {
|
if (isUrl(value)) {
|
||||||
try {
|
try {
|
||||||
let result = await Api.searchUrlCreate(value);
|
const result = await Api.searchUrlCreate(value)
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
setSearchResults([result.data])
|
setSearchResults([result.data])
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
} else return Promise.reject()
|
} else return Promise.reject()
|
||||||
} catch (reason) {
|
} catch (reason) {
|
||||||
return await Promise.reject(reason);
|
return await Promise.reject(reason)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!selectedConnector) return Promise.reject()
|
if (!selectedConnector) return Promise.reject()
|
||||||
try {
|
try {
|
||||||
let result2 = await Api.searchDetail(selectedConnector?.key, value);
|
const result2 = await Api.searchDetail(
|
||||||
|
selectedConnector?.key,
|
||||||
|
value
|
||||||
|
)
|
||||||
if (result2.ok) {
|
if (result2.ok) {
|
||||||
setSearchResults(result2.data)
|
setSearchResults(result2.data)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
} else return Promise.reject()
|
} else return Promise.reject()
|
||||||
} catch (reason1) {
|
} catch (reason1) {
|
||||||
return await Promise.reject(reason1);
|
return await Promise.reject(reason1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [selectedManga, setSelectedManga] = useState<MinimalManga | undefined>(undefined);
|
const [selectedManga, setSelectedManga] = useState<
|
||||||
const [mangaDetailOpen, setMangaDetailOpen] = useState(false);
|
MinimalManga | undefined
|
||||||
const [mangaDownloadDrawerOpen, setMangaDownloadDrawerOpen] = useState(false);
|
>(undefined)
|
||||||
|
const [mangaDetailOpen, setMangaDetailOpen] = useState(false)
|
||||||
|
const [mangaDownloadDrawerOpen, setMangaDownloadDrawerOpen] =
|
||||||
|
useState(false)
|
||||||
|
|
||||||
function openMangaDetail(manga: MinimalManga) {
|
function openMangaDetail(manga: MinimalManga) {
|
||||||
setSelectedManga(manga);
|
setSelectedManga(manga)
|
||||||
setMangaDetailOpen(true);
|
setMangaDetailOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMangaDownloadDrawer() {
|
function openMangaDownloadDrawer() {
|
||||||
setMangaDetailOpen(false);
|
setMangaDetailOpen(false)
|
||||||
setMangaDownloadDrawerOpen(true);
|
setMangaDownloadDrawerOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -127,19 +133,33 @@ export function Search(props: SearchModalProps): ReactNode {
|
|||||||
/>
|
/>
|
||||||
</Step>
|
</Step>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
<MangaCardList manga={searchResults} mangaOnClick={openMangaDetail}/>
|
<MangaCardList
|
||||||
<MangaDetail mangaKey={selectedManga?.key} open={mangaDetailOpen} setOpen={setMangaDetailOpen} actions={[
|
manga={searchResults}
|
||||||
<Button onClick={openMangaDownloadDrawer}>Download</Button>
|
mangaOnClick={openMangaDetail}
|
||||||
]} />
|
/>
|
||||||
<MangaDownloadDrawer open={mangaDownloadDrawerOpen} setOpen={setMangaDownloadDrawerOpen} mangaKey={selectedManga?.key} />
|
<MangaDetail
|
||||||
|
mangaKey={selectedManga?.key}
|
||||||
|
open={mangaDetailOpen}
|
||||||
|
setOpen={setMangaDetailOpen}
|
||||||
|
actions={[
|
||||||
|
<Button onClick={openMangaDownloadDrawer}>
|
||||||
|
Download
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<MangaDownloadDrawer
|
||||||
|
open={mangaDownloadDrawerOpen}
|
||||||
|
setOpen={setMangaDownloadDrawerOpen}
|
||||||
|
mangaKey={selectedManga?.key}
|
||||||
|
/>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchModalProps {
|
export interface SearchModalProps {
|
||||||
open: boolean;
|
open: boolean
|
||||||
setOpen: Dispatch<boolean>;
|
setOpen: Dispatch<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUrl(str: string): boolean {
|
function isUrl(str: string): boolean {
|
||||||
|
@@ -8,9 +8,13 @@ import {
|
|||||||
import { FileLibrary } from '../api/data-contracts.ts'
|
import { FileLibrary } from '../api/data-contracts.ts'
|
||||||
import { ApiContext } from './ApiContext.tsx'
|
import { ApiContext } from './ApiContext.tsx'
|
||||||
|
|
||||||
export const FileLibraryContext = createContext<FileLibrary[]>([]);
|
export const FileLibraryContext = createContext<FileLibrary[]>([])
|
||||||
|
|
||||||
export default function LibraryProvider({ children }: { children: ReactNode }) : ReactNode {
|
export default function LibraryProvider({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode
|
||||||
|
}): ReactNode {
|
||||||
const Api = useContext(ApiContext)
|
const Api = useContext(ApiContext)
|
||||||
|
|
||||||
const [state, setState] = useState<FileLibrary[]>([])
|
const [state, setState] = useState<FileLibrary[]>([])
|
||||||
@@ -23,7 +27,5 @@ export default function LibraryProvider({ children }: { children: ReactNode }) :
|
|||||||
})
|
})
|
||||||
}, [Api])
|
}, [Api])
|
||||||
|
|
||||||
return (
|
return <FileLibraryContext value={state}>{children}</FileLibraryContext>
|
||||||
<FileLibraryContext value={state}>{children}</FileLibraryContext>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user