From 9a2f24edde6b492f22c921513ab4fe1b046568cd Mon Sep 17 00:00:00 2001 From: Robonau <30987265+Robonau@users.noreply.github> Date: Sun, 22 Sep 2024 10:55:56 +0100 Subject: [PATCH] filter chapters by scanlator --- src/lib/simpleStores.ts | 3 +- .../(manga)/ChaptersFilterModal.svelte | 21 ++++ .../[MangaID]/(manga)/chaptersSide.svelte | 17 +-- .../[MangaID]/chapter/[ChapterID]/+layout.ts | 3 + .../chapter/[ChapterID]/+layout@.svelte | 9 +- .../chapter/[ChapterID]/+page.svelte | 43 +++++-- .../chapter/[ChapterID]/chapterDrawer.svelte | 105 +++++++++++++++++- src/routes/(app)/manga/[MangaID]/util.ts | 47 ++++++++ 8 files changed, 218 insertions(+), 30 deletions(-) create mode 100644 src/routes/(app)/manga/[MangaID]/util.ts diff --git a/src/lib/simpleStores.ts b/src/lib/simpleStores.ts index ec0d3761..1fab2254 100644 --- a/src/lib/simpleStores.ts +++ b/src/lib/simpleStores.ts @@ -74,7 +74,8 @@ const mangaMetaDefaults = { mobileFullScreenOnChapterPage: true, doPageIndicator: false, notes: '', - showMissingChapters: false + showMissingChapters: false, + groupPartials: [] as string[] }; type mangaMeta = typeof mangaMetaDefaults; diff --git a/src/routes/(app)/manga/[MangaID]/(manga)/ChaptersFilterModal.svelte b/src/routes/(app)/manga/[MangaID]/(manga)/ChaptersFilterModal.svelte index a3c4ae2d..1ef2063d 100644 --- a/src/routes/(app)/manga/[MangaID]/(manga)/ChaptersFilterModal.svelte +++ b/src/routes/(app)/manga/[MangaID]/(manga)/ChaptersFilterModal.svelte @@ -20,6 +20,7 @@ } from '@skeletonlabs/skeleton'; import { ChapterSort, ChapterTitle, MangaMeta } from '$lib/simpleStores'; import { enumKeys } from '$lib/util'; + import Tooltip from '$lib/components/Tooltip.svelte'; const modalStore = getModalStore(); let tabSet = localStorageStore('libraryModalTabs', 0); export let MangaID: number; @@ -103,6 +104,26 @@ > Bookmarked + + + {:else if $tabSet === 1} > & Pausable; export let MangaID: number; @@ -91,21 +92,7 @@ $: chaptersInfo = $manga?.data?.manga?.chapters.nodes; - $: filteredChapters = chaptersInfo?.filter((chapter) => { - if ($mangaMeta.ChapterUnread === 1 && chapter.isRead) return false; - if ($mangaMeta.ChapterUnread === 2 && !chapter.isRead) return false; - - if ($mangaMeta.ChapterDownloaded === 1 && !chapter.isDownloaded) - return false; - if ($mangaMeta.ChapterDownloaded === 2 && chapter.isDownloaded) - return false; - - if ($mangaMeta.ChapterBookmarked === 1 && !chapter.isBookmarked) - return false; - if ($mangaMeta.ChapterBookmarked === 2 && chapter.isBookmarked) - return false; - return true; - }); + $: filteredChapters = chaptersInfo?.filter(filterChapters(mangaMeta)); $: sortedChapters = filteredChapters ? [...filteredChapters].sort((a, b) => { diff --git a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout.ts b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout.ts index 8200e9fa..20cdc3d3 100644 --- a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout.ts +++ b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout.ts @@ -7,6 +7,9 @@ import { error } from '@sveltejs/kit'; import type { LayoutLoad } from './$types'; +export const ssr = false; +export const prerender = false; + export const load: LayoutLoad = ({ params }) => { const MangaID = parseInt(params.MangaID); const ChapterID = parseInt(params.ChapterID); diff --git a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout@.svelte b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout@.svelte index 39a6863d..2299a047 100644 --- a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout@.svelte +++ b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+layout@.svelte @@ -10,19 +10,22 @@ import IconButton from '$lib/components/IconButton.svelte'; import { getDrawerStore, AppShell } from '@skeletonlabs/skeleton'; import type { LayoutData } from './$types'; + import { writable } from 'svelte/store'; export let data: LayoutData; const drawerStore = getDrawerStore(); + const dataStore = writable(data); + + $: dataStore.set(data); + function draw() { if ($drawerStore.open) drawerStore.close(); else { drawerStore.open({ id: 'ChapterMenu', width: 'w-[280px] md:w-[480px]', - meta: { - id: data.MangaID - } + meta: dataStore }); } } diff --git a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+page.svelte b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+page.svelte index 66db6fbf..0ec6d607 100644 --- a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+page.svelte +++ b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/+page.svelte @@ -19,6 +19,7 @@ import { onMount } from 'svelte'; import type { PageData } from './$types'; import { ViewNav, chapterTitle, mangaTitle } from './chapterStores'; + import { filterChapters } from '../../util'; import { paths, type PathLayout, type Paths, type TPath } from './paths'; import { getContextClient, @@ -33,10 +34,18 @@ updateChapter } from '$lib/gql/Mutations'; import { ChapterTypeFragment } from '$lib/gql/Fragments'; + import { queryParam, ssp } from 'sveltekit-search-params'; export let data: PageData; let mangaMeta = MangaMeta(data.MangaID); + let pagenav = queryParam('pagenav', ssp.boolean(), { pushHistory: false }); + + $: if ($pagenav) { + $pagenav = null; + all = []; + } + onMount(() => { if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( @@ -135,10 +144,14 @@ if (next) currentChapterID = next.id; } + $: filteredChapters = $manga.data?.manga?.chapters.nodes?.filter( + filterChapters(mangaMeta, true) + ); + function getChapterOfID( currentID: number ): ResultOf | undefined { - return $manga.data?.manga?.chapters.nodes?.find((e) => e.id === currentID); + return filteredChapters?.find((e) => e.id === currentID); } function getChapterAfterID( @@ -146,8 +159,16 @@ _: unknown = undefined ): ResultOf | undefined { const currentChapter = getChapterOfID(currentID); - return $manga.data?.manga?.chapters.nodes?.find((e) => - currentChapter ? e.sourceOrder === currentChapter.sourceOrder + 1 : false + if (!currentChapter) return undefined; + return filteredChapters?.reduce( + (acc, e) => { + if (e.sourceOrder > currentChapter.sourceOrder) { + if (!acc) acc = e; + if (e.sourceOrder < acc.sourceOrder) acc = e; + } + return acc; + }, + undefined as ResultOf | undefined ); } @@ -155,8 +176,16 @@ currentID: number ): ResultOf | undefined { const currentChapter = getChapterOfID(currentID); - return $manga.data?.manga?.chapters.nodes?.find((e) => - currentChapter ? e.sourceOrder === currentChapter.sourceOrder - 1 : false + if (!currentChapter) return undefined; + return filteredChapters?.reduce( + (acc, e) => { + if (e.sourceOrder < currentChapter.sourceOrder) { + if (!acc) acc = e; + if (e.sourceOrder > acc.sourceOrder) acc = e; + } + return acc; + }, + undefined as ResultOf | undefined ); } @@ -200,7 +229,6 @@ return; } if (keyEvent.code === 'Space') { - console.log(keyEvent); keyEvent.preventDefault(); keyEvent.stopPropagation(); if (keyEvent.shiftKey) { @@ -427,8 +455,7 @@ $: $mangaTitle = $manga.data?.manga?.title ?? ''; $: $chapterTitle = - $manga.data?.manga?.chapters.nodes?.find((e) => e.id === currentChapterID) - ?.name ?? ''; + filteredChapters?.find((e) => e.id === currentChapterID)?.name ?? ''; $: currentChapterID, got(); function got() { diff --git a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/chapterDrawer.svelte b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/chapterDrawer.svelte index 70893342..5ff17a21 100644 --- a/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/chapterDrawer.svelte +++ b/src/routes/(app)/manga/[MangaID]/chapter/[ChapterID]/chapterDrawer.svelte @@ -12,11 +12,33 @@ import { enumKeys } from '$lib/util'; import { getDrawerStore } from '@skeletonlabs/skeleton'; import { ViewNav, chapterTitle, mangaTitle } from './chapterStores'; - import { Layout, MangaMeta, Mode } from '$lib/simpleStores'; + import { ChapterTitle, Layout, MangaMeta, Mode } from '$lib/simpleStores'; import { onMount } from 'svelte'; + import { getContextClient, queryStore } from '@urql/svelte'; + import { getManga } from '$lib/gql/Queries'; + import { filterChapters } from '../../util'; + import type { Writable } from 'svelte/store'; + import IntersectionObserver from '$lib/components/IntersectionObserver.svelte'; + import { onNavigate } from '$app/navigation'; const drawerStore = getDrawerStore(); - const mangaMeta = MangaMeta($drawerStore.meta.id); + + $: data = $drawerStore.meta as Writable<{ + MangaID: number; + ChapterID: number; + }>; + + $: mangaMeta = MangaMeta($data?.MangaID); + onMount(() => { + let elem = document.querySelector(`#chapter-${$data.ChapterID}`)!; + elem = elem.previousElementSibling ?? elem; + const to = elem?.getBoundingClientRect(); + chapterSideElement?.scrollTo({ + top: + chapterSideElement.scrollTop - + chapterSideElement.getBoundingClientRect().top + + (to?.top ?? 0) + }); if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent @@ -36,9 +58,30 @@ } }; }); + + const client = getContextClient(); + $: manga = queryStore({ + client, + query: getManga, + variables: { id: $data?.MangaID } + }); + + $: filteredChapters = $manga.data?.manga.chapters.nodes + .sort((a, b) => { + return b.sourceOrder - a.sourceOrder; + }) + .filter(filterChapters(mangaMeta, true)); + let chapterSideElement: HTMLDivElement | undefined; + + onNavigate((e) => { + const match = e.to?.url.pathname.match(/\/manga\/(\d+)\/chapter\/(\d+)/); + if (!match) { + drawerStore.close(); + } + }); -{#if mangaMeta} +{#if $mangaMeta} {/if} diff --git a/src/routes/(app)/manga/[MangaID]/util.ts b/src/routes/(app)/manga/[MangaID]/util.ts new file mode 100644 index 00000000..b19f608e --- /dev/null +++ b/src/routes/(app)/manga/[MangaID]/util.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2024 Contributors to the Suwayomi project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +import { get } from 'svelte/store'; +import type { MangaMeta } from '$lib/simpleStores'; +import type { ResultOf } from 'gql.tada'; +import type { getManga } from '$lib/gql/Queries'; + +export function filterChapters( + mangaMeta: ReturnType, + reading = false +) { + return ( + chapter: ResultOf['manga']['chapters']['nodes'][number] + ) => { + if (get(mangaMeta).groupPartials.length > 0) { + if ( + !get(mangaMeta) + .groupPartials.map((group) => { + return chapter.scanlator + ?.toLowerCase() + .includes(group.toLowerCase()); + }) + .reduce((a, c) => a || c, false) + ) { + return false; + } + } + if (!reading) { + if (get(mangaMeta).ChapterUnread === 1 && chapter.isRead) return false; + if (get(mangaMeta).ChapterUnread === 2 && !chapter.isRead) return false; + + if (get(mangaMeta).ChapterDownloaded === 1 && !chapter.isDownloaded) + return false; + if (get(mangaMeta).ChapterDownloaded === 2 && chapter.isDownloaded) + return false; + + if (get(mangaMeta).ChapterBookmarked === 1 && !chapter.isBookmarked) + return false; + if (get(mangaMeta).ChapterBookmarked === 2 && chapter.isBookmarked) + return false; + } + return true; + }; +}