2023-06-14 20:39:36 +02:00
|
|
|
import Hash from "./Hash"
|
2023-08-10 15:37:44 +02:00
|
|
|
import { MenuState } from "../../Models/MenuState"
|
2025-01-12 01:53:58 +01:00
|
|
|
import { AndroidPolyfill } from "./AndroidPolyfill"
|
2025-01-23 05:01:55 +01:00
|
|
|
import { IndexedFeatureSource } from "../FeatureSource/FeatureSource"
|
|
|
|
import { Feature } from "geojson"
|
2025-01-23 15:13:00 +01:00
|
|
|
import { Store, UIEventSource } from "../UIEventSource"
|
2023-06-07 02:42:49 +02:00
|
|
|
|
2023-06-07 18:04:25 +02:00
|
|
|
export default class ThemeViewStateHashActor {
|
2025-01-23 05:01:55 +01:00
|
|
|
private readonly _state: {
|
2025-01-28 15:42:34 +01:00
|
|
|
indexedFeatures: IndexedFeatureSource
|
|
|
|
selectedElement: UIEventSource<Feature>
|
2025-01-23 12:30:42 +01:00
|
|
|
guistate: MenuState
|
2025-01-23 05:01:55 +01:00
|
|
|
}
|
2024-08-29 03:53:54 +02:00
|
|
|
private isUpdatingHash = false
|
2023-06-07 02:42:49 +02:00
|
|
|
|
2023-08-10 15:37:44 +02:00
|
|
|
public static readonly documentation = [
|
|
|
|
"The URL-hash can contain multiple values:",
|
|
|
|
"",
|
|
|
|
"- The id of the currently selected object, e.g. `node/1234`",
|
|
|
|
"- The currently opened menu view",
|
|
|
|
"",
|
|
|
|
"### Possible hashes to open a menu",
|
|
|
|
"",
|
|
|
|
"The possible hashes are:",
|
|
|
|
"",
|
2025-01-28 15:42:34 +01:00
|
|
|
MenuState.pageNames.map((tab) => "`" + tab + "`").join(","),
|
2023-08-10 15:37:44 +02:00
|
|
|
]
|
|
|
|
|
2023-06-07 02:42:49 +02:00
|
|
|
/**
|
2024-08-29 02:46:51 +02:00
|
|
|
* Converts the hash to the appropriate theme-view state and, vice versa, sets the hash.
|
2023-06-07 18:04:25 +02:00
|
|
|
*
|
2025-01-12 01:53:58 +01:00
|
|
|
* As the navigator-back-button changes the hash first, this class thus also handles the (browser) 'back'-button events.
|
2023-06-07 02:42:49 +02:00
|
|
|
*
|
|
|
|
* Note that there is no "real" way to intercept the back button, we can only detect the removal of the hash.
|
|
|
|
* As such, we use a change in the hash to close the appropriate windows
|
|
|
|
*
|
|
|
|
*/
|
2025-01-23 05:01:55 +01:00
|
|
|
constructor(state: {
|
2025-02-10 02:04:58 +01:00
|
|
|
featureSwitches: { featureSwitchBackToThemeOverview: Store<boolean> }
|
|
|
|
indexedFeatures: IndexedFeatureSource
|
|
|
|
selectedElement: UIEventSource<Feature>
|
|
|
|
guistate: MenuState
|
2025-01-23 05:01:55 +01:00
|
|
|
}) {
|
2023-06-14 20:39:36 +02:00
|
|
|
this._state = state
|
2025-01-12 01:53:58 +01:00
|
|
|
AndroidPolyfill.onBackButton(() => this.back(), {
|
2025-02-10 02:04:58 +01:00
|
|
|
returnToIndex: state.featureSwitches.featureSwitchBackToThemeOverview,
|
2025-01-12 01:53:58 +01:00
|
|
|
})
|
2023-06-07 02:42:49 +02:00
|
|
|
|
2024-08-29 02:46:51 +02:00
|
|
|
const hashOnLoad = Hash.hash.data
|
|
|
|
const containsMenu = this.loadStateFromHash(hashOnLoad)
|
2023-06-07 18:04:25 +02:00
|
|
|
// First of all, try to recover the selected element
|
2024-08-29 12:28:59 +02:00
|
|
|
if (!containsMenu && hashOnLoad?.length > 0) {
|
2024-08-29 02:46:51 +02:00
|
|
|
state.indexedFeatures.featuresById.addCallbackAndRunD(() => {
|
2023-06-07 18:04:25 +02:00
|
|
|
// once that we have found a matching element, we can be sure the indexedFeaturesource was popuplated and that the job is done
|
2024-08-29 02:46:51 +02:00
|
|
|
return this.loadSelectedElementFromHash(hashOnLoad)
|
2023-06-07 18:04:25 +02:00
|
|
|
})
|
|
|
|
}
|
2023-06-07 02:42:49 +02:00
|
|
|
|
2024-08-29 02:46:51 +02:00
|
|
|
// At last, register callbacks on the state to update the hash when they change.
|
|
|
|
// Note: these should use 'addCallback', not 'addCallbackAndRun'
|
|
|
|
state.selectedElement.addCallback(() => this.setHash())
|
|
|
|
|
2023-06-07 18:04:25 +02:00
|
|
|
// Register a hash change listener to correctly handle the back button
|
2023-06-14 20:39:36 +02:00
|
|
|
Hash.hash.addCallback((hash) => {
|
2024-09-02 12:48:15 +02:00
|
|
|
if (this.isUpdatingHash) {
|
2024-08-29 03:53:54 +02:00
|
|
|
return
|
|
|
|
}
|
2024-08-29 02:46:51 +02:00
|
|
|
if (!hash) {
|
2023-06-07 02:42:49 +02:00
|
|
|
this.back()
|
2024-08-29 03:53:54 +02:00
|
|
|
} else {
|
|
|
|
if (!this.loadStateFromHash(hash)) {
|
2024-08-29 02:46:51 +02:00
|
|
|
this.loadSelectedElementFromHash(hash)
|
|
|
|
}
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-08-29 02:46:51 +02:00
|
|
|
for (const key in state.guistate.pageStates) {
|
|
|
|
const toggle = state.guistate.pageStates[key]
|
|
|
|
toggle.addCallback(() => this.setHash())
|
|
|
|
}
|
2023-06-07 02:42:49 +02:00
|
|
|
|
2023-06-07 18:04:25 +02:00
|
|
|
// When all is done, set the hash. This must happen last to give the code above correct info
|
|
|
|
this.setHash()
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Selects the appropriate element
|
|
|
|
* Returns true if this method can be unregistered for the first run
|
|
|
|
*/
|
|
|
|
private loadSelectedElementFromHash(hash: string): boolean {
|
|
|
|
const state = this._state
|
|
|
|
const selectedElement = state.selectedElement
|
|
|
|
|
|
|
|
// Set the hash based on the selected element...
|
|
|
|
// ... search and select an element based on the hash
|
|
|
|
if (selectedElement.data?.properties?.id === hash) {
|
|
|
|
// We already have the correct hash
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
const found = state.indexedFeatures.featuresById.data?.get(hash)
|
|
|
|
if (!found) {
|
|
|
|
return false
|
|
|
|
}
|
2024-08-29 02:46:51 +02:00
|
|
|
if (found.properties.id.startsWith("last_click")) {
|
2023-06-07 02:42:49 +02:00
|
|
|
return true
|
|
|
|
}
|
2025-05-08 11:44:03 +02:00
|
|
|
console.log(
|
|
|
|
"Setting selected element based on hash",
|
|
|
|
hash,
|
|
|
|
"; found",
|
|
|
|
found,
|
|
|
|
"current:",
|
|
|
|
selectedElement.data?.properties?.id
|
|
|
|
)
|
2023-06-07 02:42:49 +02:00
|
|
|
selectedElement.setData(found)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-08-29 02:46:51 +02:00
|
|
|
private loadStateFromHash(hash: string): boolean {
|
|
|
|
for (const page in this._state.guistate.pageStates) {
|
|
|
|
if (page === hash) {
|
|
|
|
const toggle = this._state.guistate.pageStates[page]
|
|
|
|
toggle.set(true)
|
|
|
|
console.log("Loading menu view from hash:", page)
|
|
|
|
return true
|
2024-08-02 13:33:29 +02:00
|
|
|
}
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
2024-08-29 02:46:51 +02:00
|
|
|
return false
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
|
|
|
|
2024-08-29 02:46:51 +02:00
|
|
|
/**
|
|
|
|
* Sets the hash based on:
|
|
|
|
*
|
|
|
|
* 1. Selected element ID
|
|
|
|
* 2. A selected 'page' from the menu
|
|
|
|
*
|
|
|
|
*/
|
2024-09-02 03:38:37 +02:00
|
|
|
private setHash(): void {
|
|
|
|
// Important ! This function is called by 'addCallback' and might thus never return 'true' or it will be unregistered
|
2024-08-29 03:53:54 +02:00
|
|
|
this.isUpdatingHash = true
|
|
|
|
try {
|
|
|
|
const selectedElement = this._state.selectedElement.data
|
|
|
|
if (selectedElement) {
|
|
|
|
Hash.hash.set(selectedElement.properties.id)
|
2024-09-02 03:38:37 +02:00
|
|
|
return
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
2024-08-29 03:53:54 +02:00
|
|
|
for (const page in this._state.guistate.pageStates) {
|
|
|
|
const toggle = this._state.guistate.pageStates[page]
|
|
|
|
if (toggle.data) {
|
|
|
|
Hash.hash.set(page)
|
2024-09-02 03:38:37 +02:00
|
|
|
return
|
2024-08-29 03:53:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Hash.hash.set(undefined)
|
2024-09-02 03:38:37 +02:00
|
|
|
return
|
2024-08-29 03:53:54 +02:00
|
|
|
} finally {
|
|
|
|
this.isUpdatingHash = false
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-12 01:53:58 +01:00
|
|
|
/**
|
|
|
|
* Returns 'true' if an action was taken
|
|
|
|
* @private
|
|
|
|
*/
|
2025-01-23 13:41:06 +01:00
|
|
|
private back(): boolean {
|
2023-06-07 02:42:49 +02:00
|
|
|
const state = this._state
|
2025-01-23 13:41:06 +01:00
|
|
|
return state.guistate.closeAll()
|
2023-06-07 02:42:49 +02:00
|
|
|
}
|
|
|
|
}
|