Skip to content

Commit 62cffea

Browse files
committed
feat: dashboard overhaul and remove mock data
1 parent 0c59422 commit 62cffea

34 files changed

Lines changed: 3855 additions & 2332 deletions

package-lock.json

Lines changed: 842 additions & 1547 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/components/login-popup/+login-popup.svelte

Lines changed: 119 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,86 +4,164 @@
44
import { Button } from '$lib/components/ui/button/index.js';
55
import { userState, userToken } from '$lib/store/LocalStorage.svelte';
66
import { env } from '$env/dynamic/public';
7-
import { onMount } from 'svelte';
7+
import { onMount, onDestroy } from 'svelte';
8+
import { toast } from 'svelte-sonner';
9+
10+
type LoginPayload = {
11+
token: string;
12+
id: string;
13+
login: string;
14+
name: string;
15+
stv_id?: string | null;
16+
is_channel?: boolean;
17+
pfp?: string | null;
18+
};
19+
20+
const DEFAULT_LOGIN_URL = 'https://api.potat.app/login';
821
922
let { open = $bindable(), empty = false } = $props();
23+
let loginWindow: Window | null = null;
1024
11-
let onLogin = async (): Promise<void> => {
12-
window.open(
13-
env.PUBLIC_LOGIN_URL ?? 'https://api.potat.app/login',
14-
'_blank',
15-
'width=600,height=400'
16-
);
25+
const isLoginPayload = (value: unknown): value is LoginPayload => {
26+
if (typeof value !== 'object' || value === null) {
27+
return false;
28+
}
1729
18-
// Mocked example result of postMessage from the window after successful login
19-
userToken.set('mock-test-token-definitely-real');
20-
userState.set({
21-
id: '457260003',
22-
login: 'ryanpotat',
23-
name: 'RyanPotat',
24-
stv_id: '01G6HF7Y9R000AE6YXS14X580S',
25-
is_channel: false,
26-
});
30+
const payload = value as Record<string, unknown>;
31+
return typeof payload.token === 'string' && typeof payload.id === 'string';
32+
};
2733
28-
open = false;
34+
const resolveLoginUrl = (): string => {
35+
const rawUrl = env.PUBLIC_LOGIN_URL ?? DEFAULT_LOGIN_URL;
36+
37+
if (!browser) {
38+
return rawUrl;
39+
}
40+
41+
try {
42+
return new URL(rawUrl, window.location.origin).href;
43+
} catch {
44+
return DEFAULT_LOGIN_URL;
45+
}
2946
};
3047
31-
const handleMessage = (event: MessageEvent) => {
32-
const { id, login, name, stv_id, token, is_channel } = event.data ?? {};
48+
const resolveLoginOrigin = (): string => {
49+
if (!browser) {
50+
return new URL(DEFAULT_LOGIN_URL).origin;
51+
}
52+
53+
try {
54+
return new URL(resolveLoginUrl()).origin;
55+
} catch {
56+
return new URL(DEFAULT_LOGIN_URL).origin;
57+
}
58+
};
59+
60+
const onLogin = (): void => {
61+
if (!browser) {
62+
return;
63+
}
64+
65+
const loginUrl = resolveLoginUrl();
66+
loginWindow = window.open(loginUrl, 'potat-login', 'width=600,height=800');
67+
68+
if (!loginWindow) {
69+
toast.error('Popup blocked', {
70+
description: 'Please allow popups for this site and try again.',
71+
duration: 5000,
72+
});
73+
}
74+
};
3375
34-
if (!token || !id) {
76+
const handleMessage = (event: MessageEvent<unknown>): void => {
77+
if (event.origin !== resolveLoginOrigin()) {
3578
return;
3679
}
3780
81+
if (!isLoginPayload(event.data)) {
82+
return;
83+
}
84+
85+
const { id, login, name, stv_id, token, is_channel, pfp } = event.data;
86+
3887
userToken.set(token);
3988
userState.set({
4089
id,
4190
login,
4291
name,
43-
stv_id,
44-
is_channel,
92+
stv_id: stv_id ?? '',
93+
is_channel: Boolean(is_channel),
94+
pfp: pfp ?? undefined,
4595
});
96+
97+
if (!pfp) {
98+
fetch(`https://api.potat.app/users/${login}`)
99+
.then(r => r.json())
100+
.then(res => {
101+
const connections: { platform: string; pfp?: string }[] = res?.data?.[0]?.user?.connections ?? [];
102+
const twitchConn = connections.find(c => c.platform === 'TWITCH');
103+
if (twitchConn?.pfp) {
104+
userState.update(s => s ? { ...s, pfp: twitchConn.pfp } : s);
105+
}
106+
})
107+
.catch(() => {/* non-fatal */});
108+
}
109+
110+
open = false;
111+
112+
if (loginWindow && !loginWindow.closed) {
113+
loginWindow.close();
114+
}
115+
loginWindow = null;
46116
};
47117
48118
onMount((): void => {
49-
if (browser) {
50-
window.addEventListener('message', handleMessage);
119+
if (!browser) {
120+
return;
121+
}
122+
123+
window.addEventListener('message', handleMessage);
124+
});
125+
126+
onDestroy((): void => {
127+
if (!browser) {
128+
return;
129+
}
130+
131+
window.removeEventListener('message', handleMessage);
132+
133+
if (loginWindow && !loginWindow.closed) {
134+
loginWindow.close();
51135
}
52136
});
53137
</script>
54138

55-
<AlertDialog.Root bind:open on:cancel={() => (open = false)}>
139+
<AlertDialog.Root bind:open>
56140
<AlertDialog.Trigger>
57141
{#if !empty}
58-
<Button variant="outline" size="default">
59-
Login
60-
</Button>
142+
<Button variant="outline" size="default">Login</Button>
61143
{/if}
62144
</AlertDialog.Trigger>
63145

64146
<AlertDialog.Content>
65-
66147
<AlertDialog.Header>
67-
<AlertDialog.Action on:click={onLogin}>
68-
Sign in with Twitch
69-
</AlertDialog.Action>
148+
<AlertDialog.Title>Sign in to PotatBotat</AlertDialog.Title>
149+
<AlertDialog.Description>
150+
By signing in, you agree to our
151+
<a href="/dashboard/privacy-policy">Privacy Policy</a>
152+
and <a href="/dashboard/terms">Terms of Service</a>.
153+
</AlertDialog.Description>
70154
</AlertDialog.Header>
71-
72155
<AlertDialog.Footer>
73-
<p>
74-
By signing in, you agree to our
75-
<a href="/dashboard/privacy-policy"> Privacy Policy </a>
76-
and
77-
<a href="/dashboard/terms"> Terms of Service</a>.
78-
</p>
156+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
157+
<AlertDialog.Action onclick={onLogin}>Sign in with Twitch</AlertDialog.Action>
79158
</AlertDialog.Footer>
80-
81159
</AlertDialog.Content>
82160
</AlertDialog.Root>
83161

84162
<style>
85163
a {
86-
color: #2196f3;
164+
color: hsl(var(--primary));
87165
text-decoration: none;
88166
white-space: nowrap;
89167
}

src/lib/components/nav/+top-bar.svelte

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<script lang="ts">
22
import Dropdown from '$lib/components/user-dropdown/user-dropdown.svelte';
3-
import { CircleAlert, X } from 'lucide-svelte';
43
import { Button } from '$lib/components/ui/button';
54
import { Input } from '$lib/components/ui/input';
6-
import * as Alert from '$lib/components/ui/alert';
5+
76
// TODO: $types?
87
import type { ChannelPartial } from '../../../routes/+layout';
98
import { userState } from '$lib/store/LocalStorage.svelte';
@@ -12,7 +11,6 @@
1211
1312
let { channels }: { channels: ChannelPartial[] } = $props();
1413
15-
let show = $state(true);
1614
let searchQuery: string = $state('');
1715
let openPopup = $state(false);
1816
@@ -49,16 +47,14 @@
4947

5048
<nav>
5149
<div class="left-section">
52-
<Button variant="outline">
53-
<a href="/dashboard/">PotatBotat</a>
54-
</Button>
55-
<Button variant="outline" onclick={handleClickington}>
56-
{#if $userState?.login}
57-
<a href="/dashboard/channel/{$userState?.login ?? ''}">My Channel</a>
58-
{:else}
59-
My channel
60-
{/if}
61-
</Button>
50+
<Button variant="outline" href="/dashboard/">PotatBotat</Button>
51+
{#if $userState?.login}
52+
<Button variant="outline" href="/dashboard/channel/{$userState.login}">My Channel</Button>
53+
{:else}
54+
<Button variant="outline" on:click={handleClickington}>My Channel</Button>
55+
{/if}
56+
<Button variant="outline" href="/dashboard/docs">Docs</Button>
57+
<Button variant="outline" href="/dashboard/faq">FAQ</Button>
6258
</div>
6359
<div class="search-container">
6460
<Input
@@ -89,24 +85,6 @@
8985
</div>
9086
</nav>
9187

92-
{#if show}
93-
<Alert.Root
94-
variant="warning"
95-
class="relative items-center justify-between p-4"
96-
>
97-
<CircleAlert class="h-4 w-4" />
98-
<Alert.Description>
99-
This website is currently in beta and nothing is functional. Stay tuned for poggers happy funtime features!
100-
</Alert.Description>
101-
<Button
102-
variant="ghost"
103-
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-yellow-900 dark:text-yellow-100 hover:opacity-70"
104-
on:click={() => (show = false)}
105-
>
106-
<X class="w-4 h-4" />
107-
</Button>
108-
</Alert.Root>
109-
{/if}
11088

11189
{#if openPopup}
11290
<LoginPopup bind:open={openPopup} empty={true} />

0 commit comments

Comments
 (0)