From 593ac5381a6f847366c0f5373fd15bbe435af832 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Jan 2021 01:14:16 +0100 Subject: [PATCH] Further refactoring fullscreenelement: removal of hash handling from showDataLayer --- InitUiElements.ts | 11 ++- Logic/Actors/HistoryHandling.ts | 19 ---- Logic/Actors/SelectedFeatureHandler.ts | 51 ++++++++++ State.ts | 129 ++++++++++++------------- UI/Base/LazyElement.ts | 1 - UI/Base/ScrollableFullScreen.ts | 4 - UI/ShowDataLayer.ts | 24 +---- 7 files changed, 125 insertions(+), 114 deletions(-) delete mode 100644 Logic/Actors/HistoryHandling.ts create mode 100644 Logic/Actors/SelectedFeatureHandler.ts diff --git a/InitUiElements.ts b/InitUiElements.ts index 45137c1ede..fd7427c9f7 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -33,6 +33,8 @@ import LayerConfig from "./Customizations/JSON/LayerConfig"; import ShowDataLayer from "./UI/ShowDataLayer"; import Hash from "./Logic/Web/Hash"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; +import HashHandler from "./Logic/Actors/SelectedFeatureHandler"; +import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; export class InitUiElements { @@ -231,7 +233,7 @@ export class InitUiElements { checkbox.isEnabled.setData(false); }) - State.state.selectedElement.addCallback(selected => { + State.state.selectedElement.addCallbackAndRun(selected => { if (selected !== undefined) { checkbox.isEnabled.setData(false); } @@ -258,6 +260,11 @@ export class InitUiElements { checkbox.isEnabled.setData(false); }); + State.state.selectedElement.addCallbackAndRun(feature => { + if(feature !== undefined){ + checkbox.isEnabled.setData(false); + } + }) }); } @@ -341,6 +348,8 @@ export class InitUiElements { new ShowDataLayer(source.features, State.state.leafletMap, State.state.layoutToUse.data); + + new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source); } diff --git a/Logic/Actors/HistoryHandling.ts b/Logic/Actors/HistoryHandling.ts deleted file mode 100644 index 6d1c55df43..0000000000 --- a/Logic/Actors/HistoryHandling.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {UIEventSource} from "../UIEventSource"; -import {UIElement} from "../../UI/UIElement"; - -export default class HistoryHandling { - - constructor(hash: UIEventSource, fullscreenMessage: UIEventSource<{ content: UIElement, hashText: string }>) { - hash.addCallback(h => { - if (h === undefined || h === "") { - fullscreenMessage.setData(undefined); - } - }) - - fullscreenMessage.addCallback(fs => { - hash.setData(fs?.hashText); - }) - - } - -} \ No newline at end of file diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts new file mode 100644 index 0000000000..e366a783ae --- /dev/null +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -0,0 +1,51 @@ +import {UIEventSource} from "../UIEventSource"; +import {UIElement} from "../../UI/UIElement"; +import FeatureSource from "../FeatureSource/FeatureSource"; + +/** + * Makes sure the hash shows the selected element and vice-versa + */ +export default class SelectedFeatureHandler { + private readonly _featureSource: FeatureSource; + private readonly _hash: UIEventSource; + private readonly _selectedFeature: UIEventSource; + + constructor(hash: UIEventSource, + selectedFeature: UIEventSource, + featureSource: FeatureSource) { + this._hash = hash; + this._selectedFeature = selectedFeature; + this._featureSource = featureSource; + const self = this; + hash.addCallback(h => { + if (h === undefined || h === "") { + selectedFeature.setData(undefined); + }else{ + self.selectFeature(); + } + }) + + featureSource.features.addCallback(_ => self.selectFeature()); + + selectedFeature.addCallback(feature => { + hash.setData(feature?.properties?.id ?? ""); + }) + + this.selectFeature(); + + } + + private selectFeature(){ + const features = this._featureSource?.features?.data; + if(features === undefined){ + return; + } + for (const feature of features) { + const id = feature.feature?.properties?.id; + if(id === this._hash.data){ + this._selectedFeature.setData(feature.feature); + } + } + } + +} \ No newline at end of file diff --git a/State.ts b/State.ts index 036413faeb..520eb0919d 100644 --- a/State.ts +++ b/State.ts @@ -30,6 +30,8 @@ export default class State { public static runningFromConsole: boolean = false; + + public readonly layoutToUse = new UIEventSource(undefined); /** @@ -74,11 +76,11 @@ export default class State { public readonly centerMessage = new UIEventSource(""); /** - The latest element that was selected - used to generate the right UI at the right place + The latest element that was selected */ public readonly selectedElement = new UIEventSource(undefined) - publ + public readonly featureSwitchUserbadge: UIEventSource; public readonly featureSwitchSearch: UIEventSource; public readonly featureSwitchLayers: UIEventSource; @@ -88,6 +90,7 @@ export default class State { public readonly featureSwitchMoreQuests: UIEventSource; public readonly featureSwitchShareScreen: UIEventSource; public readonly featureSwitchGeolocation: UIEventSource; + public readonly featureSwitchIsTesting: UIEventSource; /** @@ -119,36 +122,39 @@ export default class State { constructor(layoutToUse: LayoutConfig) { const self = this; + this.layoutToUse.setData(layoutToUse); - const zoom = State.asFloat( - QueryParameters.GetQueryParameter("z", "" +(layoutToUse?.startZoom ?? 1), "The initial/current zoom level") - .syncWith(LocalStorageSource.Get("zoom"))); - const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + (layoutToUse?.startLat ?? 0), "The initial/current latitude") - .syncWith(LocalStorageSource.Get("lat"))); - const lon = State.asFloat(QueryParameters.GetQueryParameter("lon", "" + (layoutToUse?.startLon ?? 0), "The initial/current longitude of the app") - .syncWith(LocalStorageSource.Get("lon"))); + // -- Location control initialization + { const zoom = State.asFloat( + QueryParameters.GetQueryParameter("z", "" + (layoutToUse?.startZoom ?? 1), "The initial/current zoom level") + .syncWith(LocalStorageSource.Get("zoom"))); + const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + (layoutToUse?.startLat ?? 0), "The initial/current latitude") + .syncWith(LocalStorageSource.Get("lat"))); + const lon = State.asFloat(QueryParameters.GetQueryParameter("lon", "" + (layoutToUse?.startLon ?? 0), "The initial/current longitude of the app") + .syncWith(LocalStorageSource.Get("lon"))); - this.locationControl = new UIEventSource({ - zoom: Utils.asFloat(zoom.data), - lat: Utils.asFloat(lat.data), - lon: Utils.asFloat(lon.data), - }).addCallback((latlonz) => { - zoom.setData(latlonz.zoom); - lat.setData(latlonz.lat); - lon.setData(latlonz.lon); - }); - - this.layoutToUse.addCallback(layoutToUse => { - const lcd = self.locationControl.data; - lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; - lcd.lat = lcd.lat ?? layoutToUse?.startLat; - lcd.lon = lcd.lon ?? layoutToUse?.startLon; - self.locationControl.ping(); - }); + this.locationControl = new UIEventSource({ + zoom: Utils.asFloat(zoom.data), + lat: Utils.asFloat(lat.data), + lon: Utils.asFloat(lon.data), + }).addCallback((latlonz) => { + zoom.setData(latlonz.zoom); + lat.setData(latlonz.lat); + lon.setData(latlonz.lon); + }); + this.layoutToUse.addCallback(layoutToUse => { + const lcd = self.locationControl.data; + lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; + lcd.lat = lcd.lat ?? layoutToUse?.startLat; + lcd.lon = lcd.lon ?? layoutToUse?.startLon; + self.locationControl.ping(); + }); + } + // Helper function to initialize feature switches function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource { const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation); // I'm so sorry about someone trying to decipher this @@ -162,60 +168,52 @@ export default class State { }), [queryParameterSource]); } - - this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, - "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode."); - this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true, - "Disables/Enables the search bar"); - this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true, - "Disables/Enables the layer control"); - this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, - "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)"); - this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true, - "Disables/enables the help menu or welcome message"); - this.featureSwitchIframe = featSw("fs-iframe", () => false, - "Disables/Enables the iframe-popup"); - this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, - "Disables/Enables the 'More Quests'-tab in the welcome message"); - this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true, - "Disables/Enables the 'Share-screen'-tab in the welcome message"); - this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true, - "Disables/Enables the geolocation button"); + // Feature switch initialization - not as a function as the UIEventSources are readonly + { + + this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, + "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode."); + this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true, + "Disables/Enables the search bar"); + this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true, + "Disables/Enables the layer control"); + this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, + "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)"); + this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true, + "Disables/enables the help menu or welcome message"); + this.featureSwitchIframe = featSw("fs-iframe", () => false, + "Disables/Enables the iframe-popup"); + this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, + "Disables/Enables the 'More Quests'-tab in the welcome message"); + this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true, + "Disables/Enables the 'Share-screen'-tab in the welcome message"); + this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true, + "Disables/Enables the geolocation button"); - const testParam = QueryParameters.GetQueryParameter("test", "false", - "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org").data; - + this.featureSwitchIsTesting = QueryParameters.GetQueryParameter("test", "false", + "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org") + .map(str => str === "true",[], b => ""+b); + } this.osmConnection = new OsmConnection( - testParam === "true", + this.featureSwitchIsTesting.data, QueryParameters.GetQueryParameter("oauth_token", undefined, "Used to complete the login"), layoutToUse?.id, true ); + + this.allElements = new ElementStorage(); + this.changes = new Changes(); + this.mangroveIdentity = new MangroveIdentity( this.osmConnection.GetLongPreference("identity", "mangrove") ); - const h = Hash.hash; - this.selectedElement.addCallback(selected => { - if (selected === undefined) { - h.setData(""); - } else { - h.setData(selected.id.replace("/","_")) - } - } - ) - h.addCallbackAndRun(hash => { - if (hash === undefined || hash === "") { - self.selectedElement.setData(undefined); - } - }) - this.installedThemes = new InstalledThemes(this.osmConnection).installedThemes; @@ -243,8 +241,6 @@ export default class State { new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); - this.allElements = new ElementStorage(); - this.changes = new Changes(); } private static asFloat(source: UIEventSource): UIEventSource { @@ -258,5 +254,6 @@ export default class State { return ("" + fl).substr(0, 8); }) } + } diff --git a/UI/Base/LazyElement.ts b/UI/Base/LazyElement.ts index 6979104919..90b353a7d8 100644 --- a/UI/Base/LazyElement.ts +++ b/UI/Base/LazyElement.ts @@ -13,7 +13,6 @@ export default class LazyElement extends UIElement { this.dumbMode = false; const self = this; this.Activate = (onElement?: (element: T) => void) => { - console.log("ACTIVATED") if (this._content === undefined) { self._content = content(); } diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index fac681335d..e51b86dd21 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -21,7 +21,6 @@ export default class ScrollableFullScreen extends UIElement { Svg.close_svg().SetClass("hidden sm:block") ]) .onClick(() => { - console.log("Clicked back!"); ScrollableFullScreen.RestoreLeaflet(); if (onClose !== undefined) { onClose(); @@ -107,7 +106,6 @@ export default class ScrollableFullScreen extends UIElement { } public static RestoreLeaflet() { - console.log("Restoring") const noTransf = document.getElementsByClassName("scrollable-fullscreen-no-transform"); for (let i = 0; i < noTransf.length; ++i) { noTransf[i].classList.remove("no-transform"); @@ -136,13 +134,11 @@ export default class ScrollableFullScreen extends UIElement { } protected InnerUpdate(htmlElement: HTMLElement) { - console.log("Inner updating scrollale", this.id) this.PrepFullscreen(htmlElement) super.InnerUpdate(htmlElement); } Update() { - console.log("Updating scrollable", this.id) super.Update(); } diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts index 73c5435c7b..cbf3d19673 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer.ts @@ -72,18 +72,9 @@ export default class ShowDataLayer { action(); } }); - Hash.hash.addCallbackAndRun(id => { - // This is a bit of an edge case: if the hash becomes an id to search, we have to show the corresponding popup - if (State.state.selectedElement !== undefined) { - return; // Something is already selected, we don't have to apply this fix - } - const action = self._onSelectedTrigger[id]; - if (action) { - action(); - } - }) update(); + } @@ -167,19 +158,6 @@ export default class ShowDataLayer { State.state.selectedElement.setData(feature); } this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id]; - if (feature.properties.id.replace(/\//g, "_") === Hash.hash.data && State.state.selectedElement.data === undefined) { - // This element is in the URL, so this is a share link - // We open the relevant popup straight away - console.log("Opening the popup due to sharelink") - uiElement.Activate( ); - popup.setContent(uiElement.Render()); - - const center = GeoOperations.centerpoint(feature).geometry.coordinates; - popup.setLatLng({lat: center[1], lng: center[0]}); - popup.openOn(State.state.leafletMap.data); - State.state.selectedElement.setData(feature); - uiElement.Update(); - } } private CreateGeojsonLayer(features: any[]): L.Layer {