enh: emoji folder icon
This commit is contained in:
parent
e42ee34672
commit
b70c0f36c0
|
|
@ -58,6 +58,14 @@ class FolderModel(BaseModel):
|
|||
class FolderForm(BaseModel):
|
||||
name: str
|
||||
data: Optional[dict] = None
|
||||
meta: Optional[dict] = None
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
class FolderUpdateForm(BaseModel):
|
||||
name: Optional[str] = None
|
||||
data: Optional[dict] = None
|
||||
meta: Optional[dict] = None
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
|
|
@ -191,7 +199,7 @@ class FolderTable:
|
|||
return
|
||||
|
||||
def update_folder_by_id_and_user_id(
|
||||
self, id: str, user_id: str, form_data: FolderForm
|
||||
self, id: str, user_id: str, form_data: FolderUpdateForm
|
||||
) -> Optional[FolderModel]:
|
||||
try:
|
||||
with get_db() as db:
|
||||
|
|
@ -222,8 +230,13 @@ class FolderTable:
|
|||
**form_data["data"],
|
||||
}
|
||||
|
||||
folder.updated_at = int(time.time())
|
||||
if "meta" in form_data:
|
||||
folder.meta = {
|
||||
**(folder.meta or {}),
|
||||
**form_data["meta"],
|
||||
}
|
||||
|
||||
folder.updated_at = int(time.time())
|
||||
db.commit()
|
||||
|
||||
return FolderModel.model_validate(folder)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mimetypes
|
|||
|
||||
from open_webui.models.folders import (
|
||||
FolderForm,
|
||||
FolderUpdateForm,
|
||||
FolderModel,
|
||||
Folders,
|
||||
)
|
||||
|
|
@ -113,22 +114,24 @@ async def get_folder_by_id(id: str, user=Depends(get_verified_user)):
|
|||
|
||||
@router.post("/{id}/update")
|
||||
async def update_folder_name_by_id(
|
||||
id: str, form_data: FolderForm, user=Depends(get_verified_user)
|
||||
id: str, form_data: FolderUpdateForm, user=Depends(get_verified_user)
|
||||
):
|
||||
folder = Folders.get_folder_by_id_and_user_id(id, user.id)
|
||||
if folder:
|
||||
existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
|
||||
folder.parent_id, user.id, form_data.name
|
||||
)
|
||||
if existing_folder and existing_folder.id != id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
|
||||
|
||||
if form_data.name is not None:
|
||||
# Check if folder with same name exists
|
||||
existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
|
||||
folder.parent_id, user.id, form_data.name
|
||||
)
|
||||
if existing_folder and existing_folder.id != id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
|
||||
)
|
||||
|
||||
try:
|
||||
folder = Folders.update_folder_by_id_and_user_id(id, user.id, form_data)
|
||||
|
||||
return folder
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
type FolderForm = {
|
||||
name: string;
|
||||
name?: string;
|
||||
data?: Record<string, any>;
|
||||
meta?: Record<string, any>;
|
||||
};
|
||||
|
||||
export const createNewFolder = async (token: string, folderForm: FolderForm) => {
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@
|
|||
import ProfilePreview from './Message/ProfilePreview.svelte';
|
||||
import ChatBubbleOvalEllipsis from '$lib/components/icons/ChatBubble.svelte';
|
||||
import FaceSmile from '$lib/components/icons/FaceSmile.svelte';
|
||||
import ReactionPicker from './Message/ReactionPicker.svelte';
|
||||
import EmojiPicker from '$lib/components/common/EmojiPicker.svelte';
|
||||
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||
import { formatDate } from '$lib/utils';
|
||||
import Emoji from '$lib/components/common/Emoji.svelte';
|
||||
|
||||
export let message;
|
||||
export let showUserProfile = true;
|
||||
|
|
@ -74,7 +75,7 @@
|
|||
<div
|
||||
class="flex gap-1 rounded-lg bg-white dark:bg-gray-850 shadow-md p-0.5 border border-gray-100 dark:border-gray-850"
|
||||
>
|
||||
<ReactionPicker
|
||||
<EmojiPicker
|
||||
onClose={() => (showButtons = false)}
|
||||
onSubmit={(name) => {
|
||||
showButtons = false;
|
||||
|
|
@ -91,7 +92,7 @@
|
|||
<FaceSmile />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</ReactionPicker>
|
||||
</EmojiPicker>
|
||||
|
||||
{#if !thread}
|
||||
<Tooltip content={$i18n.t('Reply in Thread')}>
|
||||
|
|
@ -275,20 +276,7 @@
|
|||
onReaction(reaction.name);
|
||||
}}
|
||||
>
|
||||
{#if $shortCodesToEmojis[reaction.name]}
|
||||
<img
|
||||
src="{WEBUI_BASE_URL}/assets/emojis/{$shortCodesToEmojis[
|
||||
reaction.name
|
||||
].toLowerCase()}.svg"
|
||||
alt={reaction.name}
|
||||
class=" size-4"
|
||||
loading="lazy"
|
||||
/>
|
||||
{:else}
|
||||
<div>
|
||||
{reaction.name}
|
||||
</div>
|
||||
{/if}
|
||||
<Emoji shortCode={reaction.name} />
|
||||
|
||||
{#if reaction.user_ids.length > 0}
|
||||
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">
|
||||
|
|
@ -299,7 +287,7 @@
|
|||
</Tooltip>
|
||||
{/each}
|
||||
|
||||
<ReactionPicker
|
||||
<EmojiPicker
|
||||
onSubmit={(name) => {
|
||||
onReaction(name);
|
||||
}}
|
||||
|
|
@ -311,7 +299,7 @@
|
|||
<FaceSmile />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</ReactionPicker>
|
||||
</EmojiPicker>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
import FolderMenu from '$lib/components/layout/Sidebar/Folders/FolderMenu.svelte';
|
||||
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
|
||||
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
|
||||
import Emoji from '$lib/components/common/Emoji.svelte';
|
||||
import EmojiPicker from '$lib/components/common/EmojiPicker.svelte';
|
||||
|
||||
export let folder = null;
|
||||
|
||||
|
|
@ -63,6 +65,25 @@
|
|||
}
|
||||
};
|
||||
|
||||
const updateIconHandler = async (iconName) => {
|
||||
const res = await updateFolderById(localStorage.token, folder.id, {
|
||||
meta: {
|
||||
icon: iconName
|
||||
}
|
||||
}).catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
folder.meta = { ...folder.meta, icon: iconName };
|
||||
|
||||
toast.success($i18n.t('Folder updated successfully'));
|
||||
selectedFolder.set(folder);
|
||||
onUpdate(folder);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteHandler = async () => {
|
||||
const res = await deleteFolderById(localStorage.token, folder.id).catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
|
|
@ -116,9 +137,23 @@
|
|||
|
||||
<div class="mb-3 px-6 @md:max-w-3xl justify-between w-full flex relative group items-center">
|
||||
<div class="text-center flex gap-3.5 items-center">
|
||||
<div class=" rounded-full bg-gray-50 dark:bg-gray-800 p-3 w-fit">
|
||||
<Folder className="size-4.5" strokeWidth="2" />
|
||||
</div>
|
||||
<EmojiPicker
|
||||
onClose={() => {}}
|
||||
onSubmit={(name) => {
|
||||
console.log(name);
|
||||
updateIconHandler(name);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class=" rounded-full bg-gray-50 dark:bg-gray-800 size-11 flex justify-center items-center"
|
||||
>
|
||||
{#if folder?.meta?.icon}
|
||||
<Emoji className="size-6" shortCode={folder.meta.icon} />
|
||||
{:else}
|
||||
<Folder className="size-4.5" strokeWidth="2" />
|
||||
{/if}
|
||||
</button>
|
||||
</EmojiPicker>
|
||||
|
||||
<div class="text-3xl">
|
||||
{folder.name}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { shortCodesToEmojis } from '$lib/stores';
|
||||
|
||||
export let shortCode;
|
||||
export let className = 'size-4';
|
||||
</script>
|
||||
|
||||
{#if $shortCodesToEmojis[shortCode]}
|
||||
<img
|
||||
src="{WEBUI_BASE_URL}/assets/emojis/{$shortCodesToEmojis[shortCode].toLowerCase()}.svg"
|
||||
alt={shortCode}
|
||||
class={className}
|
||||
loading="lazy"
|
||||
/>
|
||||
{:else}
|
||||
<div>
|
||||
{shortCode}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -142,11 +142,11 @@
|
|||
>
|
||||
<button class="w-full py-1.5 pl-2 flex items-center gap-1.5 text-xs font-medium">
|
||||
{#if chevron}
|
||||
<div class="text-gray-300 dark:text-gray-600">
|
||||
<div class="text-gray-300 dark:text-gray-600 p-[1px]">
|
||||
{#if open}
|
||||
<ChevronDown className=" size-3" strokeWidth="2.5" />
|
||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
||||
{:else}
|
||||
<ChevronRight className=" size-3" strokeWidth="2.5" />
|
||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@
|
|||
? `ml-[4.5rem] md:ml-0 `
|
||||
: ' transition-all duration-300 '} shrink-0 text-gray-900 dark:text-gray-200 text-sm fixed top-0 left-0 overflow-x-hidden
|
||||
"
|
||||
transition:slide={{ duration: 200, axis: 'x' }}
|
||||
transition:slide={{ duration: 250, axis: 'x' }}
|
||||
data-state={$showSidebar}
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
|
||||
import FolderModal from './Folders/FolderModal.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import Emoji from '$lib/components/common/Emoji.svelte';
|
||||
|
||||
export let open = false;
|
||||
|
||||
|
|
@ -444,15 +445,31 @@
|
|||
}}
|
||||
>
|
||||
<button
|
||||
class="text-gray-300 dark:text-gray-600"
|
||||
class="text-gray-300 dark:text-gray-600 transition-all"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{#if open}
|
||||
<ChevronDown className=" size-3" strokeWidth="2.5" />
|
||||
{#if folders[folderId]?.meta?.icon}
|
||||
<div class="flex group-hover:hidden transition-all">
|
||||
<Emoji className="size-4" shortCode={folders[folderId].meta.icon} />
|
||||
</div>
|
||||
|
||||
<div class="hidden group-hover:flex transition-all p-[1px]">
|
||||
{#if open}
|
||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
||||
{:else}
|
||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<ChevronRight className=" size-3" strokeWidth="2.5" />
|
||||
<div class="p-[1px]">
|
||||
{#if open}
|
||||
<ChevronDown className=" size-3.5" strokeWidth="2.5" />
|
||||
{:else}
|
||||
<ChevronRight className=" size-3.5" strokeWidth="2.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue