play/src/routes/loading/+page.svelte

199 lines
5.0 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
let status: 'verifying' | 'success' | 'error' = 'verifying';
let user: { username: string; global_name: string | null; display_name: string | null; avatar: string | null; id: string } | null = null;
let errorMsg = '';
// Where to go after success
$: nextParam = $page.url.searchParams.get('next');
$: safeNext = nextParam && nextParam.startsWith('/') && !nextParam.startsWith('//') ? nextParam : '/';
onMount(async () => {
try {
const res = await fetch('/api/auth/me', { credentials: 'same-origin' });
const data = await res.json();
if (data.user) {
user = data.user;
// Brief "✓ Signed in" pause so the user sees the transition
setTimeout(() => {
status = 'success';
// Then navigate after another short beat
setTimeout(() => {
window.location.href = safeNext;
}, 350);
}, 250);
} else {
// No session — bounce to login with an error
errorMsg = 'Session not found. Please sign in again.';
status = 'error';
setTimeout(() => {
window.location.href = '/login?error=session_expired';
}, 1500);
}
} catch (e) {
errorMsg = e instanceof Error ? e.message : 'Network error';
status = 'error';
setTimeout(() => {
window.location.href = '/login?error=loading_failed';
}, 1500);
}
});
</script>
<svelte:head>
<title>Verifying — TheHowlingWhispers</title>
</svelte:head>
<main class="buffer">
<article class="card">
<p class="kicker">TheHowlingWhispers</p>
<div class="spinner-wrap">
{#if status === 'verifying'}
<div class="spinner" aria-hidden="true"></div>
{:else if status === 'success'}
<div class="check" aria-hidden="true"></div>
{:else}
<div class="xmark" aria-hidden="true"></div>
{/if}
</div>
<h1>
{#if status === 'verifying'}
Verifying your session…
{:else if status === 'success'}
Signed in{#if user} as {user.display_name || user.global_name || user.username}{/if}
{:else}
Could not verify session
{/if}
</h1>
{#if user}
<p class="who">
{#if user.avatar}
<img class="avatar" src="https://cdn.discordapp.com/avatars/{user.id}/{user.avatar}.png?size=64" alt="" />
{:else}
<span class="avatar fallback">{(user.display_name || user.global_name || user.username).charAt(0).toUpperCase()}</span>
{/if}
<span class="who-name">{user.display_name || user.global_name || user.username}</span>
</p>
{/if}
{#if status === 'error'}
<p class="error">{errorMsg}</p>
{/if}
</article>
</main>
<style>
.buffer {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 24px;
background: var(--bg-void);
}
.card {
max-width: 420px;
width: 100%;
text-align: center;
}
.kicker {
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 10px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--accent-blush);
margin-bottom: 32px;
}
h1 {
font-size: 22px;
font-weight: 600;
color: #fff;
margin-bottom: 16px;
}
.spinner-wrap {
margin: 0 auto 28px;
width: 56px;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
}
.spinner {
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.1);
border-top-color: var(--accent-blush);
border-radius: 50%;
animation: spin 0.9s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.check, .xmark {
width: 56px;
height: 56px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 28px;
font-weight: 700;
animation: pop 0.3s ease;
}
.check {
background: rgba(127, 191, 127, 0.18);
border: 2px solid rgba(127, 191, 127, 0.5);
color: #b8e6c0;
}
.xmark {
background: rgba(217, 106, 122, 0.18);
border: 2px solid rgba(217, 106, 122, 0.5);
color: #ffb3c0;
}
@keyframes pop {
from { transform: scale(0.6); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
.who {
display: inline-flex;
align-items: center;
gap: 10px;
color: var(--text-soft);
font-size: 14px;
margin: 0;
padding: 8px 14px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-strong);
border-radius: 100px;
}
.avatar {
width: 22px;
height: 22px;
border-radius: 50%;
}
.avatar.fallback {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
background: var(--accent-blush);
color: var(--text-on-accent);
font-weight: 700;
font-size: 11px;
}
.who-name { color: var(--text-main); font-weight: 500; }
.error {
color: #ffb3c0;
font-size: 13px;
margin-top: 12px;
}
</style>