diff --git a/backend/open_webui/routers/folders.py b/backend/open_webui/routers/folders.py index d0cf042bf3..4f6c6cd10f 100644 --- a/backend/open_webui/routers/folders.py +++ b/backend/open_webui/routers/folders.py @@ -443,6 +443,15 @@ async def get_shared_folder_chats( chats = await Chats.get_all_chats_by_folder_id(id, db=db) + # Resolve owner names for display (avatar URLs are constructed client-side) + owner_cache: dict[str, str] = {} + for chat in chats: + uid = chat['user_id'] + if uid not in owner_cache: + u = await Users.get_user_by_id(uid, db=db) + owner_cache[uid] = u.name if u else 'Unknown' + chat['owner_name'] = owner_cache[uid] + return { 'chats': [ {**chat, 'readonly': chat['user_id'] != user.id} diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 639a71ecfd..406dc222bf 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -3030,6 +3030,7 @@ void) | null = null; @@ -122,7 +123,7 @@ " > {#if showModelSelector} - + {/if} @@ -203,6 +204,7 @@ { showShareChatModal = !showShareChatModal; diff --git a/src/lib/components/chat/Placeholder.svelte b/src/lib/components/chat/Placeholder.svelte index 4577ff9b6b..83f9a18a69 100644 --- a/src/lib/components/chat/Placeholder.svelte +++ b/src/lib/components/chat/Placeholder.svelte @@ -73,6 +73,9 @@ } $: models = selectedModels.map((id) => $_models.find((m) => m.id === id)); + + // True when viewing a shared folder the current user doesn't own + $: folderReadOnly = $selectedFolder != null && $selectedFolder.user_id !== $user?.id;
@@ -94,18 +97,19 @@
{#if $selectedFolder} { - await chats.set(await getChatList(localStorage.token, $currentChatPage)); - currentChatPage.set(1); - }} - onDelete={async () => { - await chats.set(await getChatList(localStorage.token, $currentChatPage)); - currentChatPage.set(1); + folder={$selectedFolder} + readOnly={folderReadOnly} + onUpdate={async (folder) => { + await chats.set(await getChatList(localStorage.token, $currentChatPage)); + currentChatPage.set(1); + }} + onDelete={async () => { + await chats.set(await getChatList(localStorage.token, $currentChatPage)); + currentChatPage.set(1); - selectedFolder.set(null); - }} - /> + selectedFolder.set(null); + }} + /> {:else}
@@ -210,33 +214,35 @@ {/if}
- { - dispatch('submit', e.detail); - }} - /> + {#if !($selectedFolder && folderReadOnly)} + { + dispatch('submit', e.detail); + }} + /> + {/if}
diff --git a/src/lib/components/chat/Placeholder/ChatList.svelte b/src/lib/components/chat/Placeholder/ChatList.svelte index d73bc30a9a..3dfc187089 100644 --- a/src/lib/components/chat/Placeholder/ChatList.svelte +++ b/src/lib/components/chat/Placeholder/ChatList.svelte @@ -9,6 +9,7 @@ import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; import Loader from '$lib/components/common/Loader.svelte'; import Spinner from '$lib/components/common/Spinner.svelte'; + import Tooltip from '$lib/components/common/Tooltip.svelte'; dayjs.extend(localizedFormat); @@ -54,6 +55,7 @@ let orderBy = 'updated_at'; let direction = 'desc'; // 'asc' or 'desc' + $: if (chats) { init(); } @@ -157,10 +159,20 @@ {chat?.title}
- + {/if}
{/if} diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte index 43bdc2b745..36357fd539 100644 --- a/src/lib/components/layout/Navbar/Menu.svelte +++ b/src/lib/components/layout/Navbar/Menu.svelte @@ -39,6 +39,7 @@ const i18n = getContext('i18n'); export let shareEnabled: boolean = false; + export let readOnly: boolean = false; export let shareHandler: Function; export let moveChatHandler: Function; @@ -364,7 +365,7 @@
{/if} - {#if !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))} + {#if !readOnly && !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))} - {#if !$temporaryChatEnabled && chat?.id} + {#if !readOnly && !$temporaryChatEnabled && chat?.id}
{#if $folders.length > 0} diff --git a/src/lib/components/layout/Sidebar/ChatItem.svelte b/src/lib/components/layout/Sidebar/ChatItem.svelte index d89d24a8ac..d4f9ca8f0d 100644 --- a/src/lib/components/layout/Sidebar/ChatItem.svelte +++ b/src/lib/components/layout/Sidebar/ChatItem.svelte @@ -65,6 +65,9 @@ export let shiftKey = false; export let readonly = false; + export let ownerName: string | null = null; + export let ownerUserId: string | null = null; + export let onDragEnd = () => {}; function formatTimeAgo(timestamp: number): string { @@ -534,6 +537,15 @@ {/if}
+ {#if ownerUserId} + + + + {/if} {#if unread}
diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte index 459175e5a9..979a1029ef 100644 --- a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte +++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte @@ -11,7 +11,7 @@ import { goto } from '$app/navigation'; import { toast } from 'svelte-sonner'; - import { chatId, mobile, selectedFolder, showSidebar } from '$lib/stores'; + import { chatId, mobile, selectedFolder, showSidebar, user } from '$lib/stores'; import { deleteFolderById, @@ -703,6 +703,9 @@ createdAt={chat.created_at} updatedAt={chat.updated_at} lastReadAt={chat.last_read_at} + ownerName={chat.owner_name ?? null} + ownerUserId={chat.owner_name ? chat.user_id : null} + readonly={chat.user_id !== $user?.id} {shiftKey} on:change={(e) => { dispatch('change', e.detail); diff --git a/src/lib/components/layout/Sidebar/SharedFolderItem.svelte b/src/lib/components/layout/Sidebar/SharedFolderItem.svelte index b7000695cb..1ae59f367b 100644 --- a/src/lib/components/layout/Sidebar/SharedFolderItem.svelte +++ b/src/lib/components/layout/Sidebar/SharedFolderItem.svelte @@ -108,6 +108,8 @@ title={chat.title} createdAt={chat.created_at} updatedAt={chat.updated_at} + ownerName={chat.owner_name} + ownerUserId={chat.user_id} readonly={chat.readonly ?? !isWritable} /> {/each}