mirror of
https://github.com/koloml/philomena-tagging-assistant.git
synced 2026-09-23 04:19:39 +00:00
Merge pull request #69 from koloml/feature/tag-groups
Added new Tag Groups feature with ability to customize colors of any tag with specific category
This commit is contained in:
115
src/lib/extension/CustomCategoriesResolver.ts
Normal file
115
src/lib/extension/CustomCategoriesResolver.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type {TagDropdownWrapper} from "$lib/components/TagDropdownWrapper";
|
||||
import TagGroup from "$entities/TagGroup.ts";
|
||||
import {escapeRegExp} from "$lib/utils";
|
||||
|
||||
export default class CustomCategoriesResolver {
|
||||
#tagCategories = new Map<string, string>();
|
||||
#compiledRegExps = new Map<RegExp, string>();
|
||||
#tagDropdowns: TagDropdownWrapper[] = [];
|
||||
#lastProcessedIndex = -1;
|
||||
#nextQueuedUpdate = -1;
|
||||
|
||||
constructor() {
|
||||
TagGroup.subscribe(this.#onTagGroupsReceived.bind(this));
|
||||
TagGroup.readAll().then(this.#onTagGroupsReceived.bind(this));
|
||||
}
|
||||
|
||||
public addElement(tagDropdown: TagDropdownWrapper): void {
|
||||
this.#tagDropdowns.push(tagDropdown);
|
||||
|
||||
if (!this.#tagCategories.size && !this.#compiledRegExps.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#queueUpdatingTags();
|
||||
}
|
||||
|
||||
#queueUpdatingTags() {
|
||||
clearTimeout(this.#nextQueuedUpdate);
|
||||
|
||||
this.#nextQueuedUpdate = setTimeout(
|
||||
this.#updateUnprocessedTags.bind(this),
|
||||
CustomCategoriesResolver.#unprocessedTagsTimeout
|
||||
);
|
||||
}
|
||||
|
||||
#updateUnprocessedTags() {
|
||||
const startIndex = Math.max(0, this.#lastProcessedIndex);
|
||||
|
||||
this.#tagDropdowns
|
||||
.slice(startIndex)
|
||||
.filter(CustomCategoriesResolver.#skipTagsWithOriginalCategory)
|
||||
.filter(this.#applyCustomCategoryForExactMatches.bind(this))
|
||||
.filter(this.#matchCustomCategoryByRegExp.bind(this))
|
||||
.forEach(CustomCategoriesResolver.#resetToOriginalCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply custom categories for the exact tag names.
|
||||
* @param tagDropdown Element to try applying the category for.
|
||||
* @return {boolean} Will return false when tag is processed and true when it is not found.
|
||||
* @private
|
||||
*/
|
||||
#applyCustomCategoryForExactMatches(tagDropdown: TagDropdownWrapper): boolean {
|
||||
const tagName = tagDropdown.tagName!;
|
||||
|
||||
if (!this.#tagCategories.has(tagName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
tagDropdown.tagCategory = this.#tagCategories.get(tagName)!;
|
||||
return false;
|
||||
}
|
||||
|
||||
#matchCustomCategoryByRegExp(tagDropdown: TagDropdownWrapper) {
|
||||
const tagName = tagDropdown.tagName!;
|
||||
|
||||
for (const targetRegularExpression of this.#compiledRegExps.keys()) {
|
||||
if (!targetRegularExpression.test(tagName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tagDropdown.tagCategory = this.#compiledRegExps.get(targetRegularExpression)!;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#onTagGroupsReceived(tagGroups: TagGroup[]) {
|
||||
this.#tagCategories.clear();
|
||||
this.#compiledRegExps.clear();
|
||||
this.#lastProcessedIndex = -1;
|
||||
|
||||
if (!tagGroups.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const tagGroup of tagGroups) {
|
||||
const categoryName = tagGroup.settings.category;
|
||||
|
||||
for (const tagName of tagGroup.settings.tags) {
|
||||
this.#tagCategories.set(tagName, categoryName);
|
||||
}
|
||||
|
||||
for (const tagPrefix of tagGroup.settings.prefixes) {
|
||||
this.#compiledRegExps.set(
|
||||
new RegExp(`^${escapeRegExp(tagPrefix)}`),
|
||||
categoryName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.#queueUpdatingTags();
|
||||
}
|
||||
|
||||
static #skipTagsWithOriginalCategory(tagDropdown: TagDropdownWrapper): boolean {
|
||||
return !tagDropdown.originalCategory;
|
||||
}
|
||||
|
||||
static #resetToOriginalCategory(tagDropdown: TagDropdownWrapper): void {
|
||||
tagDropdown.tagCategory = tagDropdown.originalCategory;
|
||||
}
|
||||
|
||||
static #unprocessedTagsTimeout = 0;
|
||||
}
|
||||
21
src/lib/extension/entities/TagGroup.ts
Normal file
21
src/lib/extension/entities/TagGroup.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import StorageEntity from "$lib/extension/base/StorageEntity.ts";
|
||||
|
||||
export interface TagGroupSettings {
|
||||
name: string;
|
||||
tags: string[];
|
||||
prefixes: string[];
|
||||
category: string;
|
||||
}
|
||||
|
||||
export default class TagGroup extends StorageEntity<TagGroupSettings> {
|
||||
constructor(id: string, settings: Partial<TagGroupSettings>) {
|
||||
super(id, {
|
||||
name: settings.name || '',
|
||||
tags: settings.tags || [],
|
||||
prefixes: settings.prefixes || [],
|
||||
category: settings.category || ''
|
||||
});
|
||||
}
|
||||
|
||||
static _entityName = 'groups';
|
||||
}
|
||||
@@ -13,6 +13,15 @@ const entitiesExporters: ExportersMap = {
|
||||
tags: entity.settings.tags,
|
||||
}
|
||||
},
|
||||
groups: entity => {
|
||||
return {
|
||||
v: 1,
|
||||
id: entity.id,
|
||||
name: entity.settings.name,
|
||||
tags: entity.settings.tags,
|
||||
prefixes: entity.settings.prefixes,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function exportEntityToObject(entityInstance: StorageEntity<any>, entityName: string): Record<string, any> {
|
||||
|
||||
Reference in New Issue
Block a user