173 lines
5.8 KiB
TypeScript
173 lines
5.8 KiB
TypeScript
import ThemeViewState from "../../Models/ThemeViewState"
|
|
import Hash from "./Hash"
|
|
|
|
export default class ThemeViewStateHashActor {
|
|
private readonly _state: ThemeViewState
|
|
|
|
/**
|
|
* Converts the hash to the appropriate themeview state and, vice versa, sets the hash.
|
|
*
|
|
* As the navigator-back-button changes the hash first, this class thus also handles the 'back'-button events.
|
|
*
|
|
* 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
|
|
*
|
|
* @param state
|
|
*/
|
|
constructor(state: ThemeViewState) {
|
|
this._state = state
|
|
|
|
// First of all, try to recover the selected element
|
|
if (Hash.hash.data) {
|
|
const hash = Hash.hash.data
|
|
this.loadStateFromHash(hash)
|
|
Hash.hash.setData(hash) // reapply the previous hash
|
|
state.indexedFeatures.featuresById.addCallbackAndRunD((_) => {
|
|
let unregister = this.loadSelectedElementFromHash(hash)
|
|
// once that we have found a matching element, we can be sure the indexedFeaturesource was popuplated and that the job is done
|
|
return unregister
|
|
})
|
|
}
|
|
|
|
// Register a hash change listener to correctly handle the back button
|
|
Hash.hash.addCallback((hash) => {
|
|
if (!!hash) {
|
|
// There is still a hash
|
|
// We _only_ have to (at most) close the overlays in this case
|
|
const parts = hash.split(";")
|
|
if (parts.indexOf("background") < 0) {
|
|
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
|
}
|
|
this.loadSelectedElementFromHash(hash)
|
|
} else {
|
|
this.back()
|
|
}
|
|
})
|
|
|
|
// 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())
|
|
state.guistate.allToggles.forEach(({ toggle, submenu }) => {
|
|
submenu?.addCallback((_) => this.setHash())
|
|
toggle.addCallback((_) => this.setHash())
|
|
})
|
|
|
|
// When all is done, set the hash. This must happen last to give the code above correct info
|
|
this.setHash()
|
|
}
|
|
|
|
/**
|
|
* Selects the appropriate element
|
|
* Returns true if this method can be unregistered for the first run
|
|
* @param hash
|
|
* @private
|
|
*/
|
|
private loadSelectedElementFromHash(hash: string): boolean {
|
|
const state = this._state
|
|
const selectedElement = state.selectedElement
|
|
// state.indexedFeatures.featuresById.stabilized(250)
|
|
|
|
hash = hash.split(";")[0] // The 'selectedElement' is always the _first_ item in the hash (if any)
|
|
|
|
// 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
|
|
}
|
|
if (found.properties.id === "last_click") {
|
|
return true
|
|
}
|
|
const layer = this._state.layout.getMatchingLayer(found.properties)
|
|
console.log(
|
|
"Setting selected element based on hash",
|
|
hash,
|
|
"; found",
|
|
found,
|
|
"got matching layer",
|
|
layer.id,
|
|
""
|
|
)
|
|
selectedElement.setData(found)
|
|
state.selectedLayer.setData(layer)
|
|
return true
|
|
}
|
|
|
|
private loadStateFromHash(hash: string) {
|
|
const state = this._state
|
|
const parts = hash.split(";")
|
|
outer: for (const { toggle, name, showOverOthers, submenu } of state.guistate.allToggles) {
|
|
for (const part of parts) {
|
|
if (part === name) {
|
|
toggle.setData(true)
|
|
continue outer
|
|
}
|
|
if (part.indexOf(":") < 0) {
|
|
continue
|
|
}
|
|
const [main, submenuValue] = part.split(":")
|
|
if (part !== main) {
|
|
continue
|
|
}
|
|
toggle.setData(true)
|
|
submenu?.setData(submenuValue)
|
|
continue outer
|
|
}
|
|
|
|
// If we arrive here, the loop above has not found any match
|
|
toggle.setData(false)
|
|
}
|
|
}
|
|
|
|
private setHash() {
|
|
const s = this._state
|
|
let h = ""
|
|
|
|
for (const { toggle, showOverOthers, name, submenu } of s.guistate.allToggles) {
|
|
if (showOverOthers || !toggle.data) {
|
|
continue
|
|
}
|
|
h = name
|
|
if (submenu?.data) {
|
|
h += ":" + submenu.data
|
|
}
|
|
}
|
|
|
|
if (s.selectedElement.data !== undefined) {
|
|
h = s.selectedElement.data.properties.id
|
|
}
|
|
|
|
for (const { toggle, showOverOthers, name, submenu } of s.guistate.allToggles) {
|
|
if (!showOverOthers || !toggle.data) {
|
|
continue
|
|
}
|
|
if (h) {
|
|
h += ";" + name
|
|
} else {
|
|
h = name
|
|
}
|
|
if (submenu?.data) {
|
|
h += ":" + submenu.data
|
|
}
|
|
}
|
|
Hash.hash.setData(h)
|
|
}
|
|
|
|
private back() {
|
|
console.log("Got a back event")
|
|
const state = this._state
|
|
// history.pushState(null, null, window.location.pathname);
|
|
if (state.selectedElement.data) {
|
|
state.selectedElement.setData(undefined)
|
|
return
|
|
}
|
|
if (state.guistate.closeAll()) {
|
|
return
|
|
}
|
|
}
|
|
}
|