feat(ui): add 'Unshare All Shared Chats' button to Shared Chats modal (#25848)

This commit is contained in:
G30 2026-06-16 21:06:41 -04:00 committed by GitHub
parent dfdb76cc46
commit 4584adf900
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 114 additions and 3 deletions

View file

@ -201,5 +201,15 @@ class SharedChatsTable:
except Exception:
return False
async def delete_all_by_user_id(self, user_id: str, db: Optional[AsyncSession] = None) -> bool:
"""Delete all shared chats created by a user."""
try:
async with get_async_db_context(db) as db:
await db.execute(delete(SharedChat).filter_by(user_id=user_id))
await db.commit()
return True
except Exception:
return False
SharedChats = SharedChatsTable()

View file

@ -853,10 +853,27 @@ async def unarchive_all_chats(user=Depends(get_verified_user), db: AsyncSession
############################
# GetSharedChats
# UnshareAllChats
############################
@router.delete('/share/all', response_model=bool)
async def unshare_all_chats(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
# Collect chat_ids that have shares so we can clear share_id and access grants
shared_list = await SharedChats.get_by_user_id(user.id, db=db)
chat_ids = [s.chat_id for s in shared_list]
# Delete all shared_chat rows for this user
result = await SharedChats.delete_all_by_user_id(user.id, db=db)
# Clear share_id on the original chats and remove access grants
for chat_id in chat_ids:
await Chats.update_chat_share_id_by_id(chat_id, None, db=db)
await AccessGrants.set_access_grants('shared_chat', chat_id, [], db=db)
return result
@router.get('/shared', response_model=list[SharedChatResponse])
async def get_shared_session_user_chat_list(
page: int | None = None,

View file

@ -65,6 +65,38 @@ export const unarchiveAllChats = async (token: string) => {
return res;
};
export const unshareAllChats = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/share/all`, {
method: 'DELETE',
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 importChats = async (token: string, chats: object[]) => {
let error = null;

View file

@ -2,15 +2,18 @@
import type { Writable } from 'svelte/store';
import { toast } from 'svelte-sonner';
import { getContext } from 'svelte';
import { deleteSharedChatById, getSharedChatList } from '$lib/apis/chats';
import { unshareAllChats, deleteSharedChatById, getSharedChatList } from '$lib/apis/chats';
import ChatsModal from './ChatsModal.svelte';
import UnshareAllConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Spinner from '../common/Spinner.svelte';
const i18n: Writable<any> = getContext('i18n');
export let show = false;
export let onUpdate = () => {};
let loading = false;
let chatList: any[] | null = null;
let page = 1;
@ -22,6 +25,8 @@
let chatListLoading = false;
let searchDebounceTimeout: any;
let showUnshareAllConfirmDialog = false;
let filter: any = {};
$: filter = {
...(query ? { query } : {}),
@ -97,6 +102,20 @@
}
};
const unshareAllHandler = async () => {
loading = true;
try {
await unshareAllChats(localStorage.token);
toast.success($i18n.t('All shared chats have been unshared.'));
onUpdate();
await init();
} catch (error) {
toast.error(`${error}`);
} finally {
loading = false;
}
};
const init = async () => {
chatList = await getSharedChatList(localStorage.token);
};
@ -106,6 +125,17 @@
}
</script>
<UnshareAllConfirmDialog
bind:show={showUnshareAllConfirmDialog}
message={$i18n.t(
'Are you sure you want to unshare all shared chats? This will remove all share links.'
)}
confirmLabel={$i18n.t('Unshare All')}
on:confirm={() => {
unshareAllHandler();
}}
/>
<ChatsModal
bind:show
bind:query
@ -123,4 +153,22 @@
}}
loadHandler={loadMoreChats}
{unshareHandler}
/>
>
<div slot="footer">
<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={() => {
showUnshareAllConfirmDialog = true;
}}
>
{#if loading}
<Spinner className="size-4" />
{:else}
{$i18n.t('Unshare All Shared Chats')}
{/if}
</button>
</div>
</div>
</ChatsModal>

View file

@ -122,6 +122,7 @@
"AI": "",
"All": "",
"All chats have been unarchived.": "",
"All shared chats have been unshared.": "",
"All day": "",
"All models are now hidden": "",
"All models are now visible": "",
@ -204,6 +205,7 @@
"Are you sure you want to delete this version? Child versions will be relinked to this version's parent.": "",
"Are you sure you want to delete this?": "",
"Are you sure you want to unarchive all archived chats?": "",
"Are you sure you want to unshare all shared chats? This will remove all share links.": "",
"Arena Models": "",
"Artifacts": "",
"Asc": "",
@ -2175,6 +2177,8 @@
"UI Scale": "",
"Unarchive All": "",
"Unarchive All Archived Chats": "",
"Unshare All": "",
"Unshare All Shared Chats": "",
"Unarchive Chat": "",
"Underline": "",
"Unknown": "",