implement channels fully, implement servers fully, make dms impossible
to send if no longer friends, update overview information on invalidation (form response recieved, friends update)
This commit is contained in:
parent
92a95cb365
commit
7af96ca084
8 changed files with 245 additions and 104 deletions
|
|
@ -18,11 +18,13 @@
|
||||||
|
|
||||||
let {
|
let {
|
||||||
currentPage = $bindable<string | null>(),
|
currentPage = $bindable<string | null>(),
|
||||||
|
subPage = $bindable<string | null>(),
|
||||||
data,
|
data,
|
||||||
user,
|
user,
|
||||||
...restProps
|
...restProps
|
||||||
}: {
|
}: {
|
||||||
currentPage: string | null;
|
currentPage: string | null;
|
||||||
|
subPage: string | null;
|
||||||
data: OverviewData;
|
data: OverviewData;
|
||||||
user: SessionValidationResult['user'];
|
user: SessionValidationResult['user'];
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
@ -254,6 +256,7 @@
|
||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
currentPage = friend.id;
|
currentPage = friend.id;
|
||||||
|
subPage = null;
|
||||||
}}
|
}}
|
||||||
user={friend}
|
user={friend}
|
||||||
></User>
|
></User>
|
||||||
|
|
@ -283,6 +286,7 @@
|
||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
currentPage = group.id;
|
currentPage = group.id;
|
||||||
|
subPage = null;
|
||||||
}}
|
}}
|
||||||
href="##"
|
href="##"
|
||||||
>
|
>
|
||||||
|
|
@ -309,24 +313,34 @@
|
||||||
<Sidebar.MenuSub>
|
<Sidebar.MenuSub>
|
||||||
{#each data.servers as server (server.id)}
|
{#each data.servers as server (server.id)}
|
||||||
<Sidebar.MenuSubItem>
|
<Sidebar.MenuSubItem>
|
||||||
<Sidebar.MenuSubButton>
|
<Sidebar.MenuSubButton
|
||||||
<a
|
onclick={(e) => {
|
||||||
onclick={(e) => {
|
e.preventDefault();
|
||||||
e.preventDefault();
|
currentPage = server.id;
|
||||||
currentPage = server.id;
|
subPage = null;
|
||||||
}}
|
}}
|
||||||
href="##"
|
>
|
||||||
class="flex items-center gap-2"
|
<img
|
||||||
>
|
src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + server.name}
|
||||||
<img
|
alt={server.name}
|
||||||
src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + server.name}
|
class="size-6 rounded-full"
|
||||||
alt={server.name}
|
/>
|
||||||
class="size-6 rounded-full"
|
{server.name}
|
||||||
/>
|
|
||||||
{server.name}
|
|
||||||
</a>
|
|
||||||
</Sidebar.MenuSubButton>
|
</Sidebar.MenuSubButton>
|
||||||
</Sidebar.MenuSubItem>
|
</Sidebar.MenuSubItem>
|
||||||
|
{#each server.channels as channel (channel.id)}
|
||||||
|
<a
|
||||||
|
onclick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
currentPage = server.id;
|
||||||
|
subPage = channel.id;
|
||||||
|
}}
|
||||||
|
href="##"
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
{channel.name}
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
</Sidebar.MenuSub>
|
</Sidebar.MenuSub>
|
||||||
</Collapsible.Content>
|
</Collapsible.Content>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { definePrefix, type Puuid } from './puuid';
|
||||||
export const UserID = definePrefix('user');
|
export const UserID = definePrefix('user');
|
||||||
export const GroupID = definePrefix('group');
|
export const GroupID = definePrefix('group');
|
||||||
export const ServerID = definePrefix('srv');
|
export const ServerID = definePrefix('srv');
|
||||||
|
export const ChannelID = definePrefix('ch');
|
||||||
|
|
||||||
export const FriendRequestID = definePrefix('frq');
|
export const FriendRequestID = definePrefix('frq');
|
||||||
export const DirectMessageID = definePrefix('dmid');
|
export const DirectMessageID = definePrefix('dmid');
|
||||||
|
|
||||||
|
|
@ -10,6 +12,7 @@ export type UserId = Puuid<'user'>;
|
||||||
export type GroupId = Puuid<'group'>;
|
export type GroupId = Puuid<'group'>;
|
||||||
export type ServerId = Puuid<'srv'>;
|
export type ServerId = Puuid<'srv'>;
|
||||||
export type DirectMessageId = Puuid<'dmid'>;
|
export type DirectMessageId = Puuid<'dmid'>;
|
||||||
|
export type ChannelId = Puuid<'ch'>;
|
||||||
|
|
||||||
export const Status: Record<string, 1 | 2 | 3> = {
|
export const Status: Record<string, 1 | 2 | 3> = {
|
||||||
OFFLINE: 1,
|
OFFLINE: 1,
|
||||||
|
|
@ -23,6 +26,11 @@ export type OverviewUser = {
|
||||||
image: string;
|
image: string;
|
||||||
dmId?: string;
|
dmId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface Channel {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
export interface Message {
|
export interface Message {
|
||||||
id: string;
|
id: string;
|
||||||
authorId: string;
|
authorId: string;
|
||||||
|
|
@ -42,6 +50,7 @@ export type OverviewServer = {
|
||||||
name: string;
|
name: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
image: string;
|
image: string;
|
||||||
|
channels: Channel[];
|
||||||
};
|
};
|
||||||
export type OverviewGroup = {
|
export type OverviewGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ export function definePrefix<const P extends string>(prefix: P) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
prefix,
|
prefix,
|
||||||
is(value: string): value is Puuid<P> {
|
is(value: string | undefined | null): value is Puuid<P> {
|
||||||
|
if (!value) return false;
|
||||||
return value.startsWith(prefix + '_');
|
return value.startsWith(prefix + '_');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,16 +84,38 @@ export async function validateSessionToken(token: string) {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const servers = (user.servers as string[]).length
|
let servers = (user.servers as string[]).length
|
||||||
? await db
|
? await db
|
||||||
.select({
|
.select({
|
||||||
id: table.server.id,
|
id: table.server.id,
|
||||||
name: table.server.name,
|
name: table.server.name,
|
||||||
ownerId: table.server.owner
|
ownerId: table.server.owner,
|
||||||
|
channels: table.server.channels
|
||||||
})
|
})
|
||||||
.from(table.server)
|
.from(table.server)
|
||||||
.where(inArray(table.server.id, user.servers as string[]))
|
.where(inArray(table.server.id, user.servers as string[]))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
servers = await Promise.all(
|
||||||
|
servers.map(async (z) => {
|
||||||
|
return {
|
||||||
|
...z,
|
||||||
|
channels: (
|
||||||
|
await Promise.all(
|
||||||
|
(z.channels as string[]).map(async (m) => {
|
||||||
|
const channel = await db.select().from(table.channel).where(eq(table.channel.id, m));
|
||||||
|
if (!channel || channel.length == 0) return;
|
||||||
|
return {
|
||||||
|
name: channel[0].name,
|
||||||
|
id: channel[0].id
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).filter(Boolean)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const groups = (user.groups as string[]).length
|
const groups = (user.groups as string[]).length
|
||||||
? await db
|
? await db
|
||||||
.select({
|
.select({
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,19 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||||
messages = (c.messages as Message[]) ?? [];
|
messages = (c.messages as Message[]) ?? [];
|
||||||
|
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
|
_sendToSubscribers(c.id, {
|
||||||
|
type: 'message',
|
||||||
|
id: c.id,
|
||||||
|
message: {
|
||||||
|
...message,
|
||||||
|
author: {
|
||||||
|
id: locals.user.id,
|
||||||
|
name: locals.user.username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
await db.update(table.channel).set({ messages }).where(eq(table.channel.id, channelId));
|
await db.update(table.channel).set({ messages }).where(eq(table.channel.id, channelId));
|
||||||
|
return json({ type, id: c.id, messages });
|
||||||
} else if (DirectMessageID.is(grp_srv_dm)) {
|
} else if (DirectMessageID.is(grp_srv_dm)) {
|
||||||
type = 'dms';
|
type = 'dms';
|
||||||
const dm = (
|
const dm = (
|
||||||
|
|
@ -158,7 +169,12 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
if (!dm) return new Response('DM not found.', { status: 404 });
|
if (!dm) return new Response('DM not found.', { status: 404 });
|
||||||
|
const firstMember = await db.select().from(table.user).where(eq(table.user.id, dm.firstMember));
|
||||||
|
|
||||||
|
if (!firstMember || firstMember.length == 0)
|
||||||
|
return new Response('First member is invalid.', { status: 404 });
|
||||||
|
if (!(firstMember[0].friends as string[]).includes(dm.secondMember))
|
||||||
|
return new Response('You are no longer friends.', { status: 404 });
|
||||||
messages = (dm.messages as Message[]) ?? [];
|
messages = (dm.messages as Message[]) ?? [];
|
||||||
|
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { json } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
import type { RequestHandler } from './$types';
|
import type { RequestHandler } from './$types';
|
||||||
import { _clients } from '../+server';
|
import { _clients } from '../+server';
|
||||||
import { DirectMessageID } from '$lib';
|
import { ChannelID, DirectMessageID, UserID } from '$lib';
|
||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import * as table from '$lib/server/db/schema';
|
import * as table from '$lib/server/db/schema';
|
||||||
import { or } from 'drizzle-orm';
|
import { or } from 'drizzle-orm';
|
||||||
|
|
@ -33,7 +33,7 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||||
return json({ error: 'Invalid subscribeTo value' }, { status: 400 });
|
return json({ error: 'Invalid subscribeTo value' }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let isValidDmid = false;
|
let isValid = false;
|
||||||
|
|
||||||
if (DirectMessageID.is(subscribeTo)) {
|
if (DirectMessageID.is(subscribeTo)) {
|
||||||
const find = await db
|
const find = await db
|
||||||
|
|
@ -47,13 +47,35 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (find?.length != 0) {
|
if (find?.length != 0) {
|
||||||
isValidDmid = true;
|
isValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UserID.is(subscribeTo)) {
|
||||||
|
const find = await db
|
||||||
|
.select()
|
||||||
|
.from(table.user)
|
||||||
|
.where(or(eq(table.user.id, subscribeTo)));
|
||||||
|
|
||||||
|
if (find?.length != 0) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ChannelID.is(subscribeTo)) {
|
||||||
|
const find = await db
|
||||||
|
.select()
|
||||||
|
.from(table.channel)
|
||||||
|
.where(or(eq(table.channel.id, subscribeTo)));
|
||||||
|
|
||||||
|
if (find?.length != 0) {
|
||||||
|
isValid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
isValidDmid ||
|
isValid ||
|
||||||
locals.user.groups.find((z) => z.id === subscribeTo) ||
|
locals.user.groups.find((z) => z.id === subscribeTo) ||
|
||||||
locals.user.servers.find((z) => z.id === subscribeTo)
|
locals.user.servers.find((z) => z.id === subscribeTo)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { eq } from 'drizzle-orm';
|
||||||
import { and } from 'drizzle-orm';
|
import { and } from 'drizzle-orm';
|
||||||
import { type User } from '$lib/server/db/schema';
|
import { type User } from '$lib/server/db/schema';
|
||||||
import { _sendToSubscribers } from '../api/updates/+server';
|
import { _sendToSubscribers } from '../api/updates/+server';
|
||||||
|
import { _findDmId } from '../api/messages/[[grp_srv_dm]]/[[channelId]]/[[channelId]]/+server';
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async () => {
|
||||||
const user = requireLogin();
|
const user = requireLogin();
|
||||||
return { user };
|
return { user };
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@
|
||||||
type OverviewGroup,
|
type OverviewGroup,
|
||||||
type OverviewServer,
|
type OverviewServer,
|
||||||
type UserWithStatus,
|
type UserWithStatus,
|
||||||
type ReturnMessage
|
type ReturnMessage,
|
||||||
|
type Channel,
|
||||||
|
type ChannelId,
|
||||||
|
ChannelID
|
||||||
} from '$lib';
|
} from '$lib';
|
||||||
import type { PageServerData } from './$types';
|
import type { PageServerData } from './$types';
|
||||||
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
||||||
|
|
@ -27,13 +30,17 @@
|
||||||
import SendHorizontal from '@lucide/svelte/icons/send-horizontal';
|
import SendHorizontal from '@lucide/svelte/icons/send-horizontal';
|
||||||
import PersonStanding from '@lucide/svelte/icons/person-standing';
|
import PersonStanding from '@lucide/svelte/icons/person-standing';
|
||||||
import MemberSidebar from '$lib/components/member-sidebar.svelte';
|
import MemberSidebar from '$lib/components/member-sidebar.svelte';
|
||||||
|
import User from '$lib/components/extra/User.svelte';
|
||||||
|
import { invalidate, invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let errorOpen = $state(true);
|
let errorOpen = $state(true);
|
||||||
|
|
||||||
let { form, data }: { form: ActionData; data: PageServerData } = $props();
|
let { form, data }: { form: ActionData; data: PageServerData } = $props();
|
||||||
let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
|
let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
|
||||||
|
let currentSubPageID: ChannelId | null = $state(null);
|
||||||
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
|
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
|
||||||
let ownerId: string | null = $state(null);
|
let currentSubPage: Channel | null = $state(null);
|
||||||
|
|
||||||
let isMembersTabOpen = $state(true);
|
let isMembersTabOpen = $state(true);
|
||||||
let members: UserWithStatus[] = $state([]);
|
let members: UserWithStatus[] = $state([]);
|
||||||
|
|
||||||
|
|
@ -49,7 +56,50 @@
|
||||||
servers: []
|
servers: []
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function fill_overview_data() {
|
||||||
|
overview_data.servers = data.user.servers.map((z) => {
|
||||||
|
console.log(z);
|
||||||
|
return {
|
||||||
|
id: ServerID.parse(z.id),
|
||||||
|
name: z.name,
|
||||||
|
ownerId: z.ownerId,
|
||||||
|
channels: z.channels as Channel[],
|
||||||
|
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
overview_data.groups = data.user.groups.map((z) => {
|
||||||
|
return {
|
||||||
|
id: GroupID.parse(z.id),
|
||||||
|
name: z.name,
|
||||||
|
ownerId: z.ownerId,
|
||||||
|
members: z.members,
|
||||||
|
permissions: z.permissions,
|
||||||
|
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
overview_data.friends = await Promise.all(
|
||||||
|
data.user.friends.map(async (friend) => {
|
||||||
|
const res = await fetch(`/api/status/${friend.id}`);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Failed to fetch status for ${friend.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = await res.json();
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
console.log(currentPageID, currentSubPageID);
|
||||||
if (currentPageID) {
|
if (currentPageID) {
|
||||||
if (UserID.is(currentPageID)) {
|
if (UserID.is(currentPageID)) {
|
||||||
currentPage = overview_data.friends.find((friend) => friend.id === currentPageID);
|
currentPage = overview_data.friends.find((friend) => friend.id === currentPageID);
|
||||||
|
|
@ -58,6 +108,15 @@
|
||||||
} else if (ServerID.is(currentPageID)) {
|
} else if (ServerID.is(currentPageID)) {
|
||||||
currentPage = overview_data.servers.find((server) => server.id === currentPageID);
|
currentPage = overview_data.servers.find((server) => server.id === currentPageID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentSubPageID) {
|
||||||
|
console.log((currentPage as OverviewServer).channels.find((z) => z.id == currentSubPageID));
|
||||||
|
currentSubPage = (currentPage as OverviewServer).channels.find(
|
||||||
|
(z) => z.id == currentSubPageID
|
||||||
|
) as Channel | null;
|
||||||
|
} else {
|
||||||
|
currentSubPage = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
currentPage = undefined;
|
currentPage = undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +131,6 @@
|
||||||
members = data.members;
|
members = data.members;
|
||||||
}
|
}
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
ownerId = (currentPage as OverviewGroup | OverviewServer).ownerId;
|
|
||||||
} else {
|
} else {
|
||||||
isMembersTabOpen = false;
|
isMembersTabOpen = false;
|
||||||
}
|
}
|
||||||
|
|
@ -80,26 +138,35 @@
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!currentPageID || !currentPage) return;
|
if (!currentPageID || !currentPage) return;
|
||||||
if (ServerID.is(currentPageID)) return;
|
if (ServerID.is(currentPageID) && !ChannelID.is(currentSubPageID)) return;
|
||||||
|
|
||||||
async function getMessages() {
|
async function getMessages() {
|
||||||
const targetId = currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID;
|
let path = '';
|
||||||
|
let subscribe = '';
|
||||||
|
|
||||||
const req = await fetch('/api/messages/' + targetId);
|
if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
|
||||||
|
path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
|
||||||
|
subscribe = currentSubPageID;
|
||||||
|
} else {
|
||||||
|
subscribe = currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID;
|
||||||
|
path = '/api/messages/' + subscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = await fetch(path);
|
||||||
const data = await req.json();
|
const data = await req.json();
|
||||||
|
|
||||||
messages = data.messages;
|
messages = data.messages;
|
||||||
|
|
||||||
if (previousSubscription && previousSubscription != targetId) {
|
if (previousSubscription && previousSubscription != subscribe) {
|
||||||
await fetch('/api/updates/' + sessionId, {
|
await fetch('/api/updates/' + sessionId, {
|
||||||
body: JSON.stringify({ subscribeTo: previousSubscription }),
|
body: JSON.stringify({ subscribeTo: previousSubscription }),
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousSubscription != targetId) {
|
if (previousSubscription != subscribe) {
|
||||||
await fetch('/api/updates/' + sessionId, {
|
await fetch('/api/updates/' + sessionId, {
|
||||||
body: JSON.stringify({ subscribeTo: targetId }),
|
body: JSON.stringify({ subscribeTo: subscribe }),
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -112,44 +179,10 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
async function run() {
|
async function run() {
|
||||||
overview_data.servers = data.user.servers.map((z) => {
|
if (form) {
|
||||||
return {
|
await invalidateAll();
|
||||||
id: ServerID.parse(z.id),
|
}
|
||||||
name: z.name,
|
await fill_overview_data();
|
||||||
ownerId: z.ownerId,
|
|
||||||
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name
|
|
||||||
};
|
|
||||||
});
|
|
||||||
overview_data.groups = data.user.groups.map((z) => {
|
|
||||||
return {
|
|
||||||
id: GroupID.parse(z.id),
|
|
||||||
name: z.name,
|
|
||||||
ownerId: z.ownerId,
|
|
||||||
members: z.members,
|
|
||||||
permissions: z.permissions,
|
|
||||||
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name
|
|
||||||
};
|
|
||||||
});
|
|
||||||
overview_data.friends = await Promise.all(
|
|
||||||
data.user.friends.map(async (friend) => {
|
|
||||||
const res = await fetch(`/api/status/${friend.id}`);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`Failed to fetch status for ${friend.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const status = await res.json();
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|
@ -158,7 +191,7 @@
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const sse = new EventSource('/api/updates');
|
const sse = new EventSource('/api/updates');
|
||||||
|
|
||||||
sse.addEventListener('message', (e) => {
|
sse.addEventListener('message', async (e) => {
|
||||||
const json = JSON.parse(e.data) as
|
const json = JSON.parse(e.data) as
|
||||||
| { type: 'connected'; sessionId: string }
|
| { type: 'connected'; sessionId: string }
|
||||||
| { type: 'message'; message: ReturnMessage }
|
| { type: 'message'; message: ReturnMessage }
|
||||||
|
|
@ -166,9 +199,10 @@
|
||||||
| { type: 'friends'; status: string };
|
| { type: 'friends'; status: string };
|
||||||
|
|
||||||
if (json.type == 'friends') {
|
if (json.type == 'friends') {
|
||||||
alert(json.status);
|
await invalidateAll();
|
||||||
location.reload();
|
await fill_overview_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.type == 'connected') {
|
if (json.type == 'connected') {
|
||||||
console.log('SSE connected. We are sessionID ' + json.sessionId);
|
console.log('SSE connected. We are sessionID ' + json.sessionId);
|
||||||
sessionId = json.sessionId;
|
sessionId = json.sessionId;
|
||||||
|
|
@ -207,17 +241,27 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Sidebar.Provider>
|
<Sidebar.Provider>
|
||||||
<AppSidebar bind:currentPage={currentPageID} user={data.user} data={overview_data} />
|
<AppSidebar
|
||||||
|
bind:subPage={currentSubPageID}
|
||||||
|
bind:currentPage={currentPageID}
|
||||||
|
user={data.user}
|
||||||
|
data={overview_data}
|
||||||
|
/>
|
||||||
<Sidebar.Inset class="h-svh">
|
<Sidebar.Inset class="h-svh">
|
||||||
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||||
<Sidebar.Trigger class="-ms-1" />
|
<Sidebar.Trigger class="-ms-1" />
|
||||||
{#if currentPageID && currentPage}
|
{#if currentPageID && currentPage}
|
||||||
{#if ServerID.is(currentPageID)}
|
{#if ServerID.is(currentPageID)}
|
||||||
{@const server = currentPage as OverviewServer}
|
{@const server = currentPage as OverviewServer}
|
||||||
|
{@const channel = currentSubPage as OverviewServer}
|
||||||
|
|
||||||
<img src={server!.image} alt={server!.name} class="size-6 rounded-full" />
|
<img src={server!.image} alt={server!.name} class="size-6 rounded-full" />
|
||||||
|
|
||||||
<h1>{server!.name}</h1>
|
{#if currentSubPage}
|
||||||
|
<h1>{server!.name} - {channel.name}</h1>
|
||||||
|
{:else}
|
||||||
|
<h1>{server!.name} - Server Info</h1>
|
||||||
|
{/if}
|
||||||
{:else if UserID.is(currentPageID)}
|
{:else if UserID.is(currentPageID)}
|
||||||
{@const friend = currentPage as UserWithStatus}
|
{@const friend = currentPage as UserWithStatus}
|
||||||
|
|
||||||
|
|
@ -252,46 +296,58 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
<div class="h-min shrink overflow-scroll">
|
|
||||||
{#each messages as message, i (message.id)}
|
|
||||||
{#if i === 0 || messages[i - 1].authorId !== message.authorId}
|
|
||||||
<div class="flex gap-2 px-4 py-2">
|
|
||||||
<img
|
|
||||||
src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + message.author.name}
|
|
||||||
alt={message.author.name}
|
|
||||||
class="h-6 w-6 rounded-full"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
{#if currentPageID && currentPage && ServerID.is(currentPageID) && !currentSubPageID}
|
||||||
<div class="flex items-baseline gap-2">
|
<h1>add invite creation, role creation, moderation, et cetera to this page.</h1>
|
||||||
<span class="font-semibold">{message.author.name}</span>
|
{/if}
|
||||||
<span class="text-xs text-gray-400">
|
{#if currentPageID && currentPage && ((ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) || UserID.is(currentPageID) || GroupID.is(currentPageID))}
|
||||||
{formatTimestamp(message.timestamp)}
|
<div class="h-min shrink overflow-scroll">
|
||||||
</span>
|
{#each messages as message, i (message.id)}
|
||||||
|
{#if i === 0 || messages[i - 1].authorId !== message.authorId}
|
||||||
|
<div class="flex gap-2 px-4 py-2">
|
||||||
|
<img
|
||||||
|
src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + message.author.name}
|
||||||
|
alt={message.author.name}
|
||||||
|
class="h-6 w-6 rounded-full"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-baseline gap-2">
|
||||||
|
<span class="font-semibold">{message.author.name}</span>
|
||||||
|
<span class="text-xs text-gray-400">
|
||||||
|
{formatTimestamp(message.timestamp)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="whitespace-pre-wrap">{message.content}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="ml-8 flex gap-8 px-4 py-1">
|
||||||
<div class="whitespace-pre-wrap">{message.content}</div>
|
<div class="whitespace-pre-wrap">{message.content}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{:else}
|
{/each}
|
||||||
<div class="ml-8 flex gap-8 px-4 py-1">
|
</div>
|
||||||
<div class="whitespace-pre-wrap">{message.content}</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{#if currentPageID && currentPage}
|
|
||||||
<div class="flex shrink-0 gap-2 border-t p-2">
|
<div class="flex shrink-0 gap-2 border-t p-2">
|
||||||
<Input
|
<Input
|
||||||
bind:value={inputValue}
|
bind:value={inputValue}
|
||||||
onkeydown={async (e) => {
|
onkeydown={async (e) => {
|
||||||
if (e.key == 'Enter') {
|
if (e.key == 'Enter') {
|
||||||
|
let path = '';
|
||||||
|
|
||||||
|
if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
|
||||||
|
path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
|
||||||
|
} else {
|
||||||
|
path =
|
||||||
|
'/api/messages/' +
|
||||||
|
(currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID);
|
||||||
|
}
|
||||||
let message = inputValue;
|
let message = inputValue;
|
||||||
const targetId =
|
|
||||||
currentPage && 'dmId' in currentPage ? currentPage.dmId : currentPageID;
|
|
||||||
inputValue = '';
|
inputValue = '';
|
||||||
|
|
||||||
const req = await fetch('/api/messages/' + targetId, {
|
const req = await fetch(path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ content: message })
|
body: JSON.stringify({ content: message })
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue