diff --git a/src/app.sass b/src/app.sass index a7d87c2f..7781e335 100644 --- a/src/app.sass +++ b/src/app.sass @@ -95,6 +95,7 @@ @apply rounded-b-box & > *:first-child @apply rounded-t-box + .tooltip-bottom-align-right @apply tooltip-bottom &::before diff --git a/src/gql/Mutations.gql b/src/gql/Mutations.gql index 92fc39de..4b95b063 100644 --- a/src/gql/Mutations.gql +++ b/src/gql/Mutations.gql @@ -328,3 +328,22 @@ mutation stopDownloader { } } } + +mutation deleteDownloadedChapter($id: Int!) { + deleteDownloadedChapter(input: { id: $id }) { + chapters { + isBookmarked + isDownloaded + isRead + id + chapterNumber + fetchedAt + lastPageRead + name + sourceOrder + uploadDate + pageCount + scanlator + } + } +} diff --git a/src/gql/Queries.gql b/src/gql/Queries.gql index 057dae50..39d054ce 100644 --- a/src/gql/Queries.gql +++ b/src/gql/Queries.gql @@ -69,6 +69,23 @@ query manga($id: Int!) { } } +query getSingleChapter($id: Int!) { + chapter(id: $id) { + isBookmarked + isDownloaded + isRead + id + chapterNumber + fetchedAt + lastPageRead + name + sourceOrder + uploadDate + pageCount + scanlator + } +} + query extentions { extensions { nodes { diff --git a/src/gql/Subscriptions.gql b/src/gql/Subscriptions.gql index 731ffe19..8026abcc 100644 --- a/src/gql/Subscriptions.gql +++ b/src/gql/Subscriptions.gql @@ -17,3 +17,19 @@ subscription downloadChanged { state } } + +subscription downloadsOnChapters { + downloadChanged { + queue { + progress + state + chapter { + id + } + manga { + id + } + } + state + } +} diff --git a/src/lib/generated.ts b/src/lib/generated.ts index 9537fb0e..066c82a6 100644 --- a/src/lib/generated.ts +++ b/src/lib/generated.ts @@ -2249,6 +2249,13 @@ export type StopDownloaderMutationVariables = Exact<{ [key: string]: never; }>; export type StopDownloaderMutation = { __typename?: 'Mutation', stopDownloader: { __typename?: 'StopDownloaderPayload', downloadStatus: { __typename?: 'DownloadStatus', state: DownloaderState } } }; +export type DeleteDownloadedChapterMutationVariables = Exact<{ + id: Scalars['Int']['input']; +}>; + + +export type DeleteDownloadedChapterMutation = { __typename?: 'Mutation', deleteDownloadedChapter: { __typename?: 'DeleteDownloadedChapterPayload', chapters: { __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null } } }; + export type CategoriesQueryVariables = Exact<{ notEqualTo?: InputMaybe; }>; @@ -2270,6 +2277,13 @@ export type MangaQueryVariables = Exact<{ export type MangaQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, chapters: { __typename?: 'ChapterNodeList', nodes: Array<{ __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null }> } } }; +export type GetSingleChapterQueryVariables = Exact<{ + id: Scalars['Int']['input']; +}>; + + +export type GetSingleChapterQuery = { __typename?: 'Query', chapter: { __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null } }; + export type ExtentionsQueryVariables = Exact<{ [key: string]: never; }>; @@ -2349,6 +2363,11 @@ export type DownloadChangedSubscriptionVariables = Exact<{ [key: string]: never; export type DownloadChangedSubscription = { __typename?: 'Subscription', downloadChanged: { __typename?: 'DownloadStatus', state: DownloaderState, queue: Array<{ __typename?: 'DownloadType', progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', name: string, id: number }, manga: { __typename?: 'MangaType', title: string, thumbnailUrl?: string | null, id: number } }> } }; +export type DownloadsOnChaptersSubscriptionVariables = Exact<{ [key: string]: never; }>; + + +export type DownloadsOnChaptersSubscription = { __typename?: 'Subscription', downloadChanged: { __typename?: 'DownloadStatus', state: DownloaderState, queue: Array<{ __typename?: 'DownloadType', progress: number, state: DownloadState, chapter: { __typename?: 'ChapterType', id: number }, manga: { __typename?: 'MangaType', id: number } }> } }; + export const FetchExtensionsDoc = gql` mutation fetchExtensions { @@ -2655,6 +2674,26 @@ export const StopDownloaderDoc = gql` } } `; +export const DeleteDownloadedChapterDoc = gql` + mutation deleteDownloadedChapter($id: Int!) { + deleteDownloadedChapter(input: {id: $id}) { + chapters { + isBookmarked + isDownloaded + isRead + id + chapterNumber + fetchedAt + lastPageRead + name + sourceOrder + uploadDate + pageCount + scanlator + } + } +} + `; export const CategoriesDoc = gql` query categories($notEqualTo: Int = null) { categories(filter: {id: {notEqualTo: $notEqualTo}}) { @@ -2729,6 +2768,24 @@ export const MangaDoc = gql` } } `; +export const GetSingleChapterDoc = gql` + query getSingleChapter($id: Int!) { + chapter(id: $id) { + isBookmarked + isDownloaded + isRead + id + chapterNumber + fetchedAt + lastPageRead + name + sourceOrder + uploadDate + pageCount + scanlator + } +} + `; export const ExtentionsDoc = gql` query extentions { extensions { @@ -3058,6 +3115,23 @@ export const DownloadChangedDoc = gql` } } `; +export const DownloadsOnChaptersDoc = gql` + subscription downloadsOnChapters { + downloadChanged { + queue { + progress + state + chapter { + id + } + manga { + id + } + } + state + } +} + `; export const fetchExtensions = ( options: Omit< MutationOptions, @@ -3382,6 +3456,18 @@ export const stopDownloader = ( }); return m; } +export const deleteDownloadedChapter = ( + options: Omit< + MutationOptions, + "mutation" + > + ) => { + const m = client.mutate({ + mutation: DeleteDownloadedChapterDoc, + ...options, + }); + return m; + } export const categories = ( options: Omit< WatchQueryOptions, @@ -3514,6 +3600,50 @@ export const manga = ( return client.query({query: MangaDoc, ...options}) } +export const getSingleChapter = ( + options: Omit< + WatchQueryOptions, + "query" + > + ): Readable< + ApolloQueryResult & { + query: ObservableQuery< + GetSingleChapterQuery, + GetSingleChapterQueryVariables + >; + } + > => { + const q = client.watchQuery({ + query: GetSingleChapterDoc, + ...options, + }); + var result = readable< + ApolloQueryResult & { + query: ObservableQuery< + GetSingleChapterQuery, + GetSingleChapterQueryVariables + >; + } + >( + { data: {} as any, loading: true, error: undefined, networkStatus: 1, query: q }, + (set) => { + q.subscribe((v: any) => { + set({ ...v, query: q }); + }); + } + ); + return result; + } + + export const AsyncgetSingleChapter = ( + options: Omit< + QueryOptions, + "query" + > + ) => { + return client.query({query: GetSingleChapterDoc, ...options}) + } + export const extentions = ( options: Omit< WatchQueryOptions, @@ -4052,4 +4182,15 @@ export const downloadChanged = ( } ) return q; + } +export const downloadsOnChapters = ( + options: Omit, "query"> + ) => { + const q = client.subscribe( + { + query: DownloadsOnChaptersDoc, + ...options, + } + ) + return q; } \ No newline at end of file diff --git a/src/routes/(app)/downloads/+page.svelte b/src/routes/(app)/downloads/+page.svelte index 1c77ed3c..ac0b6b4f 100644 --- a/src/routes/(app)/downloads/+page.svelte +++ b/src/routes/(app)/downloads/+page.svelte @@ -2,64 +2,217 @@ import { downloadChanged, type DownloadChangedSubscription, - DownloadState, - DownloaderState + stopDownloader, + startDownloader, + clearDownloader, + dequeueChapterDownloads } from '$lib/generated'; import IntersectionObserver from '$lib/IntersectionObserver.svelte'; import Image from '$lib/Image.svelte'; import { fade } from 'svelte/transition'; + import Icon from '@iconify/svelte'; + import { press } from 'svelte-gestures'; + import { title } from '$lib/titleStore'; let dl = downloadChanged({}); let dataa: DownloadChangedSubscription | null | undefined; + let running = false; + title.set('Downloads'); dl.subscribe((e) => { - console.log(e.errors); - console.log(e.data); + if (e.errors) { + console.log(e.errors); + } dataa = e.data; + running = e.data?.downloadChanged.state === 'STARTED'; }); + let clearLoading = false; + let pauseLoading = false; + let dequeueLoading = false; + async function handelPause() { + pauseLoading = true; + try { + if (running) { + await stopDownloader({}); + } else { + await startDownloader({}); + } + } catch (error) {} + pauseLoading = false; + } + + async function handleClear() { + clearLoading = true; + await clearDownloader({}); + clearLoading = false; + } + + let selectmode = false; + let selected = {} as { + [key: number]: DownloadChangedSubscription['downloadChanged']['queue'][0]['chapter']; + }; + let lastselected = undefined as + | DownloadChangedSubscription['downloadChanged']['queue'][0]['chapter'] + | undefined; + + function LongHandler( + chapter: DownloadChangedSubscription['downloadChanged']['queue'][0]['chapter'] + ) { + selectmode = true; + doselect(chapter); + } + function doselect( + chapter: DownloadChangedSubscription['downloadChanged']['queue'][0]['chapter'], + e?: MouseEvent & { currentTarget: EventTarget & HTMLAnchorElement } + ) { + let ids = [chapter]; + if (e && e.shiftKey) { + const chaps = dataa?.downloadChanged.queue.map((ele) => ele.chapter); + const thisone = chaps?.findIndex((ele) => ele.id === chapter.id); + const lastone = chaps?.findIndex((ele) => ele.id === lastselected?.id); + if (thisone !== undefined && lastone !== undefined && chaps !== undefined) { + const biger = lastone > thisone; + ids = chaps.slice(biger ? thisone : lastone, (biger ? lastone : thisone) + 1); + } + } + lastselected = chapter; + if (selected[chapter.id] === undefined) { + ids.forEach((ele) => (selected[ele.id] = ele)); + } else { + ids.forEach((ele) => { + delete selected[ele.id]; + selected = selected; // needed for updating ui + }); + } + } + function selectall() { + selectmode = true; + if (Object.values(selected).length === dataa?.downloadChanged.queue?.length) { + selected = {}; + } else { + dataa?.downloadChanged.queue?.forEach((ele) => (selected[ele.chapter.id] = ele.chapter)); + } + } + async function dequeue() { + dequeueLoading = true; + await dequeueChapterDownloads({ + variables: { + ids: Object.values(selected).map((e) => e.id) + } + }); + dequeueLoading = false; + }
-
static_buttons
+
+
+ + Status: {dataa?.downloadChanged.state} + +
+
+
+ {#if selectmode} + + {/if} + + +
+
+ + + +
+
+
-
{dataa?.downloadChanged.state}
{#if dataa?.downloadChanged.queue} {#each dataa.downloadChanged.queue as item (item.chapter.id)} - - {#if intersecting} - -
-
- -
-
-
{item.manga.title}
-
- {item.chapter.name} | {item.state.toLowerCase()} -
-
{/each} {/if} diff --git a/src/routes/(app)/manga/[MangaID]/+page.svelte b/src/routes/(app)/manga/[MangaID]/+page.svelte index 19706330..628f1e48 100644 --- a/src/routes/(app)/manga/[MangaID]/+page.svelte +++ b/src/routes/(app)/manga/[MangaID]/+page.svelte @@ -1,5 +1,4 @@
@@ -491,8 +556,8 @@ {/if}
{chapter.name} @@ -509,6 +574,9 @@
+ {#if dlstatus} + + {/if} {#if selectmode}
manga.chapters.nodes.length - 4 && 'dropdown-end'}" >