Add newlyRegisteredToken state to NewsStore and implement account number display and copy functionality in the registration flow. Update UI to show success message upon registration and allow users to copy their account number to clipboard.
This commit is contained in:
@@ -53,6 +53,7 @@ class NewsStore {
|
||||
lastStatusCheck = $state<number>(Date.now());
|
||||
authInfo = $state<{ required: boolean; mode: string; canReg: boolean } | null>(null);
|
||||
isAuthenticated = $state(false);
|
||||
newlyRegisteredToken = $state<string | null>(null);
|
||||
|
||||
isWails = typeof window !== 'undefined' && ((window as any).runtime || (window as any).go);
|
||||
isCapacitor = typeof window !== 'undefined' && Capacitor.isNativePlatform();
|
||||
@@ -185,7 +186,7 @@ class NewsStore {
|
||||
const response = await fetch(`${apiBase}/auth/register`, { method: 'POST' });
|
||||
if (!response.ok) throw new Error('Registration failed');
|
||||
const data = await response.json();
|
||||
await this.login(data.accountNumber);
|
||||
this.newlyRegisteredToken = data.accountNumber;
|
||||
return data.accountNumber;
|
||||
} catch {
|
||||
toast.error('Could not generate account');
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
X,
|
||||
Hash,
|
||||
ChevronRight,
|
||||
Copy,
|
||||
Check,
|
||||
} from 'lucide-svelte';
|
||||
import { db } from '$lib/db';
|
||||
import { toast } from '$lib/toast.svelte';
|
||||
@@ -115,6 +117,18 @@
|
||||
let isResizing = $state(false);
|
||||
let paneWidth = $state(newsStore.settings.paneWidth || 40);
|
||||
let showDismissHatch = $state(false);
|
||||
let copied = $state(false);
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
copied = true;
|
||||
setTimeout(() => (copied = false), 2000);
|
||||
toast.success('Account number copied to clipboard');
|
||||
} catch {
|
||||
toast.error('Failed to copy');
|
||||
}
|
||||
}
|
||||
|
||||
function startResizing(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
@@ -280,62 +294,118 @@
|
||||
<div
|
||||
class="card p-8 w-full max-w-md space-y-8 animate-in fade-in slide-in-from-bottom-8 duration-500"
|
||||
>
|
||||
<div class="text-center space-y-2">
|
||||
<div
|
||||
class="w-16 h-16 bg-accent-blue rounded-2xl flex items-center justify-center text-white mx-auto shadow-xl shadow-accent-blue/20"
|
||||
>
|
||||
<Zap size={32} fill="currentColor" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold tracking-tight pt-4">Welcome to Web News</h1>
|
||||
<p class="text-text-secondary">Please enter your account number to continue</p>
|
||||
</div>
|
||||
{#if newsStore.newlyRegisteredToken}
|
||||
<div class="text-center space-y-4">
|
||||
<div
|
||||
class="w-16 h-16 bg-green-500 rounded-2xl flex items-center justify-center text-white mx-auto shadow-xl shadow-green-500/20"
|
||||
>
|
||||
<Check size={32} />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold tracking-tight pt-4">Account Created!</h1>
|
||||
<p class="text-text-secondary text-sm">
|
||||
Save your account number now. You will need it to log back in. <br />
|
||||
<strong class="text-red-500">We cannot recover this for you.</strong>
|
||||
</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<label for="token" class="text-sm font-medium">Account Number</label>
|
||||
<div class="relative">
|
||||
<Hash
|
||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary"
|
||||
size={18}
|
||||
/>
|
||||
<input
|
||||
id="token"
|
||||
type="text"
|
||||
placeholder="1234-5678-abcd-efgh"
|
||||
class="w-full bg-bg-secondary border border-border-color rounded-xl py-3 pl-10 pr-4 focus:ring-2 focus:ring-accent-blue/20 outline-none transition-all font-mono"
|
||||
bind:value={loginToken}
|
||||
onkeydown={(e) => e.key === 'Enter' && newsStore.login(loginToken)}
|
||||
/>
|
||||
<div class="relative group">
|
||||
<button
|
||||
type="button"
|
||||
class="w-full bg-accent-blue/5 border-2 border-accent-blue/20 rounded-xl p-4 font-mono text-lg text-accent-blue break-all text-center select-all cursor-pointer group-hover:border-accent-blue/40 transition-all"
|
||||
onclick={() => copyToClipboard(newsStore.newlyRegisteredToken!)}
|
||||
>
|
||||
{newsStore.newlyRegisteredToken}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute -top-3 -right-3 bg-accent-blue text-white p-2 rounded-lg shadow-lg hover:scale-110 transition-transform"
|
||||
onclick={() => copyToClipboard(newsStore.newlyRegisteredToken!)}
|
||||
>
|
||||
{#if copied}
|
||||
<Check size={16} />
|
||||
{:else}
|
||||
<Copy size={16} />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 space-y-3">
|
||||
<button
|
||||
class="w-full btn-primary py-4 rounded-xl text-lg font-bold shadow-xl shadow-accent-blue/20 flex items-center justify-center gap-2"
|
||||
onclick={() => {
|
||||
const token = newsStore.newlyRegisteredToken;
|
||||
newsStore.newlyRegisteredToken = null;
|
||||
newsStore.login(token!);
|
||||
}}
|
||||
>
|
||||
I've saved it, let's go!
|
||||
<ChevronRight size={20} />
|
||||
</button>
|
||||
<button
|
||||
class="w-full text-xs text-text-secondary hover:underline"
|
||||
onclick={() => (newsStore.newlyRegisteredToken = null)}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center space-y-2">
|
||||
<div
|
||||
class="w-16 h-16 bg-accent-blue rounded-2xl flex items-center justify-center text-white mx-auto shadow-xl shadow-accent-blue/20"
|
||||
>
|
||||
<Zap size={32} fill="currentColor" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold tracking-tight pt-4">Welcome to Web News</h1>
|
||||
<p class="text-text-secondary">Please enter your account number to continue</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="w-full btn-primary py-4 rounded-xl text-lg font-bold shadow-xl shadow-accent-blue/20 flex items-center justify-center gap-2"
|
||||
onclick={() => newsStore.login(loginToken)}
|
||||
disabled={!loginToken}
|
||||
>
|
||||
Access Dashboard
|
||||
<ChevronRight size={20} />
|
||||
</button>
|
||||
|
||||
{#if newsStore.authInfo?.canReg}
|
||||
<div class="relative py-4">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-border-color"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-xs uppercase">
|
||||
<span class="bg-bg-primary px-2 text-text-secondary">Or</span>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<label for="token" class="text-sm font-medium">Account Number</label>
|
||||
<div class="relative">
|
||||
<Hash
|
||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary"
|
||||
size={18}
|
||||
/>
|
||||
<input
|
||||
id="token"
|
||||
type="text"
|
||||
placeholder="1234-5678-abcd-efgh"
|
||||
class="w-full bg-bg-secondary border border-border-color rounded-xl py-3 pl-10 pr-4 focus:ring-2 focus:ring-accent-blue/20 outline-none transition-all font-mono"
|
||||
bind:value={loginToken}
|
||||
onkeydown={(e) => e.key === 'Enter' && newsStore.login(loginToken)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="w-full py-4 border border-border-color rounded-xl font-semibold hover:bg-bg-secondary transition-all flex items-center justify-center gap-2"
|
||||
onclick={() => newsStore.registerAuth()}
|
||||
class="w-full btn-primary py-4 rounded-xl text-lg font-bold shadow-xl shadow-accent-blue/20 flex items-center justify-center gap-2"
|
||||
onclick={() => newsStore.login(loginToken)}
|
||||
disabled={!loginToken}
|
||||
>
|
||||
Generate New Account Number
|
||||
Access Dashboard
|
||||
<ChevronRight size={20} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if newsStore.authInfo?.canReg}
|
||||
<div class="relative py-4">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-border-color"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-xs uppercase">
|
||||
<span class="bg-bg-primary px-2 text-text-secondary">Or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="w-full py-4 border border-border-color rounded-xl font-semibold hover:bg-bg-secondary transition-all flex items-center justify-center gap-2"
|
||||
onclick={() => newsStore.registerAuth()}
|
||||
>
|
||||
Generate New Account Number
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<p class="text-[10px] text-text-secondary text-center leading-relaxed">
|
||||
Web News is privacy-focused. We don't use emails or passwords. <br />
|
||||
|
||||
@@ -64,12 +64,18 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>{article ? article.title : 'Shared Article on Webnews'}</title>
|
||||
<meta name="description" content={article ? article.description : 'A shared article on Webnews RSS reader'} />
|
||||
|
||||
<meta
|
||||
name="description"
|
||||
content={article ? article.description : 'A shared article on Webnews RSS reader'}
|
||||
/>
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:title" content={article ? article.title : 'Shared Article on Webnews'} />
|
||||
<meta property="og:description" content={article ? article.description : 'A shared article on Webnews RSS reader'} />
|
||||
<meta
|
||||
property="og:description"
|
||||
content={article ? article.description : 'A shared article on Webnews RSS reader'}
|
||||
/>
|
||||
{#if article?.imageUrl}
|
||||
<meta property="og:image" content={article.imageUrl} />
|
||||
{:else}
|
||||
@@ -79,7 +85,10 @@
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:title" content={article ? article.title : 'Shared Article on Webnews'} />
|
||||
<meta property="twitter:description" content={article ? article.description : 'A shared article on Webnews RSS reader'} />
|
||||
<meta
|
||||
property="twitter:description"
|
||||
content={article ? article.description : 'A shared article on Webnews RSS reader'}
|
||||
/>
|
||||
{#if article?.imageUrl}
|
||||
<meta property="twitter:image" content={article.imageUrl} />
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user