Channel.svelte 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import { onDestroy, onMount, tick } from 'svelte';
  4. import { showSidebar, socket } from '$lib/stores';
  5. import { getChannelById, getChannelMessages, sendMessage } from '$lib/apis/channels';
  6. import Messages from './Messages.svelte';
  7. import MessageInput from './MessageInput.svelte';
  8. import { goto } from '$app/navigation';
  9. import Navbar from './Navbar.svelte';
  10. export let id = '';
  11. let scrollEnd = true;
  12. let messagesContainerElement = null;
  13. let top = false;
  14. let channel = null;
  15. let messages = null;
  16. $: if (id) {
  17. initHandler();
  18. }
  19. const scrollToBottom = () => {
  20. messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
  21. };
  22. const initHandler = async () => {
  23. top = false;
  24. messages = null;
  25. channel = null;
  26. channel = await getChannelById(localStorage.token, id).catch((error) => {
  27. return null;
  28. });
  29. if (channel) {
  30. messages = await getChannelMessages(localStorage.token, id, 0);
  31. if (messages) {
  32. messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
  33. if (messages.length < 50) {
  34. top = true;
  35. }
  36. }
  37. } else {
  38. goto('/');
  39. }
  40. };
  41. const channelEventHandler = async (event) => {
  42. console.log(event);
  43. if (event.channel_id === id) {
  44. const type = event?.data?.type ?? null;
  45. const data = event?.data?.data ?? null;
  46. if (type === 'message') {
  47. console.log('message', data);
  48. messages = [data, ...messages];
  49. await tick();
  50. if (scrollEnd) {
  51. messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
  52. }
  53. } else if (type === 'message:update') {
  54. console.log('message:update', data);
  55. const idx = messages.findIndex((message) => message.id === data.id);
  56. if (idx !== -1) {
  57. messages[idx] = data;
  58. }
  59. } else if (type === 'message:delete') {
  60. console.log('message:delete', data);
  61. messages = messages.filter((message) => message.id !== data.id);
  62. }
  63. }
  64. };
  65. const submitHandler = async ({ content }) => {
  66. if (!content) {
  67. return;
  68. }
  69. const res = await sendMessage(localStorage.token, id, { content: content }).catch((error) => {
  70. toast.error(error);
  71. return null;
  72. });
  73. if (res) {
  74. messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
  75. }
  76. };
  77. onMount(() => {
  78. $socket?.on('channel-events', channelEventHandler);
  79. });
  80. onDestroy(() => {
  81. $socket?.off('channel-events', channelEventHandler);
  82. });
  83. </script>
  84. <div
  85. class="h-screen max-h-[100dvh] {$showSidebar
  86. ? 'md:max-w-[calc(100%-260px)]'
  87. : ''} w-full max-w-full flex flex-col"
  88. >
  89. <Navbar {channel} />
  90. <div class="flex-1">
  91. {#if channel}
  92. <div
  93. 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"
  94. id="messages-container"
  95. bind:this={messagesContainerElement}
  96. on:scroll={(e) => {
  97. scrollEnd = Math.abs(messagesContainerElement.scrollTop) <= 50;
  98. }}
  99. >
  100. {#key id}
  101. <Messages
  102. {channel}
  103. {messages}
  104. {top}
  105. onLoad={async () => {
  106. const newMessages = await getChannelMessages(localStorage.token, id, messages.length);
  107. messages = [...messages, ...newMessages];
  108. if (newMessages.length < 50) {
  109. top = true;
  110. return;
  111. }
  112. }}
  113. />
  114. {/key}
  115. </div>
  116. {/if}
  117. </div>
  118. <div class=" pb-[1rem]">
  119. <MessageInput onSubmit={submitHandler} {scrollToBottom} {scrollEnd} />
  120. </div>
  121. </div>