diff options
| author | Fuwn <[email protected]> | 2024-01-01 21:59:25 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-01-01 21:59:25 -0800 |
| commit | a35ec1a3f7b340faaaf716b4694f52ef721baec5 (patch) | |
| tree | 62dc74f475f42fdc5973e368b76debc25c9e995a /src | |
| parent | feat(oauth): add animeschedule back (diff) | |
| download | due.moe-a35ec1a3f7b340faaaf716b4694f52ef721baec5.tar.xz due.moe-a35ec1a3f7b340faaaf716b4694f52ef721baec5.zip | |
refactor(schedule): move to lib
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/Tools/Schedule/Tool.svelte | 220 | ||||
| -rw-r--r-- | src/routes/schedule/+page.svelte | 221 |
2 files changed, 223 insertions, 218 deletions
diff --git a/src/lib/Tools/Schedule/Tool.svelte b/src/lib/Tools/Schedule/Tool.svelte new file mode 100644 index 00000000..4d68d85b --- /dev/null +++ b/src/lib/Tools/Schedule/Tool.svelte @@ -0,0 +1,220 @@ +<script lang="ts"> + import Error from '$lib/Error/RateLimited.svelte'; + import type { SubsPlease, SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease'; + import { onMount } from 'svelte'; + import settings from '$stores/settings'; + import { parseOrDefault } from '$lib/Utility/parameters'; + import { browser } from '$app/environment'; + import type { Media } from '$lib/AniList/media'; + import { scheduleMediaListCollection } from '$lib/AniList/schedule'; + import { season } from '$lib/Media/Anime/season'; + import { findClosestMedia } from '$lib/Media/Anime/Airing/Subtitled/match'; + import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte'; + import { outboundLink } from '$lib/Media/links'; + import HeadTitle from '$lib/HeadTitle.svelte'; + import { onMouseEnter, onMouseLeave, onMouseMove } from '$lib/Media/hoverCover'; + import HoverCover from '$lib/Media/HoverCover.svelte'; + import Crunchyroll from '$lib/Tools/Schedule/Crunchyroll.svelte'; + + let subsPleasePromise: Promise<SubsPlease>; + let scheduledMediaPromise: Promise<Partial<Media[]>>; + const urlParameters = browser ? new URLSearchParams(window.location.search) : null; + let timeZone = parseOrDefault( + urlParameters, + 'tz', + Intl.DateTimeFormat().resolvedOptions().timeZone + ); + let day: string | null = parseOrDefault(urlParameters, 'day', null); + + onMount(async () => { + subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json()); + scheduledMediaPromise = scheduleMediaListCollection(new Date().getFullYear(), season()); + }); + + let hovering = false; + let hoveredItem: SubsPleaseEpisode | null = null; + let hoveredMedia: Media | null = null; + let imageStyle = ''; + + const shiftSubsPleaseSchedule = (schedule: SubsPlease['schedule']) => { + const shiftedSchedule: { [key: string]: SubsPleaseEpisode[] } = {}; + + if (day && Object.keys(schedule).includes(day)) { + shiftedSchedule[day] = schedule[ + day as keyof typeof schedule + ] as unknown as SubsPleaseEpisode[]; + + return shiftedSchedule; + } + + const days = Object.keys(schedule); + const currentDayIndex = days.indexOf(new Date().toLocaleString('en-us', { weekday: 'long' })); + + days + .slice(currentDayIndex) + .concat(days.slice(0, currentDayIndex)) + .forEach((day) => { + const scheduleEntry = schedule[day as keyof typeof schedule]; + shiftedSchedule[day] = Array.isArray(scheduleEntry) + ? scheduleEntry + : ([scheduleEntry] as unknown as SubsPleaseEpisode[]); + }); + + return shiftedSchedule; + }; + + const associateMedia = (media: (Media | undefined)[], title: string) => + findClosestMedia(media as Media[], title); + + const titleSelect = (media: Media | null) => + media ? media.title.english || media.title.romaji || media.title.native : null; +</script> + +<HeadTitle route="Schedule" path="/schedule" /> + +<blockquote> + <select + bind:value={timeZone} + on:change={() => + (subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json()))} + > + {#each Intl.supportedValuesOf('timeZone') as zone} + <option value={zone}> + {zone.split('/').reverse().join(', ').replace(/_/g, ' ')} + </option> + {/each} + </select> +</blockquote> + +{#await subsPleasePromise} + Loading subtitle release data ... 49.5% +{:then subsPlease} + {#if subsPlease} + {#await scheduledMediaPromise} + Loading anime schedule ... 82.5% + {:then scheduledMedia} + {#if scheduledMedia} + {@const columnCount = Math.ceil(Object.keys(subsPlease.schedule).length / 2)} + + <div id="list-container" style={`column-count: ${columnCount}`}> + {#each Object.entries(shiftSubsPleaseSchedule(subsPlease.schedule)) as [day, scheduleEntry]} + <details + open + class="list" + class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })} + > + <summary>{day}</summary> + + <ul> + {#each Object.values(scheduleEntry) as entry} + {@const media = associateMedia(scheduledMedia, entry.title)} + + <li + class="entry" + on:mouseenter={() => { + const response = onMouseEnter(media, entry); + + hovering = response.hovering; + hoveredItem = response.item; + hoveredMedia = response.media; + }} + on:mouseleave={() => { + const response = onMouseLeave(); + + hovering = response.hovering; + hoveredItem = response.item; + hoveredMedia = response.media; + }} + on:mousemove={(e) => { + const response = onMouseMove(e); + + imageStyle = response.style; + }} + > + <a + href={media + ? outboundLink(media, 'anime', $settings.displayOutboundLinksTo) + : outboundLink( + null, + 'anime', + $settings.displayOutboundLinksTo, + true, + titleSelect(media) || entry.title + )} + target="_blank" + > + {#if media} + <MediaTitleDisplay title={media.title} /> + {:else} + {entry.title} + {/if} + </a> + {#if !$settings.displayCountdownRightAligned} + <span style="opacity: 50%;">|</span> + {/if} + <span class:countdown={$settings.displayCountdownRightAligned}> + {#if media && media.nextAiringEpisode} + <span style="opacity: 50%;"> + + {media.nextAiringEpisode?.episode}{media.episodes + ? `/${media.episodes}` + : ''} at + </span> + {/if} + {entry.time} + </span> + </li> + {/each} + </ul> + + <p /> + </details> + {/each} + </div> + {:else} + Loading anime schedule ... 66% + {/if} + {:catch} + <Error type="Media" loginSessionError={false} /> + {/await} + {:else} + Loading subtitle release data ... 33% + {/if} +{:catch} + <Error type="Schedule" loginSessionError={false} /> +{/await} + +<p /> + +<details> + <summary>Crunchyroll Simulcast Release Calender</summary> + + <Crunchyroll /> +</details> + +<HoverCover {hoveredItem} {hoveredMedia} {imageStyle} {hovering} /> + +<style> + #list-container { + column-width: 250px; + } + + .list { + overflow-y: auto; + } + + .entry::after { + content: ''; + display: table; + clear: both; + } + + .countdown { + white-space: nowrap; + float: right; + } + + .today { + font-weight: bold; + } +</style> diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte index 4d68d85b..7d24c17c 100644 --- a/src/routes/schedule/+page.svelte +++ b/src/routes/schedule/+page.svelte @@ -1,220 +1,5 @@ -<script lang="ts"> - import Error from '$lib/Error/RateLimited.svelte'; - import type { SubsPlease, SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease'; - import { onMount } from 'svelte'; - import settings from '$stores/settings'; - import { parseOrDefault } from '$lib/Utility/parameters'; - import { browser } from '$app/environment'; - import type { Media } from '$lib/AniList/media'; - import { scheduleMediaListCollection } from '$lib/AniList/schedule'; - import { season } from '$lib/Media/Anime/season'; - import { findClosestMedia } from '$lib/Media/Anime/Airing/Subtitled/match'; - import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte'; - import { outboundLink } from '$lib/Media/links'; - import HeadTitle from '$lib/HeadTitle.svelte'; - import { onMouseEnter, onMouseLeave, onMouseMove } from '$lib/Media/hoverCover'; - import HoverCover from '$lib/Media/HoverCover.svelte'; - import Crunchyroll from '$lib/Tools/Schedule/Crunchyroll.svelte'; - - let subsPleasePromise: Promise<SubsPlease>; - let scheduledMediaPromise: Promise<Partial<Media[]>>; - const urlParameters = browser ? new URLSearchParams(window.location.search) : null; - let timeZone = parseOrDefault( - urlParameters, - 'tz', - Intl.DateTimeFormat().resolvedOptions().timeZone - ); - let day: string | null = parseOrDefault(urlParameters, 'day', null); - - onMount(async () => { - subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json()); - scheduledMediaPromise = scheduleMediaListCollection(new Date().getFullYear(), season()); - }); - - let hovering = false; - let hoveredItem: SubsPleaseEpisode | null = null; - let hoveredMedia: Media | null = null; - let imageStyle = ''; - - const shiftSubsPleaseSchedule = (schedule: SubsPlease['schedule']) => { - const shiftedSchedule: { [key: string]: SubsPleaseEpisode[] } = {}; - - if (day && Object.keys(schedule).includes(day)) { - shiftedSchedule[day] = schedule[ - day as keyof typeof schedule - ] as unknown as SubsPleaseEpisode[]; - - return shiftedSchedule; - } - - const days = Object.keys(schedule); - const currentDayIndex = days.indexOf(new Date().toLocaleString('en-us', { weekday: 'long' })); - - days - .slice(currentDayIndex) - .concat(days.slice(0, currentDayIndex)) - .forEach((day) => { - const scheduleEntry = schedule[day as keyof typeof schedule]; - shiftedSchedule[day] = Array.isArray(scheduleEntry) - ? scheduleEntry - : ([scheduleEntry] as unknown as SubsPleaseEpisode[]); - }); - - return shiftedSchedule; - }; - - const associateMedia = (media: (Media | undefined)[], title: string) => - findClosestMedia(media as Media[], title); - - const titleSelect = (media: Media | null) => - media ? media.title.english || media.title.romaji || media.title.native : null; +<script> + import Tool from '$lib/Tools/Schedule/Tool.svelte'; </script> -<HeadTitle route="Schedule" path="/schedule" /> - -<blockquote> - <select - bind:value={timeZone} - on:change={() => - (subsPleasePromise = fetch(`/api/subsplease?tz=${timeZone}`).then((r) => r.json()))} - > - {#each Intl.supportedValuesOf('timeZone') as zone} - <option value={zone}> - {zone.split('/').reverse().join(', ').replace(/_/g, ' ')} - </option> - {/each} - </select> -</blockquote> - -{#await subsPleasePromise} - Loading subtitle release data ... 49.5% -{:then subsPlease} - {#if subsPlease} - {#await scheduledMediaPromise} - Loading anime schedule ... 82.5% - {:then scheduledMedia} - {#if scheduledMedia} - {@const columnCount = Math.ceil(Object.keys(subsPlease.schedule).length / 2)} - - <div id="list-container" style={`column-count: ${columnCount}`}> - {#each Object.entries(shiftSubsPleaseSchedule(subsPlease.schedule)) as [day, scheduleEntry]} - <details - open - class="list" - class:today={day === new Date().toLocaleString('en-us', { weekday: 'long' })} - > - <summary>{day}</summary> - - <ul> - {#each Object.values(scheduleEntry) as entry} - {@const media = associateMedia(scheduledMedia, entry.title)} - - <li - class="entry" - on:mouseenter={() => { - const response = onMouseEnter(media, entry); - - hovering = response.hovering; - hoveredItem = response.item; - hoveredMedia = response.media; - }} - on:mouseleave={() => { - const response = onMouseLeave(); - - hovering = response.hovering; - hoveredItem = response.item; - hoveredMedia = response.media; - }} - on:mousemove={(e) => { - const response = onMouseMove(e); - - imageStyle = response.style; - }} - > - <a - href={media - ? outboundLink(media, 'anime', $settings.displayOutboundLinksTo) - : outboundLink( - null, - 'anime', - $settings.displayOutboundLinksTo, - true, - titleSelect(media) || entry.title - )} - target="_blank" - > - {#if media} - <MediaTitleDisplay title={media.title} /> - {:else} - {entry.title} - {/if} - </a> - {#if !$settings.displayCountdownRightAligned} - <span style="opacity: 50%;">|</span> - {/if} - <span class:countdown={$settings.displayCountdownRightAligned}> - {#if media && media.nextAiringEpisode} - <span style="opacity: 50%;"> - - {media.nextAiringEpisode?.episode}{media.episodes - ? `/${media.episodes}` - : ''} at - </span> - {/if} - {entry.time} - </span> - </li> - {/each} - </ul> - - <p /> - </details> - {/each} - </div> - {:else} - Loading anime schedule ... 66% - {/if} - {:catch} - <Error type="Media" loginSessionError={false} /> - {/await} - {:else} - Loading subtitle release data ... 33% - {/if} -{:catch} - <Error type="Schedule" loginSessionError={false} /> -{/await} - -<p /> - -<details> - <summary>Crunchyroll Simulcast Release Calender</summary> - - <Crunchyroll /> -</details> - -<HoverCover {hoveredItem} {hoveredMedia} {imageStyle} {hovering} /> - -<style> - #list-container { - column-width: 250px; - } - - .list { - overflow-y: auto; - } - - .entry::after { - content: ''; - display: table; - clear: both; - } - - .countdown { - white-space: nowrap; - float: right; - } - - .today { - font-weight: bold; - } -</style> +<Tool /> |