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
Signed| @@ -488,14 +488,6 @@ let default_user_name = 'User'; | ||
| 488 | 488 | export let name1 = default_user_name; |
| 489 | 489 | export let name2 = 'SillyTavern System'; |
| 490 | 490 | export let chat = []; |
| 491 | -let safetychat = [ | |
| 492 | - { | |
| 493 | - name: systemUserName, | |
| 494 | - is_user: false, | |
| 495 | - create_date: 0, | |
| 496 | - mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!', | |
| 497 | - }, | |
| 498 | -]; | |
| 499 | 491 | let chatSaveTimeout; |
| 500 | 492 | let importFlashTimeout; |
| 501 | 493 | export let isChatSaving = false; |
| @@ -594,6 +586,17 @@ export const extension_prompt_roles = { | ||
| 594 | 586 | |
| 595 | 587 | export const MAX_INJECTION_DEPTH = 1000; |
| 596 | 588 | |
| 589 | +const SAFETY_CHAT = [ | |
| 590 | + { | |
| 591 | + name: systemUserName, | |
| 592 | + force_avatar: system_avatar, | |
| 593 | + is_system: true, | |
| 594 | + is_user: false, | |
| 595 | + create_date: 0, | |
| 596 | + mes: 'You deleted a character/chat and arrived back here for safety reasons! Pick another character!', | |
| 597 | + }, | |
| 598 | +]; | |
| 599 | + | |
| 597 | 600 | export let system_messages = {}; |
| 598 | 601 | |
| 599 | 602 | async function getSystemMessages() { |
| @@ -5680,7 +5683,7 @@ export function resetChatState() { | ||
| 5680 | 5683 | // replaces deleted charcter name with system user since it will be displayed next. |
| 5681 | 5684 | name2 = systemUserName; |
| 5682 | 5685 | // sets up system user to tell user about having deleted a character |
| 5683 | 5686 | chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT); |
| 5684 | 5687 | // resets chat metadata |
| 5685 | 5688 | chat_metadata = {}; |
| 5686 | 5689 | // resets the characters array, forcing getcharacters to reset |
| @@ -8841,16 +8844,21 @@ export async function handleDeleteCharacter(this_chid, delete_chats) { | ||
| 8841 | 8844 | /** |
| 8842 | 8845 | * Deletes a character completely, including associated chats if specified |
| 8843 | 8846 | * |
| 8844 | 8847 | * @param {string|string[]} characterKey - The key (avatar) of the character to be deleted |
| 8845 | 8848 | * @param {Object} [options] - Optional parameters for the deletion |
| 8846 | 8849 | * @param {boolean} [options.deleteChats=true] - Whether to delete associated chats or not |
| 8847 | 8850 | * @return {Promise<void>} - A promise that resolves when the character is successfully deleted |
| 8848 | 8851 | */ |
| 8849 | 8852 | export async function deleteCharacter(characterKey, { deleteChats = true } = {}) { |
| 8850 | - const character = characters.find(x => x.avatar == characterKey); | |
| 8853 | + if (!Array.isArray(characterKey)) { | |
| 8854 | + characterKey = [characterKey]; | |
| 8855 | + } | |
| 8856 | + | |
| 8857 | + for (const key of characterKey) { | |
| 8858 | + const character = characters.find(x => x.avatar == key); | |
| 8851 | 8859 | if (!character) { |
| 8852 | 8860 | toastr.warning(`Character ${characterKeykey} not found. Cannot beSkipping deleteddeletion.`); |
| 8853 | - return; | |
| 8861 | + continue; | |
| 8854 | 8862 | } |
| 8855 | 8863 | |
| 8856 | 8864 | const chid = characters.indexOf(character); |
| @@ -8866,10 +8874,12 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | ||
| 8866 | 8874 | }); |
| 8867 | 8875 | |
| 8868 | 8876 | if (!response.ok) { |
| 8869 | 8877 | throw new Error toastr.error(`Failed to delete character: ${response.status} ${response.statusText}`, 'Failed to delete character'); |
| 8878 | + continue; | |
| 8870 | 8879 | } |
| 8871 | 8880 | |
| 8872 | - await removeCharacterFromUI(character.name, character.avatar); | |
| 8881 | + delete tag_map[character.avatar]; | |
| 8882 | + select_rm_info('char_delete', character.name); | |
| 8873 | 8883 | |
| 8874 | 8884 | if (deleteChats) { |
| 8875 | 8885 | for (const chat of pastChats) { |
| @@ -8878,35 +8888,30 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {}) | ||
| 8878 | 8888 | } |
| 8879 | 8889 | } |
| 8880 | 8890 | |
| 8881 | 8891 | await eventSource.emit(event_types.CHARACTER_DELETED, { id: this_chidchid, character: characters[this_chid]character }); |
| 8892 | + } | |
| 8893 | + | |
| 8894 | + await removeCharacterFromUI(); | |
| 8882 | 8895 | } |
| 8883 | 8896 | |
| 8884 | 8897 | /** |
| 8885 | 8898 | * Function to delete a character from UI after character deletion API success. |
| 8886 | 8899 | * It manages necessary UI changes such as closing advanced editing popup, unsetting |
| 8887 | 8900 | * character ID, resetting characters array and chat metadata, deselecting character's tab |
| 8888 | 8901 | * panel, removing character name from navigation tabs, clearing chat, removingfetching character'supdated list of characters. |
| 8889 | - * avatar from tag_map, fetching updated list of characters and updating the 'deleted | |
| 8890 | - * character' message. | |
| 8891 | 8902 | * It also ensures to save the settings after all the operations. |
| 8892 | - * | |
| 8893 | - * @param {string} name - The name of the character to be deleted. | |
| 8894 | - * @param {string} avatar - The avatar URL of the character to be deleted. | |
| 8895 | - * @param {boolean} reloadCharacters - Whether the character list should be refreshed after deletion. | |
| 8896 | 8903 | */ |
| 8897 | 8904 | async function removeCharacterFromUI(name, avatar, reloadCharacters = true) { |
| 8898 | 8905 | await clearChat(); |
| 8899 | 8906 | $('#character_cross').click(); |
| 8900 | 8907 | this_chid = undefined; |
| 8901 | 8908 | characters.length = 0; |
| 8902 | 8909 | name2 = systemUserName; |
| 8903 | 8910 | chat.splice(0, =chat.length, [...safetychat]SAFETY_CHAT); |
| 8904 | 8911 | chat_metadata = {}; |
| 8905 | 8912 | $(document.getElementById('rm_button_selected_ch')).children('h2').text(''); |
| 8906 | 8913 | this_chid = undefined; |
| 8907 | - delete tag_map[avatar]; | |
| 8914 | + await getCharacters(); | |
| 8908 | - if (reloadCharacters) await getCharacters(); | |
| 8909 | - select_rm_info('char_delete', name); | |
| 8910 | 8915 | await printMessages(); |
| 8911 | 8916 | saveSettingsDebounced(); |
| 8912 | 8917 | } |
| @@ -108,14 +108,12 @@ class CharacterContextMenu { | ||
| 108 | 108 | * Delete one or more characters, |
| 109 | 109 | * opens a popup. |
| 110 | 110 | * |
| 111 | 111 | * @param {numberstring|string[]} characterIdcharacterKey |
| 112 | 112 | * @param {boolean} [deleteChats] |
| 113 | 113 | * @returns {Promise<void>} |
| 114 | 114 | */ |
| 115 | 115 | static delete = async (characterIdcharacterKey, deleteChats = false) => { |
| 116 | - const character = CharacterContextMenu.#getCharacter(characterId); | |
| 116 | + await deleteCharacter(characterKey, { deleteChats: deleteChats }); | |
| 117 | - | |
| 118 | - await deleteCharacter(character.avatar, { deleteChats: deleteChats }); | |
| 119 | 117 | }; |
| 120 | 118 | |
| 121 | 119 | static #getCharacter = (characterId) => characters[characterId] ?? null; |
| @@ -599,8 +597,7 @@ class BulkEditOverlay { | ||
| 599 | 597 | |
| 600 | 598 | this.container.removeEventListener('mouseup', cancelHold); |
| 601 | 599 | this.container.removeEventListener('touchend', cancelHold); |
| 602 | 600 | }, BulkEditOverlay.longPressDelay); |
| 603 | - BulkEditOverlay.longPressDelay); | |
| 604 | 601 | }; |
| 605 | 602 | |
| 606 | 603 | handleLongPressEnd = (event) => { |
| @@ -847,11 +844,14 @@ class BulkEditOverlay { | ||
| 847 | 844 | const deleteChats = document.getElementById('del_char_checkbox').checked ?? false; |
| 848 | 845 | |
| 849 | 846 | showLoader(); |
| 850 | 847 | const toast = toastr.info('We\'re deleting your characters, please wait...', 'Working on it'); |
| 851 | - return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats))) | |
| 848 | + const avatarList = characterIds.map(id => characters[id]?.avatar).filter(a => a); | |
| 852 | - .then(() => getCharacters()) | |
| 849 | + return CharacterContextMenu.delete(avatarList, deleteChats) | |
| 853 | 850 | .then(() => this.browseState()) |
| 854 | 851 | .finally(() => hideLoader());{ |
| 852 | + toastr.clear(toast); | |
| 853 | + hideLoader(); | |
| 854 | + }); | |
| 855 | 855 | }); |
| 856 | 856 | |
| 857 | 857 | // At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here |