Nested bogus folders + back button

This commit is contained in:
Cohee
2023-11-11 14:53:08 +02:00
parent 5331b5dc8a
commit 303026e01f
5 changed files with 141 additions and 43 deletions

View File

@ -4167,7 +4167,7 @@
<div id="bogus_folder_template" class="template_element"> <div id="bogus_folder_template" class="template_element">
<div class="bogus_folder_select flex-container wide100p alignitemsflexstart"> <div class="bogus_folder_select flex-container wide100p alignitemsflexstart">
<div class="avatar flex alignitemscenter textAlignCenter"> <div class="avatar flex alignitemscenter textAlignCenter">
<i class="fa-solid fa-folder-open fa-xl"></i> <i class="bogus_folder_icon fa-solid fa-folder-open fa-xl"></i>
</div> </div>
<div class="flex-container wide100pLess70px character_select_container"> <div class="flex-container wide100pLess70px character_select_container">
<div class="wide100p character_name_block"> <div class="wide100p character_name_block">
@ -4180,6 +4180,18 @@
</div> </div>
</div> </div>
</div> </div>
<div id="bogus_folder_back_template" class="template_element">
<div class="bogus_folder_select flex-container wide100p alignitemsflexstart" id="BogusFolderBack" tagid="back">
<div class="avatar flex alignitemscenter textAlignCenter">
<i class="bogus_folder_icon fa-solid fa-xl fa-right-from-bracket fa-flip-horizontal"></i>
</div>
<div class="flex-container wide100pLess70px character_select_container height100p alignitemscenter">
<div class="wide100p character_name_block">
<span class="ch_name">Go back</span>
</div>
</div>
</div>
</div>
<div id="hotswap_template" class="template_element"> <div id="hotswap_template" class="template_element">
<div class="hotswapAvatar" title="Add a character/group to favorites to display it here!"> <div class="hotswapAvatar" title="Add a character/group to favorites to display it here!">
<img src="/img/ai4.png"> <img src="/img/ai4.png">

View File

