Tags: Add unused tags pruning to tags management

cc169fffc28f1e124f3a7403decad8b9e36d0a6d

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

2 files changed, +47 -0Showing whitespace changes
public/scripts/tags.js+43 -0
@@ -393,6 +393,10 @@ function createTagMapFromList(listElement, key) {
393 * @returns {Tag[]} A list of tags393 * @returns {Tag[]} A list of tags
394 */394 */
395function getTagsList(key, sort = true) {395function getTagsList(key, sort = true) {
396 if (key === null || key === undefined) {
397 return [];
398 }
399
396 if (!Array.isArray(tag_map[key])) {400 if (!Array.isArray(tag_map[key])) {
397 tag_map[key] = [];401 tag_map[key] = [];
398 return [];402 return [];
@@ -1547,6 +1551,44 @@ function onTagsBackupClick() {
1547 download(blob, filename, 'application/json');1551 download(blob, filename, 'application/json');
1548}1552}
15491553
1554async function onTagsPruneClick() {
1555 // Get tags which have zero tag map entries
1556 const allTagsInTagMaps = new Set(Object.values(tag_map).flat());
1557 const tagsToPrune = tags.filter(tag => !allTagsInTagMaps.has(tag.id));
1558
1559 // Get tag maps referring to deleted entities
1560 const allEntityKeys = new Set([...characters.map(c => String(c.avatar)), ...groups.map(g => String(g.id))]);
1561 const tagMapsToPrune = Object.keys(tag_map).filter(key => !allEntityKeys.has(key));
1562
1563 if (!tagsToPrune.length && !tagMapsToPrune.length) {
1564 toastr.info(t`No unused tags or references found.`);
1565 return;
1566 }
1567
1568 const confirm = await Popup.show.confirm(t`Prune ${tagsToPrune.length} tags and ${tagMapsToPrune.length} references`, t`Are you sure you want to remove all unused tags and references to missing or deleted characters and groups?`);
1569
1570 if (!confirm) {
1571 return;
1572 }
1573
1574 for (const tag of tagsToPrune) {
1575 tags.splice(tags.indexOf(tag), 1);
1576 }
1577
1578 for (const key of tagMapsToPrune) {
1579 delete tag_map[key];
1580 }
1581
1582 printCharactersDebounced();
1583 saveSettingsDebounced();
1584
1585 // Reprint the tag management popup, without having it to be opened again
1586 const tagContainer = $('#tag_view_list .tag_view_list_tags');
1587 printViewTagList(tagContainer);
1588
1589 toastr.success(t`Unused tags pruned successfully.`);
1590}
1591
1550function onTagCreateClick() {1592function onTagCreateClick() {
1551 const tagName = getFreeName('New Tag', tags.map(x => x.name));1593 const tagName = getFreeName('New Tag', tags.map(x => x.name));
1552 const tag = createNewTag(tagName);1594 const tag = createNewTag(tagName);
@@ -2198,6 +2240,7 @@ export function initTags() {
2198 $(document).on('click', '.tag_view_create', onTagCreateClick);2240 $(document).on('click', '.tag_view_create', onTagCreateClick);
2199 $(document).on('click', '.tag_view_backup', onTagsBackupClick);2241 $(document).on('click', '.tag_view_backup', onTagsBackupClick);
2200 $(document).on('click', '.tag_view_restore', onBackupRestoreClick);2242 $(document).on('click', '.tag_view_restore', onBackupRestoreClick);
2243 $(document).on('click', '.tag_view_prune', onTagsPruneClick);
2201 eventSource.on(event_types.CHARACTER_DUPLICATED, copyTags);2244 eventSource.on(event_types.CHARACTER_DUPLICATED, copyTags);
2202 eventSource.makeFirst(event_types.CHAT_CHANGED, () => selected_group ? applyTagsOnGroupSelect() : applyTagsOnCharacterSelect());2245 eventSource.makeFirst(event_types.CHAT_CHANGED, () => selected_group ? applyTagsOnGroupSelect() : applyTagsOnCharacterSelect());
22032246
public/scripts/templates/tagManagement.html+4 -0
@@ -1,6 +1,10 @@
1<div class="title_restorable alignItemsBaseline">1<div class="title_restorable alignItemsBaseline">
2 <h3 data-i18n="Tag Management">Tag Management</h3>2 <h3 data-i18n="Tag Management">Tag Management</h3>
3 <div class="flex-container alignItemsBaseline">3 <div class="flex-container alignItemsBaseline">
4 <div class="menu_button menu_button_icon tag_view_prune" data-i18n="[title]Remove unused tags" title="Remove unused tags">
5 <i class="fa-solid fa-scissors"></i>
6 <span data-i18n="Prune">Prune</span>
7 </div>
4 <div class="menu_button menu_button_icon tag_view_backup" data-i18n="[title]Save your tags to a file" title="Save your tags to a file">8 <div class="menu_button menu_button_icon tag_view_backup" data-i18n="[title]Save your tags to a file" title="Save your tags to a file">
5 <i class="fa-solid fa-file-export"></i>9 <i class="fa-solid fa-file-export"></i>
6 <span data-i18n="Backup">Backup</span>10 <span data-i18n="Backup">Backup</span>