diff --git a/InitUiElements.ts b/InitUiElements.ts index e1c8726..5f2eb40 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -27,21 +27,16 @@ import Img from "./UI/Base/Img"; import UserDetails from "./Logic/Osm/OsmConnection"; import Attribution from "./UI/BigComponents/Attribution"; import MetaTagging from "./Logic/MetaTagging"; -import FeatureSourceMerger from "./Logic/FeatureSource/FeatureSourceMerger"; -import RememberingSource from "./Logic/FeatureSource/RememberingSource"; -import FilteringFeatureSource from "./Logic/FeatureSource/FilteringFeatureSource"; -import WayHandlingApplyingFeatureSource from "./Logic/FeatureSource/WayHandlingApplyingFeatureSource"; -import NoOverlapSource from "./Logic/FeatureSource/NoOverlapSource"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import LayerResetter from "./Logic/Actors/LayerResetter"; import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs"; import LayerControlPanel from "./UI/BigComponents/LayerControlPanel"; import FeatureSwitched from "./UI/Base/FeatureSwitched"; -import FeatureDuplicatorPerLayer from "./Logic/FeatureSource/FeatureDuplicatorPerLayer"; import LayerConfig from "./Customizations/JSON/LayerConfig"; import ShowDataLayer from "./UI/ShowDataLayer"; import Hash from "./Logic/Web/Hash"; import HistoryHandling from "./Logic/Actors/HistoryHandling"; +import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; export class InitUiElements { @@ -294,7 +289,7 @@ export class InitUiElements { const fullOptions2 = new FullWelcomePaneWithTabs(); if (Hash.hash.data === undefined) { State.state.fullScreenMessage.setData({content: fullOptions2, hashText: "welcome"}) - + } Svg.help_svg() @@ -405,20 +400,9 @@ export class InitUiElements { const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap); State.state.layerUpdater = updater; + const source = new FeaturePipeline(flayers, updater); - const source = - new FilteringFeatureSource( - flayers, - State.state.locationControl, - new FeatureSourceMerger([ - new RememberingSource(new WayHandlingApplyingFeatureSource(flayers, - new NoOverlapSource(flayers, new FeatureDuplicatorPerLayer(flayers, updater)) - )), - new FeatureDuplicatorPerLayer(flayers, State.state.changes) - ]) - ); - source.features.addCallback((featuresFreshness: { feature: any, freshness: Date }[]) => { let features = featuresFreshness.map(ff => ff.feature); features.forEach(feature => { diff --git a/Logic/Actors/UpdateFromOverpass.ts b/Logic/Actors/UpdateFromOverpass.ts index ab4e95b..1b2dc06 100644 --- a/Logic/Actors/UpdateFromOverpass.ts +++ b/Logic/Actors/UpdateFromOverpass.ts @@ -130,7 +130,7 @@ export default class UpdateFromOverpass implements FeatureSource{ const w = Math.max(-180, bounds.getWest() - diff); const queryBounds = {north: n, east: e, south: s, west: w}; - const z = Math.floor(this._location.data.zoom); + const z = Math.floor(this._location.data.zoom ?? 0); this.runningQuery.setData(true); const self = this; diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts new file mode 100644 index 0000000..a9794c1 --- /dev/null +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -0,0 +1,48 @@ +import FilteringFeatureSource from "../FeatureSource/FilteringFeatureSource"; +import State from "../../State"; +import FeatureSourceMerger from "../FeatureSource/FeatureSourceMerger"; +import RememberingSource from "../FeatureSource/RememberingSource"; +import WayHandlingApplyingFeatureSource from "../FeatureSource/WayHandlingApplyingFeatureSource"; +import NoOverlapSource from "../FeatureSource/NoOverlapSource"; +import FeatureDuplicatorPerLayer from "../FeatureSource/FeatureDuplicatorPerLayer"; +import FeatureSource from "../FeatureSource/FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import LocalStorageSaver from "./LocalStorageSaver"; +import LayerConfig from "../../Customizations/JSON/LayerConfig"; +import LocalStorageSource from "./LocalStorageSource"; + +export default class FeaturePipeline implements FeatureSource { + + public features: UIEventSource<{ feature: any; freshness: Date }[]>; + + constructor(flayers: { isDisplayed: UIEventSource, layerDef: LayerConfig }[], updater: FeatureSource) { + + const overpassSource = new WayHandlingApplyingFeatureSource(flayers, + new NoOverlapSource(flayers, new FeatureDuplicatorPerLayer(flayers, updater)) + ); + + const amendedOverpassSource = + new RememberingSource(new LocalStorageSaver( + overpassSource + )); + + const merged = new FeatureSourceMerger([ + amendedOverpassSource, + new FeatureDuplicatorPerLayer(flayers, State.state.changes), + new LocalStorageSource() + ]); + merged.features.addCallbackAndRun(feats => console.log("Merged has",feats?.length)) + + const source = + new FilteringFeatureSource( + flayers, + State.state.locationControl, + merged + ); + source.features.addCallbackAndRun(feats => console.log("Filtered has",feats?.length)) + + + this.features = source.features; + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureSourceMerger.ts b/Logic/FeatureSource/FeatureSourceMerger.ts index 2d6a201..fc85daf 100644 --- a/Logic/FeatureSource/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/FeatureSourceMerger.ts @@ -9,15 +9,20 @@ export default class FeatureSourceMerger implements FeatureSource { constructor(sources: FeatureSource[]) { this._sources = sources; const self = this; - for (const source of sources) { - source.features.addCallback(() => self.Update()); + for (let i = 0; i < sources.length; i++){ + let source = sources[i]; + source.features.addCallback(() => { + self.Update(); + }); } + this.Update(); } private Update() { let all = {}; // Mapping 'id' -> {feature, freshness} for (const source of this._sources) { if(source?.features?.data === undefined){ + console.log("Not defined"); continue; } for (const f of source.features.data) { diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts index cfc9c94..c044aa2 100644 --- a/Logic/FeatureSource/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/FilteringFeatureSource.ts @@ -74,6 +74,7 @@ export default class FilteringFeatureSource implements FeatureSource { update(); }); + update(); } diff --git a/Logic/FeatureSource/LocalStorageSaver.ts b/Logic/FeatureSource/LocalStorageSaver.ts new file mode 100644 index 0000000..82aab77 --- /dev/null +++ b/Logic/FeatureSource/LocalStorageSaver.ts @@ -0,0 +1,35 @@ +/*** + * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run + * + * Technically, more an Actor then a featuresource, but it fits more neatly this ay + */ +import FeatureSource from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; + +export default class LocalStorageSaver implements FeatureSource { + public static readonly storageKey: string = "cached-features"; + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + + constructor(source: FeatureSource) { + this.features = source.features; + + this.features.addCallbackAndRun(features => { + if (features === undefined) { + return; + } + if(features.length == 0){ + return; + } + + try { + localStorage.setItem(LocalStorageSaver.storageKey, JSON.stringify(features)); + } catch (e) { + console.warn("Could not save the features to local storage:", e) + } + }) + + + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/LocalStorageSource.ts b/Logic/FeatureSource/LocalStorageSource.ts new file mode 100644 index 0000000..1cbcaca --- /dev/null +++ b/Logic/FeatureSource/LocalStorageSource.ts @@ -0,0 +1,22 @@ +import FeatureSource from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import LocalStorageSaver from "./LocalStorageSaver"; + +export default class LocalStorageSource implements FeatureSource { + public features: UIEventSource<{ feature: any; freshness: Date }[]>; + + constructor() { + this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) + try { + const fromStorage = localStorage.getItem(LocalStorageSaver.storageKey); + if (fromStorage == null) { + return; + } + const loaded = JSON.parse(fromStorage); + this.features.setData(loaded); + } catch (e) { + console.log("Could not load features from localStorage:", e) + } + + } +} \ No newline at end of file diff --git a/Models/Constants.ts b/Models/Constants.ts index 959198e..5e60f25 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import { Utils } from "../Utils"; export default class Constants { - public static vNumber = "0.4.8"; + public static vNumber = "0.4.9"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/State.ts b/State.ts index 7a564c5..8d3f354 100644 --- a/State.ts +++ b/State.ts @@ -125,11 +125,11 @@ export default class State { this.layoutToUse.setData(layoutToUse); const zoom = State.asFloat( - QueryParameters.GetQueryParameter("z", "" + layoutToUse?.startZoom, "The initial/current zoom level") + QueryParameters.GetQueryParameter("z", "" +(layoutToUse?.startZoom ?? 1), "The initial/current zoom level") .syncWith(LocalStorageSource.Get("zoom"))); - const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse?.startLat, "The initial/current latitude") + 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, "The initial/current longitude of the app") + const lon = State.asFloat(QueryParameters.GetQueryParameter("lon", "" + (layoutToUse?.startLon ?? 0), "The initial/current longitude of the app") .syncWith(LocalStorageSource.Get("lon"))); @@ -188,6 +188,9 @@ export default class State { 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.osmConnection = new OsmConnection( testParam === "true", QueryParameters.GetQueryParameter("oauth_token", undefined, @@ -245,18 +248,6 @@ export default class State { this.allElements = new ElementStorage(); this.changes = new Changes(); - - if (State.runningFromConsole) { - console.warn("running from console - not initializing map. Assuming test.html"); - return; - } - - - if (document.getElementById("leafletDiv") === null) { - console.warn("leafletDiv not found - not initializing map. Assuming test.html"); - return; - } - } private static asFloat(source: UIEventSource): UIEventSource { diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 8fd910d..3d827b0 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -18,7 +18,7 @@ export default class ScrollableFullScreen extends UIElement { State.state.selectedElement.setData(undefined); }).SetClass("only-on-mobile") .SetClass("featureinfobox-back-to-the-map") - title.SetClass("featureinfobox-title") + title.SetStyle("width: 100%; display: block;") const ornament = new Combine([new Ornament().SetStyle("height:5em;")]).SetClass("only-on-mobile") this._component = new Combine([ diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 11af718..fb43454 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -55,7 +55,6 @@ export default class EditableTagRendering extends UIElement { } if (this._configuration.multiAnswer) { const atLeastOneMatch = this._configuration.mappings.some(mp =>TagUtils.MatchesMultiAnswer(mp.if, this._tags.data)); - console.log("SOME MATCH?", atLeastOneMatch) if (!atLeastOneMatch) { return ""; } diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts index 1b03a56..181ac28 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer.ts @@ -41,6 +41,7 @@ export default class ShowDataLayer { const mp = leafletMap.data; const feats = features.data.map(ff => ff.feature); + let geoLayer = self.CreateGeojsonLayer(feats) if (layoutToUse.clustering.minNeededElements <= features.data.length) { const cl = window["L"]; // This is a dirty workaround, the clustering plugin binds to the L of the window, not of the namespace or something @@ -79,6 +80,7 @@ export default class ShowDataLayer { } }) + update(); } @@ -94,7 +96,7 @@ export default class ShowDataLayer { // We have to convert them to the appropriate icon // Click handling is done in the next step - const tagSource = State.state.allElements.getEventSourceFor(feature); + const tagSource = State.state.allElements.addOrGetElement(feature) const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));