messages
This commit is contained in:
@@ -25,6 +25,6 @@ export async function removeUserFromChannel(channelId: number, userId: number) {
|
|||||||
return await apiRequest<unknown>(`/channel/${channelId}/user/${userId}`, 'delete');
|
return await apiRequest<unknown>(`/channel/${channelId}/user/${userId}`, 'delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMessagesByChannelId(channelId: number, before_id: number, limit?: number) {
|
export async function getMessagesByChannelId(channelId: number, beforeId?: number, limit?: number) {
|
||||||
return await apiRequest<Message[]>(`/channel/${channelId}/message`, 'get', { data: { before_id, limit } });
|
return await apiRequest<Message[]>(`/channel/${channelId}/message${beforeId || limit ? '?' : ''}${beforeId ? `before=${beforeId}` : ''}${limit ? `&limit=${limit}` : ''}`, 'get');
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { derived, get, writable } from "svelte/store";
|
import { derived, get } from "svelte/store";
|
||||||
import { isErrorResponse, type User } from "../types";
|
import { isErrorResponse, type User } from "../types";
|
||||||
import { getByToken } from "$lib/api/user";
|
import { getByToken } from "$lib/api/user";
|
||||||
|
import { persisted } from "svelte-persisted-store";
|
||||||
|
|
||||||
export const token = writable<string | null>(null);
|
export const token = persisted<string | null>('token', null);
|
||||||
|
|
||||||
token.subscribe((token) => {
|
token.subscribe((token) => {
|
||||||
console.log(`updated token: ${JSON.stringify(token)}`);
|
console.log(`updated token: ${JSON.stringify(token)}`);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export type Token = {
|
|||||||
export type User = {
|
export type User = {
|
||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
avatar: string | null;
|
avatar?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,6 +31,6 @@ export type Message = {
|
|||||||
export type Channel = {
|
export type Channel = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
lastMessageId: number | null;
|
lastMessageId?: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
};
|
};
|
||||||
@@ -3,6 +3,9 @@ import type { LayoutServerLoad } from './$types';
|
|||||||
import { getByToken } from '$lib/api/user';
|
import { getByToken } from '$lib/api/user';
|
||||||
import { isErrorResponse } from '$lib/types';
|
import { isErrorResponse } from '$lib/types';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
export const ssr = false;
|
||||||
|
|
||||||
export const load = (async ({ cookies }) => {
|
export const load = (async ({ cookies }) => {
|
||||||
const token = cookies.get('token');
|
const token = cookies.get('token');
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
export let data: LayoutData;
|
export let data: LayoutData;
|
||||||
|
|
||||||
|
console.log(`loading`);
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
$token = data.token;
|
$token = data.token;
|
||||||
addUserToCache(data.user);
|
addUserToCache(data.user);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const ssr = false
|
|
||||||
@@ -1,15 +1,27 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { createMessage } from '$lib/api/message';
|
||||||
import * as Avatar from '$lib/components/ui/avatar';
|
import * as Avatar from '$lib/components/ui/avatar';
|
||||||
import { ScrollArea } from '$lib/components/ui/scroll-area';
|
import { ScrollArea } from '$lib/components/ui/scroll-area';
|
||||||
import type { Message as MessageType } from '$lib/types';
|
import { isErrorResponse, type Channel, type Message as MessageType } from '$lib/types';
|
||||||
import MessageArea from './message-area.svelte';
|
import MessageArea from './message-area.svelte';
|
||||||
import Message from './message.svelte';
|
import Message from './message.svelte';
|
||||||
import TextField from './text-field.svelte';
|
import TextField from './text-field.svelte';
|
||||||
|
|
||||||
export let username: string;
|
export let channel: Channel | null = null;
|
||||||
export let messages: MessageType[] = [];
|
export let messages: MessageType[] = [];
|
||||||
|
|
||||||
let messageArea: MessageArea;
|
let messageArea: MessageArea;
|
||||||
|
|
||||||
|
const sendMessage = (message: string) => {
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
|
createMessage(channel.id, message).then((message) => {
|
||||||
|
if (!isErrorResponse(message)) {
|
||||||
|
messages = [...messages, message];
|
||||||
|
messageArea.scroll('bottom');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex h-screen w-[95%] flex-col contain-strict">
|
<div class="flex h-screen w-[95%] flex-col contain-strict">
|
||||||
@@ -18,9 +30,9 @@
|
|||||||
<Avatar.Root class="h-12 w-12">
|
<Avatar.Root class="h-12 w-12">
|
||||||
<Avatar.Image src="/default-avatar.png" />
|
<Avatar.Image src="/default-avatar.png" />
|
||||||
|
|
||||||
<Avatar.Fallback>{username[0].toUpperCase()}</Avatar.Fallback>
|
<Avatar.Fallback>{channel?.name[0].toUpperCase() || ''}</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
<span class="text-xl font-bold">{username}</span>
|
<span class="text-xl font-bold">{channel?.name || ''}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -28,17 +40,6 @@
|
|||||||
<MessageArea {messages} bind:this={messageArea} />
|
<MessageArea {messages} bind:this={messageArea} />
|
||||||
</div>
|
</div>
|
||||||
<div class="m-4 mt-1 max-h-[40%] flex-grow">
|
<div class="m-4 mt-1 max-h-[40%] flex-grow">
|
||||||
<TextField
|
<TextField onSend={sendMessage} />
|
||||||
onSend={(message) => {
|
|
||||||
messages = [
|
|
||||||
...messages,
|
|
||||||
{ content: message, createdAt: new Date(), authorId: 1, id: 1 }
|
|
||||||
];
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
messageArea.scroll('bottom');
|
|
||||||
}, 100);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
let scrollArea: ScrollArea;
|
let scrollArea: ScrollArea;
|
||||||
|
|
||||||
messages = [...messages].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
messages = [...messages].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
||||||
|
|
||||||
export function scroll(anchor: 'top' | 'bottom') {
|
export function scroll(anchor: 'top' | 'bottom') {
|
||||||
if (scrollArea) {
|
if (scrollArea) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
{message.content}
|
{message.content}
|
||||||
</span>
|
</span>
|
||||||
<span class={cn('text-md', timestampPosition)}
|
<span class={cn('text-md', timestampPosition)}
|
||||||
>{message.createdAt.toLocaleString()}
|
>{new Date(message.createdAt).toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{#if isSelf}
|
{#if isSelf}
|
||||||
|
|||||||
@@ -11,11 +11,12 @@
|
|||||||
import type { ComponentType } from 'svelte';
|
import type { ComponentType } from 'svelte';
|
||||||
import type { MenuItem } from './(components)/sidebar-header.svelte';
|
import type { MenuItem } from './(components)/sidebar-header.svelte';
|
||||||
import ChannelList from './(components)/channel-list.svelte';
|
import ChannelList from './(components)/channel-list.svelte';
|
||||||
import { user } from '$lib/stores/user';
|
import { addChannelToCache } from '$lib/stores/cache/channels';
|
||||||
import { number } from 'zod';
|
|
||||||
|
|
||||||
export let data: LayoutData;
|
export let data: LayoutData;
|
||||||
|
|
||||||
|
for (const channel of data.channels) addChannelToCache(channel);
|
||||||
|
|
||||||
let channelList: ChannelList | undefined;
|
let channelList: ChannelList | undefined;
|
||||||
|
|
||||||
function onKeyUp(event: KeyboardEvent) {
|
function onKeyUp(event: KeyboardEvent) {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import type { Channel } from '$lib/types';
|
|||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from './$types';
|
||||||
|
|
||||||
|
|
||||||
export const load = (async () => {
|
export const load = (async ({ parent }) => {
|
||||||
|
await parent();
|
||||||
|
|
||||||
const response = await getAllChannels();
|
const response = await getAllChannels();
|
||||||
|
|
||||||
const channels = response as Channel[]
|
const channels = response as Channel[]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="flex h-full w-full items-center justify-center">
|
<div class="flex h-screen w-full items-center justify-center">
|
||||||
<div class="w-[20%] min-w-[300px] space-y-4 rounded-lg bg-secondary p-4">
|
<div class="w-[20%] min-w-[300px] space-y-4 rounded-lg bg-secondary p-4">
|
||||||
<h1 class="text-center text-4xl font-bold">No channel selected</h1>
|
<h1 class="text-center text-4xl font-bold">No channel selected</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import Channel from '../(components)/(channel)/channel.svelte';
|
import ChannelArea from '../(components)/(channel)/channel-area.svelte';
|
||||||
|
import { getCachedChannel } from '$lib/stores/cache/channels';
|
||||||
|
import type { Channel } from '$lib/types';
|
||||||
|
import { addMessageToCache } from '$lib/stores/cache/messages';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
$: username = $page.params.username;
|
let channel: Channel | null = null;
|
||||||
|
$: channelId = parseInt($page.params.channel_id);
|
||||||
|
$: getCachedChannel(channelId).then((c) => (channel = c));
|
||||||
|
|
||||||
|
for (const message of data.messages)
|
||||||
|
addMessageToCache(message);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex h-screen w-full items-center justify-center">
|
<div class="flex h-screen w-full items-center justify-center">
|
||||||
<Channel {username} messages={data.messages} />
|
<ChannelArea {channel} messages={data.messages} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
|
import { getMessagesByChannelId } from '$lib/api/channel';
|
||||||
|
import { getCachedChannel } from '$lib/stores/cache/channels';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
import { isErrorResponse } from '$lib/types';
|
||||||
|
|
||||||
const messages = [
|
export const ssr = false;
|
||||||
{ content: 'Приветdsfsdfsdfsdf', senderId: 1, createdAt: new Date(Date.now() - 1000 * 60 * 25) },
|
|
||||||
{ content: 'Приветffffffffffdsfsdfsdfffffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsffffffffdsfsdfsdffвеffffffffffdsfsdfsdffвеffветdsfsdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff дела?', senderId: 2, createdAt: new Date(Date.now() - 1000 * 60 * 20) },
|
|
||||||
{ content: 'Хорошо', senderId: 1, createdAt: new Date(Date.now() - 1000 * 60 * 15) },
|
|
||||||
{ content: 'Привет', senderId: 2, createdAt: new Date(Date.now() - 1000 * 60 * 10) },
|
|
||||||
{ content: 'Приветdsfsdfsdfsdf', senderId: 1, createdAt: new Date(Date.now() - 1000 * 60 * 25) },
|
|
||||||
{ content: 'fdsfdsfsdfКак дела?', senderId: 2, createdAt: new Date(Date.now() - 1000 * 60 * 20) },
|
|
||||||
{ content: 'Хорошо', senderId: 1, createdAt: new Date(Date.now() - 1000 * 60 * 15) },
|
|
||||||
{ content: 'Приветffffffffffdsfsdfsdfffffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsffffffffdsfsdfsdffвеffffffffffdsfsdfsdffвеffветdsfsdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', senderId: 2, createdAt: new Date(Date.now() - 1000 * 60 * 10) },
|
|
||||||
{ content: 'Приветffffffffffdsfsdfsdfffffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsfffffffdsfsdfsdffffffffffdsffffffffdsfsdfsdffвеffffffffffdsfsdfsdffвеffветdsfsdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', senderId: 1, createdAt: new Date(Date.now() - 1000 * 60 * 25) },
|
|
||||||
|
|
||||||
]
|
export const load = (async ({ params, parent }) => {
|
||||||
|
await parent();
|
||||||
|
|
||||||
|
const channelId = parseInt(params.channel_id)
|
||||||
|
const channel = await getCachedChannel(channelId);
|
||||||
|
if (!channel)
|
||||||
|
return redirect(302, '/channels');
|
||||||
|
|
||||||
|
const messages = await getMessagesByChannelId(channel.id, channel.lastMessageId);
|
||||||
|
|
||||||
|
if (isErrorResponse(messages))
|
||||||
|
return redirect(302, '/channels');
|
||||||
|
|
||||||
export const load = (async () => {
|
|
||||||
return { messages };
|
return { messages };
|
||||||
}) satisfies PageLoad;
|
}) satisfies PageLoad;
|
||||||
Reference in New Issue
Block a user