mirror of
https://github.com/koloml/furbooru-tagging-assistant.git
synced 2025-12-23 23:02:58 +00:00
Implementing the unified functions for custom events with types
This commit is contained in:
@@ -4,6 +4,12 @@ import {BaseComponent} from "$lib/components/base/BaseComponent.js";
|
||||
import {getComponent} from "$lib/components/base/ComponentUtils.js";
|
||||
import ScrapedAPI from "$lib/booru/scraped/ScrapedAPI.js";
|
||||
import {tagsBlacklist} from "$config/tags.ts";
|
||||
import {emitterAt} from "$lib/components/events/comms";
|
||||
import {
|
||||
eventActiveProfileChanged,
|
||||
eventMaintenanceStateChanged,
|
||||
eventTagsUpdated
|
||||
} from "$lib/components/events/maintenance-popup-events";
|
||||
|
||||
class BlackListedTagsEncounteredError extends Error {
|
||||
/**
|
||||
@@ -45,6 +51,8 @@ export class MaintenancePopup extends BaseComponent {
|
||||
/** @type {number|null} */
|
||||
#tagsSubmissionTimer = null;
|
||||
|
||||
#emitter = emitterAt(this);
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
@@ -95,7 +103,8 @@ export class MaintenancePopup extends BaseComponent {
|
||||
this.#activeProfile = activeProfile;
|
||||
this.container.classList.toggle('is-active', activeProfile !== null);
|
||||
this.#refreshTagsList();
|
||||
this.emit('active-profile-changed', activeProfile);
|
||||
|
||||
this.#emitter.emit(eventActiveProfileChanged, activeProfile);
|
||||
}
|
||||
|
||||
#refreshTagsList() {
|
||||
@@ -181,7 +190,7 @@ export class MaintenancePopup extends BaseComponent {
|
||||
}
|
||||
|
||||
this.#isPlanningToSubmit = true;
|
||||
this.emit('maintenance-state-change', 'waiting');
|
||||
this.#emitter.emit(eventMaintenanceStateChanged, 'waiting');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +217,7 @@ export class MaintenancePopup extends BaseComponent {
|
||||
this.#isPlanningToSubmit = false;
|
||||
this.#isSubmitting = true;
|
||||
|
||||
this.emit('maintenance-state-change', 'processing');
|
||||
this.#emitter.emit(eventMaintenanceStateChanged, 'processing');
|
||||
|
||||
let maybeTagsAndAliasesAfterUpdate;
|
||||
|
||||
@@ -249,17 +258,18 @@ export class MaintenancePopup extends BaseComponent {
|
||||
}
|
||||
|
||||
MaintenancePopup.#notifyAboutPendingSubmission(false);
|
||||
this.emit('maintenance-state-change', 'failed');
|
||||
|
||||
this.#emitter.emit(eventMaintenanceStateChanged, 'failed');
|
||||
this.#isSubmitting = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeTagsAndAliasesAfterUpdate) {
|
||||
this.emit('tags-updated', maybeTagsAndAliasesAfterUpdate);
|
||||
this.#emitter.emit(eventTagsUpdated, maybeTagsAndAliasesAfterUpdate);
|
||||
}
|
||||
|
||||
this.emit('maintenance-state-change', 'complete');
|
||||
this.#emitter.emit(eventMaintenanceStateChanged, 'complete');
|
||||
|
||||
this.#tagsToAdd.clear();
|
||||
this.#tagsToRemove.clear();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {BaseComponent} from "$lib/components/base/BaseComponent.js";
|
||||
import {getComponent} from "$lib/components/base/ComponentUtils.js";
|
||||
import {on} from "$lib/components/events/comms";
|
||||
import {eventMaintenanceStateChanged} from "$lib/components/events/maintenance-popup-events";
|
||||
|
||||
export class MaintenanceStatusIcon extends BaseComponent {
|
||||
/** @type {import('MediaBoxTools.js').MediaBoxTools} */
|
||||
@@ -16,7 +18,7 @@ export class MaintenanceStatusIcon extends BaseComponent {
|
||||
throw new Error('Status icon element initialized outside of the media box!');
|
||||
}
|
||||
|
||||
this.#mediaBoxTools.on('maintenance-state-change', this.#onMaintenanceStateChanged.bind(this));
|
||||
on(this.#mediaBoxTools, eventMaintenanceStateChanged, this.#onMaintenanceStateChanged.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {BaseComponent} from "$lib/components/base/BaseComponent.js";
|
||||
import {getComponent} from "$lib/components/base/ComponentUtils.js";
|
||||
import {MaintenancePopup} from "$lib/components/MaintenancePopup.js";
|
||||
import {on} from "$lib/components/events/comms";
|
||||
import {eventActiveProfileChanged} from "$lib/components/events/maintenance-popup-events";
|
||||
|
||||
export class MediaBoxTools extends BaseComponent {
|
||||
/** @type {import('MediaBoxWrapper.js').MediaBoxWrapper|null} */
|
||||
@@ -34,11 +36,11 @@ export class MediaBoxTools extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
this.on('active-profile-changed', this.#onActiveProfileChanged.bind(this));
|
||||
on(this, eventActiveProfileChanged, this.#onActiveProfileChanged.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CustomEvent<MaintenanceProfile|null>} profileChangedEvent
|
||||
* @param {CustomEvent<import('$entities/MaintenanceProfile.js').default|null>} profileChangedEvent
|
||||
*/
|
||||
#onActiveProfileChanged(profileChangedEvent) {
|
||||
this.container.classList.toggle('has-active-profile', profileChangedEvent.detail !== null);
|
||||
@@ -61,7 +63,7 @@ export class MediaBoxTools extends BaseComponent {
|
||||
|
||||
/**
|
||||
* Create a maintenance popup element.
|
||||
* @param {HTMLElement} childrenElements List of children elements to append to the component.
|
||||
* @param {HTMLElement[]} childrenElements List of children elements to append to the component.
|
||||
* @return {HTMLElement} The maintenance popup element.
|
||||
*/
|
||||
export function createMediaBoxTools(...childrenElements) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {BaseComponent} from "$lib/components/base/BaseComponent.js";
|
||||
import {getComponent} from "$lib/components/base/ComponentUtils.js";
|
||||
import {buildTagsAndAliasesMap} from "$lib/booru/TagsUtils.js";
|
||||
import {on} from "$lib/components/events/comms";
|
||||
import {eventTagsUpdated} from "$lib/components/events/maintenance-popup-events";
|
||||
|
||||
export class MediaBoxWrapper extends BaseComponent {
|
||||
#thumbnailContainer = null;
|
||||
@@ -13,11 +15,11 @@ export class MediaBoxWrapper extends BaseComponent {
|
||||
this.#thumbnailContainer = this.container.querySelector('.image-container');
|
||||
this.#imageLinkElement = this.#thumbnailContainer.querySelector('a');
|
||||
|
||||
this.on('tags-updated', this.#onTagsUpdatedRefreshTagsAndAliases.bind(this));
|
||||
on(this, eventTagsUpdated, this.#onTagsUpdatedRefreshTagsAndAliases.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CustomEvent<Map<string,string>>} tagsUpdatedEvent
|
||||
* @param {CustomEvent<Map<string,string>|null>} tagsUpdatedEvent
|
||||
*/
|
||||
#onTagsUpdatedRefreshTagsAndAliases(tagsUpdatedEvent) {
|
||||
const updatedMap = tagsUpdatedEvent.detail;
|
||||
|
||||
92
src/lib/components/events/comms.ts
Normal file
92
src/lib/components/events/comms.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type {MaintenancePopupEventsMap} from "$lib/components/events/maintenance-popup-events.ts";
|
||||
import {BaseComponent} from "$lib/components/base/BaseComponent";
|
||||
|
||||
interface EventsMapping extends MaintenancePopupEventsMap {
|
||||
}
|
||||
|
||||
type EventCallback<EventDetails> = (event: CustomEvent<EventDetails>) => void;
|
||||
type UnsubscribeFunction = () => void;
|
||||
type ResolvableTarget = EventTarget | BaseComponent;
|
||||
|
||||
function resolveTarget(componentOrElement: ResolvableTarget): EventTarget {
|
||||
if (componentOrElement instanceof BaseComponent) {
|
||||
return componentOrElement.container;
|
||||
}
|
||||
|
||||
return componentOrElement;
|
||||
}
|
||||
|
||||
export function emit<Event extends keyof EventsMapping>(
|
||||
targetOrComponent: ResolvableTarget,
|
||||
event: Event,
|
||||
details: EventsMapping[Event]
|
||||
) {
|
||||
const target = resolveTarget(targetOrComponent);
|
||||
|
||||
target.dispatchEvent(
|
||||
new CustomEvent(event, {
|
||||
detail: details,
|
||||
bubbles: true
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function on<Event extends keyof EventsMapping>(
|
||||
targetOrComponent: ResolvableTarget,
|
||||
eventName: Event,
|
||||
callback: EventCallback<EventsMapping[Event]>,
|
||||
options: AddEventListenerOptions | null = null
|
||||
): UnsubscribeFunction {
|
||||
const target = resolveTarget(targetOrComponent);
|
||||
const controller = new AbortController();
|
||||
|
||||
target.addEventListener(
|
||||
eventName,
|
||||
callback as EventListener,
|
||||
{
|
||||
signal: controller.signal,
|
||||
once: options?.once
|
||||
}
|
||||
);
|
||||
|
||||
return () => controller.abort();
|
||||
}
|
||||
|
||||
const onceOptions = {once: true};
|
||||
|
||||
export function once<Event extends keyof EventsMapping>(
|
||||
targetOrComponent: ResolvableTarget,
|
||||
eventName: Event,
|
||||
callback: EventCallback<EventsMapping[Event]>
|
||||
): UnsubscribeFunction {
|
||||
return on(
|
||||
targetOrComponent,
|
||||
eventName,
|
||||
callback,
|
||||
onceOptions
|
||||
);
|
||||
}
|
||||
|
||||
class TargetedEmitter {
|
||||
readonly #element: ResolvableTarget;
|
||||
|
||||
constructor(targetOrComponent: ResolvableTarget) {
|
||||
this.#element = targetOrComponent;
|
||||
}
|
||||
|
||||
emit<Event extends keyof EventsMapping>(eventName: Event, details: EventsMapping[Event]): void {
|
||||
emit(this.#element, eventName, details);
|
||||
}
|
||||
|
||||
on<Event extends keyof EventsMapping>(eventName: Event, callback: EventCallback<EventsMapping[Event]>, options: AddEventListenerOptions | null = null): UnsubscribeFunction {
|
||||
return on(this.#element, eventName, callback, options);
|
||||
}
|
||||
|
||||
once<Event extends keyof EventsMapping>(eventName: Event, callback: EventCallback<EventsMapping[Event]>): UnsubscribeFunction {
|
||||
return once(this.#element, eventName, callback);
|
||||
}
|
||||
}
|
||||
|
||||
export function emitterAt(targetOrComponent: ResolvableTarget): TargetedEmitter {
|
||||
return new TargetedEmitter(targetOrComponent);
|
||||
}
|
||||
13
src/lib/components/events/maintenance-popup-events.ts
Normal file
13
src/lib/components/events/maintenance-popup-events.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type MaintenanceProfile from "$entities/MaintenanceProfile.ts";
|
||||
|
||||
export const eventActiveProfileChanged = 'active-profile-changed';
|
||||
export const eventMaintenanceStateChanged = 'maintenance-state-change';
|
||||
export const eventTagsUpdated = 'tags-updated';
|
||||
|
||||
type MaintenanceState = 'processing' | 'failed' | 'complete' | 'waiting';
|
||||
|
||||
export interface MaintenancePopupEventsMap {
|
||||
[eventActiveProfileChanged]: MaintenanceProfile | null;
|
||||
[eventMaintenanceStateChanged]: MaintenanceState;
|
||||
[eventTagsUpdated]: Map<string, string> | null;
|
||||
}
|
||||
Reference in New Issue
Block a user