From 7b038ad37776be6c082e43d4ff8a57190e9ee152 Mon Sep 17 00:00:00 2001 From: glax Date: Tue, 22 Jul 2025 14:58:22 +0200 Subject: [PATCH] Fix MangaCard/List Style AddNotificationConnector --- tranga-website/src/App.tsx | 20 +++- .../src/Components/Mangas/MangaCard.css | 8 +- .../src/Components/Mangas/MangaCard.tsx | 8 +- .../src/Components/Mangas/MangaList.tsx | 18 +--- ...ctors.tsx => AddNotificationConnector.tsx} | 99 +++++++++++++++++-- .../src/Components/Settings/Settings.tsx | 2 +- tranga-website/src/apiClient/MangaContext.tsx | 34 ------- 7 files changed, 122 insertions(+), 67 deletions(-) rename tranga-website/src/Components/Settings/{NotificationConnectors.tsx => AddNotificationConnector.tsx} (53%) delete mode 100644 tranga-website/src/apiClient/MangaContext.tsx diff --git a/tranga-website/src/App.tsx b/tranga-website/src/App.tsx index 02b3adc..a9b5806 100644 --- a/tranga-website/src/App.tsx +++ b/tranga-website/src/App.tsx @@ -4,12 +4,12 @@ import Settings from "./Components/Settings/Settings.tsx"; import Header from "./Header.tsx"; import {createContext, useEffect, useState} from "react"; import {V2} from "./apiClient/V2.ts"; -import {GetManga, MangaContext } from './apiClient/MangaContext.tsx'; import { ApiContext } from './apiClient/ApiContext.tsx'; import MangaList from "./Components/Mangas/MangaList.tsx"; -import {MangaConnector} from "./apiClient/data-contracts.ts"; +import {Manga, MangaConnector} from "./apiClient/data-contracts.ts"; export const MangaConnectorContext = createContext([]); +export const MangaContext = createContext([]); export default function App () { const apiUriStr = localStorage.getItem("apiUri") ?? window.location.href.substring(0, window.location.href.lastIndexOf("/")) + "/api"; @@ -17,10 +17,24 @@ export default function App () { const [Api, setApi] = useState(new V2()); const [mangaConnectors, setMangaConnectors] = useState([]); + const [manga, setManga] = useState([]); + useEffect(() => { Api.mangaConnectorList().then(response => { if (response.ok) setMangaConnectors(response.data); + }); + + Api.mangaList().then(response => { + if (!response.ok) + { + setManga([]); + return; + } + Api.mangaWithIDsCreate(response.data).then(response => { + if (response.ok) + setManga(response.data); + }) }) }, [Api]); @@ -35,7 +49,7 @@ export default function App () { return ( - +
diff --git a/tranga-website/src/Components/Mangas/MangaCard.css b/tranga-website/src/Components/Mangas/MangaCard.css index a359ea0..e373804 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.css +++ b/tranga-website/src/Components/Mangas/MangaCard.css @@ -4,10 +4,10 @@ } .manga-cover-blur { - background: linear-gradient(to bottom, rgba(0,0,0,0.8), rgba(0,0,0,0.2) 75%); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(9px); - -webkit-backdrop-filter: blur(9px); + background: linear-gradient(135deg, rgba(245, 169, 184, 0.9) 20%, rgba(91, 206, 250, 0.6)); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);) + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); } .manga-card-badge-icon { diff --git a/tranga-website/src/Components/Mangas/MangaCard.tsx b/tranga-website/src/Components/Mangas/MangaCard.tsx index cc5c312..ada27c0 100644 --- a/tranga-website/src/Components/Mangas/MangaCard.tsx +++ b/tranga-website/src/Components/Mangas/MangaCard.tsx @@ -13,18 +13,16 @@ import { } from "@mui/joy"; import {Manga} from "../../apiClient/data-contracts.ts"; import {Dispatch, SetStateAction, useContext, useState} from "react"; -import {MangaContext} from "../../apiClient/MangaContext.tsx"; import "./MangaCard.css"; import MangaConnectorBadge from "./MangaConnectorBadge.tsx"; import ModalClose from "@mui/joy/ModalClose"; import {ApiContext} from "../../apiClient/ApiContext.tsx"; import MarkdownPreview from '@uiw/react-markdown-preview'; +import {MangaContext} from "../../App.tsx"; export function MangaCardFromId({mangaId} : {mangaId: string}) { - const Mangas = useContext(MangaContext); - const [manga, setManga] = useState(undefined); - - Mangas.GetManga(mangaId).then(setManga); + const mangas = useContext(MangaContext); + const manga = mangas.find(manga => manga.key === mangaId); return } diff --git a/tranga-website/src/Components/Mangas/MangaList.tsx b/tranga-website/src/Components/Mangas/MangaList.tsx index c3f935c..5e80681 100644 --- a/tranga-website/src/Components/Mangas/MangaList.tsx +++ b/tranga-website/src/Components/Mangas/MangaList.tsx @@ -1,23 +1,15 @@ -import {useContext, useState} from "react"; -import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import {MangaCardFromId} from "./MangaCard.tsx"; +import {useContext} from "react"; +import {MangaCard} from "./MangaCard.tsx"; import {Stack} from "@mui/joy"; import "./MangaList.css"; +import {MangaContext} from "../../App.tsx"; export default function MangaList (){ - const Api = useContext(ApiContext); - - const [mangaIds, setMangaIds] = useState(); - - Api.mangaList().then((response) => { - if (!response.ok) - return; - setMangaIds(response.data); - }); + const mangas = useContext(MangaContext); return ( - {mangaIds?.map(id => )} + {mangas?.map(manga => )} ); diff --git a/tranga-website/src/Components/Settings/NotificationConnectors.tsx b/tranga-website/src/Components/Settings/AddNotificationConnector.tsx similarity index 53% rename from tranga-website/src/Components/Settings/NotificationConnectors.tsx rename to tranga-website/src/Components/Settings/AddNotificationConnector.tsx index 90263fa..0c2e3d7 100644 --- a/tranga-website/src/Components/Settings/NotificationConnectors.tsx +++ b/tranga-website/src/Components/Settings/AddNotificationConnector.tsx @@ -1,8 +1,27 @@ import {ReactNode, useContext, useState} from "react"; import { ApiContext } from "../../apiClient/ApiContext"; -import {Button, Input, Modal, ModalDialog, Tab, TabList, TabPanel, Tabs} from "@mui/joy"; +import { + Button, + CircularProgress, + Input, + Modal, + ModalDialog, + Stack, + Tab, + TabList, + TabPanel, + Tabs +} from "@mui/joy"; import ModalClose from "@mui/joy/ModalClose"; import {GotifyRecord, NtfyRecord, PushoverRecord} from "../../apiClient/data-contracts.ts"; +import {Close, Done} from "@mui/icons-material"; + +enum LoadingState { + none, + loading, + success, + failure +} export default function ({open, setOpen} : {open: boolean, setOpen: (open: boolean) => void}) { @@ -25,11 +44,38 @@ export default function ({open, setOpen} : {open: boolean, setOpen: (open: boole ); } -function NotificationConnectorTab({ value, children, add }: { value: string, children: ReactNode, add: (data: any) => void }) { +function NotificationConnectorTab({ value, children, add, state }: { value: string, children: ReactNode, add: (data: any) => void, state: LoadingState }) { + const StateIndicator = (state : LoadingState) : ReactNode => { + switch (state) { + case LoadingState.loading: + return (); + case LoadingState.failure: + return (); + case LoadingState.success: + return (); + default: return null; + } + } + + // @ts-ignore + const StateColor = (state : LoadingState) => { + switch (state) { + case LoadingState.failure: + return "danger"; + case LoadingState.success: + return "success"; + default: return undefined; + } + } + + const IsLoading = (state : LoadingState) : boolean => state === LoadingState.loading; + return ( - {children} - + + {children} + + ); } @@ -37,9 +83,22 @@ function NotificationConnectorTab({ value, children, add }: { value: string, chi function Gotify() { const Api = useContext(ApiContext); const [gotifyData, setGotifyData] = useState({}); + const [loadingState, setLoadingState] = useState(LoadingState.none); + + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorGotifyUpdate(gotifyData) + .then((response) => { + if (response.ok) + setLoadingState(LoadingState.success); + else + setLoadingState(LoadingState.failure); + }) + .catch(_ => setLoadingState(LoadingState.failure)); + } return ( - Api.notificationConnectorGotifyUpdate(gotifyData)}> + setGotifyData({...gotifyData, name: e.target.value})} /> setGotifyData({...gotifyData, endpoint: e.target.value})} /> setGotifyData({...gotifyData, appToken: e.target.value})} /> @@ -51,9 +110,22 @@ function Gotify() { function Ntfy() { const Api = useContext(ApiContext); const [ntfyData, setNtfyData] = useState({}); + const [loadingState, setLoadingState] = useState(LoadingState.none); + + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorNtfyUpdate(ntfyData) + .then((response) => { + if (response.ok) + setLoadingState(LoadingState.success); + else + setLoadingState(LoadingState.failure); + }) + .catch(_ => setLoadingState(LoadingState.failure)); + } return ( - Api.notificationConnectorNtfyUpdate(ntfyData)}> + setNtfyData({...ntfyData, name: e.target.value})} /> setNtfyData({...ntfyData, endpoint: e.target.value})} /> setNtfyData({...ntfyData, topic: e.target.value})} /> @@ -67,9 +139,22 @@ function Ntfy() { function Pushover() { const Api = useContext(ApiContext); const [pushoverData, setPushoverData] = useState({}); + const [loadingState, setLoadingState] = useState(LoadingState.none); + + const Add = () => { + setLoadingState(LoadingState.loading); + Api.notificationConnectorPushoverUpdate(pushoverData) + .then((response) => { + if (response.ok) + setLoadingState(LoadingState.success); + else + setLoadingState(LoadingState.failure); + }) + .catch(_ => setLoadingState(LoadingState.failure)); + } return ( - Api.notificationConnectorPushoverUpdate(pushoverData)}> + setPushoverData({...pushoverData, name: e.target.value})} /> setPushoverData({...pushoverData, user: e.target.value})} /> setPushoverData({...pushoverData, appToken: e.target.value})} /> diff --git a/tranga-website/src/Components/Settings/Settings.tsx b/tranga-website/src/Components/Settings/Settings.tsx index fb9cddb..663ab17 100644 --- a/tranga-website/src/Components/Settings/Settings.tsx +++ b/tranga-website/src/Components/Settings/Settings.tsx @@ -15,7 +15,7 @@ import {createContext, Dispatch, useContext, useEffect, useState} from "react"; import {Article} from '@mui/icons-material'; import {TrangaSettings} from "../../apiClient/data-contracts.ts"; import {ApiContext} from "../../apiClient/ApiContext.tsx"; -import NotificationConnectors from "./NotificationConnectors.tsx"; +import NotificationConnectors from "./AddNotificationConnector.tsx"; export const SettingsContext = createContext({}); diff --git a/tranga-website/src/apiClient/MangaContext.tsx b/tranga-website/src/apiClient/MangaContext.tsx deleted file mode 100644 index 9170b20..0000000 --- a/tranga-website/src/apiClient/MangaContext.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import {createContext, useContext} from "react"; -import {Manga} from "./data-contracts.ts"; -import {ApiContext} from "./ApiContext.tsx"; - -const mangaPromises = new Map>(); -const mangas : Manga[] = []; - -export const GetManga : (id: string) => Promise = (id: string) => { - const API = useContext(ApiContext); - - const promise = mangaPromises.get(id); - if(promise) return promise; - const p = new Promise((resolve, reject) => { - let ret = mangas?.find(m => m.key == id); - if (ret) resolve(ret); - - console.log(`Fetching manga ${id}`); - API.mangaDetail(id) - .then(result => { - if (!result.ok) - throw new Error(`Error fetching manga detail ${id}`); - mangas.push(result.data); - resolve(result.data); - }).catch(reject); - }); - mangaPromises.set(id, p); - return p; -}; - -export const MangaContext = createContext<{ GetManga: (id: string) => Promise }>( - { - GetManga: GetManga - } -);