mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-13 04:45:19 +00:00
enh: move chat menu
This commit is contained in:
parent
f46837fd10
commit
d720e027ad
5 changed files with 160 additions and 31 deletions
|
|
@ -37,7 +37,8 @@
|
|||
showArtifacts,
|
||||
tools,
|
||||
toolServers,
|
||||
selectedFolder
|
||||
selectedFolder,
|
||||
pinnedChats
|
||||
} from '$lib/stores';
|
||||
import {
|
||||
convertMessagesToHistory,
|
||||
|
|
@ -55,8 +56,10 @@
|
|||
getAllTags,
|
||||
getChatById,
|
||||
getChatList,
|
||||
getPinnedChatList,
|
||||
getTagsById,
|
||||
updateChatById
|
||||
updateChatById,
|
||||
updateChatFolderIdById
|
||||
} from '$lib/apis/chats';
|
||||
import { generateOpenAIChatCompletion } from '$lib/apis/openai';
|
||||
import { processWeb, processWebSearch, processYoutubeVideo } from '$lib/apis/retrieval';
|
||||
|
|
@ -2118,6 +2121,27 @@
|
|||
}
|
||||
await sessionStorage.removeItem(`chat-input${chatId ? `-${chatId}` : ''}`);
|
||||
};
|
||||
|
||||
const moveChatHandler = async (chatId, folderId) => {
|
||||
if (chatId && folderId) {
|
||||
const res = await updateChatFolderIdById(localStorage.token, chatId, folderId).catch(
|
||||
(error) => {
|
||||
toast.error(`${error}`);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
if (res) {
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
await pinnedChats.set(await getPinnedChatList(localStorage.token));
|
||||
|
||||
toast.success($i18n.t('Chat moved successfully'));
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to move chat'));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -2192,6 +2216,8 @@
|
|||
shareEnabled={!!history.currentId}
|
||||
{initNewChat}
|
||||
showBanners={!showCommands}
|
||||
archiveChatHandler={() => {}}
|
||||
{moveChatHandler}
|
||||
onSaveTempChat={async () => {
|
||||
try {
|
||||
if (!history?.currentId || !Object.keys(history.messages).length) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
export let showBanners = true;
|
||||
|
||||
export let onSaveTempChat: () => {};
|
||||
export let archiveChatHandler: (id: string) => void;
|
||||
export let moveChatHandler: (id: string, folderId: string) => void;
|
||||
|
||||
let closedBannerIds = [];
|
||||
|
||||
|
|
@ -165,6 +167,10 @@
|
|||
shareHandler={() => {
|
||||
showShareChatModal = !showShareChatModal;
|
||||
}}
|
||||
archiveChatHandler={() => {
|
||||
archiveChatHandler(chat.id);
|
||||
}}
|
||||
{moveChatHandler}
|
||||
>
|
||||
<button
|
||||
class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@
|
|||
temporaryChatEnabled,
|
||||
theme,
|
||||
user,
|
||||
settings
|
||||
settings,
|
||||
folders
|
||||
} from '$lib/stores';
|
||||
import { flyAndScale } from '$lib/utils/transitions';
|
||||
import { getChatById } from '$lib/apis/chats';
|
||||
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import Tags from '$lib/components/chat/Tags.svelte';
|
||||
|
|
@ -30,13 +32,19 @@
|
|||
import Clipboard from '$lib/components/icons/Clipboard.svelte';
|
||||
import AdjustmentsHorizontal from '$lib/components/icons/AdjustmentsHorizontal.svelte';
|
||||
import Cube from '$lib/components/icons/Cube.svelte';
|
||||
import { getChatById } from '$lib/apis/chats';
|
||||
import Folder from '$lib/components/icons/Folder.svelte';
|
||||
import Share from '$lib/components/icons/Share.svelte';
|
||||
import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let shareEnabled: boolean = false;
|
||||
|
||||
export let shareHandler: Function;
|
||||
export let moveHandler: Function;
|
||||
export let moveChatHandler: Function;
|
||||
|
||||
export let archiveChatHandler: Function;
|
||||
|
||||
// export let tagHandler: Function;
|
||||
|
||||
export let chat;
|
||||
|
|
@ -262,30 +270,6 @@
|
|||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
|
||||
{#if !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))}
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
id="chat-share-button"
|
||||
on:click={() => {
|
||||
shareHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.75 4.5a3 3 0 1 1 .825 2.066l-8.421 4.679a3.002 3.002 0 0 1 0 1.51l8.421 4.679a3 3 0 1 1-.729 1.31l-8.421-4.678a3 3 0 1 1 0-4.132l8.421-4.679a3 3 0 0 1-.096-.755Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<div class="flex items-center">{$i18n.t('Share')}</div>
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
id="chat-overview-button"
|
||||
|
|
@ -312,6 +296,61 @@
|
|||
<div class="flex items-center">{$i18n.t('Artifacts')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<hr class="border-gray-100 dark:border-gray-800 my-1" />
|
||||
|
||||
{#if !$temporaryChatEnabled && ($user?.role === 'admin' || ($user.permissions?.chat?.share ?? true))}
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
id="chat-share-button"
|
||||
on:click={() => {
|
||||
shareHandler();
|
||||
}}
|
||||
>
|
||||
<Share />
|
||||
<div class="flex items-center">{$i18n.t('Share')}</div>
|
||||
</DropdownMenu.Item>
|
||||
{/if}
|
||||
|
||||
{#if chat?.id}
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
>
|
||||
<Folder />
|
||||
|
||||
<div class="flex items-center">{$i18n.t('Move')}</div>
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent
|
||||
class="w-full rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg max-h-52 overflow-y-auto scrollbar-hidden"
|
||||
transition={flyAndScale}
|
||||
sideOffset={8}
|
||||
>
|
||||
{#each $folders.sort((a, b) => b.updated_at - a.updated_at) as folder}
|
||||
<DropdownMenu.Item
|
||||
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-md"
|
||||
on:click={() => {
|
||||
moveChatHandler(chat?.id, folder?.id);
|
||||
}}
|
||||
>
|
||||
<Folder />
|
||||
|
||||
<div class="flex items-center">{folder?.name ?? 'Folder'}</div>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
{/if}
|
||||
|
||||
<DropdownMenu.Item
|
||||
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-md"
|
||||
on:click={() => {
|
||||
archiveChatHandler();
|
||||
}}
|
||||
>
|
||||
<ArchiveBox className="size-4" strokeWidth="2" />
|
||||
<div class="flex items-center">{$i18n.t('Archive')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
getChatList,
|
||||
getChatListByTagName,
|
||||
getPinnedChatList,
|
||||
updateChatById
|
||||
updateChatById,
|
||||
updateChatFolderIdById
|
||||
} from '$lib/apis/chats';
|
||||
import {
|
||||
chatId,
|
||||
|
|
@ -136,6 +137,29 @@
|
|||
dispatch('change');
|
||||
};
|
||||
|
||||
const moveChatHandler = async (chatId, folderId) => {
|
||||
if (chatId && folderId) {
|
||||
const res = await updateChatFolderIdById(localStorage.token, chatId, folderId).catch(
|
||||
(error) => {
|
||||
toast.error(`${error}`);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
if (res) {
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
await pinnedChats.set(await getPinnedChatList(localStorage.token));
|
||||
|
||||
dispatch('change');
|
||||
|
||||
toast.success($i18n.t('Chat moved successfully'));
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to move chat'));
|
||||
}
|
||||
};
|
||||
|
||||
let itemElement;
|
||||
|
||||
let generating = false;
|
||||
|
|
@ -485,6 +509,7 @@
|
|||
shareHandler={() => {
|
||||
showShareChatModal = true;
|
||||
}}
|
||||
{moveChatHandler}
|
||||
archiveChatHandler={() => {
|
||||
archiveChatHandler(id);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -26,14 +26,17 @@
|
|||
getChatPinnedStatusById,
|
||||
toggleChatPinnedStatusById
|
||||
} from '$lib/apis/chats';
|
||||
import { chats, settings, theme, user } from '$lib/stores';
|
||||
import { chats, folders, settings, theme, user } from '$lib/stores';
|
||||
import { createMessagesList } from '$lib/utils';
|
||||
import { downloadChatAsPDF } from '$lib/apis/utils';
|
||||
import Download from '$lib/components/icons/ArrowDownTray.svelte';
|
||||
import Folder from '$lib/components/icons/Folder.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let shareHandler: Function;
|
||||
export let moveChatHandler: Function;
|
||||
|
||||
export let cloneChatHandler: Function;
|
||||
export let archiveChatHandler: Function;
|
||||
export let renameHandler: Function;
|
||||
|
|
@ -253,6 +256,36 @@
|
|||
{/if}
|
||||
</DropdownMenu.Item>
|
||||
|
||||
{#if chatId}
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md select-none w-full"
|
||||
>
|
||||
<Folder />
|
||||
|
||||
<div class="flex items-center">{$i18n.t('Move')}</div>
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent
|
||||
class="w-full rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg max-h-52 overflow-y-auto scrollbar-hidden"
|
||||
transition={flyAndScale}
|
||||
sideOffset={8}
|
||||
>
|
||||
{#each $folders.sort((a, b) => b.updated_at - a.updated_at) as folder}
|
||||
<DropdownMenu.Item
|
||||
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-md"
|
||||
on:click={() => {
|
||||
moveChatHandler(chatId, folder.id);
|
||||
}}
|
||||
>
|
||||
<Folder />
|
||||
|
||||
<div class="flex items-center">{folder?.name ?? 'Folder'}</div>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
{/if}
|
||||
|
||||
<DropdownMenu.Item
|
||||
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-md"
|
||||
on:click={() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue