Merge pull request #2663 from d-ber/bulkTagImport Add bulk tag import

d57272c166c58ad5a83513cb32430d6920c9a8cd

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
4 files changed, +44 -14Ignore whitespace
public/css/character-group-overlay.css+1 -1
@@ -99,6 +99,6 @@
99}99}
100100
101#bulk_tag_shadow_popup #bulk_tag_popup #dialogue_popup_controls .menu_button {101#bulk_tag_shadow_popup #bulk_tag_popup #dialogue_popup_controls .menu_button {
102 width: 100px;102 width: unset;
103 padding: 0.25em;103 padding: 0.25em;
104}104}
public/script.js+1 -1
@@ -10740,7 +10740,7 @@ jQuery(async function () {
10740 }10740 }
10741 } break;10741 } break;
10742 case 'import_tags': {10742 case 'import_tags': {
10743 await importTags(characters[this_chid], { forceShow: true });10743 await importTags(characters[this_chid], { importSetting: tag_import_setting.ASK });
10744 } break;10744 } break;
10745 /*case 'delete_button':10745 /*case 'delete_button':
10746 popup_type = "del_ch";10746 popup_type = "del_ch";
public/scripts/BulkEditOverlay.js+34 -4
@@ -18,7 +18,7 @@ import {
18import { favsToHotswap } from './RossAscends-mods.js';18import { favsToHotswap } from './RossAscends-mods.js';
19import { hideLoader, showLoader } from './loader.js';19import { hideLoader, showLoader } from './loader.js';
20import { convertCharacterToPersona } from './personas.js';20import { convertCharacterToPersona } from './personas.js';
21import { createTagInput, getTagKeyForEntity, getTagsList, printTagList, tag_map, compareTagsForSort, removeTagFromMap } from './tags.js';21import { createTagInput, getTagKeyForEntity, getTagsList, printTagList, tag_map, compareTagsForSort, removeTagFromMap, importTags, tag_import_setting } from './tags.js';
2222
23/**23/**
24 * Static object representing the actions of the24 * Static object representing the actions of the
@@ -197,10 +197,10 @@ class BulkTagPopupHandler {
197 #getHtml = () => {197 #getHtml = () => {
198 const characterData = JSON.stringify({ characterIds: this.characterIds });198 const characterData = JSON.stringify({ characterIds: this.characterIds });
199 return `<div id="bulk_tag_shadow_popup">199 return `<div id="bulk_tag_shadow_popup">
200 <div id="bulk_tag_popup">200 <div id="bulk_tag_popup" class="wider_dialogue_popup">
201 <div id="bulk_tag_popup_holder">201 <div id="bulk_tag_popup_holder">
202 <h3 class="marginBot5">Modify tags of ${this.characterIds.length} characters</h3>202 <h3 class="marginBot5">Modify tags of ${this.characterIds.length} characters</h3>
203 <small class="bulk_tags_desc m-b-1">Add or remove the mutual tags of all selected characters.</small>203 <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>
204 <div id="bulk_tags_avatars_block" class="avatars_inline avatars_inline_small tags tags_inline"></div>204 <div id="bulk_tags_avatars_block" class="avatars_inline avatars_inline_small tags tags_inline"></div>
205 <br>205 <br>
206 <div id="bulk_tags_div" class="marginBot5" data-characters='${characterData}'>206 <div id="bulk_tags_div" class="marginBot5" data-characters='${characterData}'>
@@ -219,6 +219,12 @@ class BulkTagPopupHandler {
219 <i class="fa-solid fa-trash-can margin-right-10px"></i>219 <i class="fa-solid fa-trash-can margin-right-10px"></i>
220 Mutual220 Mutual
221 </div>221 </div>
222 <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">
223 Import All
224 </div>
225 <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">
226 Import Existing
227 </div>
222 <div id="bulk_tag_popup_cancel" class="menu_button" data-i18n="Cancel">Close</div>228 <div id="bulk_tag_popup_cancel" class="menu_button" data-i18n="Cancel">Close</div>
223 </div>229 </div>
224 </div>230 </div>
@@ -254,6 +260,30 @@ class BulkTagPopupHandler {
254 document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this));260 document.querySelector('#bulk_tag_popup_reset').addEventListener('click', this.resetTags.bind(this));
255 document.querySelector('#bulk_tag_popup_remove_mutual').addEventListener('click', this.removeMutual.bind(this));261 document.querySelector('#bulk_tag_popup_remove_mutual').addEventListener('click', this.removeMutual.bind(this));
256 document.querySelector('#bulk_tag_popup_cancel').addEventListener('click', this.hide.bind(this));262 document.querySelector('#bulk_tag_popup_cancel').addEventListener('click', this.hide.bind(this));
263 document.querySelector('#bulk_tag_popup_import_all_tags').addEventListener('click', this.importAllTags.bind(this));
264 document.querySelector('#bulk_tag_popup_import_existing_tags').addEventListener('click', this.importExistingTags.bind(this));
265 }
266
267 /**
268 * Import existing tags for all selected characters
269 */
270 async importExistingTags() {
271 for (const characterId of this.characterIds) {
272 await importTags(characters[characterId], { importSetting: tag_import_setting.ONLY_EXISTING });
273 }
274
275 $('#bulkTagList').empty();
276 }
277
278 /**
279 * Import all tags for all selected characters
280 */
281 async importAllTags() {
282 for (const characterId of this.characterIds) {
283 await importTags(characters[characterId], { importSetting: tag_import_setting.ALL });
284 }
285
286 $('#bulkTagList').empty();
257 }287 }
258288
259 /**289 /**
@@ -570,7 +600,7 @@ class BulkEditOverlay {
570 this.container.removeEventListener('mouseup', cancelHold);600 this.container.removeEventListener('mouseup', cancelHold);
571 this.container.removeEventListener('touchend', cancelHold);601 this.container.removeEventListener('touchend', cancelHold);
572 },602 },
573 BulkEditOverlay.longPressDelay);603 BulkEditOverlay.longPressDelay);
574 };604 };
575605
576 handleLongPressEnd = (event) => {606 handleLongPressEnd = (event) => {
public/scripts/tags.js+8 -8
@@ -708,12 +708,12 @@ const ANTI_TROLL_MAX_TAGS = 15;
708 *708 *
709 * @param {Character} character - The character709 * @param {Character} character - The character
710 * @param {object} [options] - Options710 * @param {object} [options] - Options
711 * @param {boolean} [options.forceShow=false] - Whether to force showing the import dialog711 * @param {tag_import_setting} [options.importSetting=null] - Force a tag import setting
712 * @returns {Promise<boolean>} Boolean indicating whether any tag was imported712 * @returns {Promise<boolean>} Boolean indicating whether any tag was imported
713 */713 */
714async function importTags(character, { forceShow = false } = {}) {714async function importTags(character, { importSetting = null } = {}) {
715 // Gather the tags to import based on the selected setting715 // Gather the tags to import based on the selected setting
716 const tagNamesToImport = await handleTagImport(character, { forceShow });716 const tagNamesToImport = await handleTagImport(character, { importSetting });
717 if (!tagNamesToImport?.length) {717 if (!tagNamesToImport?.length) {
718 console.debug('No tags to import');718 console.debug('No tags to import');
719 return;719 return;
@@ -732,10 +732,10 @@ async function importTags(character, { forceShow = false } = {}) {
732 *732 *
733 * @param {Character} character - The character733 * @param {Character} character - The character
734 * @param {object} [options] - Options734 * @param {object} [options] - Options
735 * @param {boolean} [options.forceShow=false] - Whether to force showing the import dialog735 * @param {tag_import_setting} [options.importSetting=null] - Force a tag import setting
736 * @returns {Promise<string[]>} Array of strings representing the tags to import736 * @returns {Promise<string[]>} Array of strings representing the tags to import
737 */737 */
738async function handleTagImport(character, { forceShow = false } = {}) {738async function handleTagImport(character, { importSetting = null } = {}) {
739 /** @type {string[]} */739 /** @type {string[]} */
740 const importTags = character.tags.map(t => t.trim()).filter(t => t)740 const importTags = character.tags.map(t => t.trim()).filter(t => t)
741 .filter(t => !IMPORT_EXLCUDED_TAGS.includes(t))741 .filter(t => !IMPORT_EXLCUDED_TAGS.includes(t))
@@ -745,9 +745,9 @@ async function handleTagImport(character, { forceShow = false } = {}) {
745 .map(newTag);745 .map(newTag);
746 const folderTags = getOpenBogusFolders();746 const folderTags = getOpenBogusFolders();
747747
748 // Choose the setting for this dialog. If from settings, verify the setting really exists, otherwise take "ASK".748 // Choose the setting for this dialog. First check override, then saved setting or finally use "ASK".
749 const setting = forceShow ? tag_import_setting.ASK749 const setting = importSetting ? importSetting :
750 : Object.values(tag_import_setting).find(setting => setting === power_user.tag_import_setting) ?? tag_import_setting.ASK;750 Object.values(tag_import_setting).find(setting => setting === power_user.tag_import_setting) ?? tag_import_setting.ASK;
751751
752 switch (setting) {752 switch (setting) {
753 case tag_import_setting.ALL:753 case tag_import_setting.ALL: