diff options
| author | Fuwn <[email protected]> | 2024-10-09 00:41:20 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-10-09 00:41:43 -0700 |
| commit | 998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch) | |
| tree | 50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/lib/List/Manga/MangaListTemplate.svelte | |
| parent | feat(graphql): add badgeCount field (diff) | |
| download | due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip | |
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/lib/List/Manga/MangaListTemplate.svelte')
| -rw-r--r-- | src/lib/List/Manga/MangaListTemplate.svelte | 762 |
1 files changed, 381 insertions, 381 deletions
diff --git a/src/lib/List/Manga/MangaListTemplate.svelte b/src/lib/List/Manga/MangaListTemplate.svelte index fe01465f..1303419f 100644 --- a/src/lib/List/Manga/MangaListTemplate.svelte +++ b/src/lib/List/Manga/MangaListTemplate.svelte @@ -1,387 +1,387 @@ <script lang="ts"> - import sampleManga from '$lib/Data/Static/SampleMedia/manga.json'; - import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media'; - import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; - import { onDestroy, onMount } from 'svelte'; - import { chapterCount } from '$lib/Media/Manga/chapters'; - import { pruneAllManga } from '$lib/Media/Manga/cache'; - import manga from '$stores/manga'; - import { database } from '$lib/Database/IDB/chapters'; - import settings from '$stores/settings'; - import lastPruneTimes from '$stores/lastPruneTimes'; - import ListTitle from '../ListTitle.svelte'; - import Error from '$lib/Error/RateLimited.svelte'; - import CleanMangaList from './CleanMangaList.svelte'; - import authorisedJson from '$lib/Data/Static/authorised.json'; - import { incrementMediaProgress } from '$lib/Media/Anime/cache'; - import { getNotificationsContext } from 'svelte-notifications'; - import { options } from '$lib/Notification/options'; - import Skeleton from '$lib/Loading/Skeleton.svelte'; - import locale from '$stores/locale'; - import { browser } from '$app/environment'; - import identity from '$stores/identity'; - - export let user: AniListAuthorisation = { - accessToken: '', - refreshToken: '', - expiresIn: 0, - tokenType: '' - }; - export let displayUnresolved: boolean; - export let due: boolean; - export let dummy = $settings.debugDummyLists || false; - - const { addNotification } = getNotificationsContext(); - const authorised = authorisedJson.includes($identity.id); - let mangaLists: Promise<Media[]>; - let startTime: number; - let endTime: number; - let lastUpdatedMedia = -1; - let previousMangaList: Media[]; - let pendingUpdate: number | null = null; - let progress = 0; - let rateLimited = false; - let forceFlag = false; - let lastListSize = 5; - - const keyCacher = setInterval(() => { - startTime = performance.now(); - endTime = -1; - mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { - addNotification - }); - }, $settings.cacheMinutes * 1000 * 60); - - onMount(async () => { - if (browser) { - const lastStoredList = localStorage.getItem(`last${due ? '' : 'Completed'}MangaListLength`); - - if (lastStoredList) lastListSize = parseInt(lastStoredList); - } - - startTime = performance.now(); - - if (dummy) { - mangaLists = Promise.resolve( - sampleManga - .filter( - (manga) => - manga.chapters && - !manga.tags.some((tag) => tag.name === 'Nudity') && - !manga.tags.some((tag) => tag.name === 'Rape') && - !manga.tags.some((tag) => tag.name === 'Tragedy') && - !manga.tags.some((tag) => tag.name === 'Bondage') && - !manga.genres.some((genre) => genre === 'Hentai') && - manga.genres.some((genre) => genre === 'Comedy') && - manga.status !== 'NOT_YET_RELEASED' - ) - .sort(() => 0.5 - Math.random()) - .map((manga) => { - manga.status = 'FINISHED'; - manga.episodes = Math.floor(Math.random() * (manga.chapters || 0)) as unknown as null; - manga.mediaListEntry.progress = Math.floor(Math.random() * (manga.episodes || 0)) + 1; - - return manga; - }) - .slice(0, 7) as unknown as Media[] - ); - } else { - mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { - addNotification - }); - } - }); - - onDestroy(() => clearInterval(keyCacher)); - - const cleanMedia = async (manga: Media[], displayUnresolved: boolean, force: boolean) => { - progress = 0; - - if (manga && dummy) return manga; - - if (manga === undefined) return []; - - if (!authorised && (await database.chapters.toArray()).length <= 0 && !force) return []; - - if (authorised) { - let refreshing = false; - - if ($lastPruneTimes.chapters === 1) { - refreshing = true; - - lastPruneTimes.setKey('chapters', new Date().getTime()); - } else { - const currentDate = new Date(); - - if ( - (currentDate.getTime() - $lastPruneTimes.chapters) / 1000 / 60 > - Math.max($settings.cacheMangaMinutes, 5) - ) { - refreshing = true; - - lastPruneTimes.setKey('chapters', currentDate.getTime()); - (async () => { - await database.chapters.bulkDelete( - (await database.chapters.toArray()).map((m) => m.id) - ); - })(); - } - } - - if (refreshing) { - addNotification( - options({ - heading: 'Manga', - description: 'Re-freshing manga data ...' - }) - ); - } - } - - const releasingMedia = manga.filter( - (media: Media) => - (due ? media.status === 'RELEASING' : media.status === 'FINISHED') && - (media.mediaListEntry || { status: 'DROPPED' }).status !== - ($settings.displayPausedMedia ? '' : 'PAUSED') && - (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED' && - (media.mediaListEntry || { progress: 0 }).progress >= - ($settings.displayNotStarted === true ? 0 : 1) - ); - let finalMedia = releasingMedia; - const progressStep = 100 / finalMedia.length / 2; - const chapterPromises = finalMedia.map((m: Media) => - database.chapters.get(m.id).then((c) => { - if (progress < 100) progress += progressStep; - - if (!due) return new Promise((resolve) => resolve(m.chapters)) as Promise<number | null>; - - if (c !== undefined) return chapterCount($identity, m, $settings.calculateGuessingDisabled); - else { - // A = On 1 second interval, - // B = a maximum of 5 requests per second are allowed. - // C = chapterCount makes 3 requests per call. - // F = A / (B / C) = 0.6 seconds - return new Promise((resolve) => setTimeout(resolve, 600)).then(() => - chapterCount($identity, m, $settings.calculateGuessingDisabled) - ); - } - }) - ); - const chapterCounts: (number | null)[] = []; - - for (let i = 0; i < chapterPromises.length; i++) { - const count = await chapterPromises[i]; - - if (count === -22) { - rateLimited = true; - - break; - } - - chapterCounts.push(count); - - if (progress < 100) progress += progressStep; - } - - finalMedia.forEach((m: Media, i) => (m.episodes = chapterCounts[i] || -1337)); - - if (!displayUnresolved) finalMedia = finalMedia.filter((m: Media) => m.episodes !== -1337); - - finalMedia.sort( - (a: Media, b: Media) => - (a.episodes || 9999) - - (a.mediaListEntry || { progress: 0 }).progress - - ((b.episodes || 9999) - (b.mediaListEntry || { progress: 0 }).progress) - ); - - finalMedia = finalMedia.filter( - (item, index, array) => - array.findIndex((i) => i.id === item.id) === index && - (item.episodes === -1337 && displayUnresolved - ? true - : (item.mediaListEntry?.progress || 0) < - ($settings.calculateChaptersRoundedDown === true - ? Math.floor(item.episodes) - : item.episodes)) - ); - - if (!endTime || endTime === -1) endTime = performance.now() - startTime; - - return finalMedia; - }; - - const updateMedia = async (id: number, progress: number | undefined, media: Media[]) => { - pendingUpdate = id; - lastUpdatedMedia = id; - - await database.chapters.delete(id); - - incrementMediaProgress(id, progress, user, () => { - previousMangaList = media; - - const foundEntry = media.find((m) => m.id === id); - - if (foundEntry && foundEntry.mediaListEntry) - foundEntry.mediaListEntry.progress = (progress || 0) + 1; - - mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { - forcePrune: true - }); - pendingUpdate = null; - }); - }; - - const cleanCache = () => { - startTime = performance.now(); - endTime = -1; - - pruneAllManga().then(() => { - mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { - forcePrune: true - }); - }); - }; + import sampleManga from '$lib/Data/Static/SampleMedia/manga.json'; + import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media'; + import type { AniListAuthorisation } from '$lib/Data/AniList/identity'; + import { onDestroy, onMount } from 'svelte'; + import { chapterCount } from '$lib/Media/Manga/chapters'; + import { pruneAllManga } from '$lib/Media/Manga/cache'; + import manga from '$stores/manga'; + import { database } from '$lib/Database/IDB/chapters'; + import settings from '$stores/settings'; + import lastPruneTimes from '$stores/lastPruneTimes'; + import ListTitle from '../ListTitle.svelte'; + import Error from '$lib/Error/RateLimited.svelte'; + import CleanMangaList from './CleanMangaList.svelte'; + import authorisedJson from '$lib/Data/Static/authorised.json'; + import { incrementMediaProgress } from '$lib/Media/Anime/cache'; + import { getNotificationsContext } from 'svelte-notifications'; + import { options } from '$lib/Notification/options'; + import Skeleton from '$lib/Loading/Skeleton.svelte'; + import locale from '$stores/locale'; + import { browser } from '$app/environment'; + import identity from '$stores/identity'; + + export let user: AniListAuthorisation = { + accessToken: '', + refreshToken: '', + expiresIn: 0, + tokenType: '' + }; + export let displayUnresolved: boolean; + export let due: boolean; + export let dummy = $settings.debugDummyLists || false; + + const { addNotification } = getNotificationsContext(); + const authorised = authorisedJson.includes($identity.id); + let mangaLists: Promise<Media[]>; + let startTime: number; + let endTime: number; + let lastUpdatedMedia = -1; + let previousMangaList: Media[]; + let pendingUpdate: number | null = null; + let progress = 0; + let rateLimited = false; + let forceFlag = false; + let lastListSize = 5; + + const keyCacher = setInterval(() => { + startTime = performance.now(); + endTime = -1; + mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { + addNotification + }); + }, $settings.cacheMinutes * 1000 * 60); + + onMount(async () => { + if (browser) { + const lastStoredList = localStorage.getItem(`last${due ? '' : 'Completed'}MangaListLength`); + + if (lastStoredList) lastListSize = parseInt(lastStoredList); + } + + startTime = performance.now(); + + if (dummy) { + mangaLists = Promise.resolve( + sampleManga + .filter( + (manga) => + manga.chapters && + !manga.tags.some((tag) => tag.name === 'Nudity') && + !manga.tags.some((tag) => tag.name === 'Rape') && + !manga.tags.some((tag) => tag.name === 'Tragedy') && + !manga.tags.some((tag) => tag.name === 'Bondage') && + !manga.genres.some((genre) => genre === 'Hentai') && + manga.genres.some((genre) => genre === 'Comedy') && + manga.status !== 'NOT_YET_RELEASED' + ) + .sort(() => 0.5 - Math.random()) + .map((manga) => { + manga.status = 'FINISHED'; + manga.episodes = Math.floor(Math.random() * (manga.chapters || 0)) as unknown as null; + manga.mediaListEntry.progress = Math.floor(Math.random() * (manga.episodes || 0)) + 1; + + return manga; + }) + .slice(0, 7) as unknown as Media[] + ); + } else { + mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { + addNotification + }); + } + }); + + onDestroy(() => clearInterval(keyCacher)); + + const cleanMedia = async (manga: Media[], displayUnresolved: boolean, force: boolean) => { + progress = 0; + + if (manga && dummy) return manga; + + if (manga === undefined) return []; + + if (!authorised && (await database.chapters.toArray()).length <= 0 && !force) return []; + + if (authorised) { + let refreshing = false; + + if ($lastPruneTimes.chapters === 1) { + refreshing = true; + + lastPruneTimes.setKey('chapters', new Date().getTime()); + } else { + const currentDate = new Date(); + + if ( + (currentDate.getTime() - $lastPruneTimes.chapters) / 1000 / 60 > + Math.max($settings.cacheMangaMinutes, 5) + ) { + refreshing = true; + + lastPruneTimes.setKey('chapters', currentDate.getTime()); + (async () => { + await database.chapters.bulkDelete( + (await database.chapters.toArray()).map((m) => m.id) + ); + })(); + } + } + + if (refreshing) { + addNotification( + options({ + heading: 'Manga', + description: 'Re-freshing manga data ...' + }) + ); + } + } + + const releasingMedia = manga.filter( + (media: Media) => + (due ? media.status === 'RELEASING' : media.status === 'FINISHED') && + (media.mediaListEntry || { status: 'DROPPED' }).status !== + ($settings.displayPausedMedia ? '' : 'PAUSED') && + (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED' && + (media.mediaListEntry || { progress: 0 }).progress >= + ($settings.displayNotStarted === true ? 0 : 1) + ); + let finalMedia = releasingMedia; + const progressStep = 100 / finalMedia.length / 2; + const chapterPromises = finalMedia.map((m: Media) => + database.chapters.get(m.id).then((c) => { + if (progress < 100) progress += progressStep; + + if (!due) return new Promise((resolve) => resolve(m.chapters)) as Promise<number | null>; + + if (c !== undefined) return chapterCount($identity, m, $settings.calculateGuessingDisabled); + else { + // A = On 1 second interval, + // B = a maximum of 5 requests per second are allowed. + // C = chapterCount makes 3 requests per call. + // F = A / (B / C) = 0.6 seconds + return new Promise((resolve) => setTimeout(resolve, 600)).then(() => + chapterCount($identity, m, $settings.calculateGuessingDisabled) + ); + } + }) + ); + const chapterCounts: (number | null)[] = []; + + for (let i = 0; i < chapterPromises.length; i++) { + const count = await chapterPromises[i]; + + if (count === -22) { + rateLimited = true; + + break; + } + + chapterCounts.push(count); + + if (progress < 100) progress += progressStep; + } + + finalMedia.forEach((m: Media, i) => (m.episodes = chapterCounts[i] || -1337)); + + if (!displayUnresolved) finalMedia = finalMedia.filter((m: Media) => m.episodes !== -1337); + + finalMedia.sort( + (a: Media, b: Media) => + (a.episodes || 9999) - + (a.mediaListEntry || { progress: 0 }).progress - + ((b.episodes || 9999) - (b.mediaListEntry || { progress: 0 }).progress) + ); + + finalMedia = finalMedia.filter( + (item, index, array) => + array.findIndex((i) => i.id === item.id) === index && + (item.episodes === -1337 && displayUnresolved + ? true + : (item.mediaListEntry?.progress || 0) < + ($settings.calculateChaptersRoundedDown === true + ? Math.floor(item.episodes) + : item.episodes)) + ); + + if (!endTime || endTime === -1) endTime = performance.now() - startTime; + + return finalMedia; + }; + + const updateMedia = async (id: number, progress: number | undefined, media: Media[]) => { + pendingUpdate = id; + lastUpdatedMedia = id; + + await database.chapters.delete(id); + + incrementMediaProgress(id, progress, user, () => { + previousMangaList = media; + + const foundEntry = media.find((m) => m.id === id); + + if (foundEntry && foundEntry.mediaListEntry) + foundEntry.mediaListEntry.progress = (progress || 0) + 1; + + mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { + forcePrune: true + }); + pendingUpdate = null; + }); + }; + + const cleanCache = () => { + startTime = performance.now(); + endTime = -1; + + pruneAllManga().then(() => { + mangaLists = mediaListCollection(user, $identity, Type.Manga, $manga, $lastPruneTimes.manga, { + forcePrune: true + }); + }); + }; </script> {#await mangaLists} - {#if previousMangaList} - <CleanMangaList - media={previousMangaList} - {cleanCache} - {lastUpdatedMedia} - {updateMedia} - {endTime} - {pendingUpdate} - {due} - {rateLimited} - {authorised} - {dummy} - /> - {:else} - {#if !authorised} - <ListTitle - count={0} - time={endTime / 1000} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - > - {#if !dummy} - <button - data-umami-event="Force Refresh Manga" - title="Force a full refresh" - on:click={() => { - cleanCache(); - - forceFlag = true; - }}>Refresh</button - > - {/if} - </ListTitle> - {:else} - <ListTitle - {progress} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - /> - {/if} - - <Skeleton card={false} count={lastListSize} height="0.9rem" list /> - {/if} + {#if previousMangaList} + <CleanMangaList + media={previousMangaList} + {cleanCache} + {lastUpdatedMedia} + {updateMedia} + {endTime} + {pendingUpdate} + {due} + {rateLimited} + {authorised} + {dummy} + /> + {:else} + {#if !authorised} + <ListTitle + count={0} + time={endTime / 1000} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + > + {#if !dummy} + <button + data-umami-event="Force Refresh Manga" + title="Force a full refresh" + on:click={() => { + cleanCache(); + + forceFlag = true; + }}>Refresh</button + > + {/if} + </ListTitle> + {:else} + <ListTitle + {progress} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + /> + {/if} + + <Skeleton card={false} count={lastListSize} height="0.9rem" list /> + {/if} {:then media} - {#await cleanMedia(media, displayUnresolved, forceFlag)} - {#if previousMangaList} - <CleanMangaList - media={previousMangaList} - {cleanCache} - {lastUpdatedMedia} - {updateMedia} - {endTime} - {pendingUpdate} - {due} - {rateLimited} - {authorised} - {dummy} - /> - {:else} - {#if !authorised} - <ListTitle - count={0} - time={endTime / 1000} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - > - {#if !dummy} - <button - data-umami-event="Force Refresh Manga" - title="Force a full refresh" - on:click={() => { - cleanCache(); - - forceFlag = true; - }}>Refresh</button - > - {/if} - </ListTitle> - {:else} - <ListTitle - {progress} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - /> - {/if} - - <Skeleton card={false} count={lastListSize} height="0.9rem" list /> - {/if} - {:then cleanedMedia} - {#if !authorised} - <ListTitle - count={cleanedMedia.length} - time={endTime / 1000} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - > - {#if !dummy} - <button - data-umami-event="Force Refresh Manga" - title="Force a full refresh" - on:click={() => { - cleanCache(); - - forceFlag = true; - }}>Refresh</button - > - {/if} - </ListTitle> - {/if} - - <CleanMangaList - media={cleanedMedia} - {cleanCache} - {lastUpdatedMedia} - {updateMedia} - {endTime} - {pendingUpdate} - {due} - {rateLimited} - {authorised} - {dummy} - /> - {:catch} - {#if authorised} - <ListTitle - count={-1337} - time={0} - title={$locale().lists.due.mangaAndLightNovels} - hideTime={dummy} - hideCount={dummy} - /> - {/if} - - <Error list={false} /> - {/await} + {#await cleanMedia(media, displayUnresolved, forceFlag)} + {#if previousMangaList} + <CleanMangaList + media={previousMangaList} + {cleanCache} + {lastUpdatedMedia} + {updateMedia} + {endTime} + {pendingUpdate} + {due} + {rateLimited} + {authorised} + {dummy} + /> + {:else} + {#if !authorised} + <ListTitle + count={0} + time={endTime / 1000} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + > + {#if !dummy} + <button + data-umami-event="Force Refresh Manga" + title="Force a full refresh" + on:click={() => { + cleanCache(); + + forceFlag = true; + }}>Refresh</button + > + {/if} + </ListTitle> + {:else} + <ListTitle + {progress} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + /> + {/if} + + <Skeleton card={false} count={lastListSize} height="0.9rem" list /> + {/if} + {:then cleanedMedia} + {#if !authorised} + <ListTitle + count={cleanedMedia.length} + time={endTime / 1000} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + > + {#if !dummy} + <button + data-umami-event="Force Refresh Manga" + title="Force a full refresh" + on:click={() => { + cleanCache(); + + forceFlag = true; + }}>Refresh</button + > + {/if} + </ListTitle> + {/if} + + <CleanMangaList + media={cleanedMedia} + {cleanCache} + {lastUpdatedMedia} + {updateMedia} + {endTime} + {pendingUpdate} + {due} + {rateLimited} + {authorised} + {dummy} + /> + {:catch} + {#if authorised} + <ListTitle + count={-1337} + time={0} + title={$locale().lists.due.mangaAndLightNovels} + hideTime={dummy} + hideCount={dummy} + /> + {/if} + + <Error list={false} /> + {/await} {/await} |