open-webui/src/lib/components/channel/Channel.svelte

213 lines
4.8 KiB
Svelte
Raw Normal View History

2024-12-22 11:49:24 +00:00
<script lang="ts">
2024-12-23 02:40:01 +00:00
import { toast } from 'svelte-sonner';
import { onDestroy, onMount, tick } from 'svelte';
2024-12-31 07:06:34 +00:00
import { goto } from '$app/navigation';
2024-12-23 02:40:01 +00:00
2024-12-27 05:51:09 +00:00
import { chatId, showSidebar, socket, user } from '$lib/stores';
2024-12-23 05:20:24 +00:00
import { getChannelById, getChannelMessages, sendMessage } from '$lib/apis/channels';
import Messages from './Messages.svelte';
import MessageInput from './MessageInput.svelte';
2024-12-23 05:33:13 +00:00
import Navbar from './Navbar.svelte';
2024-12-23 05:20:24 +00:00
2024-12-22 11:49:24 +00:00
export let id = '';
2024-12-23 02:40:01 +00:00
let scrollEnd = true;
let messagesContainerElement = null;
let top = false;
2024-12-23 05:20:24 +00:00
let channel = null;
2024-12-23 02:40:01 +00:00
let messages = null;
2024-12-27 05:51:09 +00:00
let typingUsers = [];
let typingUsersTimeout = {};
2024-12-23 02:40:01 +00:00
$: if (id) {
initHandler();
}
2024-12-23 04:56:51 +00:00
const scrollToBottom = () => {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
};
2024-12-23 02:40:01 +00:00
const initHandler = async () => {
top = false;
messages = null;
2024-12-23 05:20:24 +00:00
channel = null;
channel = await getChannelById(localStorage.token, id).catch((error) => {
return null;
});
2024-12-23 02:40:01 +00:00
2024-12-23 05:20:24 +00:00
if (channel) {
2024-12-23 07:31:33 +00:00
messages = await getChannelMessages(localStorage.token, id, 0);
2024-12-23 02:40:01 +00:00
2024-12-23 05:20:24 +00:00
if (messages) {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
2024-12-23 03:28:15 +00:00
2024-12-23 05:20:24 +00:00
if (messages.length < 50) {
top = true;
}
2024-12-23 03:28:15 +00:00
}
2024-12-23 05:20:24 +00:00
} else {
goto('/');
2024-12-23 02:40:01 +00:00
}
};
2024-12-23 03:28:15 +00:00
const channelEventHandler = async (event) => {
console.log(event);
if (event.channel_id === id) {
const type = event?.data?.type ?? null;
const data = event?.data?.data ?? null;
if (type === 'message') {
console.log('message', data);
messages = [data, ...messages];
2024-12-27 05:55:21 +00:00
if (typingUsers.find((user) => user.id === event.user.id)) {
typingUsers = typingUsers.filter((user) => user.id !== event.user.id);
}
2024-12-23 03:28:15 +00:00
await tick();
if (scrollEnd) {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
}
2024-12-23 08:12:55 +00:00
} else if (type === 'message:update') {
console.log('message:update', data);
const idx = messages.findIndex((message) => message.id === data.id);
if (idx !== -1) {
messages[idx] = data;
}
} else if (type === 'message:delete') {
console.log('message:delete', data);
messages = messages.filter((message) => message.id !== data.id);
2024-12-31 07:06:34 +00:00
} else if (type === 'message:reaction') {
console.log('message:reaction', data);
const idx = messages.findIndex((message) => message.id === data.id);
if (idx !== -1) {
messages[idx] = data;
}
2024-12-27 05:51:09 +00:00
} else if (type === 'typing') {
if (event.user.id === $user.id) {
return;
}
typingUsers = data.typing
? [
...typingUsers,
...(typingUsers.find((user) => user.id === event.user.id)
? []
: [
{
id: event.user.id,
name: event.user.name
}
])
]
: typingUsers.filter((user) => user.id !== event.user.id);
if (typingUsersTimeout[event.user.id]) {
clearTimeout(typingUsersTimeout[event.user.id]);
}
typingUsersTimeout[event.user.id] = setTimeout(() => {
typingUsers = typingUsers.filter((user) => user.id !== event.user.id);
}, 5000);
2024-12-23 03:28:15 +00:00
}
}
2024-12-23 02:40:01 +00:00
};
2024-12-23 21:43:58 +00:00
const submitHandler = async ({ content, data }) => {
2024-12-23 02:40:01 +00:00
if (!content) {
return;
}
2024-12-23 21:43:58 +00:00
const res = await sendMessage(localStorage.token, id, { content: content, data: data }).catch(
(error) => {
toast.error(error);
return null;
}
);
2024-12-23 02:40:01 +00:00
if (res) {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
}
};
2024-12-27 05:51:09 +00:00
const onChange = async () => {
$socket?.emit('channel-events', {
channel_id: id,
data: {
type: 'typing',
data: {
typing: true
}
}
});
};
2024-12-23 02:40:01 +00:00
onMount(() => {
2024-12-25 05:04:43 +00:00
if ($chatId) {
chatId.set('');
}
2024-12-23 02:40:01 +00:00
$socket?.on('channel-events', channelEventHandler);
});
onDestroy(() => {
2024-12-25 01:25:59 +00:00
// $socket?.off('channel-events', channelEventHandler);
2024-12-23 02:40:01 +00:00
});
2024-12-22 11:49:24 +00:00
</script>
2024-12-27 06:03:39 +00:00
<svelte:head>
<title>#{channel?.name ?? 'Channel'} | Open WebUI</title>
</svelte:head>
2024-12-23 05:33:13 +00:00
<div
class="h-screen max-h-[100dvh] {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
: ''} w-full max-w-full flex flex-col"
2024-12-23 21:43:58 +00:00
id="channel-container"
2024-12-23 05:33:13 +00:00
>
2024-12-23 08:35:03 +00:00
<Navbar {channel} />
2024-12-23 08:38:45 +00:00
<div class="flex-1 overflow-y-auto">
2024-12-23 08:35:03 +00:00
{#if channel}
<div
class=" pb-2.5 max-w-full z-10 scrollbar-hidden w-full h-full pt-6 flex-1 flex flex-col-reverse overflow-auto"
id="messages-container"
bind:this={messagesContainerElement}
on:scroll={(e) => {
scrollEnd = Math.abs(messagesContainerElement.scrollTop) <= 50;
}}
>
{#key id}
<Messages
{channel}
{messages}
{top}
onLoad={async () => {
const newMessages = await getChannelMessages(localStorage.token, id, messages.length);
messages = [...messages, ...newMessages];
if (newMessages.length < 50) {
top = true;
return;
}
}}
/>
{/key}
</div>
{/if}
</div>
2024-12-23 02:40:01 +00:00
<div class=" pb-[1rem]">
2024-12-27 05:51:09 +00:00
<MessageInput {typingUsers} {onChange} onSubmit={submitHandler} {scrollToBottom} {scrollEnd} />
2024-12-23 02:40:01 +00:00
</div>
</div>