123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- <script lang="ts">
- import { toast } from 'svelte-sonner';
- import { onDestroy, onMount, tick } from 'svelte';
- import { goto } from '$app/navigation';
- import { chatId, showSidebar, socket, user } from '$lib/stores';
- import { getChannelById, getChannelMessages, sendMessage } from '$lib/apis/channels';
- import Messages from './Messages.svelte';
- import MessageInput from './MessageInput.svelte';
- import Navbar from './Navbar.svelte';
- export let id = '';
- let scrollEnd = true;
- let messagesContainerElement = null;
- let top = false;
- let channel = null;
- let messages = null;
- let typingUsers = [];
- let typingUsersTimeout = {};
- $: if (id) {
- initHandler();
- }
- const scrollToBottom = () => {
- messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
- };
- const initHandler = async () => {
- top = false;
- messages = null;
- channel = null;
- channel = await getChannelById(localStorage.token, id).catch((error) => {
- return null;
- });
- if (channel) {
- messages = await getChannelMessages(localStorage.token, id, 0);
- if (messages) {
- messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
- if (messages.length < 50) {
- top = true;
- }
- }
- } else {
- goto('/');
- }
- };
- 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];
- if (typingUsers.find((user) => user.id === event.user.id)) {
- typingUsers = typingUsers.filter((user) => user.id !== event.user.id);
- }
- await tick();
- if (scrollEnd) {
- messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
- }
- } 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);
- } 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;
- }
- } 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);
- }
- }
- };
- const submitHandler = async ({ content, data }) => {
- if (!content) {
- return;
- }
- const res = await sendMessage(localStorage.token, id, { content: content, data: data }).catch(
- (error) => {
- toast.error(error);
- return null;
- }
- );
- if (res) {
- messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
- }
- };
- const onChange = async () => {
- $socket?.emit('channel-events', {
- channel_id: id,
- data: {
- type: 'typing',
- data: {
- typing: true
- }
- }
- });
- };
- onMount(() => {
- if ($chatId) {
- chatId.set('');
- }
- $socket?.on('channel-events', channelEventHandler);
- });
- onDestroy(() => {
- // $socket?.off('channel-events', channelEventHandler);
- });
- </script>
- <svelte:head>
- <title>#{channel?.name ?? 'Channel'} | Open WebUI</title>
- </svelte:head>
- <div
- class="h-screen max-h-[100dvh] {$showSidebar
- ? 'md:max-w-[calc(100%-260px)]'
- : ''} w-full max-w-full flex flex-col"
- id="channel-container"
- >
- <Navbar {channel} />
- <div class="flex-1 overflow-y-auto">
- {#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>
- <div class=" pb-[1rem]">
- <MessageInput {typingUsers} {onChange} onSubmit={submitHandler} {scrollToBottom} {scrollEnd} />
- </div>
- </div>
|