aboutsummaryrefslogtreecommitdiff
path: root/src/routes/+layout.svelte
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-09 00:41:20 -0700
committerFuwn <[email protected]>2024-10-09 00:41:43 -0700
commit998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch)
tree50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/routes/+layout.svelte
parentfeat(graphql): add badgeCount field (diff)
downloaddue.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz
due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/routes/+layout.svelte')
-rw-r--r--src/routes/+layout.svelte636
1 files changed, 318 insertions, 318 deletions
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index d9b257e9..6d7fe757 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,329 +1,329 @@
<script lang="ts">
- import type { SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
- import { env } from '$env/dynamic/public';
- import { userIdentity as getUserIdentity } from '$lib/Data/AniList/identity';
- import { onDestroy, onMount } from 'svelte';
- import userIdentity from '$stores/identity';
- import settings from '$stores/settings';
- import { browser } from '$app/environment';
- import HeadTitle from '$lib/Home/HeadTitle.svelte';
- import '../app.css';
- import { readable, type Readable } from 'svelte/store';
- import { navigating } from '$app/stores';
- import Notifications from 'svelte-notifications';
- import * as EventNotification from '$lib/Notification/Notification.svelte';
- import Root from '$lib/Home/Root.svelte';
- import root from '$lib/Utility/root';
- import { addMessages, init, locale as i18nLocale, locales } from 'svelte-i18n';
- import english from '$lib/Locale/english';
- import japanese from '$lib/Locale/japanese';
- import type { LocaleDictionary } from '$lib/Locale/layout';
- import locale from '$stores/locale';
- import Skeleton from '$lib/Loading/Skeleton.svelte';
- import subsPlease from '$stores/subsPlease';
- import Dropdown from '$lib/Layout/Dropdown.svelte';
- import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
- import subtitles from '$lib/Data/Static/subtitles.json';
- import settingsSyncPulled from '$stores/settingsSyncPulled';
- import settingsSyncTimes from '$stores/settingsSyncTimes';
- import Announcement from '$lib/Announcement.svelte';
- import Message from '$lib/Loading/Message.svelte';
- import { requestNotifications } from '$lib/Utility/notifications';
- import { database as userDatabase } from '$lib/Database/IDB/user';
-
- injectSpeedInsights();
-
- export let data;
-
- let isHeaderVisible = true;
- let previousScrollPosition = 0;
- let notificationInterval: NodeJS.Timeout | undefined = undefined;
-
- addMessages('en', english as unknown as LocaleDictionary);
- addMessages('ja', japanese as unknown as LocaleDictionary);
- init({ fallbackLocale: 'en', initialLocale: $settings.displayLanguage });
-
- $: i18nLocale.set($settings.displayLanguage);
-
- const navigationOrder = ['/', '/completed', '/schedule', '/updates', '/tools', '/settings'];
- const previousPage: Readable<string | null> = readable(null, (set) => {
- const unsubscribe = navigating.subscribe(($navigating) => {
- if ($navigating && $navigating.from) set($navigating.from.url.pathname as unknown as null);
- });
-
- return () => unsubscribe();
- });
-
- $: way = data.url.includes('/user')
- ? 200
- : $previousPage && $previousPage.includes('/user')
- ? -200
- : navigationOrder.indexOf(data.url) > navigationOrder.indexOf($previousPage ?? '/')
- ? 200
- : -200;
-
- const handleScroll = () => {
- const currentScrollPosition = window.scrollY;
-
- isHeaderVisible =
- currentScrollPosition <= 100 || currentScrollPosition < previousScrollPosition;
- previousScrollPosition = currentScrollPosition;
- };
-
- onMount(async () => {
- if (browser) {
- if (localStorage.getItem('redirect')) {
- window.location.href = localStorage.getItem('redirect') ?? '/';
-
- localStorage.removeItem('redirect');
- }
-
- window.addEventListener('scroll', handleScroll);
-
- if (localStorage.getItem('commit') !== data.commit) {
- localStorage.removeItem('identity');
- localStorage.removeItem('anime');
- localStorage.removeItem('manga');
- localStorage.removeItem('lastPruneTimes');
- localStorage.setItem('commit', data.commit);
- }
- }
-
- settings.get();
-
- if (data.user !== undefined && $userIdentity.id === -2)
- getUserIdentity(data.user).then((h) => userIdentity.set(h));
-
- if ($settings.settingsSync && $userIdentity.id !== -2)
- fetch(root(`/api/configuration?id=${$userIdentity.id}`)).then((response) => {
- if (response.ok)
- response.json().then((data) => {
- if (data && data.configuration) {
- console.log('Pulled remote configuration');
- settings.set(data.configuration);
- settingsSyncPulled.set(true);
- settingsSyncTimes.set({
- lastPull: new Date(),
- lastPush: new Date(data.updated_at + 'Z')
- });
- }
- });
- });
-
- if (!(await userDatabase.users.get($userIdentity.id)))
- userDatabase.users.put({
- id: $userIdentity.id,
- user: data.user,
- lastNotificationID: null
- });
-
- if ($settings.displayAniListNotifications && data.user !== undefined)
- if ('Notification' in window && navigator.serviceWorker) requestNotifications();
- });
-
- onDestroy(() => {
- if (browser) window.removeEventListener('scroll', handleScroll);
-
- if (notificationInterval) clearInterval(notificationInterval);
- });
-
- $: {
- if ((data.url === '/' || data.url === '/completed' || data.url === '/schedule') && !$subsPlease)
- fetch(root(`/api/subsplease?tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}`))
- .then((r) => r.json())
- .then((r) => {
- for (const day in subtitles) {
- if (!r.schedule[day]) r.schedule[day] = [];
-
- (subtitles[day as keyof typeof subtitles] as SubsPleaseEpisode[]).forEach((episode) => {
- r.schedule[day].push({
- title: episode.title,
- page: episode.page || '',
- image_url: episode.image_url || '',
- time: episode.time
- });
- });
- }
-
- subsPlease.set(r);
- });
- }
+ import type { SubsPleaseEpisode } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
+ import { env } from '$env/dynamic/public';
+ import { userIdentity as getUserIdentity } from '$lib/Data/AniList/identity';
+ import { onDestroy, onMount } from 'svelte';
+ import userIdentity from '$stores/identity';
+ import settings from '$stores/settings';
+ import { browser } from '$app/environment';
+ import HeadTitle from '$lib/Home/HeadTitle.svelte';
+ import '../app.css';
+ import { readable, type Readable } from 'svelte/store';
+ import { navigating } from '$app/stores';
+ import Notifications from 'svelte-notifications';
+ import * as EventNotification from '$lib/Notification/Notification.svelte';
+ import Root from '$lib/Home/Root.svelte';
+ import root from '$lib/Utility/root';
+ import { addMessages, init, locale as i18nLocale, locales } from 'svelte-i18n';
+ import english from '$lib/Locale/english';
+ import japanese from '$lib/Locale/japanese';
+ import type { LocaleDictionary } from '$lib/Locale/layout';
+ import locale from '$stores/locale';
+ import Skeleton from '$lib/Loading/Skeleton.svelte';
+ import subsPlease from '$stores/subsPlease';
+ import Dropdown from '$lib/Layout/Dropdown.svelte';
+ import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
+ import subtitles from '$lib/Data/Static/subtitles.json';
+ import settingsSyncPulled from '$stores/settingsSyncPulled';
+ import settingsSyncTimes from '$stores/settingsSyncTimes';
+ import Announcement from '$lib/Announcement.svelte';
+ import Message from '$lib/Loading/Message.svelte';
+ import { requestNotifications } from '$lib/Utility/notifications';
+ import { database as userDatabase } from '$lib/Database/IDB/user';
+
+ injectSpeedInsights();
+
+ export let data;
+
+ let isHeaderVisible = true;
+ let previousScrollPosition = 0;
+ let notificationInterval: NodeJS.Timeout | undefined = undefined;
+
+ addMessages('en', english as unknown as LocaleDictionary);
+ addMessages('ja', japanese as unknown as LocaleDictionary);
+ init({ fallbackLocale: 'en', initialLocale: $settings.displayLanguage });
+
+ $: i18nLocale.set($settings.displayLanguage);
+
+ const navigationOrder = ['/', '/completed', '/schedule', '/updates', '/tools', '/settings'];
+ const previousPage: Readable<string | null> = readable(null, (set) => {
+ const unsubscribe = navigating.subscribe(($navigating) => {
+ if ($navigating && $navigating.from) set($navigating.from.url.pathname as unknown as null);
+ });
+
+ return () => unsubscribe();
+ });
+
+ $: way = data.url.includes('/user')
+ ? 200
+ : $previousPage && $previousPage.includes('/user')
+ ? -200
+ : navigationOrder.indexOf(data.url) > navigationOrder.indexOf($previousPage ?? '/')
+ ? 200
+ : -200;
+
+ const handleScroll = () => {
+ const currentScrollPosition = window.scrollY;
+
+ isHeaderVisible =
+ currentScrollPosition <= 100 || currentScrollPosition < previousScrollPosition;
+ previousScrollPosition = currentScrollPosition;
+ };
+
+ onMount(async () => {
+ if (browser) {
+ if (localStorage.getItem('redirect')) {
+ window.location.href = localStorage.getItem('redirect') ?? '/';
+
+ localStorage.removeItem('redirect');
+ }
+
+ window.addEventListener('scroll', handleScroll);
+
+ if (localStorage.getItem('commit') !== data.commit) {
+ localStorage.removeItem('identity');
+ localStorage.removeItem('anime');
+ localStorage.removeItem('manga');
+ localStorage.removeItem('lastPruneTimes');
+ localStorage.setItem('commit', data.commit);
+ }
+ }
+
+ settings.get();
+
+ if (data.user !== undefined && $userIdentity.id === -2)
+ getUserIdentity(data.user).then((h) => userIdentity.set(h));
+
+ if ($settings.settingsSync && $userIdentity.id !== -2)
+ fetch(root(`/api/configuration?id=${$userIdentity.id}`)).then((response) => {
+ if (response.ok)
+ response.json().then((data) => {
+ if (data && data.configuration) {
+ console.log('Pulled remote configuration');
+ settings.set(data.configuration);
+ settingsSyncPulled.set(true);
+ settingsSyncTimes.set({
+ lastPull: new Date(),
+ lastPush: new Date(data.updated_at + 'Z')
+ });
+ }
+ });
+ });
+
+ if (!(await userDatabase.users.get($userIdentity.id)))
+ userDatabase.users.put({
+ id: $userIdentity.id,
+ user: data.user,
+ lastNotificationID: null
+ });
+
+ if ($settings.displayAniListNotifications && data.user !== undefined)
+ if ('Notification' in window && navigator.serviceWorker) requestNotifications();
+ });
+
+ onDestroy(() => {
+ if (browser) window.removeEventListener('scroll', handleScroll);
+
+ if (notificationInterval) clearInterval(notificationInterval);
+ });
+
+ $: {
+ if ((data.url === '/' || data.url === '/completed' || data.url === '/schedule') && !$subsPlease)
+ fetch(root(`/api/subsplease?tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}`))
+ .then((r) => r.json())
+ .then((r) => {
+ for (const day in subtitles) {
+ if (!r.schedule[day]) r.schedule[day] = [];
+
+ (subtitles[day as keyof typeof subtitles] as SubsPleaseEpisode[]).forEach((episode) => {
+ r.schedule[day].push({
+ title: episode.title,
+ page: episode.page || '',
+ image_url: episode.image_url || '',
+ time: episode.time
+ });
+ });
+ }
+
+ subsPlease.set(r);
+ });
+ }
</script>
<HeadTitle />
{#if !$locales.includes('en') || !$locale().navigation}
- <Message />
-
- {#if data.url === '/settings'}
- <Skeleton grid={true} count={1} height="10vh" />
- <Skeleton grid={true} count={1} height="30vh" />
- <Skeleton grid={true} count={1} height="30vh" />
- {:else}
- <Skeleton grid={true} count={1} height="10vh" />
- <Skeleton grid={true} count={1} height="80vh" />
- {/if}
+ <Message />
+
+ {#if data.url === '/settings'}
+ <Skeleton grid={true} count={1} height="10vh" />
+ <Skeleton grid={true} count={1} height="30vh" />
+ <Skeleton grid={true} count={1} height="30vh" />
+ {:else}
+ <Skeleton grid={true} count={1} height="10vh" />
+ <Skeleton grid={true} count={1} height="80vh" />
+ {/if}
{:else}
- <Announcement />
-
- <div class="container">
- <div class="card card-centered header" class:header-hidden={!isHeaderVisible}>
- <div>
- <a href={root('/')} class="header-item">{$locale().navigation.home}</a><a
- href={root('/completed')}
- class="header-item"
- >
- {$locale().navigation.completed}
- </a>
- <Dropdown
- items={[
- { name: $locale().navigation.subtitleSchedule, url: root('/schedule') },
- { name: $locale().navigation.hololive, url: root('/hololive') },
- { name: $locale().tools.tool.characterBirthdays.short, url: root('/birthdays') },
- { name: $locale().navigation.newReleases, url: root('/updates') }
- ]}
- header={false}
- >
- <span slot="title" class="header-item">{$locale().navigation.schedule}</span>
- </Dropdown>
- <a href={root('/tools')} class="header-item">{$locale().navigation.tools}</a>
- <a href={root('/settings')} class="header-item">{$locale().navigation.settings}</a>
-
- <span class="header-item opaque separator">•</span>
-
- {#if data.user}
- <Dropdown
- items={[
- { name: $locale().navigation.myProfile, url: root(`/user/${$userIdentity.name}`) },
- {
- name: $locale().navigation.myBadgeWall,
- url: root(`/user/${$userIdentity.name}/badges`)
- },
- {
- name: $locale().navigation.logOut,
- url: '#',
- preventDefault: true,
- onClick: () => {
- localStorage.removeItem('identity');
- localStorage.removeItem('commit');
-
- document.cookie = 'user=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
-
- window.location.href = root('/api/authentication/log-out');
- }
- }
- ]}
- header={false}
- >
- <span slot="title" class="header-item">
- {$locale().navigation.profile}
- </span>
- </Dropdown>
- {/if}
-
- {#if data.user === undefined}
- <a
- class="header-item"
- href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
- on:click={() => {
- localStorage.setItem(
- 'redirect',
- window.location.origin + window.location.pathname + window.location.search
- );
- }}
- >
- {$locale().navigation.logIn}
- </a>
- {:else if data.user}
- <a href={root(`/user/${$userIdentity.name}`)} class="header-item">
- <img class="avatar" src={$userIdentity.avatar} alt="Avatar" />
- </a>
- {/if}
- </div>
- </div>
-
- <p />
-
- <Notifications item={EventNotification} zIndex={5000}>
- <Root {data} {way}>
- {#if $userIdentity.id !== -1}
- <slot />
- {:else if data.url === '/settings'}
- <Skeleton grid={true} count={1} height="10vh" />
- <Skeleton grid={true} count={1} height="30vh" />
- <Skeleton grid={true} count={1} height="30vh" />
- {:else}
- <Skeleton grid={true} count={1} height="10vh" />
- <Skeleton grid={true} count={1} height="80vh" />
- {/if}
- </Root>
- </Notifications>
- </div>
+ <Announcement />
+
+ <div class="container">
+ <div class="card card-centered header" class:header-hidden={!isHeaderVisible}>
+ <div>
+ <a href={root('/')} class="header-item">{$locale().navigation.home}</a><a
+ href={root('/completed')}
+ class="header-item"
+ >
+ {$locale().navigation.completed}
+ </a>
+ <Dropdown
+ items={[
+ { name: $locale().navigation.subtitleSchedule, url: root('/schedule') },
+ { name: $locale().navigation.hololive, url: root('/hololive') },
+ { name: $locale().tools.tool.characterBirthdays.short, url: root('/birthdays') },
+ { name: $locale().navigation.newReleases, url: root('/updates') }
+ ]}
+ header={false}
+ >
+ <span slot="title" class="header-item">{$locale().navigation.schedule}</span>
+ </Dropdown>
+ <a href={root('/tools')} class="header-item">{$locale().navigation.tools}</a>
+ <a href={root('/settings')} class="header-item">{$locale().navigation.settings}</a>
+
+ <span class="header-item opaque separator">•</span>
+
+ {#if data.user}
+ <Dropdown
+ items={[
+ { name: $locale().navigation.myProfile, url: root(`/user/${$userIdentity.name}`) },
+ {
+ name: $locale().navigation.myBadgeWall,
+ url: root(`/user/${$userIdentity.name}/badges`)
+ },
+ {
+ name: $locale().navigation.logOut,
+ url: '#',
+ preventDefault: true,
+ onClick: () => {
+ localStorage.removeItem('identity');
+ localStorage.removeItem('commit');
+
+ document.cookie = 'user=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
+
+ window.location.href = root('/api/authentication/log-out');
+ }
+ }
+ ]}
+ header={false}
+ >
+ <span slot="title" class="header-item">
+ {$locale().navigation.profile}
+ </span>
+ </Dropdown>
+ {/if}
+
+ {#if data.user === undefined}
+ <a
+ class="header-item"
+ href={`https://anilist.co/api/v2/oauth/authorize?client_id=${env.PUBLIC_ANILIST_CLIENT_ID}&redirect_uri=${env.PUBLIC_ANILIST_REDIRECT_URI}&response_type=code`}
+ on:click={() => {
+ localStorage.setItem(
+ 'redirect',
+ window.location.origin + window.location.pathname + window.location.search
+ );
+ }}
+ >
+ {$locale().navigation.logIn}
+ </a>
+ {:else if data.user}
+ <a href={root(`/user/${$userIdentity.name}`)} class="header-item">
+ <img class="avatar" src={$userIdentity.avatar} alt="Avatar" />
+ </a>
+ {/if}
+ </div>
+ </div>
+
+ <p />
+
+ <Notifications item={EventNotification} zIndex={5000}>
+ <Root {data} {way}>
+ {#if $userIdentity.id !== -1}
+ <slot />
+ {:else if data.url === '/settings'}
+ <Skeleton grid={true} count={1} height="10vh" />
+ <Skeleton grid={true} count={1} height="30vh" />
+ <Skeleton grid={true} count={1} height="30vh" />
+ {:else}
+ <Skeleton grid={true} count={1} height="10vh" />
+ <Skeleton grid={true} count={1} height="80vh" />
+ {/if}
+ </Root>
+ </Notifications>
+ </div>
{/if}
<style lang="scss">
- .header {
- font-family: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
- Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- font-size: 1.05em;
- font-weight: 600;
- padding: 0.8rem 0.4rem;
- z-index: 4;
- position: sticky;
- top: 1.25rem;
- transition: transform 0.3s ease;
- }
-
- .header-hidden {
- transform: translateY(-150%);
- }
-
- :global(html.light-theme) {
- filter: invert(0);
- }
-
- :global(html.light-theme img) {
- filter: invert(0);
- }
-
- .container {
- height: 100%;
- display: flex;
- flex-direction: column;
- }
-
- :global(a) {
- text-decoration: none;
- transition: all 0.15s ease-in-out;
- }
-
- :global(a:hover) {
- text-decoration: underline;
- }
-
- .header-item {
- margin: 0 0.5rem;
- color: var(--base06);
- white-space: nowrap;
- }
-
- .header-item:hover {
- text-decoration: none;
- }
-
- .header-item:active {
- outline: none;
- }
-
- .avatar {
- width: 2em;
- display: inline-block;
- vertical-align: middle;
- border-radius: 8px;
- box-shadow: 0 1.5px 9px var(--base01), 0 0 0 4px var(--base0E), 0 4px 30px var(--base01);
- }
-
- .separator {
- color: var(--base04);
- }
+ .header {
+ font-family: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: 1.05em;
+ font-weight: 600;
+ padding: 0.8rem 0.4rem;
+ z-index: 4;
+ position: sticky;
+ top: 1.25rem;
+ transition: transform 0.3s ease;
+ }
+
+ .header-hidden {
+ transform: translateY(-150%);
+ }
+
+ :global(html.light-theme) {
+ filter: invert(0);
+ }
+
+ :global(html.light-theme img) {
+ filter: invert(0);
+ }
+
+ .container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+
+ :global(a) {
+ text-decoration: none;
+ transition: all 0.15s ease-in-out;
+ }
+
+ :global(a:hover) {
+ text-decoration: underline;
+ }
+
+ .header-item {
+ margin: 0 0.5rem;
+ color: var(--base06);
+ white-space: nowrap;
+ }
+
+ .header-item:hover {
+ text-decoration: none;
+ }
+
+ .header-item:active {
+ outline: none;
+ }
+
+ .avatar {
+ width: 2em;
+ display: inline-block;
+ vertical-align: middle;
+ border-radius: 8px;
+ box-shadow: 0 1.5px 9px var(--base01), 0 0 0 4px var(--base0E), 0 4px 30px var(--base01);
+ }
+
+ .separator {
+ color: var(--base04);
+ }
</style>