Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: loading screen, initSDK on bootstrap, fix FOUC for theme #10350

Merged
merged 11 commits into from
Aug 25, 2024
158 changes: 136 additions & 22 deletions web/src/app.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" class="dark">
<html>
<head>
<!-- (used for SSR) -->
<!-- metadata:tags -->
Expand All @@ -14,35 +14,96 @@
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180.png" />
%sveltekit.head%
<style>
/* prevent FOUC */
html {
height: 100%;
width: 100%;
}
body,
html {
margin: 0;
padding: 0;
}
@keyframes delayedVisibility {
to {
visibility: visible;
}
}
@keyframes stencil-pulse {
0% {
transform: scale(0.93);
filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0.7));
}

70% {
transform: scale(1);
filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0));
}

100% {
transform: scale(0.93);
filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0));
}
}
@keyframes loadspin {
100% {
transform: rotate(360deg);
}
}
#stencil svg {
height: 35%;
animation: stencil-pulse 1s linear infinite;
}
#stencil {
--stencil-width: 25vw;
display: flex;
width: var(--stencil-width);
margin-left: auto;
margin-right: auto;
margin-top: calc(50vh - var(--stencil-width) / 2);
margin-bottom: 100vh;
place-items: center;
justify-content: center;
overflow: hidden;
visibility: hidden;
animation:
0s linear 0.3s forwards delayedVisibility,
loadspin 2s linear infinite;
}
.bg-immich-bg {
background-color: white;
}
.dark .dark\:bg-immich-dark-bg {
background-color: black;
}
</style>
<script>
/**
* Prevent FOUC on page load.
*/
const colorThemeKeyName = 'color-theme';
let theme;
const fallbackTheme = { value: 'dark', system: false };
let item = localStorage.getItem(colorThemeKeyName);
if (item === 'dark' || item === 'light') {
fallbackTheme.value = item;
item = JSON.stringify(fallbackTheme);
localStorage.setItem(colorThemeKeyName, item);
}

theme = JSON.parse(localStorage.getItem(colorThemeKeyName));

if (theme) {
if (theme.system) {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme.value = 'dark';
} else {
theme.value = 'light';
}
}

let theme = localStorage.getItem(colorThemeKeyName);
if (!theme) {
theme = { value: 'light', system: true };
} else if (theme === 'dark' || theme === 'light') {
theme = { value: item, system: false };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line causes a JavaScript error item is undefined and one is unable to login. I suppose this was supposed to be value: theme?

localStorage.setItem(colorThemeKeyName, JSON.stringify(theme));
} else {
theme = fallbackTheme;
theme = JSON.parse(theme);
}

if (theme.value === 'light') {
let themeValue = theme.value;
if (theme.system) {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
themeValue = 'dark';
} else {
themeValue = 'light';
}
}

