import LayerConfig from "./ThemeConfig/LayerConfig" import { UIEventSource } from "../Logic/UIEventSource" import UserRelatedState from "../Logic/State/UserRelatedState" import { Utils } from "../Utils" import Zoomcontrol from "../UI/Zoomcontrol" import { LocalStorageSource } from "../Logic/Web/LocalStorageSource" import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider" export type PageType = (typeof MenuState.pageNames)[number] /** * Indicates if a menu is open, and if so, which tab is selected; * Some tabs allow to highlight an element. * * Some convenience methods are provided for this as well */ export class MenuState { public static readonly pageNames = [ "copyright", "copyright_icons", "community_index", "hotkeys", "privacy", "filter", "background", "about_theme", "download", "favourites", "usersettings", "share", "menu", ] as const /** * Contains the 'providedImage' which is currently displayed on top of the UI * This object merely acts as lock or as means to signal the need to close */ public static readonly previewedImage: UIEventSource = new UIEventSource( undefined ) public static readonly nearbyImagesFeature: UIEventSource = new UIEventSource( undefined ) public readonly pageStates: Record> public readonly highlightedLayerInFilters: UIEventSource = new UIEventSource( undefined ) public highlightedUserSetting: UIEventSource = new UIEventSource(undefined) private readonly _selectedElement: UIEventSource | undefined private isClosingAll = false constructor(selectedElement: UIEventSource | undefined) { this._selectedElement = selectedElement // Note: this class is _not_ responsible to update the Hash, @see ThemeViewStateHashActor for this const states = {} for (const pageName of MenuState.pageNames) { const toggle = new UIEventSource(false) states[pageName] = toggle } this.pageStates = >>states for (const pageName of MenuState.pageNames) { if (pageName === "menu") { continue } this.pageStates[pageName].addCallback((enabled) => { if (enabled) { this.pageStates.menu.set(false) } }) } } public openMenuIfNeeded(shouldShowWelcomeMessage: boolean, themeid: string) { const visitedBefore = LocalStorageSource.getParsed( themeid + "thememenuisopened", false ) if (!visitedBefore.data && shouldShowWelcomeMessage) { this.pageStates.about_theme.set(true) visitedBefore.set(true) } } private resetZoomIfAllClosed() { if (this.isSomethingOpen()) { return } Zoomcontrol.resetzoom() } public openFilterView(highlightLayer?: LayerConfig | string) { this.pageStates.filter.setData(true) if (highlightLayer) { if (typeof highlightLayer !== "string") { highlightLayer = highlightLayer.id } this.highlightedLayerInFilters.setData(highlightLayer) } } public openUsersettings(highlightTagRendering?: string) { if ( highlightTagRendering !== undefined && !UserRelatedState.availableUserSettingsIds.some((tr) => tr === highlightTagRendering) ) { console.error( "No tagRendering with id '" + highlightTagRendering + "'; maybe you meant:", Utils.sortedByLevenshteinDistance( highlightTagRendering, UserRelatedState.availableUserSettingsIds, (x) => x ) ) } this.highlightedUserSetting.setData(highlightTagRendering) this.pageStates.usersettings.set(true) } public isSomethingOpen(): boolean { if (MenuState.previewedImage.data !== undefined) { return true } if (this._selectedElement?.data) { return true } return Object.values(this.pageStates).some((t) => t.data) } /** * Close all floatOvers. * Returns 'true' if at least one menu was opened */ public closeAll(): boolean { if (this.isClosingAll) { return true } this.isClosingAll = true const ps = this.pageStates try { if (ps.menu.data) { ps.menu.set(false) return true } if (MenuState.previewedImage.data !== undefined) { MenuState.previewedImage.setData(undefined) return true } if (MenuState.nearbyImagesFeature.data !== undefined) { MenuState.nearbyImagesFeature.setData(undefined) return true } for (const key in ps) { const toggle = ps[key] const wasOpen = toggle.data toggle.setData(false) if (wasOpen) { return true } } if (this._selectedElement.data) { this._selectedElement.setData(undefined) return true } } finally { this.isClosingAll = false } } public setPreviewedImage(img?: Partial) { if (img === undefined && !this.isClosingAll) { return } MenuState.previewedImage.setData(img) } }