Merge pull request #17857 from silentoplayz/feat-bulk-unarchive

feat: add backend handling for unarchiving all chats
This commit is contained in:
Tim Jaeryang Baek 2025-09-28 12:45:49 -05:00 committed by GitHub
commit e71ed76165
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 9 deletions

View File

@ -366,6 +366,15 @@ class ChatTable:
except Exception:
return False
def unarchive_all_chats_by_user_id(self, user_id: str) -> bool:
try:
with get_db() as db:
db.query(Chat).filter_by(user_id=user_id).update({"archived": False})
db.commit()
return True
except Exception:
return False
def update_chat_share_id_by_id(
self, id: str, share_id: Optional[str]
) -> Optional[ChatModel]:

View File

@ -361,6 +361,16 @@ async def archive_all_chats(user=Depends(get_verified_user)):
return Chats.archive_all_chats_by_user_id(user.id)
############################
# UnarchiveAllChats
############################
@router.post("/unarchive/all", response_model=bool)
async def unarchive_all_chats(user=Depends(get_verified_user)):
return Chats.unarchive_all_chats_by_user_id(user.id)
############################
# GetSharedChatById
############################

View File

@ -33,6 +33,38 @@ export const createNewChat = async (token: string, chat: object, folderId: strin
return res;
};
export const unarchiveAllChats = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/unarchive/all`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.then((json) => {
return json;
})
.catch((err) => {
error = err.detail;
console.error(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const importChat = async (
token: string,
chat: object,

View File

@ -1,19 +1,26 @@
<script>
<script lang="ts">
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { toast } from 'svelte-sonner';
import { getContext } from 'svelte';
import { archiveChatById, getAllArchivedChats, getArchivedChatList } from '$lib/apis/chats';
import {
archiveChatById,
getAllArchivedChats,
getArchivedChatList,
unarchiveAllChats
} from '$lib/apis/chats';
import ChatsModal from './ChatsModal.svelte';
import UnarchiveAllConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Spinner from '../common/Spinner.svelte';
const i18n = getContext('i18n');
export let show = false;
export let onUpdate = () => {};
let loading = false;
let chatList = null;
let page = 1;
@ -105,13 +112,17 @@
};
const unarchiveAllHandler = async () => {
const chats = await getAllArchivedChats(localStorage.token);
for (const chat of chats) {
await archiveChatById(localStorage.token, chat.id);
loading = true;
try {
await unarchiveAllChats(localStorage.token);
toast.success($i18n.t('All chats have been unarchived.'));
onUpdate();
await init();
} catch (error) {
toast.error(`${error}`);
} finally {
loading = false;
}
onUpdate();
init();
};
const init = async () => {
@ -152,15 +163,21 @@
<div class="flex flex-wrap text-sm font-medium gap-1.5 mt-2 m-1 justify-end w-full">
<button
class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-800 rounded-3xl"
disabled={loading}
on:click={() => {
showUnarchiveAllConfirmDialog = true;
}}
>
{$i18n.t('Unarchive All Archived Chats')}
{#if loading}
<Spinner className="size-4" />
{:else}
{$i18n.t('Unarchive All Archived Chats')}
{/if}
</button>
<button
class="px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-800 rounded-3xl"
disabled={loading}
on:click={() => {
exportChatsHandler();
}}