Skip to content

Commit 76854d1

Browse files
committed
refac
1 parent 45fcf27 commit 76854d1

10 files changed

Lines changed: 146 additions & 83 deletions

File tree

backend/open_webui/routers/folders.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,15 @@ async def get_shared_folder_chats(
443443

444444
chats = await Chats.get_all_chats_by_folder_id(id, db=db)
445445

446+
# Resolve owner names for display (avatar URLs are constructed client-side)
447+
owner_cache: dict[str, str] = {}
448+
for chat in chats:
449+
uid = chat['user_id']
450+
if uid not in owner_cache:
451+
u = await Users.get_user_by_id(uid, db=db)
452+
owner_cache[uid] = u.name if u else 'Unknown'
453+
chat['owner_name'] = owner_cache[uid]
454+
446455
return {
447456
'chats': [
448457
{**chat, 'readonly': chat['user_id'] != user.id}

src/lib/components/chat/Chat.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3030,6 +3030,7 @@
30303030
<FilesOverlay show={dragged} />
30313031
<Navbar
30323032
bind:this={navbarElement}
3033+
{readOnly}
30333034
chat={{
30343035
id: $chatId,
30353036
chat: {

src/lib/components/chat/Navbar.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
const i18n = getContext('i18n');
4444
4545
export let initNewChat: Function;
46+
export let readOnly: boolean = false;
4647
export let shareEnabled: boolean = false;
4748
export let scrollTop = 0;
4849
export let scrollToTop: (() => void) | null = null;
@@ -122,7 +123,7 @@
122123
"
123124
>
124125
{#if showModelSelector}
125-
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
126+
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled && !readOnly} disabled={readOnly} />
126127
{/if}
127128
</div>
128129

@@ -203,6 +204,7 @@
203204
<Menu
204205
{chat}
205206
{shareEnabled}
207+
{readOnly}
206208
{scrollToTop}
207209
shareHandler={() => {
208210
showShareChatModal = !showShareChatModal;

src/lib/components/chat/Placeholder.svelte

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
}
7474
7575
$: models = selectedModels.map((id) => $_models.find((m) => m.id === id));
76+
77+
// True when viewing a shared folder the current user doesn't own
78+
$: folderReadOnly = $selectedFolder != null && $selectedFolder.user_id !== $user?.id;
7679
</script>
7780

7881
<div class="m-auto w-full max-w-6xl px-2 @2xl:px-20 translate-y-6 py-24 text-center">
@@ -94,18 +97,19 @@
9497
<div class="w-full flex flex-col justify-center items-center">
9598
{#if $selectedFolder}
9699
<FolderTitle
97-
folder={$selectedFolder}
98-
onUpdate={async (folder) => {
99-
await chats.set(await getChatList(localStorage.token, $currentChatPage));
100-
currentChatPage.set(1);
101-
}}
102-
onDelete={async () => {
103-
await chats.set(await getChatList(localStorage.token, $currentChatPage));
104-
currentChatPage.set(1);
100+
folder={$selectedFolder}
101+
readOnly={folderReadOnly}
102+
onUpdate={async (folder) => {
103+
await chats.set(await getChatList(localStorage.token, $currentChatPage));
104+
currentChatPage.set(1);
105+
}}
106+
onDelete={async () => {
107+
await chats.set(await getChatList(localStorage.token, $currentChatPage));
108+
currentChatPage.set(1);
105109

106-
selectedFolder.set(null);
107-
}}
108-
/>
110+
selectedFolder.set(null);
111+
}}
112+
/>
109113
{:else}
110114
<div class="flex flex-row justify-center gap-2.5 @sm:gap-3 w-fit px-5 max-w-xl">
111115
<div class="flex shrink-0 justify-center">
@@ -210,33 +214,35 @@
210214
{/if}
211215

212216
<div class="text-base font-normal @md:max-w-3xl w-full py-3 {atSelectedModel ? 'mt-2' : ''}">
213-
<MessageInput
214-
bind:this={messageInput}
215-
{history}
216-
{selectedModels}
217-
bind:files
218-
bind:prompt
219-
bind:autoScroll
220-
bind:selectedToolIds
221-
bind:selectedSkillIds
222-
bind:selectedFilterIds
223-
bind:imageGenerationEnabled
224-
bind:codeInterpreterEnabled
225-
bind:webSearchEnabled
226-
bind:atSelectedModel
227-
bind:showCommands
228-
bind:dragged
229-
{pendingOAuthTools}
230-
{toolServers}
231-
{stopResponse}
232-
{createMessagePair}
233-
placeholder={$i18n.t('How can I help you today?')}
234-
{onChange}
235-
{onUpload}
236-
on:submit={(e) => {
237-
dispatch('submit', e.detail);
238-
}}
239-
/>
217+
{#if !($selectedFolder && folderReadOnly)}
218+
<MessageInput
219+
bind:this={messageInput}
220+
{history}
221+
{selectedModels}
222+
bind:files
223+
bind:prompt
224+
bind:autoScroll
225+
bind:selectedToolIds
226+
bind:selectedSkillIds
227+
bind:selectedFilterIds
228+
bind:imageGenerationEnabled
229+
bind:codeInterpreterEnabled
230+
bind:webSearchEnabled
231+
bind:atSelectedModel
232+
bind:showCommands
233+
bind:dragged
234+
{pendingOAuthTools}
235+
{toolServers}
236+
{stopResponse}
237+
{createMessagePair}
238+
placeholder={$i18n.t('How can I help you today?')}
239+
{onChange}
240+
{onUpload}
241+
on:submit={(e) => {
242+
dispatch('submit', e.detail);
243+
}}
244+
/>
245+
{/if}
240246
</div>
241247
</div>
242248
</div>

src/lib/components/chat/Placeholder/ChatList.svelte

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
1010
import Loader from '$lib/components/common/Loader.svelte';
1111
import Spinner from '$lib/components/common/Spinner.svelte';
12+
import Tooltip from '$lib/components/common/Tooltip.svelte';
1213
1314
dayjs.extend(localizedFormat);
1415
@@ -54,6 +55,7 @@
5455
let orderBy = 'updated_at';
5556
let direction = 'desc'; // 'asc' or 'desc'
5657
58+
5759
$: if (chats) {
5860
init();
5961
}
@@ -157,10 +159,20 @@
157159
{chat?.title}
158160
</div>
159161

160-
<div class="hidden sm:flex sm:basis-2/5 items-center justify-end">
162+
<div class="hidden sm:flex sm:basis-2/5 items-center justify-end gap-2">
161163
<div class=" text-gray-500 dark:text-gray-400 text-xs">
162164
{dayjs(chat?.updated_at * 1000).calendar()}
163165
</div>
166+
167+
{#if chat.user_id && chat.owner_name}
168+
<Tooltip content={chat.owner_name}>
169+
<img
170+
src="/api/v1/users/{chat.user_id}/profile/image"
171+
alt=""
172+
class="size-5 rounded-full shrink-0 object-cover"
173+
/>
174+
</Tooltip>
175+
{/if}
164176
</div>
165177
</a>
166178
{/each}

src/lib/components/chat/Placeholder/FolderTitle.svelte

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import EmojiPicker from '$lib/components/common/EmojiPicker.svelte';
3232
3333
export let folder = null;
34+
export let readOnly: boolean = false;
3435
3536
export let onUpdate: Function = (folderId) => {};
3637
export let onDelete: Function = (folderId) => {};
@@ -205,58 +206,72 @@
205206

206207
<div class="mb-3 px-6 @md:max-w-3xl justify-between w-full flex relative group items-center">
207208
<div class="text-center flex gap-3.5 items-center">
208-
<EmojiPicker
209-
onClose={() => {}}
210-
selected={folder?.meta?.icon ?? null}
211-
onSubmit={(name) => {
212-
console.log(name);
213-
updateIconHandler(name);
214-
}}
215-
>
216-
<button
217-
aria-label={$i18n.t('Change folder icon')}
218-
class=" rounded-full bg-gray-50 dark:bg-gray-800 size-11 flex justify-center items-center"
209+
{#if readOnly}
210+
<div
211+
class="rounded-full bg-gray-50 dark:bg-gray-800 size-11 flex justify-center items-center"
219212
>
220213
{#if folder?.meta?.icon}
221214
<Emoji className="size-6" shortCode={folder.meta.icon} />
222215
{:else}
223216
<Folder className="size-4.5" strokeWidth="2" />
224217
{/if}
225-
</button>
226-
</EmojiPicker>
218+
</div>
219+
{:else}
220+
<EmojiPicker
221+
onClose={() => {}}
222+
selected={folder?.meta?.icon ?? null}
223+
onSubmit={(name) => {
224+
console.log(name);
225+
updateIconHandler(name);
226+
}}
227+
>
228+
<button
229+
aria-label={$i18n.t('Change folder icon')}
230+
class=" rounded-full bg-gray-50 dark:bg-gray-800 size-11 flex justify-center items-center"
231+
>
232+
{#if folder?.meta?.icon}
233+
<Emoji className="size-6" shortCode={folder.meta.icon} />
234+
{:else}
235+
<Folder className="size-4.5" strokeWidth="2" />
236+
{/if}
237+
</button>
238+
</EmojiPicker>
239+
{/if}
227240

228241
<div class="text-3xl line-clamp-1">
229242
{folder.name}
230243
</div>
231244
</div>
232245

233-
<div class="flex items-center translate-x-2.5">
234-
<FolderMenu
235-
align="end"
236-
onEdit={() => {
237-
showFolderModal = true;
238-
}}
239-
onShare={() => {
240-
showShareModal = true;
241-
}}
242-
onDelete={() => {
243-
showDeleteConfirm = true;
244-
}}
245-
onExport={() => {
246-
exportHandler();
247-
}}
248-
onCreateSubFolder={() => {
249-
showCreateSubFolderModal = true;
250-
}}
251-
>
252-
<button
253-
class="p-1.5 dark:hover:bg-gray-850 rounded-full touch-auto"
254-
aria-label={$i18n.t('Folder options')}
255-
on:click={(e) => {}}
246+
{#if !readOnly}
247+
<div class="flex items-center translate-x-2.5">
248+
<FolderMenu
249+
align="end"
250+
onEdit={() => {
251+
showFolderModal = true;
252+
}}
253+
onShare={() => {
254+
showShareModal = true;
255+
}}
256+
onDelete={() => {
257+
showDeleteConfirm = true;
258+
}}
259+
onExport={() => {
260+
exportHandler();
261+
}}
262+
onCreateSubFolder={() => {
263+
showCreateSubFolderModal = true;
264+
}}
256265
>
257-
<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
258-
</button>
259-
</FolderMenu>
260-
</div>
266+
<button
267+
class="p-1.5 dark:hover:bg-gray-850 rounded-full touch-auto"
268+
aria-label={$i18n.t('Folder options')}
269+
on:click={(e) => {}}
270+
>
271+
<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
272+
</button>
273+
</FolderMenu>
274+
</div>
275+
{/if}
261276
</div>
262277
{/if}

src/lib/components/layout/Navbar/Menu.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
const i18n = getContext('i18n');
4040
4141
export let shareEnabled: boolean = false;
42+
export let readOnly: boolean = false;
4243
4344
export let shareHandler: Function;
4445
export let moveChatHandler: Function;
@@ -364,7 +365,7 @@
364365
<hr class="border-gray-50/30 dark:border-gray-800/30 my-1" />
365366
{/if}
366367

367-
{#if !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))}
368+
{#if !readOnly && !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))}
368369
<button
369370
draggable="false"
370371
class="flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl select-none w-full"
@@ -438,7 +439,7 @@
438439
<div class="flex items-center">{$i18n.t('Copy')}</div>
439440
</button>
440441

441-
{#if !$temporaryChatEnabled && chat?.id}
442+
{#if !readOnly && !$temporaryChatEnabled && chat?.id}
442443
<hr class="border-gray-50/30 dark:border-gray-800/30 my-1" />
443444

444445
{#if $folders.length > 0}

src/lib/components/layout/Sidebar/ChatItem.svelte

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
export let shiftKey = false;
6666
export let readonly = false;
6767
68+
export let ownerName: string | null = null;
69+
export let ownerUserId: string | null = null;
70+
6871
export let onDragEnd = () => {};
6972
7073
function formatTimeAgo(timestamp: number): string {
@@ -534,6 +537,15 @@
534537
{/if}
535538

536539
<div class="flex self-center flex-1 w-full min-w-0">
540+
{#if ownerUserId}
541+
<Tooltip content={ownerName || 'Unknown'}>
542+
<img
543+
src="/api/v1/users/{ownerUserId}/profile/image"
544+
alt=""
545+
class="size-4 rounded-full shrink-0 object-cover mr-1.5"
546+
/>
547+
</Tooltip>
548+
{/if}
537549
{#if unread}
538550
<div class="shrink-0 self-center pr-2.5 flex transition-opacity duration-300">
539551
<div class="size-1.5 bg-sky-500 rounded-full" />

src/lib/components/layout/Sidebar/RecursiveFolder.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import { goto } from '$app/navigation';
1212
import { toast } from 'svelte-sonner';
1313
14-
import { chatId, mobile, selectedFolder, showSidebar } from '$lib/stores';
14+
import { chatId, mobile, selectedFolder, showSidebar, user } from '$lib/stores';
1515
1616
import {
1717
deleteFolderById,
@@ -703,6 +703,9 @@
703703
createdAt={chat.created_at}
704704
updatedAt={chat.updated_at}
705705
lastReadAt={chat.last_read_at}
706+
ownerName={chat.owner_name ?? null}
707+
ownerUserId={chat.owner_name ? chat.user_id : null}
708+
readonly={chat.user_id !== $user?.id}
706709
{shiftKey}
707710
on:change={(e) => {
708711
dispatch('change', e.detail);

src/lib/components/layout/Sidebar/SharedFolderItem.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@
108108
title={chat.title}
109109
createdAt={chat.created_at}
110110
updatedAt={chat.updated_at}
111+
ownerName={chat.owner_name}
112+
ownerUserId={chat.user_id}
111113
readonly={chat.readonly ?? !isWritable}
112114
/>
113115
{/each}

0 commit comments

Comments
 (0)