Use new API layout with MinimalManga

This commit is contained in:
2025-09-02 00:46:57 +02:00
parent 2f07cc61fe
commit 9846ffb789
18 changed files with 306 additions and 225 deletions

View File

@@ -10,29 +10,56 @@ import {
FileLibrary,
Manga,
MangaConnector,
MinimalManga,
} from "./apiClient/data-contracts.ts";
import Search from "./Components/Search.tsx";
import { Typography } from "@mui/joy";
import Workers from "./Components/WorkerModal/Workers.tsx";
const apiUri =
localStorage.getItem("apiUri") ??
window.location.href.substring(0, window.location.href.lastIndexOf("/")) +
"/api";
localStorage.setItem("apiUri", apiUri);
const Api = new V2({ baseUrl: apiUri });
const manga: Manga[] = [];
const promises: Map<string, Promise<Manga | undefined>> = new Map();
const getManga = async (key: string): Promise<Manga | undefined> => {
let result = manga.find((m) => m.key === key);
if (result) return result;
if (promises.has(key)) return promises.get(key);
const newPromise = retrieveManga(key);
promises.set(key, newPromise);
return newPromise;
};
const retrieveManga = async (key: string): Promise<Manga | undefined> => {
return Api.mangaDetail(key).then((response) => {
if (response.ok) {
manga.push(response.data);
return response.data;
}
return undefined;
});
};
export const MangaConnectorContext = createContext<MangaConnector[]>([]);
export const MangaContext = createContext<Manga[]>([]);
export const MangaContext = createContext<{
getManga: (key: string) => Promise<Manga | undefined>;
}>({
getManga,
});
export const FileLibraryContext = createContext<FileLibrary[]>([]);
export default function App() {
const apiUriStr =
localStorage.getItem("apiUri") ??
window.location.href.substring(0, window.location.href.lastIndexOf("/")) +
"/api";
const [apiUri, setApiUri] = useState<string>(apiUriStr);
const [Api, setApi] = useState<V2>(
new V2({
baseUrl: apiUri,
}),
);
const updateApiUri = (uri: string) => {
localStorage.setItem("apiUri", uri);
window.location.reload();
};
export default function App() {
const [mangaConnectors, setMangaConnectors] = useState<MangaConnector[]>([]);
const [manga, setManga] = useState<Manga[]>([]);
const [downloadingManga, setDownloadingManga] = useState<MinimalManga[]>([]);
const [fileLibraries, setFileLibraries] = useState<FileLibrary[]>([]);
useEffect(() => {
@@ -45,33 +72,23 @@ export default function App() {
});
Api.mangaDownloadingList().then((response) => {
if (response.ok) setManga(response.data);
if (response.ok) setDownloadingManga(response.data);
});
}, []);
useEffect(() => {
localStorage.setItem("apiUri", apiUri);
if (Api.baseUrl != apiUri)
setApi(
new V2({
baseUrl: apiUri,
}),
);
}, [apiUri]);
return (
<ApiContext.Provider value={Api}>
<FileLibraryContext value={fileLibraries}>
<MangaConnectorContext.Provider value={mangaConnectors}>
<MangaContext.Provider value={manga}>
<MangaContext.Provider value={{ getManga }}>
{Api ? (
<Sheet className={"app"}>
<Header>
<Settings setApiUri={setApiUri} />
<Settings setApiUri={updateApiUri} />
<Workers />
</Header>
<Sheet className={"app-content"}>
<MangaList mangas={manga}>
<MangaList manga={downloadingManga}>
<Search />
</MangaList>
</Sheet>

View File

@@ -57,7 +57,12 @@ export default function MangaConnectorLink({
}
>
<Link href={MangaConnectorId.websiteUrl as string}>
<img ref={imageRef} src={mangaConnector?.iconUrl} style={imageStyle} />
<img
ref={imageRef}
src={mangaConnector?.iconUrl}
style={imageStyle}
className={"manga-card-badge-icon"}
/>
{printName ? <Typography>{mangaConnector?.name}</Typography> : null}
</Link>
</Tooltip>

View File

@@ -12,12 +12,13 @@ import {
Tooltip,
Typography,
} from "@mui/joy";
import { Manga } from "../../apiClient/data-contracts.ts";
import { Manga, MinimalManga } from "../../apiClient/data-contracts.ts";
import {
Dispatch,
ReactNode,
SetStateAction,
useContext,
useEffect,
useState,
} from "react";
import "./MangaCard.css";
@@ -28,18 +29,11 @@ import MarkdownPreview from "@uiw/react-markdown-preview";
import { MangaContext } from "../../App.tsx";
import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx";
export function MangaCardFromId({ mangaId }: { mangaId: string }) {
const mangas = useContext(MangaContext);
const manga = mangas.find((manga) => manga.key === mangaId);
return <MangaCard manga={manga} />;
}
export function MangaCard({
manga,
children,
}: {
manga: Manga | undefined;
manga: MinimalManga | undefined;
children?: ReactNode;
}) {
if (manga === undefined) return PlaceHolderCard();
@@ -50,14 +44,14 @@ export function MangaCard({
<MangaConnectorBadge manga={manga}>
<Card className={"manga-card"} onClick={() => setOpen(true)}>
<CardCover className={"manga-cover"}>
<MangaCover manga={manga} />
<MangaCover mangaId={manga?.key} />
</CardCover>
<CardCover className={"manga-cover-blur"} />
<CardContent className={"manga-content"}>
<Typography level={"title-lg"}>{manga?.name}</Typography>
</CardContent>
</Card>
<MangaModal manga={manga} open={open} setOpen={setOpen}>
<MangaModal minimalManga={manga} open={open} setOpen={setOpen}>
{children}
</MangaModal>
</MangaConnectorBadge>
@@ -65,16 +59,22 @@ export function MangaCard({
}
export function MangaModal({
manga,
minimalManga,
open,
setOpen,
children,
}: {
manga: Manga | undefined;
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);
}, []);
return (
<Modal open={open} onClose={() => setOpen(false)} className={"manga-modal"}>
<ModalDialog style={{ width: "100%" }}>
@@ -89,12 +89,12 @@ export function MangaModal({
}
>
<Typography level={"h4"} width={"fit-content"}>
{manga?.name}
{manga?.name ?? minimalManga.name}
</Typography>
</Tooltip>
<Stack direction={"row"} spacing={2}>
<Box key={"Cover"} className={"manga-card"}>
<MangaCover manga={manga} />
<MangaCover mangaId={minimalManga.key} />
</Box>
<Stack
key={"Description"}
@@ -125,8 +125,12 @@ export function MangaModal({
</Stack>
<Box sx={{ flexGrow: 1 }}>
<MarkdownPreview
source={manga?.description}
style={{ background: "transparent" }}
source={manga?.description ?? "Loading..."}
style={{
background: "transparent",
maxHeight: "50vh",
overflowY: "auto",
}}
/>
</Box>
<Stack
@@ -134,7 +138,7 @@ export function MangaModal({
spacing={2}
direction={"row"}
>
{children}
{manga ? children : null}
</Stack>
</Stack>
</Stack>
@@ -156,10 +160,10 @@ function PlaceHolderCard() {
);
}
function MangaCover({ manga }: { manga: Manga | undefined }) {
function MangaCover({ mangaId }: { mangaId?: string }) {
const api = useContext(ApiContext);
const uri = manga
? `${api.baseUrl}/v2/Manga/${manga?.key}/Cover`
const uri = mangaId
? `${api.baseUrl}/v2/Manga/${mangaId}/Cover`
: "blahaj.png";
return (

View File

@@ -1,20 +1,20 @@
import { Badge } from "@mui/joy";
import { Manga } from "../../apiClient/data-contracts.ts";
import { MinimalManga } from "../../apiClient/data-contracts.ts";
import { ReactElement } from "react";
import "./MangaCard.css";
import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx";
import MangaConnectorLink from "../MangaConnectorLink.tsx";
export default function MangaConnectorBadge({
manga,
children,
}: {
manga: Manga;
manga: MinimalManga;
children?: ReactElement<any, any> | ReactElement<any, any>[] | undefined;
}) {
return (
<Badge
badgeContent={manga.mangaConnectorIdsIds?.map((id) => (
<MangaConnectorLinkFromId key={id} MangaConnectorIdId={id} />
badgeContent={manga.mangaConnectorIds?.map((id) => (
<MangaConnectorLink key={id.key} MangaConnectorId={id} />
))}
>
{children}

View File

@@ -12,12 +12,17 @@ import Drawer from "@mui/joy/Drawer";
import ModalClose from "@mui/joy/ModalClose";
import { MangaConnectorLinkFromId } from "../MangaConnectorLink.tsx";
import Sheet from "@mui/joy/Sheet";
import { FileLibraryContext } from "../../App.tsx";
import { FileLibraryContext, MangaContext } from "../../App.tsx";
import { ApiContext } from "../../apiClient/ApiContext.tsx";
import { LoadingState, StateIndicator } from "../Loading.tsx";
export default function ({ manga }: { manga: Manga }): ReactNode {
export default function ({ mangaId }: { mangaId: string }): ReactNode {
const [open, setOpen] = useState(false);
const { getManga } = useContext(MangaContext);
const [manga, setManga] = useState<Manga>();
useEffect(() => {
getManga(mangaId).then(setManga);
}, []);
return (
<>
@@ -32,7 +37,7 @@ function DownloadDrawer({
open,
setOpen,
}: {
manga: Manga;
manga: Manga | undefined;
open: boolean;
setOpen: Dispatch<boolean>;
}): ReactNode {
@@ -40,7 +45,8 @@ function DownloadDrawer({
const Api = useContext(ApiContext);
const onLibraryChange = (_: any, value: {} | null) => {
if (value === undefined) return;
if (!value) return;
if (!manga) return;
Api.mangaChangeLibraryCreate(manga.key as string, value as string);
};
@@ -52,7 +58,7 @@ function DownloadDrawer({
<Select
placeholder={"Library"}
onChange={onLibraryChange}
value={manga.libraryId}
value={manga?.libraryId}
>
{fileLibraries?.map((library) => (
<Option value={library.key} key={library.key}>
@@ -63,7 +69,7 @@ function DownloadDrawer({
</Select>
<Typography>Download from:</Typography>
<Stack>
{manga.mangaConnectorIdsIds?.map((id) => (
{manga?.mangaConnectorIdsIds?.map((id) => (
<DownloadCheckBox key={id} mangaConnectorIdId={id} />
))}
</Stack>

View File

@@ -2,15 +2,15 @@ import { ReactNode } from "react";
import { MangaCard } from "./MangaCard.tsx";
import { Stack } from "@mui/joy";
import "./MangaList.css";
import { Manga } from "../../apiClient/data-contracts.ts";
import { MinimalManga } from "../../apiClient/data-contracts.ts";
import MangaDownloadDialog from "./MangaDownloadDialog.tsx";
import MangaMerge from "./MangaMerge.tsx";
export default function MangaList({
mangas,
manga,
children,
}: {
mangas: Manga[];
manga: MinimalManga[];
children?: ReactNode;
}) {
return (
@@ -27,10 +27,10 @@ export default function MangaList({
}}
>
{children}
{mangas?.map((manga) => (
<MangaCard key={manga.key} manga={manga}>
<MangaDownloadDialog manga={manga} />
<MangaMerge manga={manga} />
{manga?.map((minimalManga) => (
<MangaCard key={minimalManga.key} manga={minimalManga}>
<MangaDownloadDialog mangaId={minimalManga.key} />
<MangaMerge manga={minimalManga} />
</MangaCard>
))}
</Stack>

View File

@@ -1,5 +1,5 @@
import { ReactNode, useContext, useEffect, useState } from "react";
import { Manga } from "../../apiClient/data-contracts.ts";
import { Manga, MinimalManga } from "../../apiClient/data-contracts.ts";
import Drawer from "@mui/joy/Drawer";
import ModalClose from "@mui/joy/ModalClose";
import { ApiContext } from "../../apiClient/ApiContext.tsx";
@@ -16,10 +16,14 @@ import {
import { KeyboardDoubleArrowRight, Warning } from "@mui/icons-material";
import { LoadingState, StateIndicator } from "../Loading.tsx";
export default function ({ manga }: { manga: Manga | undefined }): ReactNode {
export default function ({
manga,
}: {
manga: MinimalManga | undefined;
}): ReactNode {
const Api = useContext(ApiContext);
const [similar, setSimilar] = useState<Manga[]>([]);
const [similar, setSimilar] = useState<Manga[]>();
const [open, setOpen] = useState<boolean>(false);
useEffect(() => {
@@ -34,7 +38,7 @@ export default function ({ manga }: { manga: Manga | undefined }): ReactNode {
const exit = (manga: Manga) => {
setOpen(false);
setSimilar(similar.filter((m) => m.key != manga.key));
setSimilar(similar?.filter((m) => m.key != manga.key));
};
return (
@@ -47,16 +51,19 @@ export default function ({ manga }: { manga: Manga | undefined }): ReactNode {
anchor={"bottom"}
>
<ModalClose />
<Typography>Merge targets: {similar?.length ?? 0}</Typography>
<Stack direction={"row"} spacing={2} flexWrap={"wrap"} useFlexGap>
{similar.map((similarManga) => (
<MangaCard manga={similarManga}>
<ConfirmationModal
manga={manga as Manga}
similarManga={similarManga}
exit={() => exit(similarManga)}
/>
</MangaCard>
))}
{similar
? similar?.map((similarManga) => (
<MangaCard manga={similarManga}>
<ConfirmationModal
manga={manga as Manga}
similarManga={similarManga}
exit={() => exit(similarManga)}
/>
</MangaCard>
))
: "Loading..."}
</Stack>
</Drawer>
</>

View File

@@ -24,7 +24,7 @@ import {
} from "@mui/joy";
import ModalClose from "@mui/joy/ModalClose";
import { MangaConnectorContext } from "../App.tsx";
import { Manga, MangaConnector } from "../apiClient/data-contracts.ts";
import { MangaConnector, MinimalManga } from "../apiClient/data-contracts.ts";
import MangaList from "./Mangas/MangaList.tsx";
import { ApiContext } from "../apiClient/ApiContext.tsx";
import { LoadingState, StateColor, StateIndicator } from "./Loading.tsx";
@@ -67,7 +67,7 @@ function SearchDialog({
MangaConnector | undefined
>(undefined);
const [searchTerm, setSearchTerm] = useState<string>();
const [searchResults, setSearchResults] = useState<Manga[]>([]);
const [searchResults, setSearchResults] = useState<MinimalManga[]>([]);
const [loadingState, setLoadingState] = useState<LoadingState>(
LoadingState.none,
@@ -166,7 +166,7 @@ function SearchDialog({
<Typography>
Result <Chip>{searchResults.length}</Chip>
</Typography>
<MangaList mangas={searchResults} />
<MangaList manga={searchResults} />
</Step>
</Stepper>
</ModalDialog>

View File

@@ -1,14 +1,14 @@
import {SettingsItem} from "./Settings.tsx";
import { SettingsItem } from "./Settings.tsx";
import ImageCompression from "./ImageCompression.tsx";
import DownloadLanguage from "./DownloadLanguage.tsx";
import ChapterNamingScheme from "./ChapterNamingScheme.tsx";
export default function (){
return (
<SettingsItem title={"Download"}>
<ImageCompression />
<DownloadLanguage />
<ChapterNamingScheme />
</SettingsItem>
)
}
export default function () {
return (
<SettingsItem title={"Download"}>
<ImageCompression />
<DownloadLanguage />
<ChapterNamingScheme />
</SettingsItem>
);
}

View File

@@ -1,19 +1,25 @@
import {Modal, ModalDialog, Tab, TabList, Tabs} from "@mui/joy";
import { Modal, ModalDialog, Tab, TabList, Tabs } from "@mui/joy";
import ModalClose from "@mui/joy/ModalClose";
import {Dispatch} from "react";
import { Dispatch } from "react";
export default function ({open, setOpen} : {open: boolean, setOpen: Dispatch<boolean>}) {
return (
<Modal open={open} onClose={() => setOpen(false)}>
<ModalDialog>
<ModalClose />
<Tabs sx={{width:'95%'}} defaultValue={"komga"}>
<TabList>
<Tab value={"komga"}>Komga</Tab>
<Tab value={"kavita"}>Kavita</Tab>
</TabList>
</Tabs>
</ModalDialog>
</Modal>
);
}
export default function ({
open,
setOpen,
}: {
open: boolean;
setOpen: Dispatch<boolean>;
}) {
return (
<Modal open={open} onClose={() => setOpen(false)}>
<ModalDialog>
<ModalClose />
<Tabs sx={{ width: "95%" }} defaultValue={"komga"}>
<TabList>
<Tab value={"komga"}>Komga</Tab>
<Tab value={"kavita"}>Kavita</Tab>
</TabList>
</Tabs>
</ModalDialog>
</Modal>
);
}

View File

@@ -1,18 +1,20 @@
import {Button, Card, Typography} from "@mui/joy";
import {useState} from "react";
import { Button, Card, Typography } from "@mui/joy";
import { useState } from "react";
import ListLibraryConnectors from "./ListLibraryConnectors.tsx";
import AddLibraryConnector from "./AddLibraryConnector.tsx";
export default function (){
export default function () {
const [addDialogOpen, setAddDialogOpen] = useState<boolean>(false);
const [addDialogOpen, setAddDialogOpen] = useState<boolean>(false);
return (
<Card>
<Typography>Library Connectors</Typography>
<Button onClick={() => setAddDialogOpen(true)}>Add</Button>
<ListLibraryConnectors />
<AddLibraryConnector open={addDialogOpen} setOpen={() => setAddDialogOpen(false)} />
</Card>
);
}
return (
<Card>
<Typography>Library Connectors</Typography>
<Button onClick={() => setAddDialogOpen(true)}>Add</Button>
<ListLibraryConnectors />
<AddLibraryConnector
open={addDialogOpen}
setOpen={() => setAddDialogOpen(false)}
/>
</Card>
);
}

View File

@@ -1,35 +1,38 @@
import {useContext, useEffect, useState} from "react";
import {ApiContext} from "../../../apiClient/ApiContext.tsx";
import {LibraryConnector} from "../../../apiClient/data-contracts.ts";
import {Card, Chip, Input, Stack} from "@mui/joy";
import { useContext, useEffect, useState } from "react";
import { ApiContext } from "../../../apiClient/ApiContext.tsx";
import { LibraryConnector } from "../../../apiClient/data-contracts.ts";
import { Card, Chip, Input, Stack } from "@mui/joy";
export default function (){
const Api = useContext(ApiContext);
const [libraryConnectors, setLibraryConnectors] = useState<LibraryConnector[]>([]);
export default function () {
const Api = useContext(ApiContext);
const [libraryConnectors, setLibraryConnectors] = useState<
LibraryConnector[]
>([]);
useEffect(() => {
getConnectors();
}, []);
useEffect(() => {
getConnectors();
}, []);
const getConnectors = () => {
Api.libraryConnectorList().then(r => {
if(r.ok)
setLibraryConnectors(r.data);
})
};
const getConnectors = () => {
Api.libraryConnectorList().then((r) => {
if (r.ok) setLibraryConnectors(r.data);
});
};
return (
<Stack direction={"column"} spacing={1}>
{libraryConnectors.map(c => <LibraryConnectorItem key={c.key} connector={c} />)}
</Stack>
);
return (
<Stack direction={"column"} spacing={1}>
{libraryConnectors.map((c) => (
<LibraryConnectorItem key={c.key} connector={c} />
))}
</Stack>
);
}
function LibraryConnectorItem({connector} : {connector: LibraryConnector}){
return (
<Card>
<Chip>{connector.libraryType}</Chip>
<Input disabled value={connector.baseUrl} />
</Card>
);
}
function LibraryConnectorItem({ connector }: { connector: LibraryConnector }) {
return (
<Card>
<Chip>{connector.libraryType}</Chip>
<Input disabled value={connector.baseUrl} />
</Card>
);
}

View File

@@ -1,52 +1,71 @@
import {ApiContext} from "../../../apiClient/ApiContext.tsx";
import {useContext, useEffect, useState} from "react";
import { ApiContext } from "../../../apiClient/ApiContext.tsx";
import { useContext, useEffect, useState } from "react";
import { NotificationConnector } from "../../../apiClient/data-contracts.ts";
import {Card, Chip, Input, Stack, Table, Textarea, Typography} from "@mui/joy";
import {
Card,
Chip,
Input,
Stack,
Table,
Textarea,
Typography,
} from "@mui/joy";
export default function (){
const Api = useContext(ApiContext);
const [notificationConnectors, setNotificationConnectors] = useState<NotificationConnector[]>([]);
export default function () {
const Api = useContext(ApiContext);
const [notificationConnectors, setNotificationConnectors] = useState<
NotificationConnector[]
>([]);
useEffect(() => {
getConnectors();
}, []);
useEffect(() => {
getConnectors();
}, []);
const getConnectors = () => {
Api.notificationConnectorList().then(r => {
if(r.ok)
setNotificationConnectors(r.data);
})
};
const getConnectors = () => {
Api.notificationConnectorList().then((r) => {
if (r.ok) setNotificationConnectors(r.data);
});
};
return (
<Stack direction={"column"} spacing={1}>
{notificationConnectors.map(c => <NotificationConnectorItem key={c.name} connector={c} />)}
</Stack>
);
return (
<Stack direction={"column"} spacing={1}>
{notificationConnectors.map((c) => (
<NotificationConnectorItem key={c.name} connector={c} />
))}
</Stack>
);
}
function NotificationConnectorItem({connector} : {connector: NotificationConnector}){
return (
<Card>
<Typography left={"h2"}>{connector.name}</Typography>
<Input disabled startDecorator={<Chip>{connector.httpMethod}</Chip>} value={connector.url} />
<Table>
<thead>
<tr>
<th>Header</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{Object.entries(connector.headers).map(x =>
<tr key={x[0]}>
<td>{x[0]}</td>
<td>{[x[1]]}</td>
</tr>
)}
</tbody>
</Table>
<Textarea disabled value={connector.body}/>
</Card>
);
}
function NotificationConnectorItem({
connector,
}: {
connector: NotificationConnector;
}) {
return (
<Card>
<Typography left={"h2"}>{connector.name}</Typography>
<Input
disabled
startDecorator={<Chip>{connector.httpMethod}</Chip>}
value={connector.url}
/>
<Table>
<thead>
<tr>
<th>Header</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{Object.entries(connector.headers).map((x) => (
<tr key={x[0]}>
<td>{x[0]}</td>
<td>{[x[1]]}</td>
</tr>
))}
</tbody>
</Table>
<Textarea disabled value={connector.body} />
</Card>
);
}

View File

@@ -1,15 +1,12 @@
import {SettingsItem} from "./Settings.tsx";
import { SettingsItem } from "./Settings.tsx";
import FlareSolverr from "./FlareSolverr.tsx";
import LibraryConnectors from "./LibraryConnectors/LibraryConnectors.tsx";
export default function(){
return (
<SettingsItem title={"Services"}>
<FlareSolverr />
<LibraryConnectors />
</SettingsItem>
);
}
export default function () {
return (
<SettingsItem title={"Services"}>
<FlareSolverr />
<LibraryConnectors />
</SettingsItem>
);
}

View File

@@ -16,7 +16,6 @@ import "./Settings.css";
import * as React from "react";
import {
createContext,
Dispatch,
ReactNode,
useContext,
useEffect,
@@ -38,7 +37,7 @@ export const SettingsContext = createContext<TrangaSettings | undefined>(
export default function Settings({
setApiUri,
}: {
setApiUri: Dispatch<React.SetStateAction<string>>;
setApiUri: (uri: string) => void;
}) {
const Api = useContext(ApiContext);
const [settings, setSettings] = useState<TrangaSettings>();

View File

@@ -1,4 +1,4 @@
import { Dispatch, ReactNode, useContext, useState } from "react";
import { Dispatch, ReactNode, useContext, useEffect, useState } from "react";
import Drawer from "@mui/joy/Drawer";
import { Button, Option, Select, Table } from "@mui/joy";
import { BaseWorker } from "../../apiClient/data-contracts.ts";
@@ -10,11 +10,14 @@ export default function (): ReactNode {
const [workers, setWorkers] = useState<BaseWorker[]>([]);
const Api = useContext(ApiContext);
Api.workerList().then((response) => {
if (response.ok) {
setWorkers(response.data);
}
});
useEffect(() => {
Api.workerList().then((response) => {
if (response.ok) {
setWorkers(response.data);
}
});
}, []);
return (
<>
@@ -52,7 +55,7 @@ function WorkerDrawer({
<tbody>
{workers.map((worker) => {
return (
<tr>
<tr key={worker.key}>
<td>{worker.key}</td>
<td>{worker.allDependenciesFulfilled ? "yes" : "no"}</td>
<td>

View File

@@ -1,8 +1,6 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
// noinspection JSValidateJSDoc,JSUnusedGlobalSymbols
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
@@ -25,6 +23,7 @@ import {
MangaMangaConnectorId,
MetadataEntry,
MetadataSearchResult,
MinimalManga,
NotificationConnector,
NtfyRecord,
ProblemDetails,
@@ -230,7 +229,7 @@ export class V2<
* @request GET:/v2/Manga
*/
mangaList = (params: RequestParams = {}) =>
this.request<Manga[], void>({
this.request<MinimalManga[], void>({
path: `/v2/Manga`,
method: "GET",
format: "json",
@@ -260,7 +259,7 @@ export class V2<
* @request GET:/v2/Manga/Downloading
*/
mangaDownloadingList = (params: RequestParams = {}) =>
this.request<Manga[], void>({
this.request<MinimalManga[], void>({
path: `/v2/Manga/Downloading`,
method: "GET",
format: "json",
@@ -943,7 +942,7 @@ export class V2<
query: string,
params: RequestParams = {},
) =>
this.request<Manga[], ProblemDetails | void>({
this.request<MinimalManga[], ProblemDetails | void>({
path: `/v2/Search/${mangaConnectorName}/${query}`,
method: "GET",
format: "json",
@@ -958,7 +957,7 @@ export class V2<
* @request POST:/v2/Search/Url
*/
searchUrlCreate = (data: string, params: RequestParams = {}) =>
this.request<Manga, void | ProblemDetails>({
this.request<MinimalManga, void | ProblemDetails>({
path: `/v2/Search/Url`,
method: "POST",
body: data,

View File

@@ -331,6 +331,20 @@ export interface MetadataSearchResult {
coverUrl?: string | null;
}
export interface MinimalManga {
/**
* @minLength 16
* @maxLength 64
*/
key: string;
/** @minLength 1 */
name: string;
/** @minLength 1 */
description: string;
releaseStatus: MangaReleaseStatus;
mangaConnectorIds?: MangaMangaConnectorId[] | null;
}
export interface NotificationConnector {
/**
* @minLength 0