Refactor and improve bulk delete popup
- Improve bulk edit popup with display of avatars and better format - Refactor both calls of bulk delete to use the same method - Add display of filename on avatar hover for inline avatars (@Cohee you forgot this one (: )
This commit is contained in:
parent
167673fcf5
commit
bf8b6b80d7
|
@ -5357,7 +5357,7 @@ function buildAvatarList(block, entities, { templateId = 'inline_avatar_template
|
|||
avatarTemplate.attr('data-type', entity.type);
|
||||
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
||||
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entity.item.name);
|
||||
avatarTemplate.attr('title', `[Character] ${entity.item.name}`);
|
||||
avatarTemplate.attr('title', `[Character] ${entity.item.name}\nFile: ${entity.item.avatar}`);
|
||||
if (highlightFavs) {
|
||||
avatarTemplate.toggleClass('is_fav', entity.item.fav || entity.item.fav == 'true');
|
||||
avatarTemplate.find('.ch_fav').val(entity.item.fav);
|
||||
|
|
|
@ -19,18 +19,6 @@ import { hideLoader, showLoader } from './loader.js';
|
|||
import { convertCharacterToPersona } from './personas.js';
|
||||
import { createTagInput, getTagKeyForEntity, getTagsList, printTagList, tag_map, compareTagsForSort, removeTagFromMap } from './tags.js';
|
||||
|
||||
// Utility object for popup messages.
|
||||
const popupMessage = {
|
||||
deleteChat(characterCount) {
|
||||
return `<h3>Delete ${characterCount} characters?</h3>
|
||||
<b>THIS IS PERMANENT!<br><br>
|
||||
<label for="del_char_checkbox" class="checkbox_label justifyCenter">
|
||||
<input type="checkbox" id="del_char_checkbox" />
|
||||
<span>Also delete the chat files</span>
|
||||
</label><br></b>`;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Static object representing the actions of the
|
||||
* character context menu override.
|
||||
|
@ -198,6 +186,12 @@ class CharacterContextMenu {
|
|||
* Represents a tag control not bound to a single character
|
||||
*/
|
||||
class BulkTagPopupHandler {
|
||||
/**
|
||||
* Gets the HTML as a string that is going to be the popup for the bulk tag edit
|
||||
*
|
||||
* @param {Array<number>} characterIds - The characters that are shown inside the popup
|
||||
* @returns String containing the html for the popup
|
||||
*/
|
||||
static #getHtml = (characterIds) => {
|
||||
const characterData = JSON.stringify({ characterIds: characterIds });
|
||||
return `<div id="bulk_tag_shadow_popup">
|
||||
|
@ -227,8 +221,7 @@ class BulkTagPopupHandler {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
</div>`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -430,7 +423,7 @@ class BulkEditOverlay {
|
|||
|
||||
/**
|
||||
*
|
||||
* @returns {*[]}
|
||||
* @returns {number[]}
|
||||
*/
|
||||
get selectedCharacters() {
|
||||
return this.#selectedCharacters;
|
||||
|
@ -775,6 +768,29 @@ class BulkEditOverlay {
|
|||
this.browseState();
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the HTML as a string that is displayed inside the popup for the bulk delete
|
||||
*
|
||||
* @param {Array<number>} characterIds - The characters that are shown inside the popup
|
||||
* @returns String containing the html for the popup content
|
||||
*/
|
||||
static #getDeletePopupContentHtml = (characterIds) => {
|
||||
return `
|
||||
<h3 class="marginBot5">Delete ${characterIds.length} characters?</h3>
|
||||
<span class="bulk_delete_note">
|
||||
<i class="fa-solid fa-triangle-exclamation warning margin-r5"></i>
|
||||
<b>THIS IS PERMANENT!</b>
|
||||
</span>
|
||||
<div id="bulk_delete_avatars_block" class="avatars_inline avatars_inline_small tags tags_inline m-t-1"></div>
|
||||
<br>
|
||||
<div id="bulk_delete_options" class="m-b-1">
|
||||
<label for="del_char_checkbox" class="checkbox_label justifyCenter">
|
||||
<input type="checkbox" id="del_char_checkbox" />
|
||||
<span>Also delete the chat files</span>
|
||||
</label>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request user input before concurrently handle deletion
|
||||
* requests.
|
||||
|
@ -782,8 +798,9 @@ class BulkEditOverlay {
|
|||
* @returns {Promise<number>}
|
||||
*/
|
||||
handleContextMenuDelete = () => {
|
||||
callPopup(
|
||||
popupMessage.deleteChat(this.selectedCharacters.length), null)
|
||||
const characterIds = this.selectedCharacters;
|
||||
const popupContent = BulkEditOverlay.#getDeletePopupContentHtml(characterIds);
|
||||
const promise = callPopup(popupContent, null)
|
||||
.then((accept) => {
|
||||
if (true !== accept) return;
|
||||
|
||||
|
@ -791,11 +808,17 @@ class BulkEditOverlay {
|
|||
|
||||
showLoader();
|
||||
toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
|
||||
Promise.allSettled(this.selectedCharacters.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
|
||||
return Promise.allSettled(characterIds.map(async characterId => CharacterContextMenu.delete(characterId, deleteChats)))
|
||||
.then(() => getCharacters())
|
||||
.then(() => this.browseState())
|
||||
.finally(() => hideLoader());
|
||||
});
|
||||
|
||||
// At this moment the popup is already changed in the dom, but not yet closed/resolved. We build the avatar list here
|
||||
const entities = characterIds.map(id => characterToEntity(characters[id], id)).filter(entity => entity.item !== undefined);
|
||||
buildAvatarList($('#bulk_delete_avatars_block'), entities);
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,32 +84,8 @@ async function deleteCharacter(this_chid) {
|
|||
async function onDeleteButtonClick() {
|
||||
console.log('Delete button clicked');
|
||||
|
||||
// Create a mapping of chid to avatar
|
||||
let toDelete = [];
|
||||
$('.bulk_select_checkbox:checked').each((i, el) => {
|
||||
const chid = $(el).parent().attr('chid');
|
||||
const avatar = characters[chid].avatar;
|
||||
// Add the avatar to the list of avatars to delete
|
||||
toDelete.push(avatar);
|
||||
});
|
||||
|
||||
const confirm = await callPopup('<h3>Are you sure you want to delete these characters?</h3>You would need to delete the chat files manually.<br>', 'confirm');
|
||||
|
||||
if (!confirm) {
|
||||
console.log('User cancelled delete');
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the characters
|
||||
for (const avatar of toDelete) {
|
||||
console.log(`Deleting character with avatar ${avatar}`);
|
||||
await getCharacters();
|
||||
|
||||
//chid should be the key of the character with the given avatar
|
||||
const chid = Object.keys(characters).find((key) => characters[key].avatar === avatar);
|
||||
console.log(`Deleting character with chid ${chid}`);
|
||||
await deleteCharacter(chid);
|
||||
}
|
||||
// We just let the button trigger the context menu delete option
|
||||
await characterGroupOverlay.handleContextMenuDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3135,7 +3135,7 @@ body.big-avatars .missing-avatar {
|
|||
}
|
||||
}
|
||||
|
||||
span.warning {
|
||||
.warning {
|
||||
color: var(--warning);
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue