123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- <script lang="ts">
- import { goto } from '$app/navigation';
- import { socket, user } from '$lib/stores';
- import { getChannelThreadMessages, sendMessage } from '$lib/apis/channels';
- import XMark from '$lib/components/icons/XMark.svelte';
- import MessageInput from './MessageInput.svelte';
- import Messages from './Messages.svelte';
- import { onDestroy, onMount, tick } from 'svelte';
- import { toast } from 'svelte-sonner';
- export let threadId = null;
- export let channel = null;
- export let onClose = () => {};
- let messages = null;
- let top = false;
- let typingUsers = [];
- let typingUsersTimeout = {};
- let messagesContainerElement = null;
- $: if (threadId) {
- initHandler();
- }
- const scrollToBottom = () => {
- messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
- };
- const initHandler = async () => {
- messages = null;
- top = false;
- typingUsers = [];
- typingUsersTimeout = {};
- if (channel) {
- messages = await getChannelThreadMessages(localStorage.token, channel.id, threadId);
- if (messages.length < 50) {
- top = true;
- }
- await tick();
- scrollToBottom();
- } else {
- goto('/');
- }
- };
- const channelEventHandler = async (event) => {
- console.log(event);
- if (event.channel_id === channel.id) {
- const type = event?.data?.type ?? null;
- const data = event?.data?.data ?? null;
- if (type === 'message') {
- if ((data?.parent_id ?? null) === threadId) {
- if (messages) {
- messages = [data, ...messages];
- if (typingUsers.find((user) => user.id === event.user.id)) {
- typingUsers = typingUsers.filter((user) => user.id !== event.user.id);
- }
- }
- }
- } else if (type === 'message:update') {
- if (messages) {
- const idx = messages.findIndex((message) => message.id === data.id);
- if (idx !== -1) {
- messages[idx] = data;
- }
- }
- } else if (type === 'message:delete') {
- if (messages) {
- messages = messages.filter((message) => message.id !== data.id);
- }
- } else if (type.includes('message:reaction')) {
- if (messages) {
- const idx = messages.findIndex((message) => message.id === data.id);
- if (idx !== -1) {
- messages[idx] = data;
- }
- }
- } else if (type === 'typing' && event.message_id === threadId) {
- 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, channel.id, {
- parent_id: threadId,
- content: content,
- data: data
- }).catch((error) => {
- toast.error(`${error}`);
- return null;
- });
- };
- const onChange = async () => {
- $socket?.emit('channel-events', {
- channel_id: channel.id,
- message_id: threadId,
- data: {
- type: 'typing',
- data: {
- typing: true
- }
- }
- });
- };
- onMount(() => {
- $socket?.on('channel-events', channelEventHandler);
- });
- onDestroy(() => {
- $socket?.off('channel-events', channelEventHandler);
- });
- </script>
- {#if channel}
- <div class="flex flex-col w-full h-full bg-gray-50 dark:bg-gray-850">
- <div class="flex items-center justify-between px-3.5 pt-3">
- <div class=" font-medium text-lg">Thread</div>
- <div>
- <button
- class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 p-2"
- on:click={() => {
- onClose();
- }}
- >
- <XMark />
- </button>
- </div>
- </div>
- <div class=" max-h-full w-full overflow-y-auto pt-3" bind:this={messagesContainerElement}>
- <Messages
- id={threadId}
- {channel}
- {messages}
- {top}
- thread={true}
- onLoad={async () => {
- const newMessages = await getChannelThreadMessages(
- localStorage.token,
- channel.id,
- threadId,
- messages.length
- );
- messages = [...messages, ...newMessages];
- if (newMessages.length < 50) {
- top = true;
- return;
- }
- }}
- />
- <div class=" pb-[1rem]">
- <MessageInput id={threadId} {typingUsers} {onChange} onSubmit={submitHandler} />
- </div>
- </div>
- </div>
- {/if}
|