/** * External dependencies */ import { subscribe } from '@wordpress/data'; import { getPath, getQueryArg } from '@wordpress/url'; import { isNumber } from '@woocommerce/types'; interface TemplateChangeDetectorSubject { add( observer: TemplateChangeDetectorObserver ): void; getPreviousTemplateId(): string | undefined; getCurrentTemplateId(): string | undefined; notify(): void; } export interface TemplateChangeDetectorObserver { run( subject: TemplateChangeDetectorSubject ): void; } /** * This class implements the TemplateChangeDetectorSubject interface and is responsible for detecting changes in the * current template or page and notifying any observers of these changes. It maintains a list of observers and provides methods * to add observers and notify them of changes. * * The class also provides methods to get the previous and current template IDs and whether the editor is in a post or page. * * The `checkIfTemplateHasChangedAndNotifySubscribers` method is the main method of the class. It checks if the current * template has changed and, if so, notifies all observers. */ export class TemplateChangeDetector implements TemplateChangeDetectorSubject { private previousTemplateId: string | undefined; private currentTemplateId: string | undefined; private isPostOrPage: boolean; private observers: TemplateChangeDetectorObserver[] = []; constructor() { this.isPostOrPage = false; subscribe( () => { this.checkIfTemplateHasChangedAndNotifySubscribers(); }, 'core/edit-site' ); } public add( observer: TemplateChangeDetectorObserver ): void { this.observers.push( observer ); } /** * Trigger an update in each subscriber. */ public notify(): void { for ( const observer of this.observers ) { observer.run( this ); } } public getPreviousTemplateId() { return this.previousTemplateId; } public getCurrentTemplateId() { return this.currentTemplateId; } public getIsPostOrPage() { return this.isPostOrPage; } /** * Parses the template ID. * * This method takes a template or a post ID and returns it parsed in the expected format. * * @param {string | number | undefined} templateId - The template ID to parse. * @return {string | undefined} The parsed template ID. */ private parseTemplateId( templateId: string | number | undefined ): string | undefined { if ( isNumber( templateId ) ) { return String( templateId ); } return templateId?.split( '//' )[ 1 ]; } private getCurrentTemplateIdFromUrl( url: string ): string | undefined { const path = getPath( url ); const isTemplatePage = path ? path.includes( 'site-editor.php' ) : false; let templateId; if ( isTemplatePage ) { templateId = getQueryArg( url, 'postId' ); } return templateId as string; } /** * Checks if the current template or page has changed and notifies subscribers. * * If the current template ID has changed and is not undefined (which means that it is not a page, post or template), it notifies all subscribers. */ public checkIfTemplateHasChangedAndNotifySubscribers(): void { this.previousTemplateId = this.currentTemplateId; const templateId = this.getCurrentTemplateIdFromUrl( window.location.href ); this.currentTemplateId = this.parseTemplateId( templateId ); const hasChangedTemplate = this.previousTemplateId !== this.currentTemplateId; const hasTemplateId = Boolean( this.currentTemplateId ); if ( ! hasChangedTemplate || ! hasTemplateId ) { return; } this.notify(); } }