Fix bulk delete async hell (#2730)
* Fix bulk delete async hell * Remove refresh flag (always refresh) * Don't throw on deletion fetch failed * Clear toast on bulk finish
This commit is contained in:
parent
e6021bda1c
commit
1fa9710a5c
103
public/script.js
103
public/script.js
|
@ -488,14 +488,6 @@ let default_user_name = 'User';
|
|||
export let name1 = default_user_name;
|
||||
export let name2 = 'SillyTavern System';
|
||||
export let chat = [];
|
||||
let safetychat = [
|
||||
{
|
||||
name: systemUserName,
|
||||
is_user: false,
|
||||
create_date: 0,
|
||||
mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
|
||||
},
|
||||
];
|
||||
let chatSaveTimeout;
|
||||
let importFlashTimeout;
|
||||
export let isChatSaving = false;
|
||||
|
@ -594,6 +586,17 @@ export const extension_prompt_roles = {
|
|||
|
||||
export const MAX_INJECTION_DEPTH = 1000;
|
||||
|
||||
const SAFETY_CHAT = [
|
||||
{
|
||||
name: systemUserName,
|
||||
force_avatar: system_avatar,
|
||||
is_system: true,
|
||||
is_user: false,
|
||||
create_date: 0,
|
||||
mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!',
|
||||
},
|
||||
];
|
||||
|
||||
export let system_messages = {};
|
||||
|
||||
async function getSystemMessages() {
|
||||
|
@ -5680,7 +5683,7 @@ export function resetChatState() {
|
|||
// replaces deleted charcter name with system user since it will be displayed next.
|
||||
name2 = systemUserName;
|
||||
// sets up system user to tell user about having deleted a character
|
||||
chat = [...safetychat];
|
||||
chat.splice(0, chat.length, ...SAFETY_CHAT);
|
||||
// resets chat metadata
|
||||
chat_metadata = {};
|
||||
// resets the characters array, forcing getcharacters to reset
|
||||
|
@ -8841,72 +8844,74 @@ export async function handleDeleteCharacter(this_chid, delete_chats) {
|
|||
/**
|
||||
* Deletes a character completely, including associated chats if specified
|
||||
*
|
||||
* @param {string} characterKey - The key (avatar) of the character to be deleted
|
||||
* @param {string|string[]} characterKey - The key (avatar) of the character to be deleted
|
||||
* @param {Object} [options] - Optional parameters for the deletion
|
||||
* @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not
|
||||
* @return {Promise<void>} - A promise that resolves when the character is successfully deleted
|
||||
*/
|
||||
export async function deleteCharacter(characterKey, { deleteChats = true } = {}) {
|
||||
const character = characters.find(x => x.avatar == characterKey);
|
||||
if (!character) {
|
||||
toastr.warning(`Character ${characterKey} not found. Cannot be deleted.`);
|
||||
return;
|
||||
if (!Array.isArray(characterKey)) {
|
||||
characterKey = [characterKey];
|
||||
}
|
||||
|
||||
const chid = characters.indexOf(character);
|
||||
const pastChats = await getPastCharacterChats(chid);
|
||||
|
||||
const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
|
||||
|
||||
const response = await fetch('/api/characters/delete', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(msg),
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to delete character: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
await removeCharacterFromUI(character.name, character.avatar);
|
||||
|
||||
if (deleteChats) {
|
||||
for (const chat of pastChats) {
|
||||
const name = chat.file_name.replace('.jsonl', '');
|
||||
await eventSource.emit(event_types.CHAT_DELETED, name);
|
||||
for (const key of characterKey) {
|
||||
const character = characters.find(x => x.avatar == key);
|
||||
if (!character) {
|
||||
toastr.warning(`Character ${key} not found. Skipping deletion.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const chid = characters.indexOf(character);
|
||||
const pastChats = await getPastCharacterChats(chid);
|
||||
|
||||
const msg = { avatar_url: character.avatar, delete_chats: deleteChats };
|
||||
|
||||
const response = await fetch('/api/characters/delete', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(msg),
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
toastr.error(`${response.status} ${response.statusText}`, 'Failed to delete character');
|
||||
continue;
|
||||
}
|
||||
|
||||
delete tag_map[character.avatar];
|
||||
select_rm_info('char_delete', character.name);
|
||||
|
||||
if (deleteChats) {
|
||||
for (const chat of pastChats) {
|
||||
const name = chat.file_name.replace('.jsonl', '');
|
||||
await eventSource.emit(event_types.CHAT_DELETED, name);
|
||||
}
|
||||
}
|
||||
|
||||
await eventSource.emit(event_types.CHARACTER_DELETED, { id: chid, character: character });
|
||||
}
|
||||
|
||||
eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chid, character: characters[this_chid] });
|
||||
await removeCharacterFromUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to delete a character from UI after character deletion API success.
|
||||
* It manages necessary UI changes such as closing advanced editing popup, unsetting
|
||||
* character ID, resetting characters array and chat metadata, deselecting character's tab
|
||||
* panel, removing character name from navigation tabs, clearing chat, removing character's
|
||||
* avatar from tag_map, fetching updated list of characters and updating the 'deleted
|
||||
* character' message.
|
||||
* panel, removing character name from navigation tabs, clearing chat, fetching updated list of characters.
|
||||
* It also ensures to save the settings after all the operations.
|
||||
*
|
||||
* @param {string} name - The name of the character to be deleted.
|
||||
* @param {string} avatar - The avatar URL of the character to be deleted.
|
||||
* @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion.
|
||||
*/
|
||||
async function removeCharacterFromUI(name, avatar, reloadCharacters = true) {
|
||||
async function removeCharacterFromUI() {
|
||||
await clearChat();
|
||||
$('#character_cross').click();
|
||||
this_chid = undefined;
|
||||
characters.length = 0;
|
||||
name2 = systemUserName;
|
||||
chat = [...safetychat];
|
||||
chat.splice(0, chat.length, ...SAFETY_CHAT);
|
||||
chat_metadata = {};
|
||||
$(document.getElementById('rm_button_selected_ch')).children('h2').text('');
|
||||
this_chid = undefined;
|
||||
delete tag_map[avatar];
|
||||
if (reloadCharacters) await getCharacters();
|
||||
select_rm_info('char_delete', name);
|
||||
await getCharacters();
|
||||
await printMessages();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
|
|
@ -108,14 +108,12 @@ class CharacterContextMenu {
|
|||
* Delete one or more characters,
|
||||
* opens a popup.
|
||||
*
|
||||
* @param {number} characterId
|
||||
* @param {string|string[]} characterKey
|
||||
* @param {boolean} [deleteChats]
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static delete = async (characterId, deleteChats = false) => {
|
||||
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||
|
||||
await deleteCharacter(character.avatar, { deleteChats: deleteChats });
|
||||
static delete = async (characterKey, deleteChats = false) => {
|
||||
await deleteCharacter(characterKey, { deleteChats: deleteChats });
|
||||
};
|
||||
|
||||
static #getCharacter = (characterId) => characters[characterId] ?? null;
|
||||
|
@ -344,7 +342,7 @@ class BulkTagPopupHandler {
|
|||
const mutualTags = this.getMutualTags();
|
||||
|
||||
for (const characterId of this.characterIds) {
|
||||
for(const tag of mutualTags) {
|
||||
for (const tag of mutualTags) {
|
||||
removeTagFromMap(tag.id, characterId);
|
||||
}
|
||||
}
|
||||
|
@ -599,8 +597,7 @@ class BulkEditOverlay {
|
|||
|
||||
this.container.removeEventListener('mouseup', cancelHold);
|
||||
this.container.removeEventListener('touchend', cancelHold);
|
||||
},
|
||||
BulkEditOverlay.longPressDelay);
|
||||
}, BulkEditOverlay.longPressDelay);
|
||||
};
|
||||
|
||||
handleLongPressEnd = (event) => {
|
||||
|
@ -847,11 +844,14 @@ class BulkEditOverlay {
|
|||
const deleteChats = document.getElementById('del_char_checkbox').checked ?? false;
|
||||
|
||||
showLoader();
|
||||
toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
|
||||
return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
|
||||
.then(() => getCharacters())
|
||||
const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
|
||||
const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a);
|
||||
return CharacterContextMenu.delete(avatarList, deleteChats)
|
||||
.then(() => this.browseState())
|
||||
.finally(() => hideLoader());
|
||||
.finally(() => {
|
||||
toastr.clear(toast);
|
||||
hideLoader();
|
||||
});
|
||||
});
|
||||
|
||||
// At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
|
||||
|
|
Loading…
Reference in New Issue