mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-04-18 22:33:20 +02:00
Single Style for Manga
This commit is contained in:
parent
d9bbbed1c0
commit
007b49c624
@ -4,7 +4,8 @@ import '../styles/monitorMangaList.css';
|
|||||||
import {JobType} from "./interfaces/Jobs/IJob";
|
import {JobType} from "./interfaces/Jobs/IJob";
|
||||||
import '../styles/MangaCoverCard.css'
|
import '../styles/MangaCoverCard.css'
|
||||||
import DownloadAvailableChaptersJob from "./interfaces/Jobs/DownloadAvailableChaptersJob";
|
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<any>, connectedToBackend: boolean, apiUri: string, updateList: Date}) {
|
export default function MonitorJobsList({onStartSearch, onJobsChanged, connectedToBackend, apiUri, updateList} : {onStartSearch() : void, onJobsChanged: EventHandler<any>, connectedToBackend: boolean, apiUri: string, updateList: Date}) {
|
||||||
const [MonitoringJobs, setMonitoringJobs] = useState<DownloadAvailableChaptersJob[]>([]);
|
const [MonitoringJobs, setMonitoringJobs] = useState<DownloadAvailableChaptersJob[]>([]);
|
||||||
@ -35,10 +36,10 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged, connected
|
|||||||
}
|
}
|
||||||
|
|
||||||
function StartSearchMangaEntry() : ReactElement {
|
function StartSearchMangaEntry() : ReactElement {
|
||||||
return (<div key="monitorMangaEntry.StartSearch" className="startSearchEntry Manga" onClick={onStartSearch}>
|
return (<div key="monitorMangaEntry.StartSearch" className="startSearchEntry MangaItem" onClick={onStartSearch}>
|
||||||
<img src="../media/blahaj.png" alt="Blahaj"></img>
|
<img className="MangaItem-Cover" src="../media/blahaj.png" alt="Blahaj"></img>
|
||||||
<div>
|
<div>
|
||||||
<p style={{textAlign: "center", width: "100%"}} className="Manga-name">Add new Manga</p>
|
<p style={{textAlign: "center", width: "100%"}} className="MangaItem-Name">Add new Manga</p>
|
||||||
<p style={{fontSize: "42pt", textAlign: "center"}}>+</p>
|
<p style={{fontSize: "42pt", textAlign: "center"}}>+</p>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
@ -48,7 +49,12 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged, connected
|
|||||||
<div id="MonitorMangaList">
|
<div id="MonitorMangaList">
|
||||||
<StartSearchMangaEntry />
|
<StartSearchMangaEntry />
|
||||||
{MonitoringJobs.map((MonitoringJob) =>
|
{MonitoringJobs.map((MonitoringJob) =>
|
||||||
<CoverCard apiUri={apiUri} mangaId={MonitoringJob.mangaId} key={MonitoringJob.mangaId} />
|
<MangaItem apiUri={apiUri} mangaId={MonitoringJob.mangaId} key={MonitoringJob.mangaId}>
|
||||||
|
<></>
|
||||||
|
<button className="Manga-DeleteButton" onClick={() => {
|
||||||
|
Manga.DeleteManga(apiUri, MonitoringJob.mangaId);
|
||||||
|
}}>Delete</button>
|
||||||
|
</MangaItem>
|
||||||
)}
|
)}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
@ -4,7 +4,8 @@ import '../styles/queuePopUp.css';
|
|||||||
import '../styles/popup.css';
|
import '../styles/popup.css';
|
||||||
import Job from "./Job";
|
import Job from "./Job";
|
||||||
import DownloadSingleChapterJob from "./interfaces/Jobs/DownloadSingleChapterJob";
|
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}) {
|
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
|
|||||||
</div>
|
</div>
|
||||||
<div id="QueuePopUpBody" className="popupBody">
|
<div id="QueuePopUpBody" className="popupBody">
|
||||||
<div>
|
<div>
|
||||||
{RunningJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => <ItemDownloadSingleChapterJob apiUri={apiUri} job={j as DownloadSingleChapterJob} key={j.jobId} />)}
|
{RunningJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => {
|
||||||
|
let job = j as DownloadSingleChapterJob;
|
||||||
|
return <ChapterItem apiUri={apiUri} chapterId={job.chapterId} />
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{WaitingJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j => <ItemDownloadSingleChapterJob apiUri={apiUri} job={j as DownloadSingleChapterJob} key={j.jobId} />)}
|
{WaitingJobs.filter(j => j.jobType == JobType.DownloadSingleChapterJob).map(j =>{
|
||||||
|
let job = j as DownloadSingleChapterJob;
|
||||||
|
return <ChapterItem apiUri={apiUri} chapterId={job.chapterId} />
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,9 +2,8 @@ import React, {ChangeEventHandler, EventHandler, useEffect, useState} from 'reac
|
|||||||
import {MangaConnector} from "./MangaConnector";
|
import {MangaConnector} from "./MangaConnector";
|
||||||
import IMangaConnector from "./interfaces/IMangaConnector";
|
import IMangaConnector from "./interfaces/IMangaConnector";
|
||||||
import {isValidUri} from "../App";
|
import {isValidUri} from "../App";
|
||||||
import IManga, {ExtendedInfo} from "./interfaces/IManga";
|
import IManga, {MangaItem} from "./interfaces/IManga";
|
||||||
import '../styles/search.css';
|
import '../styles/search.css';
|
||||||
import '../styles/ExtendedInfo.css'
|
|
||||||
import SearchFunctions from "./SearchFunctions";
|
import SearchFunctions from "./SearchFunctions";
|
||||||
import Job from "./Job";
|
import Job from "./Job";
|
||||||
import ILocalLibrary from "./interfaces/ILocalLibrary";
|
import ILocalLibrary from "./interfaces/ILocalLibrary";
|
||||||
@ -132,15 +131,15 @@ export default function Search({apiUri, jobInterval, onJobsChanged, closeSearch}
|
|||||||
{searchResults === undefined
|
{searchResults === undefined
|
||||||
? <p></p>
|
? <p></p>
|
||||||
: searchResults.map(result =>
|
: searchResults.map(result =>
|
||||||
<ExtendedInfo key={"Searchresult-"+result.mangaId} apiUri={apiUri} manga={result} actions={[
|
<MangaItem apiUri={apiUri} mangaId={result.mangaId}>
|
||||||
<select defaultValue={selectedLibrary === null ? "" : selectedLibrary.localLibraryId} onChange={selectedLibraryChanged}>
|
<select defaultValue={selectedLibrary === null ? "" : selectedLibrary.localLibraryId} onChange={selectedLibraryChanged}>
|
||||||
{selectedLibrary === null || libraries === null ? <option value="">Loading</option>
|
{selectedLibrary === null || libraries === null ? <option value="">Loading</option>
|
||||||
: libraries.map(library => <option key={library.localLibraryId} value={library.localLibraryId}>{library.libraryName} ({library.basePath})</option>)}
|
: libraries.map(library => <option key={library.localLibraryId} value={library.localLibraryId}>{library.libraryName} ({library.basePath})</option>)}
|
||||||
</select>,
|
</select>
|
||||||
<button className="Manga-AddButton" onClick={() => {
|
<button className="Manga-AddButton" onClick={() => {
|
||||||
Job.CreateDownloadAvailableChaptersJob(apiUri, result.mangaId, {recurrenceTimeMs: jobInterval.getTime(), localLibraryId: selectedLibrary!.localLibraryId}).then(() => onJobsChanged(result.mangaId));
|
Job.CreateDownloadAvailableChaptersJob(apiUri, result.mangaId, {recurrenceTimeMs: jobInterval.getTime(), localLibraryId: selectedLibrary!.localLibraryId}).then(() => onJobsChanged(result.mangaId));
|
||||||
}}>Monitor</button>
|
}}>Monitor</button>
|
||||||
]}/>
|
</MangaItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import IManga from "./interfaces/IManga";
|
import IManga from "./interfaces/IManga";
|
||||||
import {getData, postData} from "../App";
|
import {postData} from "../App";
|
||||||
|
|
||||||
export default class SearchFunctions {
|
export default class SearchFunctions {
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ export default interface IAuthor {
|
|||||||
authorName: string;
|
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 (<p className="Manga-Author-Name">Author</p>);
|
||||||
|
|
||||||
let [name, setName] = React.useState<string>(authorId);
|
let [name, setName] = React.useState<string>(authorId);
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
@ -17,5 +20,5 @@ export function AuthorElement({apiUri, authorId} : {apiUri: string, authorId: st
|
|||||||
});
|
});
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (<span>{name}</span>);
|
return (<p className="Manga-Author-Name">{name}</p>);
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
48
Website/modules/interfaces/IChapter.tsx
Normal file
48
Website/modules/interfaces/IChapter.tsx
Normal file
@ -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<HTMLImageElement> = (e) => {
|
||||||
|
setMangaCoverHtmlImageItem(e.currentTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
let [chapter, setChapter] = useState<IChapter | null>(null);
|
||||||
|
let [manga, setManga] = useState<IManga | null>(null);
|
||||||
|
let [mangaCoverUrl, setMangaCoverUrl] = useState<string>("../../media/blahaj.png");
|
||||||
|
let [mangaCoverHtmlImageItem, setMangaCoverHtmlImageItem] = useState<HTMLImageElement | null>(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 (<div className="ChapterItem" key={chapterId}>
|
||||||
|
<img className="ChapterItem-Cover" src={mangaCoverUrl} alt="Manga Cover" onLoad={setCoverItem} onResize={setCoverItem}></img>
|
||||||
|
<p className="ChapterItem-MangaName">{manga ? manga.name : "Manga-Name"}</p>
|
||||||
|
<p className="ChapterItem-ChapterName">{chapter ? chapter.title : "Chapter-Title"}</p>
|
||||||
|
<p className="ChapterItem-Volume">Vol.{chapter ? chapter.volumeNumber : "VolNum"}</p>
|
||||||
|
<p className="ChapterItem-Chapter">Ch.{chapter ? chapter.chapterNumber : "ChNum"}</p>
|
||||||
|
<a className="ChapterItem-Website" href={chapter ? chapter.url : "#"}><img src="../../media/link.svg" alt="Link"/></a>
|
||||||
|
</div>)
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import React, {ReactElement, useEffect} from "react";
|
import React, {ReactElement, useEffect} from "react";
|
||||||
import {getData} from "../../App";
|
import {getData} from "../../App";
|
||||||
import IAuthor from "./IAuthor";
|
|
||||||
|
|
||||||
export default interface ILink {
|
export default interface ILink {
|
||||||
linkId: string;
|
linkId: string;
|
||||||
@ -8,7 +7,10 @@ export default interface ILink {
|
|||||||
linkUrl: string;
|
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 (<a className="Manga-Link-Value" href="#">Link</a>);
|
||||||
|
|
||||||
let [provider, setProvider] = React.useState<string>(linkId);
|
let [provider, setProvider] = React.useState<string>(linkId);
|
||||||
let [linkUrl, setLinkUrl] = React.useState<string>("");
|
let [linkUrl, setLinkUrl] = React.useState<string>("");
|
||||||
|
|
||||||
@ -21,5 +23,5 @@ export function LinkElement({apiUri, linkId} : {apiUri: string, linkId: string})
|
|||||||
});
|
});
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (<a href={linkUrl}>{provider}</a>);
|
return (<a className="Manga-Link-Value" href={linkUrl}>{provider}</a>);
|
||||||
}
|
}
|
@ -1,17 +1,14 @@
|
|||||||
import Manga from "../Manga";
|
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 Icon from '@mdi/react';
|
||||||
import { mdiTagTextOutline, mdiAccountEdit, mdiLinkVariant } from '@mdi/js';
|
import { mdiTagTextOutline, mdiAccountEdit, mdiLinkVariant } from '@mdi/js';
|
||||||
import MarkdownPreview from '@uiw/react-markdown-preview';
|
import MarkdownPreview from '@uiw/react-markdown-preview';
|
||||||
import {AuthorElement} from "./IAuthor";
|
import {AuthorElement} from "./IAuthor";
|
||||||
import {LinkElement} from "./ILink";
|
import {LinkElement} from "./ILink";
|
||||||
import DownloadSingleChapterJob from "./Jobs/DownloadSingleChapterJob";
|
|
||||||
import IChapter from "./IChapter";
|
|
||||||
import Chapter from "../Chapter";
|
|
||||||
|
|
||||||
export default interface IManga{
|
export default interface IManga{
|
||||||
mangaId: string;
|
mangaId: string;
|
||||||
connectorId: string;
|
idOnConnectorSite: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
websiteUrl: string;
|
websiteUrl: string;
|
||||||
@ -35,128 +32,65 @@ export enum MangaReleaseStatus {
|
|||||||
Unreleased = "Unreleased",
|
Unreleased = "Unreleased",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultManga: IManga = {
|
export function MangaItem({apiUri, mangaId, children} : {apiUri: string, mangaId: string, children?: (string | ReactElement)[]}) : ReactElement {
|
||||||
altTitleIds: [],
|
const LoadMangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
|
||||||
authorIds: [],
|
if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget))
|
||||||
connectorId: "",
|
e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, mangaId, e.currentTarget);
|
||||||
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<IManga>(defaultManga);
|
|
||||||
let [extendedInfo, setExtendedInfo] = React.useState(false);
|
|
||||||
|
|
||||||
|
let [manga, setManga] = useState<IManga | null>(null);
|
||||||
|
let [clicked, setClicked] = useState<boolean>(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Manga.GetMangaById(apiUri, mangaId).then(setContent);
|
Manga.GetMangaById(apiUri, mangaId).then(setManga);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const MangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
|
return (<div className="MangaItem" key={mangaId} is-clicked={clicked ? "clicked" : "not-clicked"} onClick={()=>setClicked(!clicked)}>
|
||||||
if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget))
|
<img className="MangaItem-Cover" src={Manga.GetMangaCoverImageUrl(apiUri, mangaId, undefined)} alt="Manga Cover" onLoad={LoadMangaCover} onResize={LoadMangaCover}></img>
|
||||||
e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget);
|
<p className="MangaItem-Connector">{manga ? manga.mangaConnectorId : "Connector"}</p>
|
||||||
}
|
<p className="MangaItem-Status" release-status={manga?.releaseStatus}></p>
|
||||||
|
<p className="MangaItem-Name">{manga ? manga.name : "Name"}</p>
|
||||||
return (
|
<a className="MangaItem-Website" href={manga ? manga.websiteUrl : "#"}><img src="../../media/link.svg" alt="Link"/></a>
|
||||||
<div className="Manga" key={manga.mangaId} onClick={(e) => {
|
<div className="MangaItem-Tags">
|
||||||
setExtendedInfo(!extendedInfo);
|
{manga ? manga.authorIds.map(authorId =>
|
||||||
}}>
|
<p className="MangaItem-Author" key={authorId} >
|
||||||
<img src={Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, undefined)} alt="Manga Cover" onLoad={MangaCover} onResize={MangaCover}></img>
|
<Icon path={mdiAccountEdit} size={0.5} />
|
||||||
<div className="SimpleCover">
|
<AuthorElement apiUri={apiUri} authorId={authorId}></AuthorElement>
|
||||||
<p className="pill connector-name">{manga.mangaConnectorId}</p>
|
</p>)
|
||||||
<div className="Manga-status" release-status={manga.releaseStatus}></div>
|
:
|
||||||
<p className="Manga-name">{manga.name}</p>
|
<p className="MangaItem-Author">
|
||||||
</div>
|
<Icon path={mdiAccountEdit} size={0.5} />
|
||||||
{extendedInfo ? <div extended-info={extendedInfo ? "yes" : "no"}>
|
<AuthorElement apiUri={apiUri} authorId={null}></AuthorElement>
|
||||||
<ExtendedInfo apiUri={apiUri} manga={manga} actions={[
|
</p>}
|
||||||
<button className="Manga-DeleteButton" onClick={() => {
|
{manga ? manga.tags.map(tag =>
|
||||||
Manga.DeleteManga(apiUri, manga.mangaId);
|
<p className="MangaItem-Tag" key={tag}>
|
||||||
}}>Delete</button>
|
<Icon path={mdiTagTextOutline} size={0.5}/>
|
||||||
]} />
|
<p className="MangaItem-Tag-Value">{tag}</p>
|
||||||
</div> : null}
|
</p>)
|
||||||
</div>);
|
:
|
||||||
}
|
<p className="MangaItem-Tag">
|
||||||
|
<Icon path={mdiTagTextOutline} size={0.5}/>
|
||||||
export function ExtendedInfo({apiUri, manga, actions} : {apiUri: string, manga: IManga, actions: ReactElement[]}) : ReactElement {
|
<p className="MangaItem-Tag-Value">Tag</p>
|
||||||
const MangaCover : ReactEventHandler<HTMLImageElement> = (e) => {
|
</p>
|
||||||
if(e.currentTarget.src != Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget))
|
}
|
||||||
e.currentTarget.src = Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, e.currentTarget);
|
{manga ? manga.linkIds.map(linkId =>
|
||||||
}
|
<p className="MangaItem-Link" key={linkId}>
|
||||||
|
<Icon path={mdiLinkVariant} size={0.5}/>
|
||||||
return(
|
<LinkElement apiUri={apiUri} linkId={linkId} />
|
||||||
<div className="SearchResult" key={manga.mangaId}>
|
</p>)
|
||||||
<img src={Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, undefined)} alt="Manga Cover" onLoad={MangaCover} onResize={MangaCover}></img>
|
:
|
||||||
<p className="connector-name">{manga.mangaConnectorId}</p>
|
<p className="MangaItem-Link">
|
||||||
<div className="Manga-status" release-status={manga.releaseStatus}></div>
|
<Icon path={mdiLinkVariant} size={0.5}/>
|
||||||
<p className="Manga-name"><a href={manga.websiteUrl}>{manga.name}<img src="../../media/link.svg"
|
<LinkElement apiUri={apiUri} linkId={null} />
|
||||||
alt=""/></a></p>
|
</p>}
|
||||||
<div className="Manga-tags">
|
|
||||||
{manga.authorIds.map(authorId =>
|
|
||||||
<p className="Manga-author" key={manga.mangaId + "-author-" + authorId} >
|
|
||||||
<Icon path={mdiAccountEdit} size={0.5} />
|
|
||||||
<AuthorElement apiUri={apiUri} authorId={authorId}></AuthorElement>
|
|
||||||
</p>)}
|
|
||||||
{manga.tags.map(tag =>
|
|
||||||
<p className="Manga-tag" key={manga.mangaId + "-tag-" + tag}>
|
|
||||||
<Icon path={mdiTagTextOutline} size={0.5}/>
|
|
||||||
{tag}
|
|
||||||
</p>)}
|
|
||||||
{manga.linkIds.map(linkId =>
|
|
||||||
<p className="Manga-link" key={manga.mangaId + "-link-" + linkId}>
|
|
||||||
<Icon path={mdiLinkVariant} size={0.5}/>
|
|
||||||
<LinkElement apiUri={apiUri} linkId={linkId}></LinkElement>
|
|
||||||
</p>)}
|
|
||||||
</div>
|
|
||||||
<MarkdownPreview className="Manga-description" source={manga.description}
|
|
||||||
style={{backgroundColor: "transparent", color: "black"}}/>
|
|
||||||
<div className="Manga-actions">
|
|
||||||
{actions.map((p, i) => <div key={i}>{p}</div>)}
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ItemDownloadSingleChapterJob({apiUri, job} : {apiUri: string, job: DownloadSingleChapterJob}){
|
|
||||||
const MangaCover : ReactEventHandler<HTMLImageElement> = (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<IChapter|null>(null);
|
|
||||||
let [manga, setManga] = React.useState<IManga|null>(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 (
|
|
||||||
<div className="DownloadSingleChapterJob" key={"DownloadSingleChapterJob-" + job.jobId}>
|
|
||||||
<img src={manga ? Manga.GetMangaCoverImageUrl(apiUri, manga.mangaId, undefined) : ""} alt="Manga Cover" onLoad={MangaCover} onResize={MangaCover}></img>
|
|
||||||
<p className="DownloadSingleChapterJob-Name">{manga ? manga.name : job.chapterId}</p>
|
|
||||||
<p className="DownloadSingleChapterJob-Title">
|
|
||||||
{chapter ? "Vol." + chapter.volumeNumber + " Ch." + chapter.chapterNumber + ": " + chapter.title : "loading"}
|
|
||||||
<a href={chapter ? chapter.url : ""}>
|
|
||||||
<img src="../../media/link.svg" alt=""/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
<MarkdownPreview className="MangaItem-Description" source={manga ? manga.description : "# Description"} />
|
||||||
|
<div className="MangaItem-Props">
|
||||||
|
{children ? children.map(c => {
|
||||||
|
if(c instanceof Element)
|
||||||
|
return c as ReactElement;
|
||||||
|
else
|
||||||
|
return <span>{c}</span>
|
||||||
|
}) : null}
|
||||||
|
</div>
|
||||||
|
</div>)
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
.pill {
|
.MangaItem-Connector {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
height: 14pt;
|
height: 14pt;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
@ -10,7 +10,7 @@
|
|||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga{
|
.MangaItem{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: var(--secondary-color);
|
background-color: var(--secondary-color);
|
||||||
width: 180px;
|
width: 180px;
|
||||||
@ -22,7 +22,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga::after{
|
.MangaItem::after{
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0; top: 0;
|
left: 0; top: 0;
|
||||||
@ -32,31 +32,46 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.MangaItem > * {
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.startSearchEntry::after{
|
.startSearchEntry::after{
|
||||||
background: initial !important;
|
background: initial !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-name{
|
.MangaItem-Name{
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
font-size: 16pt;
|
font-size: 16pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
color: white;
|
||||||
|
margin: 5px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status {
|
.MangaItem-Website {
|
||||||
display:block;
|
display: block;
|
||||||
height: 10px;
|
height: 13px;
|
||||||
width: 10px;
|
width: 13px;
|
||||||
border-radius: 50%;
|
|
||||||
margin: 5px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 12px;
|
||||||
right: 5px;
|
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;
|
z-index: 2;
|
||||||
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px;
|
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);
|
content: attr(release-status);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -77,36 +92,35 @@
|
|||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status:hover::after{
|
.MangaItem-Status:hover::after{
|
||||||
visibility:visible;
|
visibility:visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.MangaItem-Status[release-status="Ongoing"]{
|
||||||
.Manga-status[release-status="Ongoing"]{
|
|
||||||
background-color: limegreen;
|
background-color: limegreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status[release-status="Completed"]{
|
.MangaItem-Status[release-status="Completed"]{
|
||||||
background-color: blueviolet;
|
background-color: blueviolet;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status[release-status="On Hiatus"]{
|
.MangaItem-Status[release-status="On Hiatus"]{
|
||||||
background-color: darkorange;
|
background-color: darkorange;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status[release-status="Cancelled"]{
|
.MangaItem-Status[release-status="Cancelled"]{
|
||||||
background-color: firebrick;
|
background-color: firebrick;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status[release-status="Upcoming"]{
|
.MangaItem-Status[release-status="Upcoming"]{
|
||||||
background-color: aqua;
|
background-color: aqua;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga-status[release-status="Status Unavailable"]{
|
.MangaItem-Status[release-status="Status Unavailable"]{
|
||||||
background-color: gray;
|
background-color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga > img {
|
.MangaItem-Cover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -117,27 +131,65 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga p {
|
.MangaItem p {
|
||||||
margin: 2px 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Manga > .SimpleCover {
|
.MangaItem[is-clicked="not-clicked"] .MangaItem-Description, .MangaItem[is-clicked="not-clicked"] .MangaItem-Tags, .MangaItem[is-clicked="not-clicked"] .MangaItem-Props{
|
||||||
position: relative;
|
display: none !important;
|
||||||
z-index: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div[extended-info="no"]{
|
.MangaItem[is-clicked="clicked"] .MangaItem-Description, .MangaItem[is-clicked="clicked"] .MangaItem-Tags, .MangaItem[is-clicked="clicked"] .MangaItem-Props{
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div[extended-info="yes"]{
|
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
width: 80vw;
|
||||||
left: 0;
|
background-color: white;
|
||||||
top: 0;
|
padding: 3px;
|
||||||
z-index: 2;
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user