if (themeValue === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
Expand All @@ -53,6 +114,59 @@
</head>

<body class="bg-immich-bg dark:bg-immich-dark-bg">
<div id="stencil">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792">
<style type="text/css">
.st0 {
fill: #fa2921;
}
.st1 {
fill: #ed79b5;
}
.st2 {
fill: #ffb400;
}
.st3 {
fill: #1e83f7;
}
.st4 {
fill: #18c249;
}
</style>
<g>
<path
class="st0"
d="M375.48,267.63c38.64,34.21,69.78,70.87,89.82,105.42c34.42-61.56,57.42-134.71,57.71-181.3
c0-0.33,0-0.63,0-0.91c0-68.94-68.77-95.77-128.01-95.77s-128.01,26.83-128.01,95.77c0,0.94,0,2.2,0,3.72
C300.01,209.24,339.15,235.47,375.48,267.63z"
/>
<path
class="st1"
d="M164.7,455.63c24.15-26.87,61.2-55.99,103.01-80.61c44.48-26.18,88.97-44.47,128.02-52.84
c-47.91-51.76-110.37-96.24-154.6-110.91c-0.31-0.1-0.6-0.19-0.86-0.28c-65.57-21.3-112.34,35.81-130.64,92.15
c-18.3,56.34-14.04,130.04,51.53,151.34C162.05,454.77,163.25,455.16,164.7,455.63z"
/>
<path
class="st2"
d="M681.07,302.19c-18.3-56.34-65.07-113.45-130.64-92.15c-0.9,0.29-2.1,0.68-3.54,1.15
c-3.75,35.93-16.6,81.27-35.96,125.76c-20.59,47.32-45.84,88.27-72.51,118c69.18,13.72,145.86,12.98,190.26-1.14
c0.31-0.1,0.6-0.2,0.86-0.28C695.11,432.22,699.37,358.52,681.07,302.19z"
/>
<path
class="st3"
d="M336.54,510.71c-11.15-50.39-14.8-98.36-10.7-138.08c-64.03,29.57-125.63,75.23-153.26,112.76
c-0.19,0.26-0.37,0.51-0.53,0.73c-40.52,55.78-0.66,117.91,47.27,152.72c47.92,34.82,119.33,53.54,159.86-2.24
c0.56-0.76,1.3-1.78,2.19-3.01C363.28,602.32,347.02,558.08,336.54,510.71z"
/>
<path
class="st4"
d="M617.57,482.52c-35.33,7.54-82.42,9.33-130.72,4.66c-51.37-4.96-98.11-16.32-134.63-32.5
c8.33,70.03,32.73,142.73,59.88,180.6c0.19,0.26,0.37,0.51,0.53,0.73c40.52,55.78,111.93,37.06,159.86,2.24
c47.92-34.82,87.79-96.95,47.27-152.72C619.2,484.77,618.46,483.75,617.57,482.52z"
/>
</g>
</svg>
</div>
<div>%sveltekit.body%</div>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { getConfig, getConfigDefaults, updateConfig, type SystemConfigDto } from '@immich/sdk';
import { loadConfig } from '$lib/stores/server-config.store';
import { retrieveServerConfig } from '$lib/stores/server-config.store';
import { cloneDeep, isEqual } from 'lodash-es';
import { onMount } from 'svelte';
import type { SettingsResetOptions } from './admin-settings';
Expand Down Expand Up @@ -39,7 +39,7 @@
savedConfig = cloneDeep(newConfig);
notificationController.show({ message: $t('settings_saved'), type: NotificationType.Info });

await loadConfig();
await retrieveServerConfig();
} catch (error) {
handleError(error, $t('errors.unable_to_save_settings'));
}
Expand Down
105 changes: 105 additions & 0 deletions web/src/lib/components/error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { copyToClipboard } from '$lib/utils';
import { mdiCodeTags, mdiContentCopy, mdiMessage, mdiPartyPopper } from '@mdi/js';
import { t } from 'svelte-i18n';

export let error: { message: string; code?: string | number; stack?: string } | undefined | null = undefined;

const handleCopy = async () => {
if (!error) {
return;
}

await copyToClipboard(`${error.message} - ${error.code}\n${error.stack}`);
};
</script>

<div class="h-screen w-screen">
<section class="bg-immich-bg dark:bg-immich-dark-bg">
<div class="flex place-items-center border-b px-6 py-4 dark:border-b-immich-dark-gray">
<a class="flex place-items-center gap-2 hover:cursor-pointer" href="/photos">
<ImmichLogo width="55%" />
</a>
</div>
</section>

<div class="fixed top-0 flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50">
<div>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>
<div>
<div class="flex items-center justify-between gap-4 px-4 py-4">
<h1 class="font-medium text-immich-primary dark:text-immich-dark-primary">
🚨 {$t('error_title')}
</h1>
<div class="flex justify-end">
<CircleIconButton
color="primary"
icon={mdiContentCopy}
title={$t('copy_error')}
on:click={() => handleCopy()}
/>
</div>
</div>

<hr />

