username update, status message update, status overwriting, global
status in client
This commit is contained in:
parent
9ffb3cf283
commit
d9f5919b60
11 changed files with 235 additions and 79 deletions
|
|
@ -14,7 +14,8 @@
|
|||
import Button, { buttonVariants } from './ui/button/button.svelte';
|
||||
import User from './extra/User.svelte';
|
||||
import type { SessionValidationResult } from '$lib/server/auth';
|
||||
import type { OverviewData } from '$lib';
|
||||
import { Status, statuses, type OverviewData, type OverviewUser } from '$lib';
|
||||
import Label from './ui/label/label.svelte';
|
||||
|
||||
let {
|
||||
currentPage = $bindable<string | null>(),
|
||||
|
|
@ -259,6 +260,7 @@
|
|||
subPage = null;
|
||||
}}
|
||||
user={friend}
|
||||
crown={false}
|
||||
></User>
|
||||
</Sidebar.MenuSubButton>
|
||||
</Sidebar.MenuSubItem>
|
||||
|
|
@ -349,5 +351,66 @@
|
|||
</Sidebar.Menu>
|
||||
</Sidebar.Group>
|
||||
</Sidebar.Content>
|
||||
<Sidebar.Footer class="border-t-2 p-2">
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>
|
||||
<User user={user as unknown as OverviewUser} crown={false} />
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content>
|
||||
<form method="POST" action="?/updateProfile">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Edit profile</Dialog.Title>
|
||||
<Dialog.Description>Update how others see you.</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
<div class="">
|
||||
<Label for="userName">Username</Label>
|
||||
<Input
|
||||
id="userName"
|
||||
name="userName"
|
||||
placeholder="Your name"
|
||||
value={user?.username}
|
||||
required
|
||||
minlength={2}
|
||||
maxlength={32}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Presence -->
|
||||
<div class="space-y-1">
|
||||
<Label for="status">Status</Label>
|
||||
<select id="status" name="status" class="input" required>
|
||||
<option value={Status.ONLINE} selected={user?.statusOverwrite === Status.ONLINE}
|
||||
>Online</option
|
||||
>
|
||||
<option value={Status.DND} selected={user?.statusOverwrite === Status.DND}
|
||||
>Do Not Disturb</option
|
||||
>
|
||||
<option value={Status.OFFLINE} selected={user?.statusOverwrite === Status.OFFLINE}
|
||||
>Offline</option
|
||||
>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="statusMessage">Status message</Label>
|
||||
<Input
|
||||
id="statusMessage"
|
||||
name="statusMessage"
|
||||
placeholder="What's going on?"
|
||||
value={user?.statusMessage ?? ''}
|
||||
maxlength={64}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Dialog.Footer>
|
||||
<Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close>
|
||||
<Button type="submit">Save</Button>
|
||||
</Dialog.Footer>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
</Sidebar.Footer>
|
||||
<Sidebar.Rail />
|
||||
</Sidebar.Root>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { Status, type UserWithStatus } from '$lib';
|
||||
import { Status, statuses, type OverviewUser } from '$lib';
|
||||
import Crown from '@lucide/svelte/icons/crown';
|
||||
|
||||
const {
|
||||
onclick,
|
||||
user,
|
||||
crown
|
||||
}: { crown: boolean; onclick?: (e: MouseEvent) => void; user: UserWithStatus } = $props();
|
||||
}: { crown: boolean; onclick?: (e: MouseEvent) => void; user: OverviewUser } = $props();
|
||||
|
||||
let status: Status | undefined = $derived(statuses.get(user.id));
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
|
|
@ -16,20 +18,23 @@
|
|||
alt={user.username}
|
||||
class="size-6 rounded-full"
|
||||
/>
|
||||
{#if status}
|
||||
<div class="relative">
|
||||
{#if user.status === Status.OFFLINE}
|
||||
{#if status.status === Status.OFFLINE}
|
||||
<span
|
||||
class="absolute end-0 bottom-0 block size-2 rounded-full bg-gray-500 ring-1 ring-white"
|
||||
></span>
|
||||
{:else if user.status === Status.DND}
|
||||
<span class="absolute end-0 bottom-0 block size-2 rounded-full bg-red-500 ring-1 ring-white"
|
||||
{:else if status.status === Status.DND}
|
||||
<span
|
||||
class="absolute end-0 bottom-0 block size-2 rounded-full bg-red-500 ring-1 ring-white"
|
||||
></span>
|
||||
{:else if user.status === Status.ONLINE}
|
||||
{:else if status.status === Status.ONLINE}
|
||||
<span
|
||||
class="absolute end-0 bottom-0 block size-2 rounded-full bg-green-500 ring-1 ring-white"
|
||||
></span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
|
|
@ -46,8 +51,10 @@
|
|||
<Crown></Crown>
|
||||
{/if}
|
||||
</a>
|
||||
{#if status}
|
||||
<div class="pl-2 text-xs text-gray-400 italic">
|
||||
{user.statusMessage}
|
||||
{status.statusMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
type OverviewData,
|
||||
type OverviewGroup,
|
||||
type OverviewServer,
|
||||
type UserWithStatus
|
||||
type OverviewUser
|
||||
} from '$lib';
|
||||
import Button from './ui/button/button.svelte';
|
||||
import Input from './ui/input/input.svelte';
|
||||
|
|
@ -20,14 +20,14 @@
|
|||
// Props for the member sidebar.
|
||||
let {
|
||||
open = $bindable(true),
|
||||
members = $bindable<UserWithStatus[]>([]),
|
||||
members = $bindable<OverviewUser[]>([]),
|
||||
user,
|
||||
data,
|
||||
currentEntity,
|
||||
currentEntityId = $bindable<string | null>(null)
|
||||
}: {
|
||||
open: boolean;
|
||||
members: UserWithStatus[];
|
||||
members: OverviewUser[];
|
||||
data: OverviewData;
|
||||
user: SessionValidationResult['user'];
|
||||
currentEntity: OverviewGroup | OverviewServer;
|
||||
|
|
@ -49,13 +49,13 @@
|
|||
{#if user && currentEntityId}
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger><Button variant="outline"><Cog></Cog></Button></Dialog.Trigger>
|
||||
<Dialog.Content class="sm:max-w-[425px]">
|
||||
<Dialog.Content class="sm:max-w-106.25">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Group Settings</Dialog.Title>
|
||||
<Dialog.Description>Configure your group settings here.</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
<Tabs.Root value="users" class="w-[400px]">
|
||||
<Tabs.Root value="users" class="w-100">
|
||||
<Tabs.List class="grid w-full grid-cols-2">
|
||||
<Tabs.Trigger value="users">User Permissions</Tabs.Trigger>
|
||||
{#if user.id == currentEntity.ownerId}
|
||||
|
|
@ -194,9 +194,7 @@
|
|||
{#each members as member (member.id)}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({ props })}
|
||||
<User user={member} crown={member.id == currentEntity.ownerId} />
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { definePrefix, type Puuid } from './puuid';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
export const UserID = definePrefix('user');
|
||||
export const GroupID = definePrefix('group');
|
||||
|
|
@ -14,6 +15,12 @@ export type ServerId = Puuid<'srv'>;
|
|||
export type DirectMessageId = Puuid<'dmid'>;
|
||||
export type ChannelId = Puuid<'ch'>;
|
||||
|
||||
export interface Status {
|
||||
statusMessage: string;
|
||||
status: 1 | 2 | 3;
|
||||
}
|
||||
export const statuses: Map<string, Status> = new SvelteMap();
|
||||
|
||||
export const Status: Record<string, 1 | 2 | 3> = {
|
||||
OFFLINE: 1,
|
||||
DND: 2,
|
||||
|
|
@ -65,13 +72,8 @@ export type OverviewGroup = {
|
|||
};
|
||||
};
|
||||
|
||||
export interface UserWithStatus extends OverviewUser {
|
||||
status: 1 | 2 | 3;
|
||||
statusMessage: string;
|
||||
}
|
||||
|
||||
export interface OverviewData {
|
||||
friends: UserWithStatus[];
|
||||
friends: OverviewUser[];
|
||||
groups: OverviewGroup[];
|
||||
servers: OverviewServer[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { RequestEvent } from '@sveltejs/kit';
|
|||
import { eq, inArray, or } from 'drizzle-orm';
|
||||
import { sha256 } from '@oslojs/crypto/sha2';
|
||||
import { encodeBase64url, encodeHexLowerCase } from '@oslojs/encoding';
|
||||
import { db } from '$lib/server/db';
|
||||
import { db, kvStore } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { _findDmId } from '../../routes/api/messages/[[grp_srv_dm]]/[[channelId]]/[[channelId]]/+server';
|
||||
|
||||
|
|
@ -159,7 +159,8 @@ export async function validateSessionToken(token: string) {
|
|||
}
|
||||
};
|
||||
}),
|
||||
friendRequests
|
||||
friendRequests,
|
||||
statusMessage: kvStore.get('user-' + user.id + '-message')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,3 +6,8 @@ const sqlite = new Database('database.db');
|
|||
export const db = drizzle(sqlite);
|
||||
|
||||
export const kvStore = new BunSqliteKeyValue('./kvStore.db');
|
||||
|
||||
console.log('nuking kvstore');
|
||||
kvStore.getItems()?.forEach((z) => {
|
||||
if (z.key.endsWith('state')) kvStore.set(z.key, 1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { db, kvStore } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import { GroupID, ServerID } from '$lib';
|
||||
import { GroupID, ServerID, Status } from '$lib';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
|
|
@ -27,8 +27,15 @@ export const GET: RequestHandler = async ({ params }) => {
|
|||
return json({
|
||||
members: members.map((member) => ({
|
||||
id: member.id,
|
||||
status: kvStore.get('user-' + member.id + '-state'),
|
||||
statusMessage: Math.random() > 0.5 ? 'vibing 🟢' : 'not vibing',
|
||||
status:
|
||||
member.statusOverwrite == Status.OFFLINE
|
||||
? Status.OFFLINE
|
||||
: kvStore.get('user-' + member.id + '-state'),
|
||||
statusMessage:
|
||||
kvStore.get('user-' + member.id + '-state') != Status.OFFLINE &&
|
||||
member.statusOverwrite != Status.OFFLINE
|
||||
? kvStore.get('user-' + member.id + '-message')
|
||||
: '',
|
||||
username: member.username,
|
||||
image: `https://api.dicebear.com/7.x/pixel-art/svg?seed=${member.username}`
|
||||
}))
|
||||
|
|
@ -50,8 +57,15 @@ export const GET: RequestHandler = async ({ params }) => {
|
|||
return json({
|
||||
members: members.map((member) => ({
|
||||
id: member.id,
|
||||
status: kvStore.get('user-' + member.id + '-state'),
|
||||
statusMessage: Math.random() > 0.5 ? 'vibing 🟢' : 'not vibing',
|
||||
status:
|
||||
member.statusOverwrite == Status.OFFLINE
|
||||
? Status.OFFLINE
|
||||
: kvStore.get('user-' + member.id + '-state'),
|
||||
statusMessage:
|
||||
kvStore.get('user-' + member.id + '-state') != Status.OFFLINE &&
|
||||
member.statusOverwrite != Status.OFFLINE
|
||||
? kvStore.get('user-' + member.id + '-message')
|
||||
: '',
|
||||
username: member.username,
|
||||
image: `https://api.dicebear.com/7.x/pixel-art/svg?seed=${member.username}`
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,19 +1,32 @@
|
|||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { kvStore } from '$lib/server/db';
|
||||
import { db, kvStore } from '$lib/server/db';
|
||||
import { Status } from '$lib';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export const GET: RequestHandler = async ({ params }) => {
|
||||
const { userId } = params;
|
||||
|
||||
const user = await db
|
||||
.select({
|
||||
statusOverwrite: table.user.statusOverwrite
|
||||
})
|
||||
.from(table.user)
|
||||
.where(eq(table.user.id, userId));
|
||||
if (!user || user.length == 0) {
|
||||
return new Response('User missing', { status: 404 });
|
||||
}
|
||||
|
||||
let current = kvStore.get('user-' + userId + '-state');
|
||||
|
||||
if (user[0].statusOverwrite == Status.OFFLINE) {
|
||||
current = Status.OFFLINE;
|
||||
}
|
||||
|
||||
return json({
|
||||
userId,
|
||||
status: kvStore.get('user-' + userId + '-state'),
|
||||
statusMessage:
|
||||
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
||||
? Math.random() > 0.5
|
||||
? 'vibing 🟢'
|
||||
: 'not vibing'
|
||||
: ''
|
||||
status: current,
|
||||
statusMessage: current != Status.OFFLINE ? kvStore.get('user-' + userId + '-message') : ''
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function _sendToSubscribers(id: string, payload: unknown) {
|
|||
}
|
||||
}
|
||||
export function _sendToUser(userId: string, payload: unknown) {
|
||||
for (const [_, client] of _clients) {
|
||||
for (const [, client] of _clients) {
|
||||
if (client.userId == userId) {
|
||||
client.controller.enqueue(`data: ${JSON.stringify(payload)}\n\n`);
|
||||
}
|
||||
|
|
@ -59,10 +59,15 @@ export async function GET({ locals, request }) {
|
|||
|
||||
if (overwrite === Status.DND) {
|
||||
kvStore.set(`user-${userId}-state`, Status.DND);
|
||||
_sendToSubscribers(userId, { type: 'status', id: userId, status: Status.DND });
|
||||
} else {
|
||||
kvStore.set(`user-${userId}-state`, Status.ONLINE);
|
||||
_sendToSubscribers(userId, { type: 'status', id: userId, status: Status.ONLINE });
|
||||
_sendToSubscribers(userId, {
|
||||
type: 'status',
|
||||
id: userId,
|
||||
status: Status.DND,
|
||||
statusMessage:
|
||||
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
||||
? kvStore.get('user-' + userId + '-message')
|
||||
: ''
|
||||
});
|
||||
}
|
||||
|
||||
request.signal.addEventListener('abort', () => {
|
||||
|
|
@ -74,7 +79,12 @@ export async function GET({ locals, request }) {
|
|||
if (overwrite === Status.OFFLINE) return;
|
||||
|
||||
kvStore.set(`user-${userId}-state`, Status.OFFLINE);
|
||||
_sendToSubscribers(userId, { type: 'status', id: userId, status: Status.OFFLINE });
|
||||
_sendToSubscribers(userId, {
|
||||
type: 'status',
|
||||
id: userId,
|
||||
status: Status.OFFLINE,
|
||||
statusMessage: ''
|
||||
});
|
||||
});
|
||||
},
|
||||
cancel() {
|
||||
|
|
@ -85,7 +95,12 @@ export async function GET({ locals, request }) {
|
|||
if (overwrite === Status.OFFLINE) return;
|
||||
|
||||
kvStore.set(`user-${userId}-state`, Status.OFFLINE);
|
||||
_sendToSubscribers(userId, { type: 'status', id: userId, status: Status.OFFLINE });
|
||||
_sendToSubscribers(userId, {
|
||||
type: 'status',
|
||||
id: userId,
|
||||
status: Status.OFFLINE,
|
||||
statusMessage: ''
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { getRequestEvent } from '$app/server';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { db } from '$lib/server/db';
|
||||
import { db, kvStore } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { DirectMessageID, FriendRequestID, GroupID, ServerID } from '$lib';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { and } from 'drizzle-orm';
|
||||
import { type User } from '$lib/server/db/schema';
|
||||
import { _sendToSubscribers, _sendToUser } from '../api/updates/+server';
|
||||
import { _findDmId } from '../api/messages/[[grp_srv_dm]]/[[channelId]]/[[channelId]]/+server';
|
||||
import { validateUsername } from '$lib/server/auth';
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
const user = requireLogin();
|
||||
return { user };
|
||||
|
|
@ -196,6 +197,41 @@ export const actions = {
|
|||
|
||||
return { success: true };
|
||||
},
|
||||
updateProfile: async ({ request, locals }) => {
|
||||
const user = locals.user;
|
||||
if (!user) return fail(401);
|
||||
|
||||
const data = await request.formData();
|
||||
|
||||
const userName = data.get('userName')?.toString().trim();
|
||||
const status = data.has('status') ? +data.get('status')! : undefined;
|
||||
const statusMessage = data.get('statusMessage')?.toString().trim() || undefined;
|
||||
|
||||
if (!validateUsername(userName)) {
|
||||
return fail(400, { message: 'Invalid display name' });
|
||||
}
|
||||
|
||||
if (status != 1 && status != 2 && status != 3) {
|
||||
return fail(400, { message: 'Invalid status' });
|
||||
}
|
||||
|
||||
kvStore.set('user-' + user.id + '-message', statusMessage);
|
||||
await db
|
||||
.update(table.user)
|
||||
.set({
|
||||
username: userName,
|
||||
statusOverwrite: status
|
||||
})
|
||||
.where(eq(table.user.id, user.id));
|
||||
|
||||
if (statusMessage || status != user.statusOverwrite)
|
||||
_sendToSubscribers(user.id, { type: 'status', id: user.id, status: status, statusMessage });
|
||||
|
||||
if (userName != user.username)
|
||||
_sendToSubscribers(user.id, { type: 'username', status: 'name-changed' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
createGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const members = data.getAll('member').map((z) => z.toString());
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
type OverviewUser,
|
||||
type OverviewGroup,
|
||||
type OverviewServer,
|
||||
type UserWithStatus,
|
||||
type ReturnMessage,
|
||||
type Channel,
|
||||
type ChannelId,
|
||||
ChannelID
|
||||
ChannelID,
|
||||
statuses
|
||||
} from '$lib';
|
||||
import type { PageServerData } from './$types';
|
||||
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
let sse: EventSource | undefined;
|
||||
let messagesElement: HTMLDivElement | undefined = $state();
|
||||
let isMembersTabOpen = $state(true);
|
||||
let members: UserWithStatus[] = $state([]);
|
||||
let members: OverviewUser[] = $state([]);
|
||||
|
||||
let messages: ReturnMessage[] = $state([]);
|
||||
let inputValue = $state();
|
||||
|
|
@ -88,11 +88,14 @@
|
|||
|
||||
const status = await res.json();
|
||||
|
||||
statuses.set(friend.id, {
|
||||
status: status.status,
|
||||
statusMessage: status.statusMessage
|
||||
});
|
||||
|
||||
return {
|
||||
id: UserID.parse(friend.id),
|
||||
username: friend.username,
|
||||
status: status.status,
|
||||
statusMessage: status.statusMessage,
|
||||
dmId: friend.dmId,
|
||||
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + friend.username
|
||||
};
|
||||
|
|
@ -198,9 +201,10 @@
|
|||
const json = JSON.parse(e.data) as
|
||||
| { type: 'connected'; sessionId: string }
|
||||
| { type: 'message'; message: ReturnMessage }
|
||||
| { type: 'status'; id: string; status: 1 | 2 | 3 }
|
||||
| { type: 'status'; id: string; status: 1 | 2 | 3; statusMessage: string }
|
||||
| { type: 'friends'; status: string }
|
||||
| { type: 'group'; status: string }
|
||||
| { type: 'username'; status: string }
|
||||
| { type: 'server'; status: string };
|
||||
if (json.type == 'friends') {
|
||||
toast('Invalidation from friends updates, recieved ' + json.status);
|
||||
|
|
@ -216,25 +220,29 @@
|
|||
toast('Invalidation from group updates, recieved ' + json.status);
|
||||
if (json.status == 'removed-from-group' && GroupID.is(currentPageID)) {
|
||||
currentPageID = null;
|
||||
currentPage = null;
|
||||
currentPage = undefined;
|
||||
}
|
||||
|
||||
await invalidateAll();
|
||||
|
||||
await fill_overview_data();
|
||||
}
|
||||
if (json.type == 'username') {
|
||||
toast('Invalidation from username updates, recieved ' + json.status);
|
||||
await invalidateAll();
|
||||
|
||||
await fill_overview_data();
|
||||
}
|
||||
if (json.type == 'connected') {
|
||||
console.log('SSE connected. We are sessionID ' + json.sessionId);
|
||||
sessionId = json.sessionId;
|
||||
}
|
||||
|
||||
if (json.type == 'status') {
|
||||
//@TODO update everywhere where user is used
|
||||
const friend = overview_data.friends.find((z) => z.id == json.id);
|
||||
|
||||
if (friend) {
|
||||
friend.status = json.status;
|
||||
}
|
||||
statuses.set(json.id, {
|
||||
status: json.status,
|
||||
statusMessage: json.statusMessage
|
||||
});
|
||||
}
|
||||
|
||||
if (json.type == 'message') {
|
||||
|
|
@ -294,18 +302,12 @@
|
|||
<h1>{server!.name} - Server Info</h1>
|
||||
{/if}
|
||||
{:else if UserID.is(currentPageID)}
|
||||
{@const friend = currentPage as UserWithStatus}
|
||||
{@const friend = currentPage as OverviewUser}
|
||||
|
||||
<img src={friend.image} alt={friend!.username} class="size-6 rounded-full" />
|
||||
|
||||
<h1>
|
||||
{friend!.username} [{friend.status == Status.ONLINE
|
||||
? 'Online!'
|
||||
: friend.status == Status.DND
|
||||
? 'DND'
|
||||
: friend.status == Status.OFFLINE
|
||||
? 'Offline'
|
||||
: 'Unknown'}]
|
||||
{friend!.username}
|
||||
</h1>
|
||||
{:else if GroupID.is(currentPageID)}
|
||||
{@const group = currentPage as OverviewGroup}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue