From 007b49c62444ef40566ab7cf9ee62bf1355d037c Mon Sep 17 00:00:00 2001 From: glax Date: Sun, 16 Mar 2025 22:25:43 +0100 Subject: [PATCH] Single Style for Manga --- Website/modules/MonitorJobsList.tsx | 16 ++- Website/modules/QueuePopUp.tsx | 13 +- Website/modules/Search.tsx | 9 +- Website/modules/SearchFunctions.tsx | 2 +- Website/modules/interfaces/IAuthor.tsx | 7 +- Website/modules/interfaces/IChapter.ts | 10 -- Website/modules/interfaces/IChapter.tsx | 48 +++++++ Website/modules/interfaces/ILink.tsx | 8 +- Website/modules/interfaces/IManga.tsx | 182 ++++++++---------------- Website/styles/ExtendedInfo.css | 142 ------------------ Website/styles/MangaCoverCard.css | 132 +++++++++++------ 11 files changed, 234 insertions(+), 335 deletions(-) delete mode 100644 Website/modules/interfaces/IChapter.ts create mode 100644 Website/modules/interfaces/IChapter.tsx delete mode 100644 Website/styles/ExtendedInfo.css diff --git a/Website/modules/MonitorJobsList.tsx b/Website/modules/MonitorJobsList.tsx index 9b2f92d..738d846 100644 --- a/Website/modules/MonitorJobsList.tsx +++ b/Website/modules/MonitorJobsList.tsx @@ -4,7 +4,8 @@ import '../styles/monitorMangaList.css'; import {JobType} from "./interfaces/Jobs/IJob"; import '../styles/MangaCoverCard.css' import DownloadAvailableChaptersJob from "./interfaces/Jobs/DownloadAvailableChaptersJob"; -import {CoverCard} from "./interfaces/IManga"; +import {MangaItem} from "./interfaces/IManga"; +import Manga from "./Manga"; export default function MonitorJobsList({onStartSearch, onJobsChanged, connectedToBackend, apiUri, updateList} : {onStartSearch() : void, onJobsChanged: EventHandler, connectedToBackend: boolean, apiUri: string, updateList: Date}) { const [MonitoringJobs, setMonitoringJobs] = useState([]); @@ -35,10 +36,10 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged, connected } function StartSearchMangaEntry() : ReactElement { - return (
- Blahaj + return (
+ Blahaj
-

Add new Manga

+

Add new Manga

+

); @@ -48,7 +49,12 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged, connected
{MonitoringJobs.map((MonitoringJob) => - + + <> + + )}
); } \ No newline at end of file diff --git a/Website/modules/QueuePopUp.tsx b/Website/modules/QueuePopUp.tsx index 9663d93..994aa37 100644 --- a/Website/modules/QueuePopUp.tsx +++ b/Website/modules/QueuePopUp.tsx @@ -4,7 +4,8 @@ import '../styles/queuePopUp.css'; import '../styles/popup.css'; import Job from "./Job"; import DownloadSingleChapterJob from "./interfaces/Jobs/DownloadSingleChapterJob"; -import { ItemDownloadSingleChapterJob } from "./interfaces/IManga"; +import { MangaItem } from "./interfaces/IManga"; +import {ChapterItem} from "./interfaces/IChapter"; export default function QueuePopUp({connectedToBackend, children, apiUri} : {connectedToBackend: boolean, children: JSX.Element[], apiUri: string}) { @@ -59,10 +60,16 @@ export default function QueuePopUp({connectedToBackend, children, apiUri} : {con
- {RunningJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => )} + {RunningJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => { + let job = j as DownloadSingleChapterJob; + return + })}
- {WaitingJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => )} + {WaitingJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j =>{ + let job = j as DownloadSingleChapterJob; + return + })}
diff --git a/Website/modules/Search.tsx b/Website/modules/Search.tsx index eebe6a2..b7067da 100644 --- a/Website/modules/Search.tsx +++ b/Website/modules/Search.tsx @@ -2,9 +2,8 @@ import React, {ChangeEventHandler, EventHandler, useEffect, useState} from 'reac import {MangaConnector} from "./MangaConnector"; import IMangaConnector from "./interfaces/IMangaConnector"; import {isValidUri} from "../App"; -import IManga, {ExtendedInfo} from "./interfaces/IManga"; +import IManga, {MangaItem} from "./interfaces/IManga"; import '../styles/search.css'; -import '../styles/ExtendedInfo.css' import SearchFunctions from "./SearchFunctions"; import Job from "./Job"; import ILocalLibrary from "./interfaces/ILocalLibrary"; @@ -132,15 +131,15 @@ export default function Search({apiUri, jobInterval, onJobsChanged, closeSearch} {searchResults === undefined ?

