diff options
| author | Fuwn <[email protected]> | 2024-01-21 17:56:22 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-01-21 17:56:22 -0800 |
| commit | 9d1f8edc9ea2871fd234a15140a52726b8ffb583 (patch) | |
| tree | d4aec25b367482b838931d30ea0edb339074ec9e /src/lib/Tools/Wrapped.svelte | |
| parent | refactor(schedule): move module out of tools (diff) | |
| download | due.moe-9d1f8edc9ea2871fd234a15140a52726b8ffb583.tar.xz due.moe-9d1f8edc9ea2871fd234a15140a52726b8ffb583.zip | |
refactor(wrapped): move tool to module
Diffstat (limited to 'src/lib/Tools/Wrapped.svelte')
| -rw-r--r-- | src/lib/Tools/Wrapped.svelte | 790 |
1 files changed, 0 insertions, 790 deletions
diff --git a/src/lib/Tools/Wrapped.svelte b/src/lib/Tools/Wrapped.svelte deleted file mode 100644 index 488535cd..00000000 --- a/src/lib/Tools/Wrapped.svelte +++ /dev/null @@ -1,790 +0,0 @@ -<script lang="ts"> - import userIdentity from '$stores/userIdentity'; - import { - userIdentity as getUserIdentity, - type AniListAuthorisation - } from '$lib/AniList/identity'; - import { onMount } from 'svelte'; - import { tops, wrapped, type TopMedia, SortOptions } from '$lib/AniList/wrapped'; - import { - fullActivityHistory, - activityHistory as getActivityHistory - } from '$lib/AniList/activity'; - import { Type, mediaListCollection, type Media } from '$lib/AniList/media'; - import anime from '$stores/anime'; - import lastPruneTimes from '$stores/lastPruneTimes'; - import manga from '$stores/manga'; - import Error from '$lib/Error/RateLimited.svelte'; - import { domToBlob } from 'modern-screenshot'; - import { browser } from '$app/environment'; - import { page } from '$app/stores'; - import { clearAllParameters } from '../Utility/parameters'; - import { nbsp } from '../Utility/html'; - import SettingHint from '$lib/Settings/SettingHint.svelte'; - import { database } from '$lib/Database/activities'; - import Activity from './Wrapped/Top/Activity.svelte'; - import Anime from './Wrapped/Top/Anime.svelte'; - import Manga from './Wrapped/Top/Manga.svelte'; - import ActivityHistory from './Wrapped/ActivityHistory.svelte'; - import MediaExtras from './Wrapped/MediaExtras.svelte'; - import MediaPanel from './Wrapped/Media.svelte'; - import Watermark from './Wrapped/Watermark.svelte'; - import Loading from '$lib/Utility/Loading.svelte'; - - export let user: AniListAuthorisation; - - const currentYear = new Date(Date.now()).getFullYear(); - let selectedYear = new Date(Date.now()).getFullYear(); - let currentUserIdentity = { - name: '', - id: -1, - avatar: 'https://s4.anilist.co/file/anilistcdn/user/avatar/large/default.png' - }; - let episodes = 0; - let chapters = 0; - let minutesWatched = 0; - let animeList: Media[] | undefined = undefined; - let mangaList: Media[] | undefined = undefined; - let calculatedAnimeList: Media[] | undefined = undefined; - let calculatedMangaList: Media[] | undefined = undefined; - let originalAnimeList: Media[] | undefined = undefined; - let originalMangaList: Media[] | undefined = undefined; - let transparency = false; - let lightTheme = true; - let watermark = false; - let includeMusic = false; - let includeSpecials = true; - let includeRepeats = false; - let width = 1920; - let lightMode = false; - let highestRatedCount = 5; - let genreTagCount = 5; - let mounted = false; - let generated = false; - let disableActivityHistory = true; - let excludedKeywordsInput = ''; - let excludedKeywords: string[] = []; - let useFullActivityHistory = false; - let topGenresTags = true; - let topMedia: TopMedia; - let highestRatedMediaPercentage = true; - let highestRatedGenreTagPercentage = true; - let genreTagsSort = SortOptions.SCORE; - let mediaSort = SortOptions.SCORE; - let includeMovies = true; - let includeOVAs = true; - let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = 'ORIGINAL'; - let includeOngoingMediaFromPreviousYears = false; - - $: { - if (browser && mounted) { - $page.url.searchParams.set('transparency', transparency.toString()); - $page.url.searchParams.set('lightTheme', lightTheme.toString()); - $page.url.searchParams.set('watermark', watermark.toString()); - $page.url.searchParams.set('includeMusic', includeMusic.toString()); - $page.url.searchParams.set('includeSpecials', includeSpecials.toString()); - $page.url.searchParams.set('includeRepeats', includeRepeats.toString()); - $page.url.searchParams.set('lightMode', lightMode.toString()); - $page.url.searchParams.set('highestRatedCount', highestRatedCount.toString()); - $page.url.searchParams.set('genreTagCount', genreTagCount.toString()); - $page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString()); - $page.url.searchParams.set( - 'highestRatedMediaPercentage', - highestRatedMediaPercentage.toString() - ); - $page.url.searchParams.set( - 'highestRatedGenreTagPercentage', - highestRatedGenreTagPercentage.toString() - ); - $page.url.searchParams.set('genreTagsSort', genreTagsSort.toString()); - $page.url.searchParams.set('mediaSort', mediaSort.toString()); - $page.url.searchParams.set('includeMovies', includeMovies.toString()); - $page.url.searchParams.set('includeOVAs', includeOVAs.toString()); - - history.replaceState(null, '', `?${$page.url.searchParams.toString()}`); - } - } - $: { - includeMusic = includeMusic; - includeSpecials = includeSpecials; - includeRepeats = includeRepeats; - disableActivityHistory = disableActivityHistory; - highestRatedMediaPercentage = highestRatedMediaPercentage; - highestRatedGenreTagPercentage = highestRatedGenreTagPercentage; - topGenresTags = topGenresTags; - genreTagsSort = genreTagsSort; - mediaSort = mediaSort; - includeMovies = includeMovies; - includeOVAs = includeOVAs; - selectedYear = selectedYear; - includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears; - - update().then(updateWidth).catch(updateWidth); - } - $: { - animeList = animeList; - mangaList = mangaList; - highestRatedCount = highestRatedCount; - - new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth); - } - $: { - genreTagCount = genreTagCount; - - if (animeList && mangaList) - topMedia = tops( - [...(animeList || []), ...(mangaList || [])], - genreTagCount, - genreTagsSort, - excludedKeywords - ); - - new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth); - } - $: { - excludedKeywords = excludedKeywords; - - if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) { - animeList = originalAnimeList; - mangaList = originalMangaList; - animeList = excludeKeywords(animeList as Media[]); - mangaList = excludeKeywords(mangaList as Media[]); - } - - updateWidth(); - } - $: genreTagTitle = (() => { - switch (genreTagsSort) { - case SortOptions.SCORE: - return 'Highest Rated'; - case SortOptions.MINUTES_WATCHED: - return 'Most Watched'; - case SortOptions.COUNT: - return 'Most Common'; - } - })(); - $: animeMostTitle = (() => { - switch (mediaSort) { - case SortOptions.SCORE: - return 'Highest Rated'; - case SortOptions.MINUTES_WATCHED: - return 'Most Watched'; - case SortOptions.COUNT: - return 'Most Common'; - } - })(); - $: mangaMostTitle = (() => { - switch (mediaSort) { - case SortOptions.SCORE: - return 'Highest Rated'; - case SortOptions.MINUTES_WATCHED: - return 'Most Read'; - case SortOptions.COUNT: - return 'Most Common'; - } - })(); - - const updateWidth = () => { - if (!browser) return; - - const wrappedContainer = document.querySelector('#wrapped') as HTMLElement; - - if (!wrappedContainer) return; - - wrappedContainer.style.width = `1920px`; - - const reset = () => { - let topWidths = 0; - let middleWidths = 0; - let bottomWidths = 0; - - wrappedContainer.querySelectorAll('.category').forEach((item) => { - const category = item as HTMLElement; - const style = window.getComputedStyle(category); - const width = - category.offsetWidth + - parseFloat(style.marginLeft) + - parseFloat(style.marginRight) + - parseFloat(style.paddingLeft) + - parseFloat(style.paddingRight) + - parseFloat(style.borderLeftWidth) + - parseFloat(style.borderRightWidth); - - if (category.classList.contains('top-category')) { - topWidths += width; - } else if (category.classList.contains('middle-category')) { - middleWidths += width; - } else if (category.classList.contains('bottom-category')) { - bottomWidths += width; - } - }); - - let requiredWidth = topWidths > middleWidths ? topWidths : middleWidths; - - if (!disableActivityHistory && bottomWidths > requiredWidth) requiredWidth = bottomWidths; - - requiredWidth += wrappedContainer.offsetWidth - wrappedContainer.clientWidth; - - wrappedContainer.style.width = `${requiredWidth}px`; - width = requiredWidth; - }; - - reset(); - reset(); - }; - - onMount(async () => { - clearAllParameters([ - 'transparency', - 'lightTheme', - 'watermark', - 'includeMusic', - 'includeSpecials', - 'includeRepeats', - 'forceDark', - 'highestRatedCount', - 'genreTagCount', - 'disableActivityHistory', - 'highestRatedMediaPercentage', - 'highestRatedGenreTagPercentage', - 'genreTagsSort', - 'mediaSort', - 'includeMovies', - 'includeOVAs' - ]); - - if (browser) { - transparency = $page.url.searchParams.get('transparency') === 'true'; - lightTheme = $page.url.searchParams.get('lightTheme') === 'true'; - watermark = $page.url.searchParams.get('watermark') === 'true'; - includeMusic = $page.url.searchParams.get('includeMusic') === 'true'; - includeSpecials = $page.url.searchParams.get('includeSpecials') === 'true'; - includeRepeats = $page.url.searchParams.get('includeRepeats') === 'true'; - lightMode = $page.url.searchParams.get('lightMode') === 'true'; - highestRatedCount = parseInt($page.url.searchParams.get('highestRatedCount') || '5', 10); - genreTagCount = parseInt($page.url.searchParams.get('genreTagCount') || '5', 10); - disableActivityHistory = $page.url.searchParams.get('disableActivityHistory') === 'true'; - highestRatedMediaPercentage = - $page.url.searchParams.get('highestRatedMediaPercentage') === 'true'; - highestRatedGenreTagPercentage = - $page.url.searchParams.get('highestRatedGenreTagPercentage') === 'true'; - // genreTagsSort = parseInt($page.url.searchParams.get('genreTagsSort') || '0', 10); - // mediaSort = parseInt($page.url.searchParams.get('mediaSort') || '0', 10); - includeMovies = $page.url.searchParams.get('includeMovies') === 'true'; - includeOVAs = $page.url.searchParams.get('includeOVAs') === 'true'; - } - - if (user !== undefined) { - if ($userIdentity === '') userIdentity.set(JSON.stringify(await getUserIdentity(user))); - - currentUserIdentity = JSON.parse($userIdentity); - currentUserIdentity.name = currentUserIdentity.name; - } else currentUserIdentity.id = -2; - - await update().then(() => (mounted = true)); - }); - - const update = async () => { - if (currentUserIdentity.id === -1) return; - - let rawAnimeList = await mediaListCollection( - user, - currentUserIdentity, - Type.Anime, - $anime, - $lastPruneTimes.anime, - { - forcePrune: true, - includeCompleted: true, - all: true - } - ); - calculatedAnimeList = rawAnimeList - .filter( - (item, index, self) => - self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index && - (includeMusic ? true : item.format !== 'MUSIC') && - (includeRepeats - ? true - : item.startDate.year === selectedYear || item.endDate.year === selectedYear - ? true - : item.mediaListEntry?.repeat === 0) && - (item.mediaListEntry?.startedAt.year === selectedYear || - item.mediaListEntry?.completedAt.year === selectedYear || - ((item.mediaListEntry?.createdAt - ? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.progress >= 1 - : false)) && - (includeMovies ? true : item.format !== 'MOVIE') && - (includeSpecials ? true : item.format !== 'SPECIAL') && - (includeOVAs ? true : item.format !== 'OVA') - ) - .sort((a, b) => { - switch (mediaSort) { - case SortOptions.MINUTES_WATCHED: - if (a.duration === undefined || a.mediaListEntry?.progress === undefined) return 1; - else if (b.duration === undefined || b.mediaListEntry?.progress === undefined) - return -1; - else - return ( - b.duration * b.mediaListEntry.progress - a.duration * a.mediaListEntry.progress - ); - case SortOptions.SCORE: - default: - if (a.mediaListEntry?.score === undefined) return 1; - else if (b.mediaListEntry?.score === undefined) return -1; - else return b.mediaListEntry?.score - a.mediaListEntry?.score; - } - }); - - animeList = rawAnimeList - .filter( - (item, index, self) => - self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index && - (includeMusic ? true : item.format !== 'MUSIC') && - (includeRepeats - ? true - : item.startDate.year === selectedYear || item.endDate.year === selectedYear - ? true - : item.mediaListEntry?.repeat === 0) && - (item.mediaListEntry?.startedAt.year === selectedYear || - item.mediaListEntry?.completedAt.year === selectedYear || - ((item.mediaListEntry?.createdAt - ? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.progress >= 1 - : false) || - (includeOngoingMediaFromPreviousYears - ? (item.mediaListEntry?.updatedAt - ? new Date(item.mediaListEntry?.updatedAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.status === 'CURRENT' - : false - : false)) && - (includeMovies ? true : item.format !== 'MOVIE') && - (includeSpecials ? true : item.format !== 'SPECIAL') && - (includeOVAs ? true : item.format !== 'OVA') - ) - .sort((a, b) => { - switch (mediaSort) { - case SortOptions.MINUTES_WATCHED: - if (a.duration === undefined || a.mediaListEntry?.progress === undefined) return 1; - else if (b.duration === undefined || b.mediaListEntry?.progress === undefined) - return -1; - else - return ( - b.duration * b.mediaListEntry.progress - a.duration * a.mediaListEntry.progress - ); - case SortOptions.SCORE: - default: - if (a.mediaListEntry?.score === undefined) return 1; - else if (b.mediaListEntry?.score === undefined) return -1; - else return b.mediaListEntry?.score - a.mediaListEntry?.score; - } - }); - - let rawMangaList = await mediaListCollection( - user, - currentUserIdentity, - Type.Manga, - $manga, - $lastPruneTimes.manga, - { - forcePrune: true, - includeCompleted: true, - all: true - } - ); - calculatedMangaList = rawMangaList - .filter( - (item, index, self) => - self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index && - (includeRepeats ? true : item.mediaListEntry?.repeat === 0) && - (item.mediaListEntry?.startedAt.year === selectedYear || - item.mediaListEntry?.completedAt.year === selectedYear || - ((item.mediaListEntry?.createdAt - ? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.progress >= 1 - : false)) - ) - .sort((a, b) => { - if (a.mediaListEntry?.score === undefined) return 1; - else if (b.mediaListEntry?.score === undefined) return -1; - else return b.mediaListEntry?.score - a.mediaListEntry?.score; - }); - - mangaList = rawMangaList - .filter( - (item, index, self) => - self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index && - (includeRepeats ? true : item.mediaListEntry?.repeat === 0) && - (item.mediaListEntry?.startedAt.year === selectedYear || - item.mediaListEntry?.completedAt.year === selectedYear || - ((item.mediaListEntry?.createdAt - ? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.progress >= 1 - : false) || - (includeOngoingMediaFromPreviousYears - ? (item.mediaListEntry?.updatedAt - ? new Date(item.mediaListEntry?.updatedAt * 1000).getFullYear() === selectedYear - : false) && item.mediaListEntry - ? item.mediaListEntry?.status === 'CURRENT' - : false - : false)) - ) - .sort((a, b) => { - if (a.mediaListEntry?.score === undefined) return 1; - else if (b.mediaListEntry?.score === undefined) return -1; - else return b.mediaListEntry?.score - a.mediaListEntry?.score; - }); - - episodes = 0; - minutesWatched = 0; - chapters = 0; - - for (const media of calculatedAnimeList) { - episodes += media.mediaListEntry?.progress || 0; - minutesWatched += (media.mediaListEntry?.progress || 0) * media.duration || 0; - } - - for (const media of calculatedMangaList) chapters += media.mediaListEntry?.progress || 0; - }; - - /* eslint-disable @typescript-eslint/no-explicit-any */ - // const year = (statistic: { startYears: any }) => - // statistic.startYears.find((y: { startYear: number }) => y.startYear === 2023); - - const screenshot = async () => { - let element = document.querySelector('#wrapped') as HTMLElement; - - if (element !== null) { - domToBlob(element, { - backgroundColor: transparency ? 'transparent' : lightTheme ? '#edf1f5' : '#0b1622', - quality: 1, - scale: 2, - fetch: { - requestInit: { - mode: 'cors' - }, - bypassingCache: true - } - }).then((blob) => { - const downloadWrapper = document.createElement('a'); - // const wrappedImageButton = document.getElementById( - // 'wrapped-image-download' - // ) as HTMLAnchorElement; - const image = document.createElement('img'); - const object = (window.URL || window.webkitURL || window || {}).createObjectURL(blob); - - // downloadWrapper.download = `due_dot_moe_wrapped_${dark ? 'dark' : 'light'}.png`; - downloadWrapper.href = object; - downloadWrapper.target = '_blank'; - image.src = object; - - downloadWrapper.appendChild(image); - - // if (wrappedImageButton !== null) { - // wrappedImageButton.href = object; - // } - - const wrappedFinal = document.getElementById('wrapped-final'); - - if (wrappedFinal !== null) { - wrappedFinal.innerHTML = ''; - - wrappedFinal.appendChild(downloadWrapper); - - generated = true; - } - - downloadWrapper.click(); - }); - } - }; - - // const abbreviate = (string: string, maxLength = 40, enabled = true) => { - // if (!enabled) { - // return string; - // } - - // if (string.length <= maxLength) { - // return string; - // } - - // return string.slice(0, maxLength - 3) + ' …'; - // }; - - const submitExcludedKeywords = () => { - if (excludedKeywordsInput.length <= 0 && excludedKeywords.length > 0) { - animeList = originalAnimeList; - mangaList = originalMangaList; - excludedKeywords = []; - } else if (excludedKeywordsInput.length >= 0 && excludedKeywords.length <= 0) { - originalAnimeList = animeList; - originalMangaList = mangaList; - } - - if (excludedKeywordsInput.length > 0) - excludedKeywords = excludedKeywordsInput - .split(',') - .map((k) => k.trim()) - .filter((k) => k.length > 0); - }; - - const excludeKeywords = (media: Media[]) => { - if (excludedKeywords.length <= 0) return media; - - return media.filter((m) => { - for (const keyword of excludedKeywords) { - if (m.title.english?.toLowerCase().includes(keyword.toLowerCase())) return false; - if (m.title.romaji?.toLowerCase().includes(keyword.toLowerCase())) return false; - if (m.title.native?.toLowerCase().includes(keyword.toLowerCase())) return false; - } - - return true; - }); - }; - - const pruneFullYear = async () => { - await database.activities.bulkDelete((await database.activities.toArray()).map((m) => m.page)); - }; - - // const mergeArraySort = (a: any, b: any, mode: 'tags' | 'genres') => { - // let merged = [...a, ...b].sort((a, b) => b.meanScore - a.meanScore); - - // merged = merged.filter( - // (item, index, self) => - // self.findIndex((itemToCompare) => - // mode === 'genres' - // ? itemToCompare.genre === item.genre - // : itemToCompare.tag.name === item.tag.name - // ) === index - // ); - - // return merged; - // }; - - // const randomCoverFromTop10 = ( - // statistics: { anime: any; manga: any }, - // mode: 'tags' | 'genres' - // ) => { - // const top = mergeArraySort(statistics.anime[mode], statistics.manga[mode], mode); - - // return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]); - // }; -</script> - -{#if currentUserIdentity.id === -2} - <div class="card">Please log in to view this page.</div> -{:else if currentUserIdentity.id !== -1} - {#await selectedYear !== currentYear || useFullActivityHistory || new Date().getMonth() <= 6 ? fullActivityHistory(user, currentUserIdentity, selectedYear) : getActivityHistory(currentUserIdentity)} - <Loading> - {@html nbsp(`Loading${useFullActivityHistory ? ' full-year' : ''} activity history ...`)} - </Loading> - {:then activities} - {#await wrapped(user, currentUserIdentity, selectedYear)} - <Loading> - {@html nbsp('Loading user data ...')} - </Loading> - {:then wrapped} - <div id="list-container"> - <div class="card"> - <div - id="wrapped" - class:light-theme={lightMode} - style={`width: ${width}px; flex-shrink: 0;`} - class:transparent={transparency} - > - {#if !disableActivityHistory && activityHistoryPosition === 'TOP' && activities.length > 0 && selectedYear === currentYear} - <ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} /> - {/if} - <div class="categories-grid" style="padding-bottom: 0;"> - <Activity - {wrapped} - identity={currentUserIdentity} - year={selectedYear} - {activities} - {useFullActivityHistory} - {updateWidth} - /> - <Anime animeList={calculatedAnimeList} {minutesWatched} {episodes} /> - <Manga mangaList={calculatedMangaList} {chapters} /> - </div> - {#if !disableActivityHistory && activityHistoryPosition === 'BELOW_TOP' && activities.length > 0 && selectedYear === currentYear} - <ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} /> - {/if} - <MediaPanel - {animeList} - {mangaList} - {highestRatedMediaPercentage} - {highestRatedCount} - {updateWidth} - {wrapped} - {animeMostTitle} - {mangaMostTitle} - /> - {#if topMedia && topGenresTags && ((topMedia.topGenreMedia && topMedia.genres.length > 0) || (topMedia.topTagMedia && topMedia.tags.length > 0))} - <MediaExtras - {topMedia} - {genreTagTitle} - {highestRatedGenreTagPercentage} - {updateWidth} - /> - {/if} - {#if !disableActivityHistory && activityHistoryPosition === 'ORIGINAL' && activities.length > 0 && selectedYear === currentYear} - <ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} /> - {/if} - {#if watermark} - <Watermark /> - {/if} - </div> - </div> - <div class="list"> - <div class:card={generated}> - <div id="wrapped-final" /> - - {#if generated} - <p /> - - <blockquote style="margin: 0 0 0 1.5rem;"> - Click on the image to download, or right click and select "Save Image As...". - </blockquote> - {/if} - </div> - - {#if generated} - <p /> - {/if} - - <div id="options" class="card"> - <button on:click={screenshot} data-umami-event="Generate Wrapped"> - Generate image - </button> - - <details class="no-shadow" open> - <summary>Display</summary> - - <input type="checkbox" bind:checked={watermark} /> Show watermark<br /> - <input type="checkbox" bind:checked={transparency} /> Enable background transparency<br - /> - <input type="checkbox" bind:checked={lightMode} /> - Enable light mode<br /> - <input type="checkbox" bind:checked={topGenresTags} /> - Show top genres and tags<br /> - <input - type="checkbox" - bind:checked={disableActivityHistory} - disabled={selectedYear !== currentYear} - /> - Hide activity history<br /> - <input type="checkbox" bind:checked={highestRatedMediaPercentage} /> Show highest - rated media percentages<br /> - <input type="checkbox" bind:checked={highestRatedGenreTagPercentage} /> Show highest - rated genre and tag percentages<br /> - <input type="checkbox" bind:checked={includeOngoingMediaFromPreviousYears} /> Show - ongoing media from previous years<br /> - <select bind:value={activityHistoryPosition}> - <option value="TOP">Above Top Row</option> - <option value="BELOW_TOP">Below Top Row</option> - <option value="ORIGINAL">Bottom</option> - </select> - Activity history position<br /> - <select bind:value={highestRatedCount}> - {#each [3, 4, 5, 6, 7, 8, 9, 10] as count} - <option value={count}>{count}</option> - {/each} - </select> - Highest rated media count<br /> - <select bind:value={genreTagCount}> - {#each [3, 4, 5, 6, 7, 8, 9, 10] as count} - <option value={count}>{count}</option> - {/each} - </select> - Highest genre and tag count<br /> - <button on:click={updateWidth}>Find best fit</button> - <button on:click={() => (width -= 25)}>-25px</button> - <button on:click={() => (width += 25)}>+25px</button> - Width adjustment<br /> - </details> - - <details class="no-shadow" open> - <summary>Calculation</summary> - - <input type="checkbox" bind:checked={useFullActivityHistory} /> - Enable full-year activity<button class="smaller-button" on:click={pruneFullYear} - >Refresh data</button - > - <br /> - <select bind:value={selectedYear}> - {#each Array.from({ length: currentYear - 2012 }) as _, i} - <option value={currentYear - i}> - {currentYear - i} - </option> - {/each} - </select> - Calculate for year<br /> - <select bind:value={mediaSort}> - <option value={SortOptions.SCORE}>Score</option> - <option value={SortOptions.MINUTES_WATCHED}>Minutes Watched/Read</option> - </select> - Anime and manga sort<br /> - <select bind:value={genreTagsSort}> - <option value={SortOptions.SCORE}>Score</option> - <option value={SortOptions.MINUTES_WATCHED}>Minutes Watched/Read</option> - <option value={SortOptions.COUNT}>Count</option> - </select> - Genre and tag sort<br /> - <input type="checkbox" bind:checked={includeMusic} /> Include music<br /> - <input type="checkbox" bind:checked={includeRepeats} /> Include rewatches & rereads<br - /> - <input type="checkbox" bind:checked={includeSpecials} /> Include specials<br /> - <input type="checkbox" bind:checked={includeOVAs} /> Include OVAs<br /> - <input type="checkbox" bind:checked={includeMovies} /> Include movies<br /> - <input - type="text" - bind:value={excludedKeywordsInput} - on:keypress={(e) => { - e.key === 'Enter' && submitExcludedKeywords(); - }} - /> - Excluded keywords - <button on:click={submitExcludedKeywords} title="Or click your Enter key" - >Submit</button - > - <br /> - <SettingHint>Comma separated list (e.g., "My Hero, Kaguya")</SettingHint> - </details> - </div> - </div> - </div> - {:catch} - <Error type="User" card list={false} /> - {/await} - {:catch} - <Error - card - type={`${useFullActivityHistory ? 'Full-year activity' : 'Activity'} history`} - loginSessionError={!useFullActivityHistory} - list={false} - > - {#if useFullActivityHistory} - <p> - With <b>many</b> activities, it may take multiple attempts to obtain all of your activity history - from AniList. If this occurs, wait one minute and try again to continue populating your local - activity history database. - </p> - {/if} - </Error> - {/await} -{:else} - <Loading> - {@html nbsp('Loading user identity ...')} - </Loading> -{/if} - -<style> - @import './Wrapped/wrapped.css'; -</style> |