Browse Source

enh: drag and drop folders

Timothy J. Baek 6 months ago
parent
commit
36a541d6b0

+ 1 - 1
backend/open_webui/apps/webui/routers/folders.py

@@ -137,7 +137,7 @@ async def update_folder_name_by_id(
 
 
 class FolderParentIdForm(BaseModel):
-    parent_id: str
+    parent_id: Optional[str] = None
 
 
 @router.post("/{id}/update/parent")

+ 34 - 1
src/lib/apis/folders/index.ts

@@ -164,10 +164,43 @@ export const updateFolderIsExpandedById = async (
 	return res;
 };
 
+export const updateFolderParentIdById = async (token: string, id: string, parentId?: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}/update/parent`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			parent_id: parentId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 type FolderItems = {
 	chat_ids: string[];
 	file_ids: string[];
-	folder_ids: string[];
 };
 
 export const updateFolderItemsById = async (token: string, id: string, items: FolderItems) => {

+ 22 - 2
src/lib/components/layout/Sidebar.svelte

@@ -43,7 +43,7 @@
 	import Folder from '../common/Folder.svelte';
 	import Plus from '../icons/Plus.svelte';
 	import Tooltip from '../common/Tooltip.svelte';
-	import { createNewFolder, getFolders } from '$lib/apis/folders';
+	import { createNewFolder, getFolders, updateFolderParentIdById } from '$lib/apis/folders';
 	import Folders from './Sidebar/Folders.svelte';
 
 	const BREAKPOINT = 768;
@@ -656,7 +656,12 @@
 
 			<div class=" flex-1 flex flex-col overflow-y-auto scrollbar-hidden">
 				{#if !search && folders}
-					<Folders {folders} />
+					<Folders
+						{folders}
+						on:update={async (e) => {
+							await initFolders();
+						}}
+					/>
 				{/if}
 
 				<Folder
@@ -677,6 +682,21 @@
 									initChatList();
 								}
 							}
+						} else if (type === 'folder') {
+							if (folders[id].parent_id === null) {
+								return;
+							}
+
+							const res = await updateFolderParentIdById(localStorage.token, id, null).catch(
+								(error) => {
+									toast.error(error);
+									return null;
+								}
+							);
+
+							if (res) {
+								await initFolders();
+							}
 						}
 					}}
 				>

+ 11 - 1
src/lib/components/layout/Sidebar/Folders.svelte

@@ -1,4 +1,7 @@
 <script lang="ts">
+	import { createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
 	import RecursiveFolder from './RecursiveFolder.svelte';
 	export let folders = {};
 
@@ -15,5 +18,12 @@
 </script>
 
 {#each folderList as folderId (folderId)}
-	<RecursiveFolder className="px-2" {folders} {folderId} />
+	<RecursiveFolder
+		className="px-2"
+		{folders}
+		{folderId}
+		on:update={(e) => {
+			dispatch('update', e.detail);
+		}}
+	/>
 {/each}

+ 36 - 4
src/lib/components/layout/Sidebar/RecursiveFolder.svelte

@@ -11,7 +11,11 @@
 
 	import FolderOpen from '$lib/components/icons/FolderOpen.svelte';
 	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
-	import { updateFolderIsExpandedById, updateFolderNameById } from '$lib/apis/folders';
+	import {
+		updateFolderIsExpandedById,
+		updateFolderNameById,
+		updateFolderParentIdById
+	} from '$lib/apis/folders';
 	import { toast } from 'svelte-sonner';
 
 	export let open = true;
@@ -41,7 +45,7 @@
 		draggedOver = true;
 	};
 
-	const onDrop = (e) => {
+	const onDrop = async (e) => {
 		e.preventDefault();
 		e.stopPropagation();
 		if (dragged || parentDragged) {
@@ -56,7 +60,28 @@
 				const dataTransfer = e.dataTransfer.getData('text/plain');
 				const data = JSON.parse(dataTransfer);
 				console.log(data);
-				dispatch('drop', data);
+
+				const { type, id } = data;
+
+				if (type === 'folder') {
+					if (id === folderId) {
+						return;
+					}
+					// Move the folder
+					const res = await updateFolderParentIdById(localStorage.token, id, folderId).catch(
+						(error) => {
+							toast.error(error);
+							return null;
+						}
+					);
+
+					if (res) {
+						dispatch('update');
+					}
+				} else if (type === 'chat') {
+					// Move the chat
+					console.log('Move the chat');
+				}
 			} catch (error) {
 				console.error(error);
 			}
@@ -285,7 +310,14 @@
 				>
 					{#if folders[folderId]?.childrenIds}
 						{#each folders[folderId]?.childrenIds as childId (`${folderId}-${childId}`)}
-							<svelte:self {folders} folderId={childId} parentDragged={dragged} />
+							<svelte:self
+								{folders}
+								folderId={childId}
+								parentDragged={dragged}
+								on:update={(e) => {
+									dispatch('update', e.detail);
+								}}
+							/>
 						{/each}
 					{/if}