<div class="immich-scrollbar max-h-[75vh] min-h-[300px] gap-4 overflow-y-auto p-4 pb-4">
<div class="flex w-full flex-col gap-2">
<p class="text-red-500">{error?.message} ({error?.code})</p>
{#if error?.stack}
<label for="stacktrace">{$t('stacktrace')}</label>
<pre id="stacktrace" class="text-xs">{error?.stack || 'No stack'}</pre>
{/if}
</div>
</div>

<hr />

<div class="flex place-content-center place-items-center justify-around">
<!-- href="https://github.com/immich-app/immich/issues/new" -->
<a
href="https://discord.immich.app"
target="_blank"
rel="noopener noreferrer"
class="flex grow basis-0 justify-center p-4"
>
<div class="flex flex-col place-content-center place-items-center gap-2">
<Icon path={mdiMessage} size={24} />
<p class="text-sm">{$t('get_help')}</p>
</div>
</a>

<a
href="https://github.com/immich-app/immich/releases"
target="_blank"
rel="noopener noreferrer"
class="flex grow basis-0 justify-center p-4"
>
<div class="flex flex-col place-content-center place-items-center gap-2">
<Icon path={mdiPartyPopper} size={24} />
<p class="text-sm">{$t('read_changelog')}</p>
</div>
</a>

<a
href="https://immich.app/docs/guides/docker-help"
target="_blank"
rel="noopener noreferrer"
class="flex grow basis-0 justify-center p-4"
>
<div class="flex flex-col place-content-center place-items-center gap-2">
<Icon path={mdiCodeTags} size={24} />
<p class="text-sm">{$t('check_logs')}</p>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
2 changes: 2 additions & 0 deletions web/src/lib/components/forms/admin-registration-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import Button from '../elements/buttons/button.svelte';
import PasswordField from '../shared-components/password-field.svelte';
import { t } from 'svelte-i18n';
import { retrieveServerConfig } from '$lib/stores/server-config.store';

let email = '';
let password = '';
Expand All @@ -31,6 +32,7 @@

try {
await signUpAdmin({ signUpDto: { email, password, name } });
await retrieveServerConfig();
await goto(AppRoute.AUTH_LOGIN);
} catch (error) {
handleError(error, $t('errors.unable_to_create_admin_account'));
Expand Down
6 changes: 2 additions & 4 deletions web/src/lib/components/forms/login-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
import { oauth } from '$lib/utils';
import { getServerErrorMessage, handleError } from '$lib/utils/handle-error';
import { getServerConfig, login } from '@immich/sdk';
import { login } from '@immich/sdk';
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import Button from '../elements/buttons/button.svelte';
Expand Down Expand Up @@ -58,11 +58,9 @@
try {
errorMessage = '';
loading = true;

const user = await login({ loginCredentialDto: { email, password } });
const serverConfig = await getServerConfig();

if (user.isAdmin && !serverConfig.isOnboarded) {
if (user.isAdmin && !$serverConfig.isOnboarded) {
await onOnboarding();
return;
}
Expand Down
2 changes: 1 addition & 1 deletion web/src/lib/stores/server-config.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const serverConfig = writable<ServerConfig>({
externalDomain: '',
});

export const loadConfig = async () => {
export const retrieveServerConfig = async () => {
const [flags, config] = await Promise.all([getServerFeatures(), getServerConfig()]);

featureFlags.update(() => ({ ...flags, loaded: true }));
Expand Down
2 changes: 1 addition & 1 deletion web/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface DownloadRequestOptions<T = unknown> {
onDownloadProgress?: (event: ProgressEvent<XMLHttpRequestEventTarget>) => void;
}

export const initApp = async () => {
export const initLanguage = async () => {
const preferenceLang = get(lang);
for (const { code, loader } of langs) {
register(code, loader);
Expand Down
21 changes: 21 additions & 0 deletions web/src/lib/utils/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { retrieveServerConfig } from '$lib/stores/server-config.store';
import { initLanguage } from '$lib/utils';
import { defaults } from '@immich/sdk';
import { memoize } from 'lodash-es';

type fetchType = typeof fetch;

export function initSDK(fetch: fetchType) {
// set event.fetch on the fetch-client used by @immich/sdk
// https://kit.svelte.dev/docs/load#making-fetch-requests
// https://github.com/oazapfts/oazapfts/blob/main/README.md#fetch-options
defaults.fetch = fetch;
}

async function _init(fetch: fetchType) {
initSDK(fetch);
await initLanguage();
await retrieveServerConfig();
}

export const init = memoize(_init, () => 'singlevalue');
Loading
Loading