199 lines
5.0 KiB
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>
|