parent
36cf68a9f4
commit
99ee1b887a
|
@ -10,10 +10,12 @@ import {
|
||||||
getRequestHeaders,
|
getRequestHeaders,
|
||||||
this_chid
|
this_chid
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
|
|
||||||
import {favsToHotswap} from "./RossAscends-mods.js";
|
import {favsToHotswap} from "./RossAscends-mods.js";
|
||||||
import {convertCharacterToPersona} from "./personas.js";
|
import {convertCharacterToPersona} from "./personas.js";
|
||||||
import {createTagInput, getTagKeyForCharacter, tag_map} from "./tags.js";
|
import {createTagInput, getTagKeyForCharacter, tag_map} from "./tags.js";
|
||||||
|
|
||||||
|
// Utility object for popup messages.
|
||||||
const popupMessage = {
|
const popupMessage = {
|
||||||
deleteChat(characterCount) {
|
deleteChat(characterCount) {
|
||||||
return `<h3>Delete ${characterCount} characters?</h3>
|
return `<h3>Delete ${characterCount} characters?</h3>
|
||||||
|
@ -25,29 +27,29 @@ const popupMessage = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleFavoriteHighlight = (characterId) => {
|
/**
|
||||||
const element = document.getElementById(`CharID${characterId}`);
|
* Static object representing the actions of the
|
||||||
element.classList.toggle('is_fav');
|
* character context menu override.
|
||||||
}
|
*/
|
||||||
|
|
||||||
class CharacterGroupOverlayState {
|
|
||||||
static browse = 0;
|
|
||||||
static select = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CharacterContextMenu {
|
class CharacterContextMenu {
|
||||||
|
/**
|
||||||
|
* Tag one or more characters,
|
||||||
|
* opens a popup.
|
||||||
|
*
|
||||||
|
* @param selectedCharacters
|
||||||
|
*/
|
||||||
static tag = (selectedCharacters) => {
|
static tag = (selectedCharacters) => {
|
||||||
BulkTagPopupHandler.show(selectedCharacters);
|
BulkTagPopupHandler.show(selectedCharacters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicate a character
|
* Duplicate one or more characters
|
||||||
*
|
*
|
||||||
* @param characterId
|
* @param characterId
|
||||||
* @returns {Promise<Response>}
|
* @returns {Promise<Response>}
|
||||||
*/
|
*/
|
||||||
static duplicate = async (characterId) => {
|
static duplicate = async (characterId) => {
|
||||||
const character = CharacterContextMenu.getCharacter(characterId);
|
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||||
|
|
||||||
return fetch('/dupecharacter', {
|
return fetch('/dupecharacter', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -58,13 +60,15 @@ class CharacterContextMenu {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Favorite a character
|
* Favorite a character
|
||||||
* and toggle its ui element.
|
* and highlight it.
|
||||||
*
|
*
|
||||||
* @param characterId
|
* @param characterId
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static favorite = async (characterId) => {
|
static favorite = async (characterId) => {
|
||||||
const character = CharacterContextMenu.getCharacter(characterId);
|
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||||
|
|
||||||
|
// Only set fav for V2 spec
|
||||||
const data = {
|
const data = {
|
||||||
name: character.name,
|
name: character.name,
|
||||||
avatar: character.avatar,
|
avatar: character.avatar,
|
||||||
|
@ -80,22 +84,34 @@ class CharacterContextMenu {
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if (response.ok) toggleFavoriteHighlight(characterId)
|
if (response.ok) {
|
||||||
else response.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');
|
||||||
|
} else {
|
||||||
|
response.json().then(json => toastr.error('Character not saved. Error: ' + json.message + '. Field: ' + json.error));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the given characters to personas.
|
* Convert one or more characters to persona,
|
||||||
* Shows popup for each.
|
* may open a popup for one or more characters.
|
||||||
*
|
*
|
||||||
* @param characterId
|
* @param characterId
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static persona = async (characterId) => await convertCharacterToPersona(characterId);
|
static persona = async (characterId) => await convertCharacterToPersona(characterId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete one or more characters,
|
||||||
|
* opens a popup.
|
||||||
|
*
|
||||||
|
* @param characterId
|
||||||
|
* @param deleteChats
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
static delete = async (characterId, deleteChats = false) => {
|
static delete = async (characterId, deleteChats = false) => {
|
||||||
const character = CharacterContextMenu.getCharacter(characterId);
|
const character = CharacterContextMenu.#getCharacter(characterId);
|
||||||
|
|
||||||
return fetch('/deletecharacter', {
|
return fetch('/deletecharacter', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -123,12 +139,19 @@ class CharacterContextMenu {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
eventSource.emit('characterDeleted', { id: this_chid, character: characters[this_chid] });
|
eventSource.emit('characterDeleted', { id: this_chid, character: characters[this_chid] });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static getCharacter = (characterId) => characters[characterId] ?? null;
|
static #getCharacter = (characterId) => characters[characterId] ?? null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the context menu at the given position
|
||||||
|
*
|
||||||
|
* @param positionX
|
||||||
|
* @param positionY
|
||||||
|
*/
|
||||||
static show = (positionX, positionY) => {
|
static show = (positionX, positionY) => {
|
||||||
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
let contextMenu = document.getElementById(BulkEditOverlay.contextMenuId);
|
||||||
contextMenu.style.left = `${positionX}px`;
|
contextMenu.style.left = `${positionX}px`;
|
||||||
|
@ -137,8 +160,16 @@ class CharacterContextMenu {
|
||||||
document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden');
|
document.getElementById(BulkEditOverlay.contextMenuId).classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the context menu
|
||||||
|
*/
|
||||||
static hide = () => document.getElementById(BulkEditOverlay.contextMenuId).classList.add('hidden');
|
static hide = () => document.getElementById(BulkEditOverlay.contextMenuId).classList.add('hidden');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the context menu for the given overlay
|
||||||
|
*
|
||||||
|
* @param characterGroupOverlay
|
||||||
|
*/
|
||||||
constructor(characterGroupOverlay) {
|
constructor(characterGroupOverlay) {
|
||||||
const contextMenuItems = [
|
const contextMenuItems = [
|
||||||
{id: 'character_context_menu_favorite', callback: characterGroupOverlay.handleContextMenuFavorite},
|
{id: 'character_context_menu_favorite', callback: characterGroupOverlay.handleContextMenuFavorite},
|
||||||
|
@ -153,7 +184,7 @@ class CharacterContextMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends/Removes the bulk tag popup
|
* Represents a tag control not bound to a single character
|
||||||
*/
|
*/
|
||||||
class BulkTagPopupHandler {
|
class BulkTagPopupHandler {
|
||||||
static #getHtml = (characterIds) => {
|
static #getHtml = (characterIds) => {
|
||||||
|
@ -180,6 +211,11 @@ class BulkTagPopupHandler {
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append and show the tag control
|
||||||
|
*
|
||||||
|
* @param characters - The characters assigned to this control
|
||||||
|
*/
|
||||||
static show(characters) {
|
static show(characters) {
|
||||||
document.body.insertAdjacentHTML('beforeend', this.#getHtml(characters));
|
document.body.insertAdjacentHTML('beforeend', this.#getHtml(characters));
|
||||||
createTagInput('#bulkTagInput', '#bulkTagList');
|
createTagInput('#bulkTagInput', '#bulkTagList');
|
||||||
|
@ -187,6 +223,9 @@ class BulkTagPopupHandler {
|
||||||
document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this, characters));
|
document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this, characters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide and remove the tag control
|
||||||
|
*/
|
||||||
static hide() {
|
static hide() {
|
||||||
let popupElement = document.querySelector('#bulk_tag_shadow_popup');
|
let popupElement = document.querySelector('#bulk_tag_shadow_popup');
|
||||||
if (popupElement) {
|
if (popupElement) {
|
||||||
|
@ -194,6 +233,11 @@ class BulkTagPopupHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty the tag map for the given characters
|
||||||
|
*
|
||||||
|
* @param characterIds
|
||||||
|
*/
|
||||||
static resetTags(characterIds) {
|
static resetTags(characterIds) {
|
||||||
characterIds.forEach((characterId) => {
|
characterIds.forEach((characterId) => {
|
||||||
const key = getTagKeyForCharacter(characterId);
|
const key = getTagKeyForCharacter(characterId);
|
||||||
|
@ -202,6 +246,20 @@ class BulkTagPopupHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CharacterGroupOverlayState {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
static browse = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
static select = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement a SingletonPattern, allowing access to the group overlay instance
|
* Implement a SingletonPattern, allowing access to the group overlay instance
|
||||||
* from everywhere via (new CharacterGroupOverlay())
|
* from everywhere via (new CharacterGroupOverlay())
|
||||||
|
@ -273,7 +331,14 @@ class BulkEditOverlay {
|
||||||
bulkEditOverlayInstance = Object.freeze(this);
|
bulkEditOverlayInstance = Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the overlay to browse mode
|
||||||
|
*/
|
||||||
browseState = () => this.state = CharacterGroupOverlayState.browse;
|
browseState = () => this.state = CharacterGroupOverlayState.browse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the overlay to select mode
|
||||||
|
*/
|
||||||
selectState = () => this.state = CharacterGroupOverlayState.select;
|
selectState = () => this.state = CharacterGroupOverlayState.select;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,6 +359,32 @@ class BulkEditOverlay {
|
||||||
grid.addEventListener('click', this.handleCancelClick);
|
grid.addEventListener('click', this.handleCancelClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle state changes
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handleStateChange = () => {
|
||||||
|
switch (this.state) {
|
||||||
|
case CharacterGroupOverlayState.browse:
|
||||||
|
this.container.classList.remove(BulkEditOverlay.selectModeClass);
|
||||||
|
this.#enableClickEventsForCharacters();
|
||||||
|
this.clearSelectedCharacters();
|
||||||
|
this.disableContextMenu();
|
||||||
|
this.#disableBulkEditButtonHighlight();
|
||||||
|
CharacterContextMenu.hide();
|
||||||
|
break;
|
||||||
|
case CharacterGroupOverlayState.select:
|
||||||
|
this.container.classList.add(BulkEditOverlay.selectModeClass);
|
||||||
|
this.#disableClickEventsForCharacters();
|
||||||
|
this.enableContextMenu();
|
||||||
|
this.#enableBulkEditButtonHighlight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stateChangeCallbacks.forEach(callback => callback(this.state));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block the browsers native context menu and
|
* Block the browsers native context menu and
|
||||||
* set a click event to hide the custom context menu.
|
* set a click event to hide the custom context menu.
|
||||||
|
@ -331,27 +422,6 @@ class BulkEditOverlay {
|
||||||
this.state = CharacterGroupOverlayState.browse;
|
this.state = CharacterGroupOverlayState.browse;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStateChange = () => {
|
|
||||||
switch (this.state) {
|
|
||||||
case CharacterGroupOverlayState.browse:
|
|
||||||
this.container.classList.remove(BulkEditOverlay.selectModeClass);
|
|
||||||
this.#enableClickEventsForCharacters();
|
|
||||||
this.clearSelectedCharacters();
|
|
||||||
this.disableContextMenu();
|
|
||||||
this.#disableBulkEditButtonHighlight();
|
|
||||||
CharacterContextMenu.hide();
|
|
||||||
break;
|
|
||||||
case CharacterGroupOverlayState.select:
|
|
||||||
this.container.classList.add(BulkEditOverlay.selectModeClass);
|
|
||||||
this.#disableClickEventsForCharacters();
|
|
||||||
this.enableContextMenu();
|
|
||||||
this.#enableBulkEditButtonHighlight();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stateChangeCallbacks.forEach(callback => callback(this.state));
|
|
||||||
}
|
|
||||||
|
|
||||||
#enableClickEventsForCharacters = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)]
|
#enableClickEventsForCharacters = () => [...this.container.getElementsByClassName(BulkEditOverlay.characterClass)]
|
||||||
.forEach(element => element.removeEventListener('click', this.toggleCharacterSelected));
|
.forEach(element => element.removeEventListener('click', this.toggleCharacterSelected));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue