diff --git a/tranga-website/.prettierrc b/tranga-website/.prettierrc index 9dbc052..ab12164 100644 --- a/tranga-website/.prettierrc +++ b/tranga-website/.prettierrc @@ -2,7 +2,7 @@ "trailingComma": "es5", "tabWidth": 4, "singleQuote": true, - "printWidth": 80, + "printWidth": 100, "semi": true, "bracketSpacing": true, "objectWrap": "collapse", diff --git a/tranga-website/eslint.config.js b/tranga-website/eslint.config.js index bf91bc4..97a8913 100644 --- a/tranga-website/eslint.config.js +++ b/tranga-website/eslint.config.js @@ -13,10 +13,7 @@ export default tseslint.config( plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], }, } ); diff --git a/tranga-website/src/App.tsx b/tranga-website/src/App.tsx index 23ddd03..b831576 100644 --- a/tranga-website/src/App.tsx +++ b/tranga-website/src/App.tsx @@ -9,14 +9,12 @@ import MangaList from './Components/Mangas/MangaList.tsx'; import { Search } from './Search.tsx'; import MangaConnectorProvider from './contexts/MangaConnectorContext.tsx'; import LibraryProvider from './contexts/FileLibraryContext.tsx'; +import MangaDetail from './MangaDetail.tsx'; export default function App() { const [apiUri, setApiUri] = useState( localStorage.getItem('apiUri') ?? - window.location.href.substring( - 0, - window.location.href.lastIndexOf('/') - ) + '/api' + window.location.href.substring(0, window.location.href.lastIndexOf('/')) + '/api' ); const [apiConfig, setApiConfig] = useState({ baseUrl: apiUri }); useEffect(() => { @@ -24,6 +22,14 @@ export default function App() { }, [apiUri]); const [searchOpen, setSearchOpen] = useState(false); + const [downloadDrawerOpen, setDownloadDrawerOpen] = useState(false); + const [selectedMangaKey, setSelectedMangaKey] = useState(); + const [downloadSectionOpen, setDownloadSectionOpen] = useState(false); + function openMangaDownloadDrawer(mangaKey: string, downloadSectionOpen?: boolean) { + setDownloadDrawerOpen(true); + setSelectedMangaKey(mangaKey); + setDownloadSectionOpen(downloadSectionOpen ?? false); + } return ( @@ -36,11 +42,19 @@ export default function App() { openMangaDownloadDrawer(m.key)} openSearch={() => setSearchOpen(true)} /> openMangaDownloadDrawer(m.key, true)} + /> + diff --git a/tranga-website/src/Components/Inputs/TInput.tsx b/tranga-website/src/Components/Inputs/TInput.tsx index 1ca83c4..aac3afe 100644 --- a/tranga-website/src/Components/Inputs/TInput.tsx +++ b/tranga-website/src/Components/Inputs/TInput.tsx @@ -6,9 +6,9 @@ import './loadingBorder.css'; export default function TInput(props: TInputProps) { const [state, setState] = useState(TState.clean); - const [value, setValue] = useState< - string | number | readonly string[] | undefined - >(props.defaultValue); + const [value, setValue] = useState( + props.defaultValue + ); const [initialValue, setInitialValue] = useState< string | number | readonly string[] | undefined >(props.defaultValue); diff --git a/tranga-website/src/Components/Inputs/TProps.ts b/tranga-website/src/Components/Inputs/TProps.ts index 7134836..6354166 100644 --- a/tranga-website/src/Components/Inputs/TProps.ts +++ b/tranga-website/src/Components/Inputs/TProps.ts @@ -34,7 +34,5 @@ export const TColor = (state: TState): ColorPaletteProp => { export default interface TProps { disabled?: boolean; - completionAction?: ( - value?: string | number | readonly string[] - ) => Promise; + completionAction?: (value?: string | number | readonly string[]) => Promise; } diff --git a/tranga-website/src/Components/Mangas/MangaCard.css b/tranga-website/src/Components/Mangas/MangaCard.css index ed19680..aa9e549 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.css +++ b/tranga-website/src/Components/Mangas/MangaCard.css @@ -11,11 +11,7 @@ } .manga-card-cover-blur { - background: linear-gradient( - 150deg, - rgba(245, 169, 184, 0.8) 50%, - rgba(91, 206, 250, 0.3) - ); + background: linear-gradient(150deg, rgba(245, 169, 184, 0.8) 50%, rgba(91, 206, 250, 0.3)); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); backdrop-filter: blur(2px) brightness(70%); -webkit-backdrop-filter: blur(2px) brightness(70%); diff --git a/tranga-website/src/Components/Mangas/MangaCard.tsx b/tranga-website/src/Components/Mangas/MangaCard.tsx index 18f1ced..50c4ca0 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.tsx +++ b/tranga-website/src/Components/Mangas/MangaCard.tsx @@ -10,11 +10,7 @@ import { import { EventHandler, ReactNode, useContext } from 'react'; import './MangaCard.css'; import MangaConnectorIcon from './MangaConnectorIcon.tsx'; -import { - Manga, - MangaReleaseStatus, - MinimalManga, -} from '../../api/data-contracts.ts'; +import { Manga, MangaReleaseStatus, MinimalManga } from '../../api/data-contracts.ts'; import { ApiContext } from '../../contexts/ApiContext.tsx'; export default function MangaCard(props: MangaCardProps): ReactNode { @@ -23,14 +19,10 @@ export default function MangaCard(props: MangaCardProps): ReactNode { return ( ( - + ))} className={'manga-card-badge'} - color={releaseColor( - props.manga?.releaseStatus ?? MangaReleaseStatus.Unreleased - )}> + color={releaseColor(props.manga?.releaseStatus ?? MangaReleaseStatus.Unreleased)}> @@ -46,9 +38,7 @@ export default function MangaCard(props: MangaCardProps): ReactNode { - {props.manga?.name ?? ( - {stringWithRandomLength()} - )} + {props.manga?.name ?? {stringWithRandomLength()}} diff --git a/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx b/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx index e7c11bf..47bb070 100644 --- a/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx +++ b/tranga-website/src/Components/Mangas/MangaConnectorIcon.tsx @@ -12,9 +12,7 @@ export default function MangaConnectorIcon({ }): ReactNode { const Api = useContext(ApiContext); - const [connector, setConnector] = useState( - mangaConnector - ); + const [connector, setConnector] = useState(mangaConnector); useEffect(() => { if (mangaConnector) { diff --git a/tranga-website/src/Components/Mangas/MangaList.tsx b/tranga-website/src/Components/Mangas/MangaList.tsx index 1f6b663..96933da 100644 --- a/tranga-website/src/Components/Mangas/MangaList.tsx +++ b/tranga-website/src/Components/Mangas/MangaList.tsx @@ -1,36 +1,39 @@ import { Stack } from '@mui/joy'; import './MangaList.css'; import { Dispatch, ReactNode, useContext, useEffect, useState } from 'react'; -import { - Manga, - MangaReleaseStatus, - MinimalManga, -} from '../../api/data-contracts.ts'; +import { Manga, MangaReleaseStatus, MinimalManga } from '../../api/data-contracts.ts'; import { ApiContext } from '../../contexts/ApiContext.tsx'; import MangaCard from './MangaCard.tsx'; -export default function MangaList({ - openSearch, -}: { - openSearch: () => void; -}): ReactNode { +export default function MangaList(props: MangaListProps): ReactNode { const Api = useContext(ApiContext); - const [downloadingManga, setDownloadingManga] = useState( - [] - ); + const [downloadingManga, setDownloadingManga] = useState([]); + const [interval, setIntervalState] = useState(); useEffect(() => { + if (interval) { + clearInterval(interval); + setIntervalState(undefined); + } + if (!Api) return; + updateDownloadingManga(); + setIntervalState(setInterval(updateDownloadingManga, 5000)); + }, [Api]); + + const updateDownloadingManga = () => { Api.mangaDownloadingList().then((data) => { if (data.ok) { setDownloadingManga(data.data); } }); - }, [Api]); + }; return ( - + void; + mangaOnClick?: Dispatch; +} + export function MangaCardList(props: MangaCardListProps): ReactNode { return ( { + const schemeChanged = async (value: string | number | readonly string[] | undefined) => { if (typeof value != 'string') return Promise.reject(); try { - const response = - await Api.settingsChapterNamingSchemePartialUpdate(value); + const response = await Api.settingsChapterNamingSchemePartialUpdate(value); if (response.ok) return Promise.resolve(); else return Promise.reject(); } catch { diff --git a/tranga-website/src/Components/Settings/DownloadLanguage.tsx b/tranga-website/src/Components/Settings/DownloadLanguage.tsx index 3e0e665..604b953 100644 --- a/tranga-website/src/Components/Settings/DownloadLanguage.tsx +++ b/tranga-website/src/Components/Settings/DownloadLanguage.tsx @@ -7,13 +7,10 @@ export default function DownloadLanguage(): ReactNode { const settings = useContext(SettingsContext); const Api = useContext(ApiContext); - const languageChanged = async ( - value: string | number | readonly string[] | undefined - ) => { + const languageChanged = async (value: string | number | readonly string[] | undefined) => { if (typeof value != 'string') return Promise.reject(); try { - const response = - await Api.settingsDownloadLanguagePartialUpdate(value); + const response = await Api.settingsDownloadLanguagePartialUpdate(value); if (response.ok) return Promise.resolve(); else return Promise.reject(); } catch { diff --git a/tranga-website/src/Components/Settings/FlareSolverr.tsx b/tranga-website/src/Components/Settings/FlareSolverr.tsx index a1713b0..3c8872e 100644 --- a/tranga-website/src/Components/Settings/FlareSolverr.tsx +++ b/tranga-website/src/Components/Settings/FlareSolverr.tsx @@ -7,9 +7,7 @@ export default function FlareSolverr(): ReactNode { const settings = useContext(SettingsContext); const Api = useContext(ApiContext); - const uriChanged = async ( - value: string | number | readonly string[] | undefined - ) => { + const uriChanged = async (value: string | number | readonly string[] | undefined) => { if (typeof value != 'string') return Promise.reject(); try { const response = await Api.settingsFlareSolverrUrlCreate(value); diff --git a/tranga-website/src/Components/Settings/Maintenance.tsx b/tranga-website/src/Components/Settings/Maintenance.tsx index 116dbbe..7d310ca 100644 --- a/tranga-website/src/Components/Settings/Maintenance.tsx +++ b/tranga-website/src/Components/Settings/Maintenance.tsx @@ -18,9 +18,7 @@ export default function Maintenance() { return ( - - Cleanup unused Manga - + Cleanup unused Manga ); } diff --git a/tranga-website/src/Components/Settings/Settings.tsx b/tranga-website/src/Components/Settings/Settings.tsx index 77a3fad..468101f 100644 --- a/tranga-website/src/Components/Settings/Settings.tsx +++ b/tranga-website/src/Components/Settings/Settings.tsx @@ -12,13 +12,7 @@ import { } from '@mui/joy'; import './Settings.css'; import * as React from 'react'; -import { - createContext, - ReactNode, - useContext, - useEffect, - useState, -} from 'react'; +import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; import { SxProps } from '@mui/joy/styles/types'; import ImageCompression from './ImageCompression.tsx'; import FlareSolverr from './FlareSolverr.tsx'; @@ -29,15 +23,9 @@ import { ApiContext } from '../../contexts/ApiContext.tsx'; import { TrangaSettings } from '../../api/data-contracts.ts'; import TInput from '../Inputs/TInput.tsx'; -export const SettingsContext = createContext( - undefined -); +export const SettingsContext = createContext(undefined); -export default function Settings({ - setApiUri, -}: { - setApiUri: (uri: string) => void; -}) { +export default function Settings({ setApiUri }: { setApiUri: (uri: string) => void }) { const Api = useContext(ApiContext); const [settings, setSettings] = useState(); @@ -49,9 +37,7 @@ export default function Settings({ }); }, [Api]); - const apiUriChanged = ( - value: string | number | readonly string[] | undefined - ) => { + const apiUriChanged = (value: string | number | readonly string[] | undefined) => { if (typeof value != 'string') return Promise.reject(); setApiUri(value); return Promise.resolve(); @@ -90,13 +76,7 @@ export default function Settings({ ); } -export function SettingsItem({ - title, - children, -}: { - title: string; - children: ReactNode; -}) { +export function SettingsItem({ title, children }: { title: string; children: ReactNode }) { return ( {title} diff --git a/tranga-website/src/Header.tsx b/tranga-website/src/Header.tsx index 53964ec..1e2e6d3 100644 --- a/tranga-website/src/Header.tsx +++ b/tranga-website/src/Header.tsx @@ -5,11 +5,7 @@ import './Header.css'; import { Article, GitHub } from '@mui/icons-material'; import { ApiContext } from './contexts/ApiContext.tsx'; -export default function Header({ - children, -}: { - children?: ReactNode; -}): ReactElement { +export default function Header({ children }: { children?: ReactNode }): ReactElement { const Api = useContext(ApiContext); return ( @@ -17,11 +13,7 @@ export default function Header({ (props.manga); + const [library, setLibrary] = useState(); + const [downloadFromMap, setDownloadFromMap] = useState>( + new Map() + ); useEffect(() => { if (!props.open) return; if (!props.mangaKey) return; if (props.manga != undefined) return; + setLibrary(undefined); Manga.GetManga(props.mangaKey).then(setManga); }, [Api, Manga, props]); - const theme = useTheme(); + useEffect(() => { + const newMap = new Map(); + setLibrary(Libraries.find((library) => library.key == manga?.fileLibraryId)); + manga?.mangaConnectorIds.forEach((id) => { + newMap.set(id, id.useForDownload); + }); + setDownloadFromMap(newMap); + }, [manga, Libraries]); + + const setDownload = async (): Promise => { + if (!manga) return Promise.reject(); + if (library) { + const s = await Api.mangaChangeLibraryCreate(manga.key, library?.key) + .then((result) => result.ok) + .catch(() => false); + if (!s) return Promise.reject(); + } + for (const kv of downloadFromMap) { + const s = await Api.mangaSetAsDownloadFromCreate( + manga?.key, + kv[0].mangaConnectorName, + kv[1] + ) + .then((result) => result.ok) + .catch(() => false); + if (!s) return Promise.reject(); + } + return Promise.resolve(); + }; + + const onLibraryChange = (_: any, value: string | null) => { + setLibrary(Libraries.find((library) => library.key == value)); + }; return ( props.setOpen(false)}> -
- - {manga?.name} - - + + {manga?.name} + + + + sx={{ flexShrink: 1 }}> + sx={{ backgroundColor: theme.palette.primary.plainColor }}> {tag} ))} @@ -82,10 +128,7 @@ export default function MangaDetail(props: MangaDetailProps): ReactNode { + sx={{ backgroundColor: theme.palette.success.plainColor }}> {author.name} ))} @@ -93,10 +136,7 @@ export default function MangaDetail(props: MangaDetailProps): ReactNode { + sx={{ backgroundColor: theme.palette.neutral.plainColor }}> {link.provider} ))} @@ -110,18 +150,70 @@ export default function MangaDetail(props: MangaDetailProps): ReactNode { }} /> - - {props.actions} - -
+
+ + + + Download + + + + + Select a Library to Download to: + + + + + Select which connectors you want to download this Manga from: + + + {manga?.mangaConnectorIds.map((id) => ( + + + downloadFromMap.set(id, c.target.checked) + } + label={ +
+ + + {id.mangaConnectorName} + +
+ } + /> +
+ ))} +
+
+ Download All +
+
+
); @@ -132,5 +224,5 @@ export interface MangaDetailProps { mangaKey?: string; open: boolean; setOpen: Dispatch; - actions?: ReactNode[]; + downloadOpen?: boolean; } diff --git a/tranga-website/src/MangaDownloadDrawer.tsx b/tranga-website/src/MangaDownloadDrawer.tsx deleted file mode 100644 index fd4ee62..0000000 --- a/tranga-website/src/MangaDownloadDrawer.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import { Dispatch, ReactNode, useContext, useEffect, useState } from 'react'; -import { - Box, - Card, - Checkbox, - Drawer, - List, - ListItem, - Option, - Select, - Stack, - Typography, -} from '@mui/joy'; -import ModalClose from '@mui/joy/ModalClose'; -import { FileLibrary, 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 { - const Api = useContext(ApiContext); - const Manga = useContext(MangaContext); - const Libraries = useContext(FileLibraryContext); - - const [manga, setManga] = useState(props.manga); - const [library, setLibrary] = useState(); - const [downloadFromMap, setDownloadFromMap] = useState< - Map - >(new Map()); - - useEffect(() => { - if (!props.open) return; - if (!props.mangaKey) return; - if (props.manga != undefined) return; - Manga.GetManga(props.mangaKey).then(setManga); - }, [Api, Manga, props]); - - useEffect(() => { - const newMap = new Map(); - setLibrary( - Libraries.find((library) => library.key == manga?.fileLibraryId) - ); - manga?.mangaConnectorIds.forEach((id) => { - newMap.set(id, id.useForDownload); - }); - setDownloadFromMap(newMap); - }, [manga]); - - const setDownload = async (): Promise => { - if (!manga) return Promise.reject(); - if (library) { - const s = await Api.mangaChangeLibraryCreate( - manga.key, - library?.key - ) - .then((result) => result.ok) - .catch(() => false); - if (!s) return Promise.reject(); - } - for (const kv of downloadFromMap) { - const s = await Api.mangaSetAsDownloadFromCreate( - manga?.key, - kv[0].mangaConnectorName, - kv[1] - ) - .then((result) => result.ok) - .catch(() => false); - if (!s) return Promise.reject(); - } - return Promise.resolve(); - }; - - const onLibraryChange = (_: any, value: string | null) => { - setLibrary(Libraries.find((library) => library.key == value)); - }; - - return ( - props.setOpen(false)} - anchor="left" - size="md"> - - - Download - {manga?.name} - - - - Select a Library to Download to: - - - - - - Select which connectors you want to download this - Manga from: - - - {manga?.mangaConnectorIds.map((id) => ( - - - downloadFromMap.set( - id, - c.target.checked - ) - } - label={ -
- - - {id.mangaConnectorName} - -
- } - /> -
- ))} -
-
- - Download All - -
-
-
- ); -} - -export interface MangaDownloadDrawerProps { - manga?: Manga; - mangaKey?: string; - open: boolean; - setOpen: Dispatch; -} diff --git a/tranga-website/src/Search.tsx b/tranga-website/src/Search.tsx index 0e33b0c..2e88415 100644 --- a/tranga-website/src/Search.tsx +++ b/tranga-website/src/Search.tsx @@ -1,6 +1,5 @@ import { Dispatch, ReactNode, useContext, useEffect, useState } from 'react'; import { - Button, List, ListItem, ListItemDecorator, @@ -18,8 +17,6 @@ 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 MangaDetail from './MangaDetail.tsx'; -import MangaDownloadDrawer from './MangaDownloadDrawer.tsx'; export function Search(props: SearchModalProps): ReactNode { const Api = useContext(ApiContext); @@ -32,8 +29,7 @@ export function Search(props: SearchModalProps): ReactNode { } }, [props]); - const [selectedConnector, setSelectedConnector] = - useState(); + const [selectedConnector, setSelectedConnector] = useState(); const [searchResults, setSearchResults] = useState([]); const startSearch = async ( @@ -54,10 +50,7 @@ export function Search(props: SearchModalProps): ReactNode { } else { if (!selectedConnector) return Promise.reject(); try { - const result2 = await Api.searchDetail( - selectedConnector?.key, - value - ); + const result2 = await Api.searchDetail(selectedConnector?.key, value); if (result2.ok) { setSearchResults(result2.data); return Promise.resolve(); @@ -68,23 +61,6 @@ export function Search(props: SearchModalProps): ReactNode { } }; - const [selectedManga, setSelectedManga] = useState< - MinimalManga | undefined - >(undefined); - const [mangaDetailOpen, setMangaDetailOpen] = useState(false); - const [mangaDownloadDrawerOpen, setMangaDownloadDrawerOpen] = - useState(false); - - function openMangaDetail(manga: MinimalManga) { - setSelectedManga(manga); - setMangaDetailOpen(true); - } - - function openMangaDownloadDrawer() { - setMangaDetailOpen(false); - setMangaDownloadDrawerOpen(true); - } - return ( 1}> - - Select a connector - + Select a connector {MangaConnectors.map((c) => ( setSelectedConnector(c)}> - + 2}> - - Enter a search term or URL - + Enter a search term or URL - - Download - , - ]} - /> - @@ -159,6 +114,7 @@ export function Search(props: SearchModalProps): ReactNode { export interface SearchModalProps { open: boolean; setOpen: Dispatch; + mangaOnClick?: (manga: MinimalManga) => void; } function isUrl(str: string): boolean { diff --git a/tranga-website/src/api/V2.ts b/tranga-website/src/api/V2.ts index ac6b397..546667b 100644 --- a/tranga-website/src/api/V2.ts +++ b/tranga-website/src/api/V2.ts @@ -33,9 +33,7 @@ import { } from './data-contracts'; import { ContentType, HttpClient, RequestParams } from './http-client'; -export class V2< - SecurityDataType = unknown, -> extends HttpClient { +export class V2 extends HttpClient { /** * No description * @@ -159,10 +157,7 @@ export class V2< * @summary Creates a new API.Schema.LibraryContext.LibraryConnectors.LibraryConnector * @request PUT:/v2/LibraryConnector */ - libraryConnectorUpdate = ( - data: LibraryConnector, - params: RequestParams = {} - ) => + libraryConnectorUpdate = (data: LibraryConnector, params: RequestParams = {}) => this.request({ path: `/v2/LibraryConnector`, method: 'PUT', @@ -178,10 +173,7 @@ export class V2< * @summary Returns API.Schema.LibraryContext.LibraryConnectors.LibraryConnector with LibraryConnectorId * @request GET:/v2/LibraryConnector/{LibraryConnectorId} */ - libraryConnectorDetail = ( - libraryConnectorId: string, - params: RequestParams = {} - ) => + libraryConnectorDetail = (libraryConnectorId: string, params: RequestParams = {}) => this.request({ path: `/v2/LibraryConnector/${libraryConnectorId}`, method: 'GET', @@ -196,10 +188,7 @@ export class V2< * @summary Deletes API.Schema.LibraryContext.LibraryConnectors.LibraryConnector with LibraryConnectorId * @request DELETE:/v2/LibraryConnector/{LibraryConnectorId} */ - libraryConnectorDelete = ( - libraryConnectorId: string, - params: RequestParams = {} - ) => + libraryConnectorDelete = (libraryConnectorId: string, params: RequestParams = {}) => this.request({ path: `/v2/LibraryConnector/${libraryConnectorId}`, method: 'DELETE', @@ -305,11 +294,7 @@ export class V2< * @request DELETE:/v2/Manga/{MangaId} */ mangaDelete = (mangaId: string, params: RequestParams = {}) => - this.request({ - path: `/v2/Manga/${mangaId}`, - method: 'DELETE', - ...params, - }); + this.request({ path: `/v2/Manga/${mangaId}`, method: 'DELETE', ...params }); /** * No description * @@ -382,10 +367,7 @@ export class V2< * @summary Returns all downloaded API.Controllers.DTOs.Chapter for API.Controllers.DTOs.Manga with MangaId * @request GET:/v2/Manga/{MangaId}/Chapters/Downloaded */ - mangaChaptersDownloadedList = ( - mangaId: string, - params: RequestParams = {} - ) => + mangaChaptersDownloadedList = (mangaId: string, params: RequestParams = {}) => this.request({ path: `/v2/Manga/${mangaId}/Chapters/Downloaded`, method: 'GET', @@ -400,10 +382,7 @@ export class V2< * @summary Returns all API.Controllers.DTOs.Chapter not downloaded for API.Controllers.DTOs.Manga with MangaId * @request GET:/v2/Manga/{MangaId}/Chapters/NotDownloaded */ - mangaChaptersNotDownloadedList = ( - mangaId: string, - params: RequestParams = {} - ) => + mangaChaptersNotDownloadedList = (mangaId: string, params: RequestParams = {}) => this.request({ path: `/v2/Manga/${mangaId}/Chapters/NotDownloaded`, method: 'GET', @@ -418,10 +397,7 @@ export class V2< * @summary Returns the latest API.Controllers.DTOs.Chapter of requested API.Controllers.DTOs.Manga available on API.MangaConnectors.MangaConnector * @request GET:/v2/Manga/{MangaId}/Chapter/LatestAvailable */ - mangaChapterLatestAvailableList = ( - mangaId: string, - params: RequestParams = {} - ) => + mangaChapterLatestAvailableList = (mangaId: string, params: RequestParams = {}) => this.request({ path: `/v2/Manga/${mangaId}/Chapter/LatestAvailable`, method: 'GET', @@ -436,10 +412,7 @@ export class V2< * @summary Returns the latest API.Controllers.DTOs.Chapter of requested API.Controllers.DTOs.Manga that is downloaded * @request GET:/v2/Manga/{MangaId}/Chapter/LatestDownloaded */ - mangaChapterLatestDownloadedList = ( - mangaId: string, - params: RequestParams = {} - ) => + mangaChapterLatestDownloadedList = (mangaId: string, params: RequestParams = {}) => this.request({ path: `/v2/Manga/${mangaId}/Chapter/LatestDownloaded`, method: 'GET', @@ -474,11 +447,7 @@ export class V2< * @summary Move API.Controllers.DTOs.Manga to different API.Schema.MangaContext.FileLibrary * @request POST:/v2/Manga/{MangaId}/ChangeLibrary/{LibraryId} */ - mangaChangeLibraryCreate = ( - mangaId: string, - libraryId: string, - params: RequestParams = {} - ) => + mangaChangeLibraryCreate = (mangaId: string, libraryId: string, params: RequestParams = {}) => this.request({ path: `/v2/Manga/${mangaId}/ChangeLibrary/${libraryId}`, method: 'POST', @@ -575,10 +544,7 @@ export class V2< * @summary Returns the API.MangaConnectors.MangaConnector (Scanlation-Sites) with the requested Name * @request GET:/v2/MangaConnector/{MangaConnectorName} */ - mangaConnectorDetail = ( - mangaConnectorName: string, - params: RequestParams = {} - ) => + mangaConnectorDetail = (mangaConnectorName: string, params: RequestParams = {}) => this.request({ path: `/v2/MangaConnector/${mangaConnectorName}`, method: 'GET', @@ -748,10 +714,7 @@ export class V2< * @summary Creates a new API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector * @request PUT:/v2/NotificationConnector */ - notificationConnectorUpdate = ( - data: NotificationConnector, - params: RequestParams = {} - ) => + notificationConnectorUpdate = (data: NotificationConnector, params: RequestParams = {}) => this.request({ path: `/v2/NotificationConnector`, method: 'PUT', @@ -797,10 +760,7 @@ export class V2< * @summary Creates a new Gotify-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector * @request PUT:/v2/NotificationConnector/Gotify */ - notificationConnectorGotifyUpdate = ( - data: GotifyRecord, - params: RequestParams = {} - ) => + notificationConnectorGotifyUpdate = (data: GotifyRecord, params: RequestParams = {}) => this.request({ path: `/v2/NotificationConnector/Gotify`, method: 'PUT', @@ -817,10 +777,7 @@ export class V2< * @summary Creates a new Ntfy-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector * @request PUT:/v2/NotificationConnector/Ntfy */ - notificationConnectorNtfyUpdate = ( - data: NtfyRecord, - params: RequestParams = {} - ) => + notificationConnectorNtfyUpdate = (data: NtfyRecord, params: RequestParams = {}) => this.request({ path: `/v2/NotificationConnector/Ntfy`, method: 'PUT', @@ -837,10 +794,7 @@ export class V2< * @summary Creates a new Pushover-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector * @request PUT:/v2/NotificationConnector/Pushover */ - notificationConnectorPushoverUpdate = ( - data: PushoverRecord, - params: RequestParams = {} - ) => + notificationConnectorPushoverUpdate = (data: PushoverRecord, params: RequestParams = {}) => this.request({ path: `/v2/NotificationConnector/Pushover`, method: 'PUT', @@ -887,10 +841,7 @@ export class V2< * @summary Returns the API.Schema.MangaContext.MangaConnectorId`1 with API.Schema.MangaContext.MangaConnectorId`1.Key * @request GET:/v2/Query/Manga/MangaConnectorId/{MangaConnectorIdId} */ - queryMangaMangaConnectorIdDetail = ( - mangaConnectorIdId: string, - params: RequestParams = {} - ) => + queryMangaMangaConnectorIdDetail = (mangaConnectorIdId: string, params: RequestParams = {}) => this.request({ path: `/v2/Query/Manga/MangaConnectorId/${mangaConnectorIdId}`, method: 'GET', @@ -920,10 +871,7 @@ export class V2< * @summary Returns the API.Schema.MangaContext.MangaConnectorId`1 with API.Schema.MangaContext.MangaConnectorId`1.Key * @request GET:/v2/Query/Chapter/MangaConnectorId/{MangaConnectorIdId} */ - queryChapterMangaConnectorIdDetail = ( - mangaConnectorIdId: string, - params: RequestParams = {} - ) => + queryChapterMangaConnectorIdDetail = (mangaConnectorIdId: string, params: RequestParams = {}) => this.request({ path: `/v2/Query/Chapter/MangaConnectorId/${mangaConnectorIdId}`, method: 'GET', @@ -938,11 +886,7 @@ export class V2< * @summary Initiate a search for a API.Schema.MangaContext.Manga on API.Controllers.DTOs.MangaConnector with searchTerm * @request GET:/v2/Search/{MangaConnectorName}/{Query} */ - searchDetail = ( - mangaConnectorName: string, - query: string, - params: RequestParams = {} - ) => + searchDetail = (mangaConnectorName: string, query: string, params: RequestParams = {}) => this.request({ path: `/v2/Search/${mangaConnectorName}/${query}`, method: 'GET', @@ -990,11 +934,7 @@ export class V2< * @request GET:/v2/Settings/UserAgent */ settingsUserAgentList = (params: RequestParams = {}) => - this.request({ - path: `/v2/Settings/UserAgent`, - method: 'GET', - ...params, - }); + this.request({ path: `/v2/Settings/UserAgent`, method: 'GET', ...params }); /** * No description * @@ -1003,10 +943,7 @@ export class V2< * @summary Set a new UserAgent * @request PATCH:/v2/Settings/UserAgent */ - settingsUserAgentPartialUpdate = ( - data: string, - params: RequestParams = {} - ) => + settingsUserAgentPartialUpdate = (data: string, params: RequestParams = {}) => this.request({ path: `/v2/Settings/UserAgent`, method: 'PATCH', @@ -1023,11 +960,7 @@ export class V2< * @request DELETE:/v2/Settings/UserAgent */ settingsUserAgentDelete = (params: RequestParams = {}) => - this.request({ - path: `/v2/Settings/UserAgent`, - method: 'DELETE', - ...params, - }); + this.request({ path: `/v2/Settings/UserAgent`, method: 'DELETE', ...params }); /** * No description * @@ -1053,12 +986,7 @@ export class V2< MangaInfo?: number; }, any - >({ - path: `/v2/Settings/RequestLimits`, - method: 'GET', - format: 'json', - ...params, - }); + >({ path: `/v2/Settings/RequestLimits`, method: 'GET', format: 'json', ...params }); /** * @description

NOT IMPLEMENTED

* @@ -1068,11 +996,7 @@ export class V2< * @request PATCH:/v2/Settings/RequestLimits */ settingsRequestLimitsPartialUpdate = (params: RequestParams = {}) => - this.request({ - path: `/v2/Settings/RequestLimits`, - method: 'PATCH', - ...params, - }); + this.request({ path: `/v2/Settings/RequestLimits`, method: 'PATCH', ...params }); /** * No description * @@ -1120,10 +1044,7 @@ export class V2< * @originalName settingsRequestLimitsDelete * @duplicate */ - settingsRequestLimitsDelete2 = ( - requestType: RequestType, - params: RequestParams = {} - ) => + settingsRequestLimitsDelete2 = (requestType: RequestType, params: RequestParams = {}) => this.request({ path: `/v2/Settings/RequestLimits/${requestType}`, method: 'DELETE', @@ -1152,10 +1073,7 @@ export class V2< * @summary Set the Image-Compression-Level for Images * @request PATCH:/v2/Settings/ImageCompressionLevel/{level} */ - settingsImageCompressionLevelPartialUpdate = ( - level: number, - params: RequestParams = {} - ) => + settingsImageCompressionLevelPartialUpdate = (level: number, params: RequestParams = {}) => this.request({ path: `/v2/Settings/ImageCompressionLevel/${level}`, method: 'PATCH', @@ -1170,11 +1088,7 @@ export class V2< * @request GET:/v2/Settings/BWImages */ settingsBwImagesList = (params: RequestParams = {}) => - this.request({ - path: `/v2/Settings/BWImages`, - method: 'GET', - ...params, - }); + this.request({ path: `/v2/Settings/BWImages`, method: 'GET', ...params }); /** * No description * @@ -1183,10 +1097,7 @@ export class V2< * @summary Enable/Disable conversion of Images to Black and White * @request PATCH:/v2/Settings/BWImages/{enabled} */ - settingsBwImagesPartialUpdate = ( - enabled: boolean, - params: RequestParams = {} - ) => + settingsBwImagesPartialUpdate = (enabled: boolean, params: RequestParams = {}) => this.request({ path: `/v2/Settings/BWImages/${enabled}`, method: 'PATCH', @@ -1214,10 +1125,7 @@ export class V2< * @summary Sets the Chapter Naming Scheme * @request PATCH:/v2/Settings/ChapterNamingScheme */ - settingsChapterNamingSchemePartialUpdate = ( - data: string, - params: RequestParams = {} - ) => + settingsChapterNamingSchemePartialUpdate = (data: string, params: RequestParams = {}) => this.request({ path: `/v2/Settings/ChapterNamingScheme`, method: 'PATCH', @@ -1233,10 +1141,7 @@ export class V2< * @summary Sets the FlareSolverr-URL * @request POST:/v2/Settings/FlareSolverr/Url */ - settingsFlareSolverrUrlCreate = ( - data: string, - params: RequestParams = {} - ) => + settingsFlareSolverrUrlCreate = (data: string, params: RequestParams = {}) => this.request({ path: `/v2/Settings/FlareSolverr/Url`, method: 'POST', @@ -1294,10 +1199,7 @@ export class V2< * @summary Sets the language in which Manga are downloaded * @request PATCH:/v2/Settings/DownloadLanguage/{Language} */ - settingsDownloadLanguagePartialUpdate = ( - language: string, - params: RequestParams = {} - ) => + settingsDownloadLanguagePartialUpdate = (language: string, params: RequestParams = {}) => this.request({ path: `/v2/Settings/DownloadLanguage/${language}`, method: 'PATCH', @@ -1341,10 +1243,7 @@ export class V2< * @summary Get all API.Workers.BaseWorker in requested API.Workers.WorkerExecutionState * @request GET:/v2/Worker/State/{State} */ - workerStateDetail = ( - state: WorkerExecutionState, - params: RequestParams = {} - ) => + workerStateDetail = (state: WorkerExecutionState, params: RequestParams = {}) => this.request({ path: `/v2/Worker/State/${state}`, method: 'GET', @@ -1375,11 +1274,7 @@ export class V2< * @request DELETE:/v2/Worker/{WorkerId} */ workerDelete = (workerId: string, params: RequestParams = {}) => - this.request({ - path: `/v2/Worker/${workerId}`, - method: 'DELETE', - ...params, - }); + this.request({ path: `/v2/Worker/${workerId}`, method: 'DELETE', ...params }); /** * No description * diff --git a/tranga-website/src/api/http-client.ts b/tranga-website/src/api/http-client.ts index a4b9646..633bcdf 100644 --- a/tranga-website/src/api/http-client.ts +++ b/tranga-website/src/api/http-client.ts @@ -32,10 +32,7 @@ export interface FullRequestParams extends Omit { cancelToken?: CancelToken; } -export type RequestParams = Omit< - FullRequestParams, - 'body' | 'method' | 'query' | 'path' ->; +export type RequestParams = Omit; export interface ApiConfig { baseUrl?: string; @@ -46,8 +43,7 @@ export interface ApiConfig { customFetch?: typeof fetch; } -export interface HttpResponse - extends Response { +export interface HttpResponse extends Response { data: D; error: E; } @@ -67,8 +63,7 @@ export class HttpClient { private securityData: SecurityDataType | null = null; private securityWorker?: ApiConfig['securityWorker']; private abortControllers = new Map(); - private customFetch = (...fetchParams: Parameters) => - fetch(...fetchParams); + private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); private baseApiParams: RequestParams = { credentials: 'same-origin', @@ -101,9 +96,7 @@ export class HttpClient { protected toQueryString(rawQuery?: QueryParamsType): string { const query = rawQuery || {}; - const keys = Object.keys(query).filter( - (key) => 'undefined' !== typeof query[key] - ); + const keys = Object.keys(query).filter((key) => 'undefined' !== typeof query[key]); return keys .map((key) => Array.isArray(query[key]) @@ -120,19 +113,15 @@ export class HttpClient { private contentFormatters: Record any> = { [ContentType.Json]: (input: any) => - input !== null && - (typeof input === 'object' || typeof input === 'string') + input !== null && (typeof input === 'object' || typeof input === 'string') ? JSON.stringify(input) : input, [ContentType.JsonApi]: (input: any) => - input !== null && - (typeof input === 'object' || typeof input === 'string') + input !== null && (typeof input === 'object' || typeof input === 'string') ? JSON.stringify(input) : input, [ContentType.Text]: (input: any) => - input !== null && typeof input !== 'string' - ? JSON.stringify(input) - : input, + input !== null && typeof input !== 'string' ? JSON.stringify(input) : input, [ContentType.FormData]: (input: any) => Object.keys(input || {}).reduce((formData, key) => { const property = input[key]; @@ -149,10 +138,7 @@ export class HttpClient { [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), }; - protected mergeRequestParams( - params1: RequestParams, - params2?: RequestParams - ): RequestParams { + protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { return { ...this.baseApiParams, ...params1, @@ -165,9 +151,7 @@ export class HttpClient { }; } - protected createAbortSignal = ( - cancelToken: CancelToken - ): AbortSignal | undefined => { + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { if (this.abortControllers.has(cancelToken)) { const abortController = this.abortControllers.get(cancelToken); if (abortController) { @@ -202,16 +186,13 @@ export class HttpClient { ...params }: FullRequestParams): Promise> => { const secureParams = - ((typeof secure === 'boolean' - ? secure - : this.baseApiParams.secure) && + ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {}; const requestParams = this.mergeRequestParams(params, secureParams); const queryString = query && this.toQueryString(query); - const payloadFormatter = - this.contentFormatters[type || ContentType.Json]; + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; const responseFormat = format || requestParams.format; return this.customFetch( @@ -220,18 +201,12 @@ export class HttpClient { ...requestParams, headers: { ...(requestParams.headers || {}), - ...(type && type !== ContentType.FormData - ? { 'Content-Type': type } - : {}), + ...(type && type !== ContentType.FormData ? { 'Content-Type': type } : {}), }, signal: - (cancelToken - ? this.createAbortSignal(cancelToken) - : requestParams.signal) || null, - body: - typeof body === 'undefined' || body === null - ? null - : payloadFormatter(body), + (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || + null, + body: typeof body === 'undefined' || body === null ? null : payloadFormatter(body), } ).then(async (response) => { const r = response.clone() as HttpResponse; diff --git a/tranga-website/src/contexts/FileLibraryContext.tsx b/tranga-website/src/contexts/FileLibraryContext.tsx index 43db36a..6c8798a 100644 --- a/tranga-website/src/contexts/FileLibraryContext.tsx +++ b/tranga-website/src/contexts/FileLibraryContext.tsx @@ -1,20 +1,10 @@ -import { - createContext, - ReactNode, - useContext, - useEffect, - useState, -} from 'react'; +import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; import { FileLibrary } from '../api/data-contracts.ts'; import { ApiContext } from './ApiContext.tsx'; export const FileLibraryContext = createContext([]); -export default function LibraryProvider({ - children, -}: { - children: ReactNode; -}): ReactNode { +export default function LibraryProvider({ children }: { children: ReactNode }): ReactNode { const Api = useContext(ApiContext); const [state, setState] = useState([]); diff --git a/tranga-website/src/contexts/MangaConnectorContext.tsx b/tranga-website/src/contexts/MangaConnectorContext.tsx index d86e816..68e7657 100644 --- a/tranga-website/src/contexts/MangaConnectorContext.tsx +++ b/tranga-website/src/contexts/MangaConnectorContext.tsx @@ -1,20 +1,10 @@ -import { - createContext, - ReactNode, - useContext, - useEffect, - useState, -} from 'react'; +import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; import { MangaConnector } from '../api/data-contracts.ts'; import { ApiContext } from './ApiContext.tsx'; export const MangaConnectorContext = createContext([]); -export default function MangaConnectorProvider({ - children, -}: { - children: ReactNode; -}) { +export default function MangaConnectorProvider({ children }: { children: ReactNode }) { const Api = useContext(ApiContext); const [state, setState] = useState([]); @@ -27,7 +17,5 @@ export default function MangaConnectorProvider({ }); }, [Api]); - return ( - {children} - ); + return {children}; } diff --git a/tranga-website/src/contexts/MangaContext.tsx b/tranga-website/src/contexts/MangaContext.tsx index c12ab36..c692597 100644 --- a/tranga-website/src/contexts/MangaContext.tsx +++ b/tranga-website/src/contexts/MangaContext.tsx @@ -3,20 +3,14 @@ import { ApiContext } from './ApiContext.tsx'; import { Manga } from '../api/data-contracts.ts'; import { V2 } from '../api/V2.ts'; -export const MangaContext = createContext({ - GetManga: () => Promise.reject(), -}); +export const MangaContext = createContext({ GetManga: () => Promise.reject() }); const manga: Map = new Map(); const promises: Map> = new Map(); export default function MangaProvider({ children }: { children: ReactNode }) { const Api = useContext(ApiContext); - return ( - getManga(k, Api) }}> - {children} - - ); + return getManga(k, Api) }}>{children}; } function getManga(key: string, Api: V2): Promise { diff --git a/tranga-website/tsconfig.json b/tranga-website/tsconfig.json index f6df6c7..eb69b0d 100644 --- a/tranga-website/tsconfig.json +++ b/tranga-website/tsconfig.json @@ -1,7 +1,4 @@ { "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] } diff --git a/tranga-website/vite.config.ts b/tranga-website/vite.config.ts index 5da3bdc..909e6a6 100644 --- a/tranga-website/vite.config.ts +++ b/tranga-website/vite.config.ts @@ -2,7 +2,4 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; // https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], - server: { host: '127.0.0.1' }, -}); +export default defineConfig({ plugins: [react()], server: { host: '127.0.0.1' } });