mirror of
https://github.com/open-webui/open-webui
synced 2025-01-19 09:16:44 +00:00
feat: channel threads
This commit is contained in:
parent
16a48ef4eb
commit
a754a4388a
@ -94,12 +94,18 @@
|
||||
}
|
||||
} else if (type === 'message:delete') {
|
||||
messages = messages.filter((message) => message.id !== data.id);
|
||||
} else if (type === 'message:reply') {
|
||||
const idx = messages.findIndex((message) => message.id === data.id);
|
||||
|
||||
if (idx !== -1) {
|
||||
messages[idx] = data;
|
||||
}
|
||||
} else if (type.includes('message:reaction')) {
|
||||
const idx = messages.findIndex((message) => message.id === data.id);
|
||||
if (idx !== -1) {
|
||||
messages[idx] = data;
|
||||
}
|
||||
} else if (type === 'typing') {
|
||||
} else if (type === 'typing' && event.message_id === null) {
|
||||
if (event.user.id === $user.id) {
|
||||
return;
|
||||
}
|
||||
@ -242,6 +248,7 @@
|
||||
|
||||
<div class=" pb-[1rem]">
|
||||
<MessageInput
|
||||
id="root"
|
||||
{typingUsers}
|
||||
{onChange}
|
||||
onSubmit={submitHandler}
|
||||
|
@ -23,6 +23,8 @@
|
||||
export let placeholder = $i18n.t('Send a Message');
|
||||
export let transparentBackground = false;
|
||||
|
||||
export let id = null;
|
||||
|
||||
let draggedOver = false;
|
||||
|
||||
let recording = false;
|
||||
@ -257,7 +259,7 @@
|
||||
|
||||
await tick();
|
||||
|
||||
const chatInputElement = document.getElementById('chat-input');
|
||||
const chatInputElement = document.getElementById(`chat-input-${id}`);
|
||||
chatInputElement?.focus();
|
||||
};
|
||||
|
||||
@ -267,7 +269,7 @@
|
||||
|
||||
onMount(async () => {
|
||||
window.setTimeout(() => {
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const chatInput = document.getElementById(`chat-input-${id}`);
|
||||
chatInput?.focus();
|
||||
}, 0);
|
||||
|
||||
@ -373,7 +375,7 @@
|
||||
recording = false;
|
||||
|
||||
await tick();
|
||||
document.getElementById('chat-input')?.focus();
|
||||
document.getElementById(`chat-input-${id}`)?.focus();
|
||||
}}
|
||||
on:confirm={async (e) => {
|
||||
const { text, filename } = e.detail;
|
||||
@ -381,7 +383,7 @@
|
||||
recording = false;
|
||||
|
||||
await tick();
|
||||
document.getElementById('chat-input')?.focus();
|
||||
document.getElementById(`chat-input-${id}`)?.focus();
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
@ -478,61 +480,21 @@
|
||||
</InputMenu>
|
||||
</div>
|
||||
|
||||
{#if $settings?.richTextInput ?? true}
|
||||
<div
|
||||
class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
|
||||
>
|
||||
<RichTextInput
|
||||
bind:value={content}
|
||||
id="chat-input"
|
||||
messageInput={true}
|
||||
shiftEnter={!$mobile ||
|
||||
!(
|
||||
'ontouchstart' in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
navigator.msMaxTouchPoints > 0
|
||||
)}
|
||||
{placeholder}
|
||||
largeTextAsFile={$settings?.largeTextAsFile ?? false}
|
||||
on:keydown={async (e) => {
|
||||
e = e.detail.event;
|
||||
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
|
||||
if (
|
||||
!$mobile ||
|
||||
!(
|
||||
'ontouchstart' in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
navigator.msMaxTouchPoints > 0
|
||||
)
|
||||
) {
|
||||
// Prevent Enter key from creating a new line
|
||||
// Uses keyCode '13' for Enter key for chinese/japanese keyboards
|
||||
if (e.keyCode === 13 && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Submit the content when Enter key is pressed
|
||||
if (content !== '' && e.keyCode === 13 && !e.shiftKey) {
|
||||
submitHandler();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
console.log('Escape');
|
||||
}
|
||||
}}
|
||||
on:paste={async (e) => {
|
||||
e = e.detail.event;
|
||||
console.log(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<textarea
|
||||
id="chat-input"
|
||||
class="scrollbar-hidden bg-transparent dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px]"
|
||||
{placeholder}
|
||||
<div
|
||||
class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
|
||||
>
|
||||
<RichTextInput
|
||||
bind:value={content}
|
||||
id={`chat-input-${id}`}
|
||||
messageInput={true}
|
||||
shiftEnter={!$mobile ||
|
||||
!(
|
||||
'ontouchstart' in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
navigator.msMaxTouchPoints > 0
|
||||
)}
|
||||
{placeholder}
|
||||
largeTextAsFile={$settings?.largeTextAsFile ?? false}
|
||||
on:keydown={async (e) => {
|
||||
e = e.detail.event;
|
||||
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
|
||||
@ -560,17 +522,12 @@
|
||||
console.log('Escape');
|
||||
}
|
||||
}}
|
||||
rows="1"
|
||||
on:input={async (e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
|
||||
}}
|
||||
on:focus={async (e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
|
||||
on:paste={async (e) => {
|
||||
e = e.detail.event;
|
||||
console.log(e);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="self-end mb-1.5 flex space-x-1 mr-1">
|
||||
{#if content === ''}
|
||||
|
@ -77,7 +77,7 @@
|
||||
? 'max-w-full'
|
||||
: 'max-w-5xl'} mx-auto group hover:bg-gray-300/5 dark:hover:bg-gray-700/5 transition relative"
|
||||
>
|
||||
{#if (message.user_id === $user.id || $user.role === 'admin') && !edit}
|
||||
{#if !edit}
|
||||
<div
|
||||
class=" absolute {showButtons ? '' : 'invisible group-hover:visible'} right-1 -top-2 z-30"
|
||||
>
|
||||
@ -116,26 +116,28 @@
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Edit')}>
|
||||
<button
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
|
||||
on:click={() => {
|
||||
edit = true;
|
||||
editedContent = message.content;
|
||||
}}
|
||||
>
|
||||
<Pencil />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{#if message.user_id === $user.id || $user.role === 'admin'}
|
||||
<Tooltip content={$i18n.t('Edit')}>
|
||||
<button
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
|
||||
on:click={() => {
|
||||
edit = true;
|
||||
editedContent = message.content;
|
||||
}}
|
||||
>
|
||||
<Pencil />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={$i18n.t('Delete')}>
|
||||
<button
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
|
||||
on:click={() => (showDeleteConfirmDialog = true)}
|
||||
>
|
||||
<GarbageBin />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip content={$i18n.t('Delete')}>
|
||||
<button
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
|
||||
on:click={() => (showDeleteConfirmDialog = true)}
|
||||
>
|
||||
<GarbageBin />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@ -326,7 +328,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if message.reply_count > 0}
|
||||
{#if !thread && message.reply_count > 0}
|
||||
<div class="flex items-center gap-1.5 -mt-0.5 mb-1.5">
|
||||
<button
|
||||
class="flex items-center text-xs py-1 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition"
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { socket } from '$lib/stores';
|
||||
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 { onMount } from 'svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
export let threadId = null;
|
||||
@ -44,6 +44,64 @@
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
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') {
|
||||
const idx = messages.findIndex((message) => message.id === data.id);
|
||||
|
||||
if (idx !== -1) {
|
||||
messages[idx] = data;
|
||||
}
|
||||
} else if (type === 'message:delete') {
|
||||
messages = messages.filter((message) => message.id !== data.id);
|
||||
} else if (type.includes('message:reaction')) {
|
||||
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;
|
||||
@ -71,6 +129,10 @@
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
$socket?.on('channel-events', channelEventHandler);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if channel}
|
||||
@ -113,6 +175,6 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<MessageInput {typingUsers} {onChange} onSubmit={submitHandler} />
|
||||
<MessageInput id={threadId} {typingUsers} {onChange} onSubmit={submitHandler} />
|
||||
</div>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user