@ -989,16 +989,28 @@ export async function selectCharacterById(id) {
} }
} }
function getTagBlock(item) { function getTagBlock(item, entities) {
const count = Object.values(tag_map).flat().filter(x => x == item.id).length; let count = 0;
for (const entity of entities) {
if (entitiesFilter.isElementTagged(entity, item.id)) {
count++;
}
}
const template = $('#bogus_folder_template .bogus_folder_select').clone(); const template = $('#bogus_folder_template .bogus_folder_select').clone();
template.attr({ 'tagid': item.id, 'id': `BogusFolder${item.id}` }); template.attr({ 'tagid': item.id, 'id': `BogusFolder${item.id}` });
template.find('.avatar').css({'background-color': item.color, 'color': item.color2 }); template.find('.avatar').css({ 'background-color': item.color, 'color': item.color2 });
template.find('.ch_name').text(item.name); template.find('.ch_name').text(item.name);
template.find('.bogus_folder_counter').text(count); template.find('.bogus_folder_counter').text(count);
return template; return template;
} }
function getBackBlock() {
const template = $('#bogus_folder_back_template .bogus_folder_select').clone();
return template;
}
function getEmptyBlock() { function getEmptyBlock() {
const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog']; const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog'];
const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it']; const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it'];
@ -1060,10 +1072,9 @@ async function printCharacters(fullRefresh = false) {
saveCharactersPage = 0; saveCharactersPage = 0;
printTagFilters(tag_filter_types.character); printTagFilters(tag_filter_types.character);
printTagFilters(tag_filter_types.group_member); printTagFilters(tag_filter_types.group_member);
const isBogusFolderOpen = !!entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.bogus;
// Return to main list // Return to main list
if (isBogusFolderOpen) { if (isBogusFolderOpen()) {
entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [] }); entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [] });
} }
@ -1072,8 +1083,11 @@ async function printCharacters(fullRefresh = false) {
} }
const storageKey = 'Characters_PerPage'; const storageKey = 'Characters_PerPage';
const listId = '#rm_print_characters_block';
const entities = getEntitiesList({ doFilter: true });
$("#rm_print_characters_pagination").pagination({ $("#rm_print_characters_pagination").pagination({
dataSource: getEntitiesList({ doFilter: true }), dataSource: entities,
pageSize: Number(localStorage.getItem(storageKey)) || per_page_default, pageSize: Number(localStorage.getItem(storageKey)) || per_page_default,
sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000], sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],
pageRange: 1, pageRange: 1,
@ -1086,20 +1100,25 @@ async function printCharacters(fullRefresh = false) {
formatNavigator: PAGINATION_TEMPLATE, formatNavigator: PAGINATION_TEMPLATE,
showNavigator: true, showNavigator: true,
callback: function (data) { callback: function (data) {
$("#rm_print_characters_block").empty(); $(listId).empty();
for (const i of data) { if (isBogusFolderOpen()) {
if (i.type === 'character') { $(listId).append(getBackBlock());
$("#rm_print_characters_block").append(getCharacterBlock(i.item, i.id));
}
if (i.type === 'group') {
$("#rm_print_characters_block").append(getGroupBlock(i.item));
}
if (i.type === 'tag') {
$("#rm_print_characters_block").append(getTagBlock(i.item));
}
} }
if (!data.length) { if (!data.length) {
$("#rm_print_characters_block").append(getEmptyBlock()); $(listId).append(getEmptyBlock());
}
for (const i of data) {
switch (i.type) {
case 'character':
$(listId).append(getCharacterBlock(i.item, i.id));
break;
case 'group':
$(listId).append(getGroupBlock(i.item));
break;
case 'tag':
$(listId).append(getTagBlock(i.item, entities));
break;
}
} }
eventSource.emit(event_types.CHARACTER_PAGE_LOADED); eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
}, },
@ -1110,26 +1129,60 @@ async function printCharacters(fullRefresh = false) {
saveCharactersPage = e; saveCharactersPage = e;
}, },
afterRender: function () { afterRender: function () {
$('#rm_print_characters_block').scrollTop(0); $(listId).scrollTop(0);
}, },
}); });
favsToHotswap(); favsToHotswap();
} }
export function getEntitiesList({ doFilter } = {}) { /**
let entities = []; * Indicates whether a user is currently in a bogus folder.
entities.push(...characters.map((item, index) => ({ item, id: index, type: 'character' }))); * @returns {boolean} If currently viewing a folder
entities.push(...groups.map((item) => ({ item, id: item.id, type: 'group' }))); */
function isBogusFolderOpen() {
return !!entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.bogus;
}
if (power_user.bogus_folders) { export function getEntitiesList({ doFilter } = {}) {
entities.push(...tags.map((item) => ({ item, id: item.id, type: 'tag' }))); function characterToEntity(character, id) {
return { item: character, id, type: 'character' };
} }
function groupToEntity(group) {
return { item: group, id: group.id, type: 'group' };
}
function tagToEntity(tag) {
return { item: structuredClone(tag), id: tag.id, type: 'tag' };
}
let entities = [
...characters.map((item, index) => characterToEntity(item, index)),
...groups.map(item => groupToEntity(item)),
...(power_user.bogus_folders ? tags.map(item => tagToEntity(item)) : []),
];
if (doFilter) { if (doFilter) {
entities = entitiesFilter.applyFilters(entities); entities = entitiesFilter.applyFilters(entities);
} }
if (isBogusFolderOpen()) {
// Get tags of entities within the bogus folder
const filterData = structuredClone(entitiesFilter.getFilterData(FILTER_TYPES.TAG));
entities = entities.filter(x => x.type !== 'tag');
const otherTags = tags.filter(x => !filterData.selected.includes(x.id));
const bogusTags = [];
for (const entity of entities) {
for (const tag of otherTags) {
if (!bogusTags.includes(tag) && entitiesFilter.isElementTagged(entity, tag.id)) {
bogusTags.push(tag);
}
}
}
entities.push(...bogusTags.map(item => tagToEntity(item)));
}
sortEntitiesList(entities); sortEntitiesList(entities);
return entities; return entities;
} }
@ -7523,8 +7576,25 @@ jQuery(async function () {
$(document).on("click", ".bogus_folder_select", function () { $(document).on("click", ".bogus_folder_select", function () {
const tagId = $(this).attr('tagid'); const tagId = $(this).attr('tagid');
console.log('Bogus folder clicked', tagId); console.log('Bogus folder clicked', tagId);
entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [tagId], bogus: true, });
}) const filterData = structuredClone(entitiesFilter.getFilterData(FILTER_TYPES.TAG));
if (!Array.isArray(filterData.selected)) {
filterData.selected = [];
filterData.excluded = [];
filterData.bogus = false;
}
if (tagId === 'back') {
filterData.selected.pop();
filterData.bogus = filterData.selected.length > 0;
} else {
filterData.selected.push(tagId);
filterData.bogus = true;
}
entitiesFilter.setFilterData(FILTER_TYPES.TAG, filterData);
});
$(document).on("input", ".edit_textarea", function () { $(document).on("input", ".edit_textarea", function () {
scroll_holder = $("#chat").scrollTop(); scroll_holder = $("#chat").scrollTop();

View File

@ -69,6 +69,20 @@ export class FilterHelper {
return data.filter(entity => fuzzySearchResults.includes(entity.uid)); return data.filter(entity => fuzzySearchResults.includes(entity.uid));
} }
/**
* Checks if the given entity is tagged with the given tag ID.
* @param {object} entity Searchable entity
* @param {string} tagId Tag ID to check
* @returns {boolean} Whether the entity is tagged with the given tag ID
*/
isElementTagged(entity, tagId) {
const isCharacter = entity.type === 'character';
const lookupValue = isCharacter ? entity.item.avatar : String(entity.id);
const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
return isTagged;
}
/** /**
* Applies a tag filter to the data. * Applies a tag filter to the data.
* @param {any[]} data The data to filter. * @param {any[]} data The data to filter.
@ -82,19 +96,12 @@ export class FilterHelper {
return data; return data;
} }
function isElementTagged(entity, tagId) { const getIsTagged = (entity) => {
const isCharacter = entity.type === 'character'; const tagFlags = selected.map(tagId => this.isElementTagged(entity, tagId));
const lookupValue = isCharacter ? entity.item.avatar : String(entity.id);
const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
return isTagged;
}
function getIsTagged(entity) {
const tagFlags = selected.map(tagId => isElementTagged(entity, tagId));
const trueFlags = tagFlags.filter(x => x); const trueFlags = tagFlags.filter(x => x);
const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0; const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0;
const excludedTagFlags = excluded.map(tagId => isElementTagged(entity, tagId)); const excludedTagFlags = excluded.map(tagId => this.isElementTagged(entity, tagId));
const isExcluded = excludedTagFlags.includes(true); const isExcluded = excludedTagFlags.includes(true);
if (isExcluded) { if (isExcluded) {

View File

@ -1649,7 +1649,17 @@ function sortEntitiesList(entities) {
return; return;
} }
entities.sort((a, b) => sortFunc(a.item, b.item)); entities.sort((a, b) => {
if (a.type === 'tag' && b.type !== 'tag') {
return -1;
}
if (a.type !== 'tag' && b.type === 'tag') {
return 1;
}
return sortFunc(a.item, b.item);
});
} }
async function saveTheme() { async function saveTheme() {

View File

@ -38,14 +38,13 @@ export const tag_filter_types = {
}; };
const ACTIONABLE_TAGS = { const ACTIONABLE_TAGS = {
FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star', class: 'filterByFavorites' }, FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star', class: 'filterByFavorites' },
GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users', class: 'filterByGroups' }, GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users', class: 'filterByGroups' },
VIEW: { id: 2, name: 'Manage tags', color: 'rgba(150, 100, 100, 0.5)', action: onViewTagsListClick, icon: 'fa-solid fa-gear', class: 'manageTags' },
HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags', class: 'showTagList' }, HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags', class: 'showTagList' },
} }
const InListActionable = { const InListActionable = {
VIEW: { id: 2, name: 'Manage tags', color: 'rgba(150, 100, 100, 0.5)', action: onViewTagsListClick, icon: 'fa-solid fa-gear' },
} }
const DEFAULT_TAGS = [ const DEFAULT_TAGS = [
@ -321,9 +320,9 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
tagElement.on('click', () => action.bind(tagElement)(filter)); tagElement.on('click', () => action.bind(tagElement)(filter));
tagElement.addClass('actionable'); tagElement.addClass('actionable');
} }
if (action && tag.id === 2) { /*if (action && tag.id === 2) {
tagElement.addClass('innerActionable hidden'); tagElement.addClass('innerActionable hidden');
} }*/
$(listElement).append(tagElement); $(listElement).append(tagElement);
} }