diff --git a/assets/__app.html b/assets/__app.html index 59b0183..3a7f99c 100644 --- a/assets/__app.html +++ b/assets/__app.html @@ -12,6 +12,7 @@ + diff --git a/assets/global.css b/assets/global.css index c693f30..6a079d6 100644 --- a/assets/global.css +++ b/assets/global.css @@ -7,7 +7,7 @@ html, body { body { color: #333; margin: 0; - padding: 8px; + padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } diff --git a/assets/swiper.min.css b/assets/swiper.min.css new file mode 100644 index 0000000..2547a11 --- /dev/null +++ b/assets/swiper.min.css @@ -0,0 +1,13 @@ +/** + * Swiper 8.1.5 + * Most modern mobile touch slider and framework with hardware accelerated transitions + * https://swiperjs.com + * + * Copyright 2014-2022 Vladimir Kharlampidi + * + * Released under the MIT License + * + * Released on: May 16, 2022 + */ + + @font-face{font-family:swiper-icons;src:url('data:application/font-woff;charset=utf-8;base64, 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');font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;box-sizing:content-box}.swiper-android .swiper-slide,.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-pointer-events{touch-action:pan-y}.swiper-pointer-events.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d,.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide,.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top,.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-horizontal.swiper-css-mode>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-vertical.swiper-css-mode>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center} \ No newline at end of file diff --git a/package.json b/package.json index 65f0194..36e79e3 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ }, "dependencies": { "fflate": "^0.6.10", - "streamsaver": "^2.0.5" + "streamsaver": "^2.0.5", + "swiper": "^8.1.5" } } diff --git a/rollup.config.js b/rollup.config.js index fb4c67b..c4804a3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,16 +1,15 @@ import svelte from 'rollup-plugin-svelte-hot'; -import Hmr from 'rollup-plugin-hot' +import Hmr from 'rollup-plugin-hot'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import livereload from 'rollup-plugin-livereload'; import { terser } from 'rollup-plugin-terser'; -import { copySync, removeSync } from 'fs-extra' -import { spassr } from 'spassr' -import getConfig from '@roxi/routify/lib/utils/config' -import autoPreprocess from 'svelte-preprocess' -import postcssImport from 'postcss-import' -import { injectManifest } from 'rollup-plugin-workbox' - +import { copySync, removeSync } from 'fs-extra'; +import { spassr } from 'spassr'; +import getConfig from '@roxi/routify/lib/utils/config'; +import autoPreprocess from 'svelte-preprocess'; +import postcssImport from 'postcss-import'; +import { injectManifest } from 'rollup-plugin-workbox'; const { distDir } = getConfig() // use Routify's distDir for SSOT const assetsDir = 'assets' @@ -22,7 +21,6 @@ const production = !process.env.ROLLUP_WATCH; removeSync(distDir) removeSync(buildDir) - const serve = () => ({ writeBundle: async () => { const options = { diff --git a/src/components/artList.svelte b/src/components/artList.svelte new file mode 100644 index 0000000..7d52ac2 --- /dev/null +++ b/src/components/artList.svelte @@ -0,0 +1,35 @@ + + +
+ {#await list} + Loading art + {:then list} + {#each list.data.sort((a, b) => a.attributes.volume - b.attributes.volume) as item} + + {/each} + {/await} +
+ + \ No newline at end of file diff --git a/src/components/chapter.svelte b/src/components/chapter.svelte index 46d15a7..4519209 100644 --- a/src/components/chapter.svelte +++ b/src/components/chapter.svelte @@ -30,8 +30,8 @@ {chapter.attributes.volume ? "Vol " + chapter.attributes.volume : ""} {chapter.attributes.chapter ? "Chapter " + chapter.attributes.chapter : ""} -
{chapter.attributes.title}
-
{scanlationGroup}
+
{chapter.attributes.title || " "}
+
{scanlationGroup || "Unknown group"}
!disabledDownload && dispatch("view")}>View diff --git a/src/components/tabs/tabs.svelte b/src/components/tabs/tabs.svelte new file mode 100644 index 0000000..d6d9a29 --- /dev/null +++ b/src/components/tabs/tabs.svelte @@ -0,0 +1,31 @@ + + +
+ {#each list as item} + + {/each} +
+ + \ No newline at end of file diff --git a/src/pages/[manga]/index.svelte b/src/pages/[manga]/index.svelte index 5b29a1b..8aa4757 100644 --- a/src/pages/[manga]/index.svelte +++ b/src/pages/[manga]/index.svelte @@ -6,6 +6,11 @@ import request from "../../util/request"; import { BaseGenerator } from "../../util/baseGenerator"; import { arraysEqual } from "../../util/arrays"; + import { makeRequest } from "../../util/anilist"; + import Tabs from "../../components/tabs/tabs.svelte"; + import { slide } from "svelte/transition"; + import { Swiper, SwiperSlide } from 'swiper/svelte'; + import ArtList from "../../components/artList.svelte"; export var scoped; @@ -19,10 +24,10 @@ $: title = manga.title.en || manga.title.jp || Object.values(manga.title)[0]; async function getMangaChapters(id) { - const data = await request("manga/" + id + "/feed?limit=500&translatedLanguage[]=en&includes[]=scanlation_group"); + const data = await request("manga/" + id + "/feed?limit=500&translatedLanguage[]=en&translatedLanguage[]=uk&includes[]=scanlation_group"); console.log(data); data.data = data.data - .filter(datum => !datum.attributes?.externalUrl) + .filter(item => !item.attributes?.externalUrl) .sort((a, b) => a.attributes.chapter - b.attributes.chapter); return data; } @@ -213,6 +218,54 @@ } }); } + + function anilistInfo(title) { + return makeRequest(` + query ($search: String) { + Media(search: $search, format: MANGA) { + id + type + format + status + chapters + volumes + countryOfOrigin + bannerImage + genres + synonyms + averageScore + popularity + isFavourite + isFavouriteBlocked + isAdult + siteUrl + coverImage { + large + medium + color + } + } + }`, { search: title }).then(t => t.data.Media); + } + + let anilistData; + $: anilistData = anilistInfo(title); + + var selected = "Chapters"; + const tabs = ["Chapters", "Art", "More information"]; + + $: { + if(swiper && tabs.indexOf(selected) !== swiper.realIndex) swiper.slideTo(tabs.indexOf(selected)); + } + + let swiper; + function swiperInit(e) { + swiper = e.detail[0]; + } + function swiperUpdate() { + if(selected !== tabs[swiper.realIndex]) + selected = tabs[swiper.realIndex]; + } @@ -222,7 +275,14 @@ +{#await anilistData then data} + {#if data.bannerImage} + + {/if} +{/await} +
+

{title}

@@ -232,7 +292,7 @@ {#if manga.year} {manga.year} · {/if} - {manga.status} + {#await anilistData then data} {data.status} {data.isAdult ? "ยท 18+" : ""} {/await}

@@ -263,68 +323,131 @@
{#if copyrightOpen} - {/if}
-
-
+ -

- {text} -

-
+ console.log(e.detail[0])} + > + +
+
+
+ +

+ {text} +

+
+ + {#if queue.length > 0} +

{queue.length} downloads queued.

+ {/if} + +
+ + + +
+ +
+

+ + Do not close the tab when a download is in progress. + +

+ +
- {#if queue.length > 0} -

{queue.length} downloads queued.

- {/if} - -
- - - -
- -
-

- - Do not close the tab when a download is in progress. - -

- -
- - {#await chapters} - Loading chapters... - {:then chapters} - {#if chapters.data.length === 0} -

No chapters found.

- {/if} - - - {#each chapters.data as chapter, i} - select(chapter)} on:download={() => downloadSingle(chapter)} /> - {/each} - -
- {/await} - -

DISCLAIMER: This site isn't distributing any content and is using mangadex.org API. Website is open source, and I don't claim any copyright on the publications.

-
+ {#await chapters} + Loading chapters... + {:then chapters} + {#if chapters.data.length === 0} +

No chapters found.

+ {/if} + + + {#each chapters.data as chapter, i} + select(chapter)} on:download={() => downloadSingle(chapter)} /> + {/each} + +
+ {/await} +
+
+ +
+ +
+
+ +
+ {#await anilistData then data} + Anilist entry

+ + Genres: + {#each data.genres as genre} + {genre} + {/each} +
+ + AL popularity: {data.popularity}
+ favorite on AL: {data.isFavourite ? "yes" : "no"}
+ AL score: {data.averageScore}
+ Also known as: {data.synonyms.join(", ")} {Object.values(manga.title).filter(t => t !== title).join(", ")} + {/await} +
+
+