mirror of
https://github.com/danbulant/Mangades
synced 2026-06-19 06:11:38 +00:00
refactor list show
This commit is contained in:
parent
7efc821074
commit
fffd98425a
7 changed files with 187 additions and 179 deletions
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
import { goto } from "$app/navigation";
|
||||
import request from "../util/request";
|
||||
import Item from "./item.svelte";
|
||||
import { showType } from "./showTypeChooser.svelte";
|
||||
|
||||
export var entries;
|
||||
export var itemsList;
|
||||
|
||||
var isLoading = false;
|
||||
async function find(entry) {
|
||||
|
|
@ -11,7 +12,7 @@
|
|||
var query = new URLSearchParams();
|
||||
query.set("title", entry.media.title.romaji);
|
||||
query.set("limit", 20);
|
||||
console.log(entry);
|
||||
console.log("anilist entry", entry);
|
||||
let result;
|
||||
try {
|
||||
result = await request("manga", query);
|
||||
|
|
@ -21,12 +22,12 @@
|
|||
alert("Failed to search mangadex.");
|
||||
return;
|
||||
}
|
||||
console.log(result.data);
|
||||
console.log("anilist mangadex data", result.data);
|
||||
let item = result.data.find(t => t.attributes.links.al === entry.media.id.toString());
|
||||
if(!item) item = result.data.find(t => t.attributes.title.en?.toLowerCase() === entry.media.title.english.toLowerCase());
|
||||
if(!item) item = result.data.find(t => t.attributes.title.ja?.toLowerCase() === entry.media.title.native.toLowerCase());
|
||||
if(!item) item = result.data.find(t => t.attributes.altTitles.find(t => Object.values(t).find(t => t.toLowerCase() === entry.media.title.native.toLowerCase())));
|
||||
console.log(item);
|
||||
console.log("anilist mangadex item", item);
|
||||
if(!item) {
|
||||
alert(`Couldn't find any mangadex entry.`);
|
||||
isLoading = false;
|
||||
|
|
@ -42,21 +43,18 @@
|
|||
</dialog>
|
||||
{/if}
|
||||
|
||||
<div class="items" class:items-list={itemsList}>
|
||||
<div class="items" class:list={$showType == "list"}>
|
||||
{#each entries.sort((a, b) => a.priority - b.priority) as entry}
|
||||
<div class="item" class:r18={entry.media.isAdult} on:click={() => find(entry)}>
|
||||
<div class="flex">
|
||||
<img src={entry.media.coverImage.large} width=100 height=142 alt="{entry.media.title.userPreferred}" title="{entry.media.title.userPreferred}">
|
||||
<div class="info">
|
||||
<h3>{entry.media.title.userPreferred}</h3>
|
||||
<span>[{entry.progress}/{entry.media.chapters || "?"}]</span> <br>
|
||||
<span>{entry.score || "?"}/10</span>
|
||||
{#if entry.notes}
|
||||
<p>{entry.notes}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Item
|
||||
r18={entry.media.isAdult}
|
||||
cover={entry.media.coverImage.large}
|
||||
title={entry.media.title.userPreferred}
|
||||
lastChapter={entry.media.chapters}
|
||||
chapterProgress={entry.progress}
|
||||
score={entry.score || "?"}
|
||||
description={entry.notes}
|
||||
coverColor={entry.media.coverImage.color == "null" ? null : entry.media.coverImage.color}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
|
@ -68,9 +66,8 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 100;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
.items {
|
||||
display: grid;
|
||||
|
|
@ -78,41 +75,7 @@
|
|||
gap: 1rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||
}
|
||||
.items-list.items {
|
||||
.items.list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.items-list .item img {
|
||||
height: 10rem;
|
||||
}
|
||||
.r18 img {
|
||||
filter: blur(10px);
|
||||
}
|
||||
.info {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: .2s opacity;
|
||||
}
|
||||
.items-list .info {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
.item img {
|
||||
border-radius: 5px;
|
||||
height: 15rem;
|
||||
width: auto;
|
||||
box-shadow: 0 0 0 white;
|
||||
transition: .4s box-shadow, .3s height;
|
||||
}
|
||||
@media(prefers-reduced-motion) {
|
||||
.item img, .item:hover img {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.item:hover img {
|
||||
box-shadow: 0 0 10px grey;
|
||||
}
|
||||
</style>
|
||||
84
src/lib/components/item.svelte
Normal file
84
src/lib/components/item.svelte
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts">
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import { showType } from "./showTypeChooser.svelte";
|
||||
|
||||
export var r18: boolean = false;
|
||||
export var cover: string | null = null;
|
||||
export var title: string;
|
||||
export var lastChapter: string | null = null;
|
||||
export var chapterProgress: string | null = null;
|
||||
export var lastVolume: string | null = null;
|
||||
export var volumeProgress: string | null = null;
|
||||
export var description: string | null = null;
|
||||
export var score: string | null = null;
|
||||
export var coverWidth: number | null = null;
|
||||
export var coverHeight: number | null = null;
|
||||
|
||||
export var coverColor: string | null = null;
|
||||
</script>
|
||||
|
||||
<div class="item" class:grid={$showType == "grid"} class:comfortable={$showType == "comfortable"} class:coverOnly={$showType == "cover-only"} class:list={$showType == "list"}>
|
||||
<div class="flex" class:r18 on:click>
|
||||
{#if cover}
|
||||
<img class="cover" style="{coverColor ? "--box-shadow-color: " + coverColor : ""}" draggable="false" src="{cover}" alt="{title}" {title} width={coverWidth} height={coverHeight}>
|
||||
{:else}
|
||||
Broken art
|
||||
{/if}
|
||||
<div class="info">
|
||||
<h3>{title}</h3>
|
||||
{#if lastChapter || chapterProgress}
|
||||
<span>CH {chapterProgress ? chapterProgress + "/" : ""}{lastChapter || "?"}</span>
|
||||
{/if}
|
||||
{#if lastVolume || volumeProgress}
|
||||
<span>vol {volumeProgress ? volumeProgress + "/" : ""}{lastVolume}</span>
|
||||
{/if}
|
||||
{#if score}
|
||||
<span>{score}/10</span>
|
||||
{/if}
|
||||
{#if description}
|
||||
<p><SvelteMarkdown source={description} isInline /></p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.list .item img {
|
||||
height: 10rem;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.r18 img {
|
||||
filter: blur(10px);
|
||||
}
|
||||
.info {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: .2s opacity;
|
||||
}
|
||||
.list .info {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
.item img {
|
||||
--box-shadow-color: white;
|
||||
border-radius: 5px;
|
||||
height: 15rem;
|
||||
width: auto;
|
||||
box-shadow: 0 0 0 var(--box-shadow-color);
|
||||
transition: .4s box-shadow, .3s height;
|
||||
}
|
||||
@media(prefers-reduced-motion) {
|
||||
.item img, .item:hover img {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.item:hover img {
|
||||
box-shadow: 0 0 20px var(--box-shadow-color);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<script>
|
||||
export var list = false;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button on:click={() => list = !list} class:active={list == false}>Grid</button><!--
|
||||
--><button on:click={() => list = !list} class:active={list == true}>List</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
}
|
||||
button {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
}
|
||||
button:first-child {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
button:last-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
button.active {
|
||||
background: rgb(202, 202, 202);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,39 +1,23 @@
|
|||
<script>
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import { imageproxy } from "$lib/util/request";
|
||||
import Item from "./item.svelte";
|
||||
import { showType } from "./showTypeChooser.svelte";
|
||||
|
||||
export var entries;
|
||||
export var itemsList;
|
||||
|
||||
$: console.log(entries);
|
||||
|
||||
function open(entry) {}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="items" class:items-list={itemsList}>
|
||||
<div class="items" class:list={$showType == "list"}>
|
||||
{#each entries as entry}
|
||||
{@const title = entry.attributes.title.en || entry.attributes.title.ja || Object.values(entry.attributes.title)[0]}
|
||||
<a href="/{entry.id}" class="item" class:r18={!["safe", "suggestive"].includes(entry.attributes.contentRating)} on:click={() => open(entry)}>
|
||||
<div class="flex">
|
||||
{#if entry.relationships.find(t => t.type === "cover_art")}
|
||||
<img class="cover" draggable="false" src="{imageproxy}https://uploads.mangadex.org/covers/{entry.id}/{entry.relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg" alt="{title}" {title}>
|
||||
{:else}
|
||||
Broken art
|
||||
{/if}
|
||||
<div class="info">
|
||||
<h3>{title}</h3>
|
||||
{#if entry.attributes.lastChapter}
|
||||
<span>CH {entry.attributes.lastChapter}</span>
|
||||
{/if}
|
||||
{#if entry.attributes.lastVolume}
|
||||
<span>vol {entry.attributes.lastVolume}</span>
|
||||
{/if}
|
||||
{#if entry.attributes.description.en}
|
||||
<p><SvelteMarkdown source={entry.attributes.description.en} isInline /></p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<a href="/{entry.id}">
|
||||
<Item
|
||||
r18={!["safe", "suggestive"].includes(entry.attributes.contentRating)}
|
||||
cover={imageproxy + entry.relationships.find(t => t.type === "cover_art") ? `https://uploads.mangadex.org/covers/${entry.id}/${entry.relationships.find(t => t.type === "cover_art").attributes.fileName}.512.jpg` : null}
|
||||
title={entry.attributes.title.en || entry.attributes.title.ja || Object.values(entry.attributes.title)[0]}
|
||||
lastChapter={entry.attributes.lastChapter}
|
||||
lastVolume={entry.attributes.lastVolume}
|
||||
description={entry.attributes.description.en}
|
||||
/>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -46,9 +30,6 @@
|
|||
a:hover {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
.items {
|
||||
display: grid;
|
||||
|
|
@ -56,41 +37,7 @@
|
|||
gap: 1rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||
}
|
||||
.items-list.items {
|
||||
.items.list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.items-list .item img {
|
||||
height: 10rem;
|
||||
}
|
||||
.r18 img {
|
||||
filter: blur(10px);
|
||||
}
|
||||
.info {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: .2s opacity;
|
||||
}
|
||||
.items-list .info {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
.item img {
|
||||
border-radius: 5px;
|
||||
height: 15rem;
|
||||
width: auto;
|
||||
box-shadow: 0 0 0 white;
|
||||
transition: .4s box-shadow, .3s height;
|
||||
}
|
||||
@media(prefers-reduced-motion) {
|
||||
.item img, .item:hover img {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.item:hover img {
|
||||
box-shadow: 0 0 10px grey;
|
||||
}
|
||||
</style>
|
||||
34
src/lib/components/showTypeChooser.svelte
Normal file
34
src/lib/components/showTypeChooser.svelte
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script context="module" lang="ts">
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
export var showType: Writable<"grid" | "comfortable" | "list" | "cover-only"> = writable("grid");
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button on:click={() => $showType = "grid"} class:active={$showType == "grid"}>Grid</button><!--
|
||||
--><button on:click={() => $showType = "list"} class:active={$showType == "list"}>List</button><!--
|
||||
<!-- -><button on:click={() => $showType = "comfortable"} class:active={$showType == "comfortable"}>Comfortable grid</button><!--
|
||||
-><button on:click={() => $showType = "cover-only"} class:active={$showType == "cover-only"}>Cover only</button> -->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
margin-left: auto;
|
||||
width: max-content;
|
||||
}
|
||||
button {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
}
|
||||
button:first-child {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
button:last-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
button.active {
|
||||
background: rgb(202, 202, 202);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import request from "$lib/util/request";
|
||||
import { getUserDetails, getUserManga, isLogedIn } from "$lib/util/anilist";
|
||||
import AnilistItems from "$lib/components/anilistItems.svelte";
|
||||
import ListOrGrid from "$lib/components/listOrGrid.svelte";
|
||||
import ShowTypeChooser from "$lib/components/showTypeChooser.svelte";
|
||||
import ratelimit from '$lib/util/ratelimit';
|
||||
import MangadexItems from '$lib/components/mangadexItems.svelte';
|
||||
import { goto } from "$app/navigation";
|
||||
|
|
@ -113,7 +113,6 @@
|
|||
|
||||
let userDetails = isLogedIn() && getUserDetails();
|
||||
let userManga = isLogedIn() && getUserManga();
|
||||
let listStyle = false;
|
||||
</script>
|
||||
|
||||
<svelte:window on:scroll={scroll} />
|
||||
|
|
@ -156,7 +155,7 @@
|
|||
|
||||
<div class="flex">
|
||||
<a href="https://discord.gg/XKPbz5xRuK">Made by TechmandanCZ#3372</a>
|
||||
<ListOrGrid bind:list={listStyle} />
|
||||
<ShowTypeChooser />
|
||||
</div>
|
||||
|
||||
{#if result}
|
||||
|
|
@ -164,14 +163,14 @@
|
|||
{#await result}
|
||||
Loading...
|
||||
{:then result}
|
||||
<MangadexItems entries={result.data} itemsList={listStyle} />
|
||||
<MangadexItems entries={result.data} />
|
||||
{/await}
|
||||
{:else}
|
||||
<div>
|
||||
{#await userManga then userManga}
|
||||
{#each userManga.data.MediaListCollection.lists as list}
|
||||
<h2>{list.name}</h2>
|
||||
<AnilistItems entries={list.entries} itemsList={listStyle} />
|
||||
<AnilistItems entries={list.entries} />
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
|
|
@ -181,28 +180,6 @@
|
|||
Sign in via Anilist to view your manga list and search for manga online. You can still read manga or download it without signing in using direct mangadex URLs.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<hr>
|
||||
|
||||
<b>Shameless plug:</b>
|
||||
|
||||
<p>
|
||||
Be sure to check out my <a href="https://danbulant.eu">main site</a>, and my game <a href="https://heaventaker.danbulant.eu/">Heaventaker</a>.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
Website's source code available on <b><a href="https://github.com/danbulant/mangades">GitHub</a></b> under GPLv3. Hosted on Cloudflare Pages, a static site hosting, where none of the images are stored.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>This site works by using Mangadex API <i>from your browser</i>, and loading or downloading the manga directly from MD@H, without using my servers (so I don't host any content seen here, nor can I delete it). The whole site is client side only (it runs in your browser). I cannot delete any content (images or text), only hide it from this specific site - but there are tons of other sites, and anybody with decent coding skills can clone this page and remove the rule hiding the content (this page is open source).</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>DISCLAIMER: This site isn't distributing any content and is using mangadex.org API. All of the site's requests are done client side, my servers aren't sharing any manga data. Website is open source, and I don't claim any copyright on the publications. <i>If you believe in good faith you're downloading copyrighted content, file a DMCA at yourself, your operating system (as it took a part in the download), your ISP, your browser and all the free libraries that are used in any of the previous (made by volunteers), and then here. /s</i></p>
|
||||
</main>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
@ -251,4 +228,8 @@
|
|||
a:hover:not(.button) {
|
||||
color: rgb(0,100,200);
|
||||
}
|
||||
|
||||
main {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
29
src/routes/about/+page.svelte
Normal file
29
src/routes/about/+page.svelte
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
<main>
|
||||
<h1>Manga reader</h1>
|
||||
<a href="https://discord.gg/XKPbz5xRuK">Made by TechmandanCZ#3372</a>
|
||||
|
||||
<hr>
|
||||
|
||||
<b>Shameless plug:</b>
|
||||
|
||||
<p>
|
||||
Be sure to check out my <a href="https://danbulant.eu">main site</a>, and my game <a href="https://heaventaker.danbulant.eu/">Heaventaker</a>.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
Website's source code available on <b><a href="https://github.com/danbulant/mangades">GitHub</a></b> under GPLv3. Hosted on Cloudflare Pages, a static site hosting, where none of the images are stored.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>This site works by using Mangadex API <i>from your browser</i>, and loading or downloading the manga directly from MD@H, without using my servers (so I don't host any content seen here, nor can I delete it). The whole site is client side only (it runs in your browser). I cannot delete any content (images or text), only hide it from this specific site - but there are tons of other sites, and anybody with decent coding skills can clone this page and remove the rule hiding the content (this page is open source).</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>DISCLAIMER: This site isn't distributing any content and is using mangadex.org API. All of the site's requests are done client side, my servers aren't sharing any manga data. Website is open source, and I don't claim any copyright on the publications. <i>If you believe in good faith you're downloading copyrighted content, file a DMCA at yourself, your operating system (as it took a part in the download), your ISP, your browser and all the free libraries that are used in any of the previous (made by volunteers), and then here. /satire</i></p>
|
||||
|
||||
<p>For DMCA requests, I recommend going to the mangadex page of the selected manga instead and reporting it there, as it will be taken down more quickly. You can of course report it to me and I'll block the page from viewing it, but it's trivial to remove that (1 required software installed to run this site, edit single line, run 2 more commands and you have bypassed the block), so I really recommend removing the source instead.</p>
|
||||
</main>
|
||||
Loading…
Reference in a new issue