: searchResults.map(result => - , + - ]}/> + ) } diff --git a/Website/modules/SearchFunctions.tsx b/Website/modules/SearchFunctions.tsx index 97260e4..2ef8589 100644 --- a/Website/modules/SearchFunctions.tsx +++ b/Website/modules/SearchFunctions.tsx @@ -1,5 +1,5 @@ import IManga from "./interfaces/IManga"; -import {getData, postData} from "../App"; +import {postData} from "../App"; export default class SearchFunctions { diff --git a/Website/modules/interfaces/IAuthor.tsx b/Website/modules/interfaces/IAuthor.tsx index 1db7701..713d109 100644 --- a/Website/modules/interfaces/IAuthor.tsx +++ b/Website/modules/interfaces/IAuthor.tsx @@ -6,7 +6,10 @@ export default interface IAuthor { authorName: string; } -export function AuthorElement({apiUri, authorId} : {apiUri: string, authorId: string}) : ReactElement{ +export function AuthorElement({apiUri, authorId} : {apiUri: string, authorId: string | null}) : ReactElement{ + if(authorId === null) + return (

Author

); + let [name, setName] = React.useState(authorId); useEffect(()=> { @@ -17,5 +20,5 @@ export function AuthorElement({apiUri, authorId} : {apiUri: string, authorId: st }); }, []) - return ({name}); + return (

{name}

); } \ No newline at end of file diff --git a/Website/modules/interfaces/IChapter.ts b/Website/modules/interfaces/IChapter.ts deleted file mode 100644 index e255ff6..0000000 --- a/Website/modules/interfaces/IChapter.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default interface IChapter{ - chapterId: string; - volumeNumber: number; - chapterNumber: string; - url: string; - title: string | undefined; - archiveFileName: string; - downloaded: boolean; - parentMangaId: string; -} \ No newline at end of file diff --git a/Website/modules/interfaces/IChapter.tsx b/Website/modules/interfaces/IChapter.tsx new file mode 100644 index 0000000..e06c242 --- /dev/null +++ b/Website/modules/interfaces/IChapter.tsx @@ -0,0 +1,48 @@ +import React, {ReactElement, ReactEventHandler, useEffect, useState} from "react"; +import Manga from "../Manga"; +import IManga from "./IManga"; +import Chapter from "../Chapter"; + +export default interface IChapter{ + chapterId: string; + volumeNumber: number; + chapterNumber: string; + url: string; + title: string | undefined; + archiveFileName: string; + downloaded: boolean; + parentMangaId: string; +} + +export function ChapterItem({apiUri, chapterId} : {apiUri: string, chapterId: string}) : ReactElement { + const setCoverItem : ReactEventHandler = (e) => { + setMangaCoverHtmlImageItem(e.currentTarget); + } + + let [chapter, setChapter] = useState(null); + let [manga, setManga] = useState(null); + let [mangaCoverUrl, setMangaCoverUrl] = useState("../../media/blahaj.png"); + let [mangaCoverHtmlImageItem, setMangaCoverHtmlImageItem] = useState(null); + useEffect(() => { + Chapter.GetChapterFromId(apiUri, chapterId).then(setChapter); + }, []); + useEffect(() => { + if(chapter === null) + manga = null; + else + Manga.GetMangaById(apiUri, chapter.parentMangaId).then(setManga); + }, [chapter]); + useEffect(() => { + if(chapter != null && mangaCoverHtmlImageItem != null) + setMangaCoverUrl(Manga.GetMangaCoverImageUrl(apiUri, chapter.parentMangaId, mangaCoverHtmlImageItem)); + }, [chapter, mangaCoverHtmlImageItem]); + + return (
+ Manga Cover +

{manga ? manga.name : "Manga-Name"}

+

{chapter ? chapter.title : "Chapter-Title"}

+

Vol.{chapter ? chapter.volumeNumber : "VolNum"}

+

Ch.{chapter ? chapter.chapterNumber : "ChNum"}

+ Link +
) +} \ No newline at end of file diff --git a/Website/modules/interfaces/ILink.tsx b/Website/modules/interfaces/ILink.tsx index 3019dc3..126377f 100644 --- a/Website/modules/interfaces/ILink.tsx +++ b/Website/modules/interfaces/ILink.tsx @@ -1,6 +1,5 @@ import React, {ReactElement, useEffect} from "react"; import {getData} from "../../App"; -import IAuthor from "./IAuthor"; export default interface ILink { linkId: string; @@ -8,7 +7,10 @@ export default interface ILink { linkUrl: string; } -export function LinkElement({apiUri, linkId} : {apiUri: string, linkId: string}) : ReactElement{ +export function LinkElement({apiUri, linkId} : {apiUri: string, linkId: string | null}) : ReactElement{ + if(linkId === null) + return (Link); + let [provider, setProvider] = React.useState(linkId); let [linkUrl, setLinkUrl] = React.useState(""); @@ -21,5 +23,5 @@ export function LinkElement({apiUri, linkId} : {apiUri: string, linkId: string}) }); }, []) - return ({provider}); + return ({provider}); } \ No newline at end of file diff --git a/Website/modules/interfaces/IManga.tsx b/Website/modules/interfaces/IManga.tsx index 162e33d..206f698 100644 --- a/Website/modules/interfaces/IManga.tsx +++ b/Website/modules/interfaces/IManga.tsx @@ -1,17 +1,14 @@ import Manga from "../Manga"; -import React, {ReactElement, ReactEventHandler, useEffect} from "react"; +import React, {Children, ReactElement, ReactEventHandler, useEffect, useState} from "react"; import Icon from '@mdi/react'; import { mdiTagTextOutline, mdiAccountEdit, mdiLinkVariant } from '@mdi/js'; import MarkdownPreview from '@uiw/react-markdown-preview'; import {AuthorElement} from "./IAuthor"; import {LinkElement} from "./ILink"; -import DownloadSingleChapterJob from "./Jobs/DownloadSingleChapterJob"; -import IChapter from "./IChapter"; -import Chapter from "../Chapter"; export default interface IManga{ mangaId: string; - connectorId: string; + idOnConnectorSite: string; name: string; description: string; websiteUrl: string; @@ -35,128 +32,65 @@ export enum MangaReleaseStatus { Unreleased = "Unreleased", } -export const defaultManga: IManga = { - altTitleIds: [], - authorIds: [], - connectorId: "", - description: "", - folderName: "", - ignoreChapterBefore: 0, - linkIds: [], - mangaConnectorId: "", - name: "", - originalLanguage: "", - releaseStatus: MangaReleaseStatus.Unreleased, - tags: [], - websiteUrl: "", - year: 0, - mangaId: "" -} - -export function CoverCard({apiUri, mangaId} : {apiUri: string, mangaId: string}) : ReactElement { - let [manga, setContent] = React.useState(defaultManga); - let [extendedInfo, setExtendedInfo] = React.useState(false); +export function MangaItem({apiUri, mangaId, children} : {apiUri: string, mangaId: string, children?: (string | ReactElement)[]}) : ReactElement { + const LoadMangaCover : ReactEventHandler = (e) => { + if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget)) + e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget); + } + let [manga, setManga] = useState(null); + let [clicked, setClicked] = useState(false); useEffect(() => { - Manga.GetMangaById(apiUri, mangaId).then(setContent); + Manga.GetMangaById(apiUri, mangaId).then(setManga); }, []); - const MangaCover : ReactEventHandler = (e) => { - if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget)) - e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget); - } - - return ( -
{ - setExtendedInfo(!extendedInfo); - }}> - Manga Cover -
-

