mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
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:
@ -5357,7 +5357,7 @@ function buildAvatarList(block, entities, { templateId = 'inline_avatar_template
|
|||||||
avatarTemplate.attr('data-type', entity.type);
|
avatarTemplate.attr('data-type', entity.type);
|
||||||
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
||||||
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entity.item.name);
|
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) {
|
if (highlightFavs) {
|
||||||
avatarTemplate.toggleClass('is_fav', entity.item.fav || entity.item.fav == 'true');
|
avatarTemplate.toggleClass('is_fav', entity.item.fav || entity.item.fav == 'true');
|
||||||
avatarTemplate.find('.ch_fav').val(entity.item.fav);
|
avatarTemplate.find('.ch_fav').val(entity.item.fav);
|
||||||
|
@ -19,18 +19,6 @@ import { hideLoader, showLoader } from './loader.js';
|
|||||||
import { convertCharacterToPersona } from './personas.js';
|
import { convertCharacterToPersona } from './personas.js';
|
||||||
import { createTagInput, getTagKeyForEntity, getTagsList, printTagList, tag_map, compareTagsForSort, removeTagFromMap } from './tags.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
|
* Static object representing the actions of the
|
||||||
* character context menu override.
|
* character context menu override.
|
||||||
@ -198,6 +186,12 @@ class CharacterContextMenu {
|
|||||||
* Represents a tag control not bound to a single character
|
* Represents a tag control not bound to a single character
|
||||||
*/
|
*/
|
||||||
class BulkTagPopupHandler {
|
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) => {
|
static #getHtml = (characterIds) => {
|
||||||
const characterData = JSON.stringify({ characterIds: characterIds });
|
const characterData = JSON.stringify({ characterIds: characterIds });
|
||||||
return `<div id="bulk_tag_shadow_popup">
|
return `<div id="bulk_tag_shadow_popup">
|
||||||
@ -227,8 +221,7 @@ class BulkTagPopupHandler {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>`;
|
||||||
`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -430,7 +423,7 @@ class BulkEditOverlay {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns {*[]}
|
* @returns {number[]}
|
||||||
*/
|
*/
|
||||||
get selectedCharacters() {
|
get selectedCharacters() {
|
||||||
return this.#selectedCharacters;
|
return this.#selectedCharacters;
|
||||||
@ -775,6 +768,29 @@ class BulkEditOverlay {
|
|||||||
this.browseState();
|
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
|
* Request user input before concurrently handle deletion
|
||||||
* requests.
|
* requests.
|
||||||
@ -782,8 +798,9 @@ class BulkEditOverlay {
|
|||||||
* @returns {Promise<number>}
|
* @returns {Promise<number>}
|
||||||
*/
|
*/
|
||||||
handleContextMenuDelete = () => {
|
handleContextMenuDelete = () => {
|
||||||
callPopup(
|
const characterIds = this.selectedCharacters;
|
||||||
popupMessage.deleteChat(this.selectedCharacters.length), null)
|
const popupContent = BulkEditOverlay.#getDeletePopupContentHtml(characterIds);
|
||||||
|
const promise = callPopup(popupContent, null)
|
||||||
.then((accept) => {
|
.then((accept) => {
|
||||||
if (true !== accept) return;
|
if (true !== accept) return;
|
||||||
|
|
||||||
@ -791,11 +808,17 @@ class BulkEditOverlay {
|
|||||||
|
|
||||||
showLoader();
|
showLoader();
|
||||||
toastr.info('We\'re deleting your characters, please wait...', 'Working on it');
|
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(() => getCharacters())
|
||||||
.then(() => this.browseState())
|
.then(() => this.browseState())
|
||||||
.finally(() => hideLoader());
|
.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() {
|
async function onDeleteButtonClick() {
|
||||||
console.log('Delete button clicked');
|
console.log('Delete button clicked');
|
||||||
|
|
||||||
// Create a mapping of chid to avatar
|
// We just let the button trigger the context menu delete option
|
||||||
let toDelete = [];
|
await characterGroupOverlay.handleContextMenuDelete();
|
||||||
$('.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3135,7 +3135,7 @@ body.big-avatars .missing-avatar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span.warning {
|
.warning {
|
||||||
color: var(--warning);
|
color: var(--warning);
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user