mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-22 15:00:07 +00:00
Co-authored-by: Kyle Mendell <kmendell@ofkm.us> Co-authored-by: Kyle Mendell <ksm@ofkm.us>
132 lines
4.3 KiB
Svelte
132 lines
4.3 KiB
Svelte
<script module lang="ts">
|
|
// Persist the last failing background image URL across route remounts so
|
|
// login pages without a background do not briefly retry the image and stutter.
|
|
let persistedMissingBackgroundImageUrl: string | undefined;
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { afterNavigate } from '$app/navigation';
|
|
import { page } from '$app/state';
|
|
import { m } from '$lib/paraglide/messages';
|
|
import appConfigStore from '$lib/stores/application-configuration-store';
|
|
import { cachedBackgroundImage } from '$lib/utils/cached-image-util';
|
|
import { cn } from '$lib/utils/style';
|
|
import { type Snippet } from 'svelte';
|
|
import { MediaQuery } from 'svelte/reactivity';
|
|
import * as Card from './ui/card';
|
|
|
|
let {
|
|
children,
|
|
showAlternativeSignInMethodButton = false
|
|
}: {
|
|
children: Snippet;
|
|
showAlternativeSignInMethodButton?: boolean;
|
|
} = $props();
|
|
|
|
let missingBackgroundImageUrl = $state<string | undefined>(persistedMissingBackgroundImageUrl);
|
|
let loadedBackgroundImageUrl = $state<string | undefined>();
|
|
let isInitialLoad = $state(false);
|
|
let backgroundImageUrl = $derived(cachedBackgroundImage.getUrl());
|
|
let imageError = $derived(missingBackgroundImageUrl === backgroundImageUrl);
|
|
let imageLoaded = $derived(loadedBackgroundImageUrl === backgroundImageUrl);
|
|
let animate = $derived(isInitialLoad && imageLoaded && !$appConfigStore.disableAnimations);
|
|
|
|
afterNavigate((e) => {
|
|
isInitialLoad = !e?.from?.url;
|
|
});
|
|
|
|
function onBackgroundImageLoad() {
|
|
loadedBackgroundImageUrl = backgroundImageUrl;
|
|
if (persistedMissingBackgroundImageUrl === backgroundImageUrl) {
|
|
persistedMissingBackgroundImageUrl = undefined;
|
|
missingBackgroundImageUrl = undefined;
|
|
}
|
|
}
|
|
|
|
function onBackgroundImageError() {
|
|
loadedBackgroundImageUrl = undefined;
|
|
persistedMissingBackgroundImageUrl = backgroundImageUrl;
|
|
missingBackgroundImageUrl = backgroundImageUrl;
|
|
}
|
|
|
|
const isDesktop = new MediaQuery('min-width: 1024px');
|
|
let alternativeSignInButton = $state({
|
|
href: '/login/alternative',
|
|
label: m.alternative_sign_in_methods()
|
|
});
|
|
|
|
appConfigStore.subscribe((config) => {
|
|
if (config.emailOneTimeAccessAsUnauthenticatedEnabled) {
|
|
alternativeSignInButton.href = '/login/alternative';
|
|
alternativeSignInButton.label = m.alternative_sign_in_methods();
|
|
} else {
|
|
alternativeSignInButton.href = '/login/alternative/code';
|
|
alternativeSignInButton.label = m.sign_in_with_login_code();
|
|
}
|
|
|
|
if (page.url.pathname != '/login') {
|
|
alternativeSignInButton.href = `${alternativeSignInButton.href}?redirect=${encodeURIComponent(page.url.pathname + page.url.search)}`;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{#if isDesktop.current}
|
|
<div class="h-screen items-center overflow-hidden text-center flex justify-center">
|
|
<div
|
|
class="flex h-full w-[650px] 2xl:w-[800px] p-16 {cn(
|
|
showAlternativeSignInMethodButton && 'pb-0'
|
|
)}"
|
|
>
|
|
<div class="flex h-full w-full flex-col overflow-hidden">
|
|
<div class="relative flex grow flex-col items-center justify-center overflow-auto p-1">
|
|
{@render children()}
|
|
</div>
|
|
{#if showAlternativeSignInMethodButton}
|
|
<div class="mb-4 flex items-center justify-center">
|
|
<a
|
|
href={alternativeSignInButton.href}
|
|
class="text-muted-foreground text-xs transition-colors hover:underline"
|
|
>
|
|
{alternativeSignInButton.label}
|
|
</a>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
{#if !imageError}
|
|
<!-- Background image -->
|
|
<div class="m-6 flex h-[calc(100vh-3rem)] overflow-hidden rounded-[40px]">
|
|
<img
|
|
src={backgroundImageUrl}
|
|
class="h-full object-cover {cn(animate && 'animate-bg-zoom')}"
|
|
alt={m.login_background()}
|
|
onload={onBackgroundImageLoad}
|
|
onerror={onBackgroundImageError}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div
|
|
class="flex h-screen items-center justify-center bg-cover bg-center text-center"
|
|
style="background-image: url({cachedBackgroundImage.getUrl()});"
|
|
>
|
|
<Card.Root class="mx-3 w-full max-w-md">
|
|
<Card.CardContent
|
|
class="px-4 py-10 sm:p-10 {showAlternativeSignInMethodButton ? 'pb-3 sm:pb-3' : ''}"
|
|
>
|
|
{@render children()}
|
|
{#if showAlternativeSignInMethodButton}
|
|
<a
|
|
href={alternativeSignInButton.href}
|
|
class="text-muted-foreground mt-7 flex justify-center text-xs transition-colors hover:underline"
|
|
>
|
|
{alternativeSignInButton.label}
|
|
</a>
|
|
{/if}
|
|
</Card.CardContent>
|
|
</Card.Root>
|
|
</div>
|
|
{/if}
|