| 'use strict'; |
|
|
| import { |
| characterGroupOverlay, |
| characters, |
| event_types, |
| eventSource, |
| getCharacters, |
| getRequestHeaders, |
| buildAvatarList, |
| characterToEntity, |
| printCharactersDebounced, |
| deleteCharacter, |
| } from '../script.js'; |
|
|
| import { favsToHotswap } from './RossAscends-mods.js'; |
| import { hideLoader, showLoader } from './loader.js'; |
| import { convertCharacterToPersona } from './personas.js'; |
| import { callGenericPopup, POPUP_TYPE } from './popup.js'; |
| import { createTagInput, getTagKeyForEntity, getTagsList, printTagList, tag_map, compareTagsForSort, removeTagFromMap, importTags, tag_import_setting } from './tags.js'; |
|
|
| |
| |
| |
| |
| class CharacterContextMenu { |
| |
| |
| |
| |
| |
| |
| static tag = (selectedCharacters) => { |
| characterGroupOverlay.bulkTagPopupHandler.show(selectedCharacters); |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| static duplicate = async (characterId) => { |
| const character = CharacterContextMenu.#getCharacter(characterId); |
| const body = { avatar_url: character.avatar }; |
|
|
| const result = await fetch('/api/characters/duplicate', { |
| method: 'POST', |
| headers: getRequestHeaders(), |
| body: JSON.stringify(body), |
| }); |
|
|
| if (!result.ok) { |
| throw new Error('Character not duplicated'); |
| } |
|
|
| const data = await result.json(); |
| await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| static favorite = async (characterId) => { |
| const character = CharacterContextMenu.#getCharacter(characterId); |
| const newFavState = !character.data.extensions.fav; |
|
|
| const data = { |
| name: character.name, |
| avatar: character.avatar, |
| data: { |
| extensions: { |
| fav: newFavState, |
| }, |
| }, |
| fav: newFavState, |
| }; |
|
|
| const mergeResponse = await fetch('/api/characters/merge-attributes', { |
| method: 'POST', |
| headers: getRequestHeaders(), |
| body: JSON.stringify(data), |
| }); |
|
|
| if (!mergeResponse.ok) { |
| mergeResponse.json().then(json => toastr.error(`Character not saved. Error: ${json.message}. Field: ${json.error}`)); |
| } |
|
|
| const element = document.getElementById(`CharID${characterId}`); |
| element.classList.toggle('is_fav'); |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| static persona = async (characterId) => void(await convertCharacterToPersona(characterId)); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| static delete = async (characterKey, deleteChats = false) => { |
| await deleteCharacter(characterKey, { deleteChats: deleteChats }); |
| }; |
|
|
| static #getCharacter = (characterId) => characters[characterId] ?? null; |
|
|
| |
| |
| |
| |
| |
| |
| static show = (positionX, positionY) => { |
| let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId); |
| contextMenu.style.left = `${positionX}px`; |
| contextMenu.style.top = `${positionY}px`; |
|
|
| document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden'); |
|
|
| |
| const boundingRect = contextMenu.getBoundingClientRect(); |
| if (boundingRect.right > window.innerWidth) { |
| contextMenu.style.left = `${positionX - (boundingRect.right - window.innerWidth)}px`; |
| } |
| if (boundingRect.bottom > window.innerHeight) { |
| contextMenu.style.top = `${positionY - (boundingRect.bottom - window.innerHeight)}px`; |
| } |
| }; |
|
|
| |
| |
| |
| static hide = () => document.getElementById(BulkEditOverlay.contextMenuId).classList.add('hidden'); |
|
|
| |
| |
| |
| |
| |
| constructor(characterGroupOverlay) { |
| const contextMenuItems = [ |
| { id: 'character_context_menu_favorite', callback: characterGroupOverlay.handleContextMenuFavorite }, |
| { id: 'character_context_menu_duplicate', callback: characterGroupOverlay.handleContextMenuDuplicate }, |
| { id: 'character_context_menu_delete', callback: characterGroupOverlay.handleContextMenuDelete }, |
| { id: 'character_context_menu_persona', callback: characterGroupOverlay.handleContextMenuPersona }, |
| { id: 'character_context_menu_tag', callback: characterGroupOverlay.handleContextMenuTag }, |
| ]; |
|
|
| contextMenuItems.forEach(contextMenuItem => document.getElementById(contextMenuItem.id).addEventListener('click', contextMenuItem.callback)); |
| } |
| } |
|
|
| |
| |
| |
| class BulkTagPopupHandler { |
| |
| |
| |
| |
| characterIds; |
|
|
| |
| |
| |
| |
| currentMutualTags; |
|
|
| |
| |
| |
| |
| |
| constructor() { } |
|
|
| |
| |
| |
| |
| |
| #getHtml = () => { |
| const characterData = JSON.stringify({ characterIds: this.characterIds }); |
| return `<div id="bulk_tag_shadow_popup"> |
| <div id="bulk_tag_popup" class="wider_dialogue_popup"> |
| <div id="bulk_tag_popup_holder"> |
| <h3 class="marginBot5">Modify tags of ${this.characterIds.length} characters</h3> |
| <small class="bulk_tags_desc m-b-1">Add or remove the mutual tags of all selected characters. Import all or existing tags for all selected characters.</small> |
| <div id="bulk_tags_avatars_block" class="avatars_inline avatars_inline_small tags tags_inline"></div> |
| <br> |
| <div id="bulk_tags_div" class="marginBot5" data-characters='${characterData}'> |
| <div class="tag_controls"> |
| <input id="bulkTagInput" class="text_pole tag_input wide100p margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" maxlength="25" /> |
| <div class="tags_view menu_button fa-solid fa-tags" title="View all tags" data-i18n="[title]View all tags"></div> |
| </div> |
| <div id="bulkTagList" class="m-t-1 tags"></div> |
| </div> |
| <div id="dialogue_popup_controls" class="m-t-1"> |
| <div id="bulk_tag_popup_reset" class="menu_button" title="Remove all tags from the selected characters" data-i18n="[title]Remove all tags from the selected characters"> |
| <i class="fa-solid fa-trash-can margin-right-10px"></i> |
| All |
| </div> |
| <div id="bulk_tag_popup_remove_mutual" class="menu_button" title="Remove all mutual tags from the selected characters" data-i18n="[title]Remove all mutual tags from the selected characters"> |
| <i class="fa-solid fa-trash-can margin-right-10px"></i> |
| Mutual |
| </div> |
| <div id="bulk_tag_popup_import_all_tags" class="menu_button" title="Import all tags from selected characters" data-i18n="[title]Import all tags from selected characters"> |
| Import All |
| </div> |
| <div id="bulk_tag_popup_import_existing_tags" class="menu_button" title="Import existing tags from selected characters" data-i18n="[title]Import existing tags from selected characters"> |
| Import Existing |
| </div> |
| <div id="bulk_tag_popup_cancel" class="menu_button" data-i18n="Cancel">Close</div> |
| </div> |
| </div> |
| </div> |
| </div>`; |
| }; |
|
|
| |
| |
| |
| |
| |
| show(characterIds) { |
| |
| this.characterIds = characterIds.slice(); |
|
|
| if (this.characterIds.length == 0) { |
| console.log('No characters selected for bulk edit tags.'); |
| return; |
| } |
|
|
| document.body.insertAdjacentHTML('beforeend', this.#getHtml()); |
|
|
| const entities = this.characterIds.map(id => characterToEntity(characters[id], id)).filter(entity => entity.item !== undefined); |
| buildAvatarList($('#bulk_tags_avatars_block'), entities); |
|
|
| |
| printTagList($('#bulkTagList'), { tags: () => this.getMutualTags(), tagOptions: { removable: true } }); |
|
|
| |
| createTagInput('#bulkTagInput', '#bulkTagList', { tags: () => this.getMutualTags(), tagOptions: { removable: true } }); |
|
|
| document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this)); |
| document.querySelector('#bulk_tag_popup_remove_mutual').addEventListener('click', this.removeMutual.bind(this)); |
| document.querySelector('#bulk_tag_popup_cancel').addEventListener('click', this.hide.bind(this)); |
| document.querySelector('#bulk_tag_popup_import_all_tags').addEventListener('click', this.importAllTags.bind(this)); |
| document.querySelector('#bulk_tag_popup_import_existing_tags').addEventListener('click', this.importExistingTags.bind(this)); |
| } |
|
|
| |
| |
| |
| async importExistingTags() { |
| for (const characterId of this.characterIds) { |
| await importTags(characters[characterId], { importSetting: tag_import_setting.ONLY_EXISTING }); |
| } |
|
|
| $('#bulkTagList').empty(); |
| } |
|
|
| |
| |
| |
| async importAllTags() { |
| for (const characterId of this.characterIds) { |
| await importTags(characters[characterId], { importSetting: tag_import_setting.ALL }); |
| } |
|
|
| $('#bulkTagList').empty(); |
| } |
|
|
| |
| |
| |
| |
| |
| getMutualTags() { |
| if (this.characterIds.length == 0) { |
| return []; |
| } |
|
|
| if (this.characterIds.length === 1) { |
| |
| return getTagsList(getTagKeyForEntity(this.characterIds[0])); |
| } |
|
|
| |
| const allTags = this.characterIds.map(cid => getTagsList(getTagKeyForEntity(cid))); |
| const mutualTags = allTags.reduce((mutual, characterTags) => |
| mutual.filter(tag => characterTags.some(cTag => cTag.id === tag.id)), |
| ); |
|
|
| this.currentMutualTags = mutualTags.sort(compareTagsForSort); |
| return this.currentMutualTags; |
| } |
|
|
| |
| |
| |
| hide() { |
| let popupElement = document.querySelector('#bulk_tag_shadow_popup'); |
| if (popupElement) { |
| document.body.removeChild(popupElement); |
| } |
|
|
| |
| } |
|
|
| |
| |
| |
| resetTags() { |
| for (const characterId of this.characterIds) { |
| const key = getTagKeyForEntity(characterId); |
| if (key) tag_map[key] = []; |
| } |
|
|
| $('#bulkTagList').empty(); |
|
|
| printCharactersDebounced(); |
| } |
|
|
| |
| |
| |
| removeMutual() { |
| const mutualTags = this.getMutualTags(); |
|
|
| for (const characterId of this.characterIds) { |
| for (const tag of mutualTags) { |
| removeTagFromMap(tag.id, characterId.toString()); |
| } |
| } |
|
|
| $('#bulkTagList').empty(); |
|
|
| printCharactersDebounced(); |
| } |
| } |
|
|
| class BulkEditOverlayState { |
| |
| |
| |
| |
| static browse = 0; |
|
|
| |
| |
| |
| |
| static select = 1; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| let bulkEditOverlayInstance = null; |
|
|
| class BulkEditOverlay { |
| static containerId = 'rm_print_characters_block'; |
| static contextMenuId = 'character_context_menu'; |
| static characterClass = 'character_select'; |
| static groupClass = 'group_select'; |
| static bogusFolderClass = 'bogus_folder_select'; |
| static selectModeClass = 'group_overlay_mode_select'; |
| static selectedClass = 'character_selected'; |
| static legacySelectedClass = 'bulk_select_checkbox'; |
| static bulkSelectedCountId = 'bulkSelectedCount'; |
|
|
| static longPressDelay = 2500; |
|
|
| #state = BulkEditOverlayState.browse; |
| #longPress = false; |
| #stateChangeCallbacks = []; |
| #selectedCharacters = []; |
| #bulkTagPopupHandler = new BulkTagPopupHandler(); |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| lastSelected = { characterId: undefined, select: undefined }; |
|
|
| |
| |
| |
| |
| |
| #contextMenuOpen = false; |
|
|
| |
| |
| |
| |
| |
| #cancelNextToggle = false; |
|
|
| |
| |
| |
| container = null; |
|
|
| get state() { |
| return this.#state; |
| } |
|
|
| set state(newState) { |
| if (this.#state === newState) return; |
|
|
| eventSource.emit(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_BEFORE, newState) |
| .then(() => { |
| this.#state = newState; |
| eventSource.emit(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.state); |
| }); |
| } |
|
|
| get isLongPress() { |
| return this.#longPress; |
| } |
|
|
| set isLongPress(longPress) { |
| this.#longPress = longPress; |
| } |
|
|
| get stateChangeCallbacks() { |
| return this.#stateChangeCallbacks; |
| } |
|
|
| |
| |
| |
| |
| get selectedCharacters() { |
| return this.#selectedCharacters; |
| } |
|
|
| |
| |
| |
| |
| |
| get bulkTagPopupHandler() { |
| return this.#bulkTagPopupHandler; |
| } |
|
|
| constructor() { |
| if (bulkEditOverlayInstance instanceof BulkEditOverlay) |
| return bulkEditOverlayInstance; |
|
|
| this.container = document.getElementById(BulkEditOverlay.containerId); |
|
|
| eventSource.on(event_types.CHARACTER_GROUP_OVERLAY_STATE_CHANGE_AFTER, this.handleStateChange); |
| bulkEditOverlayInstance = Object.freeze(this); |
| } |
|
|
| |
| |
| |
| browseState = () => this.state = BulkEditOverlayState.browse; |
|
|
| |
| |
| |
| selectState = () => this.state = BulkEditOverlayState.select; |
|
|
| |
| |
| |
| onPageLoad = () => { |
| this.browseState(); |
|
|
| const elements = this.#getEnabledElements(); |
| elements.forEach(element => element.addEventListener('touchstart', this.handleHold)); |
| elements.forEach(element => element.addEventListener('mousedown', this.handleHold)); |
| elements.forEach(element => element.addEventListener('contextmenu', this.handleDefaultContextMenu)); |
|
|
| elements.forEach(element => element.addEventListener('touchend', this.handleLongPressEnd)); |
| elements.forEach(element => element.addEventListener('mouseup', this.handleLongPressEnd)); |
| elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd)); |
| elements.forEach(element => element.addEventListener('touchmove', this.handleLongPressEnd)); |
|
|
| |
| |
| |
| }; |
|
|
| |
| |
| |
| |
| |
| handleStateChange = () => { |
| switch (this.state) { |
| case BulkEditOverlayState.browse: |
| this.container.classList.remove(BulkEditOverlay.selectModeClass); |
| this.#contextMenuOpen = false; |
| this.#enableClickEventsForCharacters(); |
| this.#enableClickEventsForGroups(); |
| this.clearSelectedCharacters(); |
| this.disableContextMenu(); |
| this.#disableBulkEditButtonHighlight(); |
| CharacterContextMenu.hide(); |
| break; |
| case BulkEditOverlayState.select: |
| this.container.classList.add(BulkEditOverlay.selectModeClass); |
| this.#disableClickEventsForCharacters(); |
| this.#disableClickEventsForGroups(); |
| this.enableContextMenu(); |
| this.#enableBulkEditButtonHighlight(); |
| break; |
| } |
|
|
| this.stateChangeCallbacks.forEach(callback => callback(this.state)); |
| }; |
|
|
| |
| |
| |
| |
| enableContextMenu = () => { |
| this.container.addEventListener('contextmenu', this.handleContextMenuShow); |
| document.addEventListener('click', this.handleContextMenuHide); |
| }; |
|
|
| |
| |
| |
| |
| disableContextMenu = () => { |
| this.container.removeEventListener('contextmenu', this.handleContextMenuShow); |
| document.removeEventListener('click', this.handleContextMenuHide); |
| }; |
|
|
| handleDefaultContextMenu = (event) => { |
| if (this.isLongPress) { |
| event.preventDefault(); |
| event.stopPropagation(); |
| return false; |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| handleHold = (event) => { |
| if (0 !== event.button && event.type !== 'touchstart') return; |
| if (this.#contextMenuOpen) { |
| this.#contextMenuOpen = false; |
| this.#cancelNextToggle = true; |
| CharacterContextMenu.hide(); |
| return; |
| } |
|
|
| let cancel = false; |
|
|
| const cancelHold = (event) => cancel = true; |
| this.container.addEventListener('mouseup', cancelHold); |
| this.container.addEventListener('touchend', cancelHold); |
|
|
| this.isLongPress = true; |
|
|
| setTimeout(() => { |
| if (this.isLongPress && !cancel) { |
| if (this.state === BulkEditOverlayState.browse) { |
| this.selectState(); |
| } else if (this.state === BulkEditOverlayState.select) { |
| this.#contextMenuOpen = true; |
| const [x, y] = this.#getContextMenuPosition(event); |
| CharacterContextMenu.show(x, y); |
| } |
| } |
|
|
| this.container.removeEventListener('mouseup', cancelHold); |
| this.container.removeEventListener('touchend', cancelHold); |
| }, BulkEditOverlay.longPressDelay); |
| }; |
|
|
| handleLongPressEnd = (event) => { |
| this.isLongPress = false; |
| if (this.#contextMenuOpen) event.stopPropagation(); |
| }; |
|
|
| handleCancelClick = () => { |
| if (false === this.#contextMenuOpen) this.state = BulkEditOverlayState.browse; |
| this.#contextMenuOpen = false; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| #getContextMenuPosition = (event) => [ |
| event.clientX || event.touches[0].clientX, |
| event.clientY || event.touches[0].clientY, |
| ]; |
|
|
| #stopEventPropagation = (event) => { |
| if (this.#contextMenuOpen) { |
| this.handleContextMenuHide(event); |
| } |
| event.stopPropagation(); |
| }; |
|
|
| #enableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.removeEventListener('click', this.#stopEventPropagation)); |
|
|
| #disableClickEventsForGroups = () => this.#getDisabledElements().forEach((element) => element.addEventListener('click', this.#stopEventPropagation)); |
|
|
| #enableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.removeEventListener('click', this.toggleCharacterSelected)); |
|
|
| #disableClickEventsForCharacters = () => this.#getEnabledElements().forEach(element => element.addEventListener('click', this.toggleCharacterSelected)); |
|
|
| #enableBulkEditButtonHighlight = () => document.getElementById('bulkEditButton').classList.add('bulk_edit_overlay_active'); |
|
|
| #disableBulkEditButtonHighlight = () => document.getElementById('bulkEditButton').classList.remove('bulk_edit_overlay_active'); |
|
|
| #getEnabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)]; |
|
|
| #getDisabledElements = () => [...this.container.getElementsByClassName(BulkEditOverlay.groupClass), ...this.container.getElementsByClassName(BulkEditOverlay.bogusFolderClass)]; |
|
|
| toggleCharacterSelected = event => { |
| event.stopPropagation(); |
|
|
| const character = event.currentTarget; |
|
|
| if (!this.#contextMenuOpen && !this.#cancelNextToggle) { |
| if (event.shiftKey) { |
| |
| document.getSelection().removeAllRanges(); |
|
|
| this.handleShiftClick(character); |
| } else { |
| this.toggleSingleCharacter(character); |
| } |
| } |
|
|
| this.#cancelNextToggle = false; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| handleShiftClick = (currentCharacter) => { |
| const characterId = Number(currentCharacter.getAttribute('data-chid')); |
| const select = !this.selectedCharacters.includes(characterId); |
|
|
| if (this.lastSelected.characterId >= 0 && this.lastSelected.select !== undefined) { |
| |
| if (select === this.lastSelected.select) { |
| this.toggleCharactersInRange(currentCharacter, select); |
| } |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| toggleSingleCharacter = (character, { markState = true } = {}) => { |
| const characterId = Number(character.getAttribute('data-chid')); |
|
|
| const select = !this.selectedCharacters.includes(characterId); |
| const legacyBulkEditCheckbox = (character.querySelector('.' + BulkEditOverlay.legacySelectedClass)); |
|
|
| if (select) { |
| character.classList.add(BulkEditOverlay.selectedClass); |
| if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = true; |
| this.#selectedCharacters.push(characterId); |
| } else { |
| character.classList.remove(BulkEditOverlay.selectedClass); |
| if (legacyBulkEditCheckbox) legacyBulkEditCheckbox.checked = false; |
| this.#selectedCharacters = this.#selectedCharacters.filter(item => characterId !== item); |
| } |
|
|
| this.updateSelectedCount(); |
|
|
| if (markState) { |
| this.lastSelected.characterId = characterId; |
| this.lastSelected.select = select; |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| updateSelectedCount = (countOverride = undefined) => { |
| const count = countOverride ?? this.selectedCharacters.length; |
| $(`#${BulkEditOverlay.bulkSelectedCountId}`).text(count).attr('title', `${count} characters selected`); |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| toggleCharactersInRange = (currentCharacter, select) => { |
| const currentCharacterId = Number(currentCharacter.getAttribute('data-chid')); |
| const characters = Array.from(document.querySelectorAll('#' + BulkEditOverlay.containerId + ' .' + BulkEditOverlay.characterClass)); |
|
|
| const startIndex = characters.findIndex(c => Number(c.getAttribute('data-chid')) === Number(this.lastSelected.characterId)); |
| const endIndex = characters.findIndex(c => Number(c.getAttribute('data-chid')) === currentCharacterId); |
|
|
| for (let i = Math.min(startIndex, endIndex); i <= Math.max(startIndex, endIndex); i++) { |
| const character = characters[i]; |
| const characterId = Number(character.getAttribute('data-chid')); |
| const isCharacterSelected = this.selectedCharacters.includes(characterId); |
|
|
| |
| |
| if ((select && !isCharacterSelected || !select && isCharacterSelected) && character instanceof HTMLElement) { |
| this.toggleSingleCharacter(character, { markState: currentCharacterId == characterId }); |
| } |
| } |
| }; |
|
|
| handleContextMenuShow = (event) => { |
| event.preventDefault(); |
| const [x,y] = this.#getContextMenuPosition(event); |
| CharacterContextMenu.show(x, y); |
| this.#contextMenuOpen = true; |
| }; |
|
|
| handleContextMenuHide = (event) => { |
| let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId); |
| if (false === contextMenu.contains(event.target)) { |
| CharacterContextMenu.hide(); |
| this.#contextMenuOpen = false; |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| handleContextMenuFavorite = async () => { |
| const promises = []; |
|
|
| for (const characterId of this.selectedCharacters) { |
| promises.push(CharacterContextMenu.favorite(characterId)); |
| } |
|
|
| await Promise.allSettled(promises); |
| await getCharacters(); |
| await favsToHotswap(); |
| this.browseState(); |
| }; |
|
|
| |
| |
| |
| |
| |
| handleContextMenuDuplicate = () => Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.duplicate(characterId))) |
| .then(() => getCharacters()) |
| .then(() => this.browseState()); |
|
|
| |
| |
| |
| |
| |
| handleContextMenuPersona = async () => { |
| for (const characterId of this.selectedCharacters) { |
| await CharacterContextMenu.persona(characterId); |
| } |
|
|
| this.browseState(); |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| 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>`; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| handleContextMenuDelete = () => { |
| const characterIds = this.selectedCharacters; |
| const popupContent = $(BulkEditOverlay.#getDeletePopupContentHtml(characterIds)); |
| const checkbox = popupContent.find('#del_char_checkbox'); |
| const promise = callGenericPopup(popupContent, POPUP_TYPE.CONFIRM) |
| .then((accept) => { |
| if (!accept) return; |
|
|
| const deleteChats = checkbox.prop('checked') ?? false; |
|
|
| showLoader(); |
| 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(() => { |
| toastr.clear(toast); |
| hideLoader(); |
| }); |
| }); |
|
|
| |
| const entities = characterIds.map(id => characterToEntity(characters[id], id)).filter(entity => entity.item !== undefined); |
| buildAvatarList($('#bulk_delete_avatars_block'), entities); |
|
|
| return promise; |
| }; |
|
|
| |
| |
| |
| handleContextMenuTag = () => { |
| CharacterContextMenu.tag(this.selectedCharacters); |
| this.browseState(); |
| }; |
|
|
| addStateChangeCallback = callback => this.stateChangeCallbacks.push(callback); |
|
|
| |
| |
| |
| |
| clearSelectedCharacters = () => { |
| document.querySelectorAll('#' + BulkEditOverlay.containerId + ' .' + BulkEditOverlay.selectedClass) |
| .forEach(element => element.classList.remove(BulkEditOverlay.selectedClass)); |
| this.selectedCharacters.length = 0; |
| }; |
| } |
|
|
| export { BulkEditOverlayState, CharacterContextMenu, BulkEditOverlay }; |
|
|