From dfab62599926f8303a1138b0ac40269e7f266784 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sun, 23 Feb 2025 03:38:06 +0400 Subject: [PATCH 1/2] Added event returned from booru on form submissions --- src/lib/components/events/booru-events.ts | 5 +++++ src/lib/components/events/comms.ts | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 src/lib/components/events/booru-events.ts diff --git a/src/lib/components/events/booru-events.ts b/src/lib/components/events/booru-events.ts new file mode 100644 index 0000000..a726d9b --- /dev/null +++ b/src/lib/components/events/booru-events.ts @@ -0,0 +1,5 @@ +export const eventFetchComplete = 'fetchcomplete'; + +export interface BooruEventsMap { + [eventFetchComplete]: null; // Site sends the response, but extension will not get it due to isolation. +} diff --git a/src/lib/components/events/comms.ts b/src/lib/components/events/comms.ts index 30f4961..359e78d 100644 --- a/src/lib/components/events/comms.ts +++ b/src/lib/components/events/comms.ts @@ -1,12 +1,15 @@ import type { MaintenancePopupEventsMap } from "$lib/components/events/maintenance-popup-events"; import { BaseComponent } from "$lib/components/base/BaseComponent"; import type { FullscreenViewerEventsMap } from "$lib/components/events/fullscreen-viewer-events"; +import type { BooruEventsMap } from "$lib/components/events/booru-events"; -interface EventsMapping extends MaintenancePopupEventsMap, FullscreenViewerEventsMap { -} +type EventsMapping = + MaintenancePopupEventsMap + & FullscreenViewerEventsMap + & BooruEventsMap; type EventCallback = (event: CustomEvent) => void; -type UnsubscribeFunction = () => void; +export type UnsubscribeFunction = () => void; type ResolvableTarget = EventTarget | BaseComponent; function resolveTarget(componentOrElement: ResolvableTarget): EventTarget { From 5613c6fdca36f40211172b391f4e1cea46fef877 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Sun, 23 Feb 2025 04:22:29 +0400 Subject: [PATCH 2/2] Wait for new element to be inserted and notify tags dropdown about it --- src/lib/components/TagDropdownWrapper.ts | 11 +++- src/lib/components/TagsForm.ts | 59 +++++++++++++++++++ src/lib/components/events/comms.ts | 4 +- src/lib/components/events/tags-form-events.ts | 5 ++ 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/lib/components/events/tags-form-events.ts diff --git a/src/lib/components/TagDropdownWrapper.ts b/src/lib/components/TagDropdownWrapper.ts index 0f3335c..cca7caa 100644 --- a/src/lib/components/TagDropdownWrapper.ts +++ b/src/lib/components/TagDropdownWrapper.ts @@ -3,6 +3,8 @@ import MaintenanceProfile from "$entities/MaintenanceProfile"; import MaintenanceSettings from "$lib/extension/settings/MaintenanceSettings"; import { getComponent } from "$lib/components/base/component-utils"; import CustomCategoriesResolver from "$lib/extension/CustomCategoriesResolver"; +import { on } from "$lib/components/events/comms"; +import { eventFormEditorUpdated } from "$lib/components/events/tags-form-events"; const categoriesResolver = new CustomCategoriesResolver(); @@ -278,5 +280,12 @@ export function watchTagDropdownsInTagsEditor() { for (const tagDropdownElement of closestTagEditor.querySelectorAll('.tag.dropdown')) { wrapTagDropdown(tagDropdownElement); } - }) + }); + + // When form is submitted, its DOM is completely updated. We need to fetch those tags in this case. + on(document.body, eventFormEditorUpdated, event => { + for (const tagDropdownElement of event.detail.querySelectorAll('.tag.dropdown')) { + wrapTagDropdown(tagDropdownElement); + } + }); } diff --git a/src/lib/components/TagsForm.ts b/src/lib/components/TagsForm.ts index 60aa018..ed6681d 100644 --- a/src/lib/components/TagsForm.ts +++ b/src/lib/components/TagsForm.ts @@ -1,7 +1,66 @@ import { BaseComponent } from "$lib/components/base/BaseComponent"; import { getComponent } from "$lib/components/base/component-utils"; +import { emit, on, type UnsubscribeFunction } from "$lib/components/events/comms"; +import { eventFetchComplete } from "$lib/components/events/booru-events"; +import { eventFormEditorUpdated } from "$lib/components/events/tags-form-events"; export class TagsForm extends BaseComponent { + protected init() { + // Site sending the event when form is submitted vie Fetch API. We use this event to reload our logic here. + const unsubscribe = on( + this.container, + eventFetchComplete, + () => this.#waitAndDetectUpdatedForm(unsubscribe), + ); + } + + #waitAndDetectUpdatedForm(unsubscribe: UnsubscribeFunction): void { + const elementContainingTagEditor = this.container + .closest('#image_tags_and_source') + ?.parentElement; + + if (!elementContainingTagEditor) { + return; + } + + const observer = new MutationObserver(() => { + const tagsFormElement = elementContainingTagEditor.querySelector('#tags-form'); + + if (!tagsFormElement || getComponent(tagsFormElement)) { + return; + } + + const tagFormComponent = new TagsForm(tagsFormElement); + tagFormComponent.initialize(); + + const fullTagEditor = tagFormComponent.parentTagEditorElement; + + if (fullTagEditor) { + emit(document.body, eventFormEditorUpdated, fullTagEditor); + } else { + console.info('Tag form is not in the tag editor. Event is not sent.'); + } + + observer.disconnect(); + unsubscribe(); + }); + + observer.observe(elementContainingTagEditor, { + subtree: true, + childList: true, + }); + + // Make sure to forcibly disconnect everything after a while. + setTimeout(() => { + observer.disconnect(); + unsubscribe(); + }, 5000); + } + + get parentTagEditorElement(): HTMLElement | null { + return this.container.closest('.js-tagsauce') + } + /** * Collect all the tag categories available on the page and color the tags in the editor according to them. */ diff --git a/src/lib/components/events/comms.ts b/src/lib/components/events/comms.ts index 359e78d..3624491 100644 --- a/src/lib/components/events/comms.ts +++ b/src/lib/components/events/comms.ts @@ -2,11 +2,13 @@ import type { MaintenancePopupEventsMap } from "$lib/components/events/maintenan import { BaseComponent } from "$lib/components/base/BaseComponent"; import type { FullscreenViewerEventsMap } from "$lib/components/events/fullscreen-viewer-events"; import type { BooruEventsMap } from "$lib/components/events/booru-events"; +import type { TagsFormEventsMap } from "$lib/components/events/tags-form-events"; type EventsMapping = MaintenancePopupEventsMap & FullscreenViewerEventsMap - & BooruEventsMap; + & BooruEventsMap + & TagsFormEventsMap; type EventCallback = (event: CustomEvent) => void; export type UnsubscribeFunction = () => void; diff --git a/src/lib/components/events/tags-form-events.ts b/src/lib/components/events/tags-form-events.ts new file mode 100644 index 0000000..64461dc --- /dev/null +++ b/src/lib/components/events/tags-form-events.ts @@ -0,0 +1,5 @@ +export const eventFormEditorUpdated = 'tags-form-updated'; + +export interface TagsFormEventsMap { + [eventFormEditorUpdated]: HTMLElement; +}