183 lines
4.7 KiB
Svelte
183 lines
4.7 KiB
Svelte
<script lang="ts">
|
|
import { untrack } from "svelte";
|
|
import { auth } from "./auth/auth.svelte";
|
|
import { connectionBuilder, getStdbHost, getStdbDbName } from "./config";
|
|
import InnerSpacetimeDBProvider from "./InnerSpacetimeDBProvider.svelte";
|
|
|
|
let { children, onCancel } = $props<{
|
|
children: any,
|
|
onCancel?: () => void
|
|
}>();
|
|
|
|
// 1. Connection Builder Lifecycle
|
|
// We MUST wait for OIDC to finish its loading/silent-renew phase
|
|
// before we construct the initial SpacetimeDB connection.
|
|
let builder = $state<any>(null);
|
|
let lastUsedOidcToken = $state<string | undefined>(undefined);
|
|
let providerKey = $state(0);
|
|
|
|
$effect(() => {
|
|
// Hold off until OIDC is settled
|
|
if (auth.isLoading) return;
|
|
|
|
const currentToken = auth.user?.id_token;
|
|
|
|
// Only (re)create the builder if we don't have one, or if the token changed.
|
|
if (!builder || currentToken !== lastUsedOidcToken) {
|
|
console.log(`[SpacetimeProvider] Initializing connection (Auth Settled). OIDC present: ${!!currentToken}`);
|
|
|
|
untrack(() => {
|
|
builder = connectionBuilder(currentToken);
|
|
lastUsedOidcToken = currentToken;
|
|
providerKey += 1; // Force re-mount of InnerSpacetimeDBProvider for a clean handshake
|
|
});
|
|
}
|
|
});
|
|
|
|
// Reactive labels for the loading screen
|
|
const host = getStdbHost();
|
|
const dbName = getStdbDbName();
|
|
|
|
// Unified status message logic
|
|
const statusInfo = $derived.by(() => {
|
|
if (auth.isLoading) {
|
|
return {
|
|
title: "Authenticating...",
|
|
icon: "fa-id-card",
|
|
message: "Verifying your identity with the provider."
|
|
};
|
|
}
|
|
|
|
if (!builder) {
|
|
return {
|
|
title: "Preparing Handshake...",
|
|
icon: "fa-key",
|
|
message: "Finalizing security credentials."
|
|
};
|
|
}
|
|
|
|
// Default connecting state
|
|
return {
|
|
title: "Connecting to SpacetimeDB...",
|
|
icon: "fa-circle-notch",
|
|
spin: true,
|
|
message: "Establishing a secure connection to the chat server."
|
|
};
|
|
});
|
|
</script>
|
|
|
|
{#if builder && !auth.isLoading}
|
|
{#key providerKey}
|
|
<InnerSpacetimeDBProvider
|
|
{builder}
|
|
{onCancel}
|
|
{host}
|
|
{dbName}
|
|
oidcToken={auth.user?.id_token}
|
|
>
|
|
{@render children()}
|
|
</InnerSpacetimeDBProvider>
|
|
{/key}
|
|
{:else}
|
|
<div class="login-screen">
|
|
<div class="login-card" style="text-align: center;">
|
|
<i class="fas {statusInfo.icon} {statusInfo.spin ? 'fa-spin' : 'fa-pulse'}" style="font-size: 3rem; color: var(--brand); margin-bottom: 20px;"></i>
|
|
<h1>{statusInfo.title}</h1>
|
|
<p style="color: var(--text-muted); margin-top: 8px; margin-bottom: 24px;">{statusInfo.message}</p>
|
|
|
|
<div class="connection-details-box">
|
|
<div class="connection-detail">
|
|
<div class="detail-label">Host</div>
|
|
<div class="detail-value">{host}</div>
|
|
</div>
|
|
<div class="connection-detail">
|
|
<div class="detail-label">Database</div>
|
|
<div class="detail-value">{dbName}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{#if onCancel}
|
|
<button
|
|
onclick={onCancel}
|
|
class="btn-secondary"
|
|
style="width: 100%;"
|
|
>
|
|
Cancel
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.login-screen {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
width: 100vw;
|
|
background: radial-gradient(
|
|
circle at center,
|
|
var(--background-secondary) 0%,
|
|
var(--background-tertiary) 100%
|
|
);
|
|
background-color: var(--background-tertiary);
|
|
}
|
|
|
|
.login-card {
|
|
background-color: var(--background-primary);
|
|
padding: 32px;
|
|
border-radius: 8px;
|
|
width: 480px;
|
|
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.login-card h1 {
|
|
color: var(--header-primary);
|
|
margin-bottom: 8px;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.login-card p {
|
|
color: var(--text-normal);
|
|
margin-bottom: 24px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.connection-details-box {
|
|
background-color: var(--background-tertiary);
|
|
padding: 16px;
|
|
border-radius: 8px;
|
|
margin-bottom: 24px;
|
|
text-align: left;
|
|
font-size: 0.8rem;
|
|
border: 1px solid var(--background-modifier-accent);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.detail-label {
|
|
color: var(--text-muted);
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
font-size: 0.65rem;
|
|
margin-bottom: 4px;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.detail-value {
|
|
color: var(--text-normal);
|
|
font-family: var(--font-code);
|
|
word-break: break-all;
|
|
line-height: 1.4;
|
|
}
|
|
</style>
|