{manga.mangaConnectorId}

-
-

{manga.name}

-
- {extendedInfo ?
- { - Manga.DeleteManga(apiUri, manga.mangaId); - }}>Delete - ]} /> -
: null} -
); -} - -export function ExtendedInfo({apiUri, manga, actions} : {apiUri: string, manga: IManga, actions: ReactElement[]}) : ReactElement { - const MangaCover : ReactEventHandler = (e) => { - if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget)) - e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget); - } - - return( -
- Manga Cover -

{manga.mangaConnectorId}

-
-

{manga.name}

-
- {manga.authorIds.map(authorId => -

- - -

)} - {manga.tags.map(tag => -

- - {tag} -

)} - {manga.linkIds.map(linkId => -

- - -

)} -
- -
- {actions.map((p, i) =>
{p}
)} -
-
); -} - -export function ItemDownloadSingleChapterJob({apiUri, job} : {apiUri: string, job: DownloadSingleChapterJob}){ - const MangaCover : ReactEventHandler = (e) => { - if(manga === null) - return; - if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget)) - e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget); - } - - let [chapter, setChapter] = React.useState(null); - let [manga, setManga] = React.useState(null); - - useEffect(() => { - Chapter.GetChapterFromId(apiUri, job.chapterId).then(setChapter); - }, []); - - useEffect(() => { - if(chapter === null){ - setManga(null); - return; - } - Manga.GetMangaById(apiUri, chapter.parentMangaId).then(setManga); - }, [chapter]); - - return ( -
- Manga Cover -

{manga ? manga.name : job.chapterId}

-

- {chapter ? "Vol." + chapter.volumeNumber + " Ch." + chapter.chapterNumber + ": " + chapter.title : "loading"} - - - -

+ return (
setClicked(!clicked)}> + Manga Cover +

{manga ? manga.mangaConnectorId : "Connector"}

+

+

{manga ? manga.name : "Name"}

+ Link +
+ {manga ? manga.authorIds.map(authorId => +

+ + +

) + : +

+ + +

} + {manga ? manga.tags.map(tag => +

+ +

{tag}

+

) + : +

+ +

Tag

+

+ } + {manga ? manga.linkIds.map(linkId => +

+ + +

) + : +

+ + +

}
- ); + +
+ {children ? children.map(c => { + if(c instanceof Element) + return c as ReactElement; + else + return {c} + }) : null} +
+
) } \ No newline at end of file diff --git a/Website/styles/ExtendedInfo.css b/Website/styles/ExtendedInfo.css deleted file mode 100644 index b4f8717..0000000 --- a/Website/styles/ExtendedInfo.css +++ /dev/null @@ -1,142 +0,0 @@ -.SearchResult { - background-color: var(--second-background-color); - border-radius: 2px; - padding: 5px 5px 9px 5px; - position: relative; - max-width: 100%; - width: fit-content; - height: 328px; - display: grid; - grid-template-columns: 220px 300px 380px; - grid-template-rows: 55px 55px 190px auto; - column-gap: 10px; - grid-template-areas: - "cover header header" - "cover alltags alltags" - "cover description description" - "cover footer button"; -} - -.SearchResult p { - margin: 2px 0; -} - -.SearchResult > img { - grid-area: cover; - position: relative; - height: 100%; - width: 100%; - z-index: 0; - border: 2px solid var(--primary-color); - border-radius: 4px; -} - -.SearchResult > .connector-name { - grid-area: cover; - position: absolute; - z-index: 1; - left: 2px; - top: 2px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - width: 100%; - background-color: var(--accent-color); - margin: 0; - padding: 2px 0; - text-align: center; - color: var(--secondary-color); -} - -.SearchResult > .Manga-status { - grid-area: header; -} - -.SearchResult > .Manga-name { - grid-area: header; - color: black; - padding: 0 30px 0 0; -} - -.SearchResult > .Manga-tags { - display: flex; - flex-direction: row; - flex-wrap: wrap; - grid-area: alltags; - color: white; - padding: 0; - margin: 0; - white-space: nowrap; - max-height: 100%; - overflow-y: scroll; -} - -.SearchResult > .Manga-tags p { - margin: 2px; - padding: 5px; - font-size: 10pt; - height: fit-content; - width: min-content; -} - -.SearchResult > .Manga-tags p > * { - margin: 0 2px; -} - -.SearchResult .Manga-author { - background-color: green; -} - -.SearchResult .Manga-tag { - background-color: blue; -} - -.SearchResult .Manga-link{ - background-color: brown; -} - -.SearchResult .Manga-link > a, .Manga-link > a:visited { - color: white; -} - -.SearchResult > .Manga-description { - grid-area: description; - color: black; - overflow-y: scroll; -} - -.SearchResult > .Manga-actions button, .Manga-actions select { - background-color: white; - border: 1px solid var(--primary-color); - border-radius: 4px; - width: fit-content; - height: fit-content; - padding: 5px 10px; -} - -.SearchResult > .Manga-actions { - grid-area: button; - padding: 0; - margin: 5px 0 0 0; - display: flex; - flex-direction: row; - justify-content: flex-end; - align-items: center; -} - -.SearchResult > .Manga-AddButton:hover { - background-color: #eee; -} - -.SearchResult a, .SearchResult a:visited { - color: initial; -} - -.SearchResult a img { - filter: brightness(0) saturate(100%) invert(0%) sepia(0%) saturate(7480%) hue-rotate(141deg) brightness(111%) contrast(99%); - position: relative; - bottom: 7px; -} - -.startSearchEntry { - position: relative; -} \ No newline at end of file diff --git a/Website/styles/MangaCoverCard.css b/Website/styles/MangaCoverCard.css index daa3cb2..026a7cd 100644 --- a/Website/styles/MangaCoverCard.css +++ b/Website/styles/MangaCoverCard.css @@ -1,4 +1,4 @@ -.pill { +.MangaItem-Connector { flex-grow: 0; height: 14pt; font-size: 12pt; @@ -10,7 +10,7 @@ margin: 10px 0; } -.Manga{ +.MangaItem{ cursor: pointer; background-color: var(--secondary-color); width: 180px; @@ -22,7 +22,7 @@ flex-shrink: 0; } -.Manga::after{ +.MangaItem::after{ content: ''; position: absolute; left: 0; top: 0; @@ -32,31 +32,46 @@ z-index: 0; } +.MangaItem > * { + z-index: 1; + position: relative; +} + .startSearchEntry::after{ background: initial !important; } -.Manga-name{ +.MangaItem-Name{ width: fit-content; font-size: 16pt; font-weight: bold; color: white; + margin: 5px 0 !important; } -.Manga-status { - display:block; - height: 10px; - width: 10px; - border-radius: 50%; - margin: 5px; +.MangaItem-Website { + display: block; + height: 13px; + width: 13px; position: absolute; - top: 5px; - right: 5px; + top: 12px; + right: 10px; + z-index: 2; +} + +.MangaItem-Status { + display:block; + height: 15px; + width: 15px; + border-radius: 50%; + position: absolute; + top: 15px; + right: 35px; z-index: 2; box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px; } -.Manga-status::after { +.MangaItem-Status::after { content: attr(release-status); position: absolute; top: 0; @@ -77,36 +92,35 @@ background-color: inherit; } -.Manga-status:hover::after{ +.MangaItem-Status:hover::after{ visibility:visible; } - -.Manga-status[release-status="Ongoing"]{ +.MangaItem-Status[release-status="Ongoing"]{ background-color: limegreen; } -.Manga-status[release-status="Completed"]{ +.MangaItem-Status[release-status="Completed"]{ background-color: blueviolet; } -.Manga-status[release-status="On Hiatus"]{ +.MangaItem-Status[release-status="On Hiatus"]{ background-color: darkorange; } -.Manga-status[release-status="Cancelled"]{ +.MangaItem-Status[release-status="Cancelled"]{ background-color: firebrick; } -.Manga-status[release-status="Upcoming"]{ +.MangaItem-Status[release-status="Upcoming"]{ background-color: aqua; } -.Manga-status[release-status="Status Unavailable"]{ +.MangaItem-Status[release-status="Status Unavailable"]{ background-color: gray; } -.Manga > img { +.MangaItem-Cover { position: absolute; top: 0; left: 0; @@ -117,27 +131,65 @@ z-index: 0; } -.Manga p { - margin: 2px 0; +.MangaItem p { + margin: 0; } -.Manga > .SimpleCover { - position: relative; - z-index: 1; - width: 100%; - height: 100%; - left: 0; - top: 0; +.MangaItem[is-clicked="not-clicked"] .MangaItem-Description, .MangaItem[is-clicked="not-clicked"] .MangaItem-Tags, .MangaItem[is-clicked="not-clicked"] .MangaItem-Props{ + display: none !important; } -div[extended-info="no"]{ - display: none; -} - -div[extended-info="yes"]{ +.MangaItem[is-clicked="clicked"] .MangaItem-Description, .MangaItem[is-clicked="clicked"] .MangaItem-Tags, .MangaItem[is-clicked="clicked"] .MangaItem-Props{ display: block; - position: absolute; - left: 0; - top: 0; - z-index: 2; + width: 80vw; + background-color: white; + padding: 3px; +} + +.MangaItem-Tags { + display: flex !important; + flex-direction: row; + flex-wrap: wrap; +} + +.MangaItem-Tags > * { + display: flex; + flex-direction: row; + width: max-content; + margin: 2px 1px !important; + white-space: nowrap; + color: white; + padding: 2px; +} + +.MangaItem-Tags > * > * { + margin: 0 2px !important; + align-items: center; +} + +.MangaItem-Tags a, .MangaItem-Tags a:visited { + color: blue; +} + +.MangaItem-Author { + background-color: green; +} + +.MangaItem-Tag { + background-color: blue; +} + +.MangaItem-Link{ + background-color: brown; +} + +.MangaItem-Description { + color: black; + max-height: 40vh; +} + +.MangaItem-Props { + display: flex !important; + flex-direction: row; + justify-content: flex-end; } \ No newline at end of file