forked from MapComplete/MapComplete
		
	Refactoring: introduction of global state to simplify getting common objects
This commit is contained in:
		
							parent
							
								
									afaaaaadb1
								
							
						
					
					
						commit
						004eead4ee
					
				
					 34 changed files with 532 additions and 506 deletions
				
			
		|  | @ -57,9 +57,9 @@ export class Artwork extends LayerDefinition { | ||||||
|         const artistQuestion = new TagRenderingOptions({ |         const artistQuestion = new TagRenderingOptions({ | ||||||
|             question: t.artist.question, |             question: t.artist.question, | ||||||
|             freeform: { |             freeform: { | ||||||
|                 key: "artist", |                 key: "artist_name", | ||||||
|                 template: "$$$", |                 template: "$$$", | ||||||
|                 renderTemplate: "{artist}" |                 renderTemplate: "{artist_name}" | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -171,7 +171,7 @@ export class Widths extends LayerDefinition { | ||||||
|             return { |             return { | ||||||
|                 icon: null, |                 icon: null, | ||||||
|                 color: c, |                 color: c, | ||||||
|                 weight: 10, |                 weight: 9, | ||||||
|                 dashArray: dashArray |                 dashArray: dashArray | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -259,15 +259,14 @@ export class Widths extends LayerDefinition { | ||||||
|                         tags.targetWidth = r(props.targetWidth); |                         tags.targetWidth = r(props.targetWidth); | ||||||
|                         tags.short = ""; |                         tags.short = ""; | ||||||
|                         if (props.width < props.targetWidth) { |                         if (props.width < props.targetWidth) { | ||||||
|                             tags.short = "Er is dus <b class='alert'>" + r(props.targetWidth - props.width) + "m</b> te weinig" |                             tags.short = r(props.targetWidth - props.width) | ||||||
|                         } |                         } | ||||||
|                     }, |                     }, | ||||||
|                     freeform: { |                     mappings:[ | ||||||
|                         key: "width:carriageway", |                         {k: new Tag("short","*"), txt:  "De totale nodige ruimte voor vlot en veilig verkeer is dus <b>{targetWidth}m</b><br>" + | ||||||
|                         renderTemplate: "De totale nodige ruimte voor vlot en veilig verkeer is dus <b>{targetWidth}m</b><br>" + |                                 "Er is dus <span class='alert'>{short}m</span> te weinig", substitute: true}, | ||||||
|                             "{short}", |                         {k: new Tag("short",""), txt:  "De totale nodige ruimte voor vlot en veilig verkeer is dus <span class='thanks'>{targetWidth}m</span>"} | ||||||
|                         template: "$$$", |                     ] | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             ).OnlyShowIf(this._notCarFree), |             ).OnlyShowIf(this._notCarFree), | ||||||
|              |              | ||||||
|  |  | ||||||
|  | @ -1,12 +1,6 @@ | ||||||
| import {LayerDefinition} from "./LayerDefinition"; | import {LayerDefinition} from "./LayerDefinition"; | ||||||
| import {UIElement} from "../UI/UIElement"; | import {UIElement} from "../UI/UIElement"; | ||||||
| import {FixedUiElement} from "../UI/Base/FixedUiElement"; |  | ||||||
| import Translation from "../UI/i18n/Translation"; |  | ||||||
| import Translations from "../UI/i18n/Translations"; | import Translations from "../UI/i18n/Translations"; | ||||||
| import Locale from "../UI/i18n/Locale"; |  | ||||||
| import {VariableUiElement} from "../UI/Base/VariableUIElement"; |  | ||||||
| import {UIEventSource} from "../UI/UIEventSource"; |  | ||||||
| import {OsmConnection, UserDetails} from "../Logic/Osm/OsmConnection"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). |  * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). | ||||||
|  | @ -83,59 +77,5 @@ export class Layout { | ||||||
|         this.welcomeBackMessage = Translations.W(welcomeBackMessage); |         this.welcomeBackMessage = Translations.W(welcomeBackMessage); | ||||||
|         this.welcomeTail = Translations.W(welcomeTail); |         this.welcomeTail = Translations.W(welcomeTail); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class WelcomeMessage extends UIElement { |  | ||||||
|     private readonly layout: Layout; |  | ||||||
|     private readonly userDetails: UIEventSource<UserDetails>; |  | ||||||
|     private languagePicker: UIElement; |  | ||||||
|     private osmConnection: OsmConnection; |  | ||||||
| 
 |  | ||||||
|     private readonly description: UIElement; |  | ||||||
|     private readonly plzLogIn: UIElement; |  | ||||||
|     private readonly welcomeBack: UIElement; |  | ||||||
|     private readonly tail: UIElement; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     constructor(layout: Layout, |  | ||||||
|                 languagePicker: UIElement, |  | ||||||
|                 osmConnection: OsmConnection) { |  | ||||||
|         super(osmConnection?.userDetails); |  | ||||||
|         this.languagePicker = languagePicker; |  | ||||||
|         this.ListenTo(Locale.language); |  | ||||||
|         this.osmConnection = osmConnection; |  | ||||||
|         this.layout = layout; |  | ||||||
|         this.userDetails = osmConnection?.userDetails; |  | ||||||
| 
 |  | ||||||
|         this.description = layout.welcomeMessage; |  | ||||||
|         this.plzLogIn = layout.gettingStartedPlzLogin; |  | ||||||
|         this.welcomeBack = layout.welcomeBackMessage; |  | ||||||
|         this.tail = layout.welcomeTail; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     InnerRender(): string { |  | ||||||
| 
 |  | ||||||
|         let loginStatus = ""; |  | ||||||
|         if (this.userDetails !== undefined) { |  | ||||||
|             loginStatus = (this.userDetails.data.loggedIn ? this.welcomeBack : this.plzLogIn).Render(); |  | ||||||
|             loginStatus = loginStatus + "<br/>" |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return "<span>" + |  | ||||||
|             this.description.Render() + |  | ||||||
|             "<br/>" + |  | ||||||
|             loginStatus + |  | ||||||
|             this.tail.Render() + |  | ||||||
|             "<br/>" + |  | ||||||
|             this.languagePicker.Render() + |  | ||||||
|             "</span>"; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected InnerUpdate(htmlElement: HTMLElement) { |  | ||||||
|         this.osmConnection?.registerActivateOsmAUthenticationClass() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ export class SmoothnessLayer extends LayerDefinition { | ||||||
|         this.name = "smoothness"; |         this.name = "smoothness"; | ||||||
|         this.minzoom = 17; |         this.minzoom = 17; | ||||||
|         this.overpassFilter = new Or([ |         this.overpassFilter = new Or([ | ||||||
|  |             new Tag("highway","unclassified"), | ||||||
|             new Tag("highway", "residential"), |             new Tag("highway", "residential"), | ||||||
|             new Tag("highway", "cycleway"), |             new Tag("highway", "cycleway"), | ||||||
|             new Tag("highway", "footway"), |             new Tag("highway", "footway"), | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import {FixedUiElement} from "../UI/Base/FixedUiElement"; | ||||||
| import {SaveButton} from "../UI/SaveButton"; | import {SaveButton} from "../UI/SaveButton"; | ||||||
| import {Changes} from "../Logic/Osm/Changes"; | import {Changes} from "../Logic/Osm/Changes"; | ||||||
| import {VariableUiElement} from "../UI/Base/VariableUIElement"; | import {VariableUiElement} from "../UI/Base/VariableUIElement"; | ||||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; | import {Dependencies, TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; | ||||||
| import {OnlyShowIfConstructor} from "./OnlyShowIf"; | import {OnlyShowIfConstructor} from "./OnlyShowIf"; | ||||||
| import {UserDetails} from "../Logic/Osm/OsmConnection"; | import {UserDetails} from "../Logic/Osm/OsmConnection"; | ||||||
| import {TextField} from "../UI/Input/TextField"; | import {TextField} from "../UI/Input/TextField"; | ||||||
|  | @ -17,6 +17,7 @@ import Translations from "../UI/i18n/Translations"; | ||||||
| import Locale from "../UI/i18n/Locale"; | import Locale from "../UI/i18n/Locale"; | ||||||
| import * as EmailValidator from 'email-validator'; | import * as EmailValidator from 'email-validator'; | ||||||
| import {parsePhoneNumberFromString} from 'libphonenumber-js' | import {parsePhoneNumberFromString} from 'libphonenumber-js' | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export class TagRenderingOptions implements TagDependantUIElementConstructor { | export class TagRenderingOptions implements TagDependantUIElementConstructor { | ||||||
|  | @ -144,8 +145,8 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     construct(dependencies: { tags: UIEventSource<any>, changes: Changes }): TagDependantUIElement { |     construct(dependencies: Dependencies): TagDependantUIElement { | ||||||
|         return new TagRendering(dependencies.tags, dependencies.changes, this.options); |         return new TagRendering(dependencies.tags,  this.options); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IsKnown(properties: any): boolean { |     IsKnown(properties: any): boolean { | ||||||
|  | @ -161,7 +162,6 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { | ||||||
| class TagRendering extends UIElement implements TagDependantUIElement { | class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private _userDetails: UIEventSource<UserDetails>; |  | ||||||
|     private _priority: number; |     private _priority: number; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -189,7 +189,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|     private readonly _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     private readonly _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(tags: UIEventSource<any>, changes: Changes, options: { |     constructor(tags: UIEventSource<any>, options: { | ||||||
|         priority?: number |         priority?: number | ||||||
| 
 | 
 | ||||||
|         question?: string | UIElement, |         question?: string | UIElement, | ||||||
|  | @ -206,13 +206,12 @@ class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|     }) { |     }) { | ||||||
|         super(tags); |         super(tags); | ||||||
|         this.ListenTo(Locale.language); |         this.ListenTo(Locale.language); | ||||||
|         const self = this; |  | ||||||
|         this.ListenTo(this._questionSkipped); |         this.ListenTo(this._questionSkipped); | ||||||
|         this.ListenTo(this._editMode); |         this.ListenTo(this._editMode); | ||||||
|  |         this.ListenTo(State.state.osmConnection.userDetails); | ||||||
| 
 | 
 | ||||||
|         this._userDetails = changes.login.userDetails; |  | ||||||
|         this.ListenTo(this._userDetails); |  | ||||||
| 
 | 
 | ||||||
|  |         const self = this; | ||||||
|         |         | ||||||
|         this._priority = options.priority ?? 0; |         this._priority = options.priority ?? 0; | ||||||
|         this._tagsPreprocessor = function (properties) { |         this._tagsPreprocessor = function (properties) { | ||||||
|  | @ -265,7 +264,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|         const save = () => { |         const save = () => { | ||||||
|             const selection = self._questionElement.GetValue().data; |             const selection = self._questionElement.GetValue().data; | ||||||
|             if (selection) { |             if (selection) { | ||||||
|                 changes.addTag(tags.data.id, selection); |                 State.state.changes.addTag(tags.data.id, selection); | ||||||
|             } |             } | ||||||
|             self._editMode.setData(false); |             self._editMode.setData(false); | ||||||
|         } |         } | ||||||
|  | @ -521,7 +520,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { | ||||||
|             } |             } | ||||||
|             const html = answer.Render(); |             const html = answer.Render(); | ||||||
|             let editButton = ""; |             let editButton = ""; | ||||||
|             if (this._userDetails.data.loggedIn && this._question !== undefined) { |             if (State.state.osmConnection.userDetails.data.loggedIn && this._question !== undefined) { | ||||||
|                 editButton = this._editButton.Render(); |                 editButton = this._editButton.Render(); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,9 +3,13 @@ import {Changes} from "../Logic/Osm/Changes"; | ||||||
| import {UIElement} from "../UI/UIElement"; | import {UIElement} from "../UI/UIElement"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | export interface Dependencies { | ||||||
|  |     tags: UIEventSource<any> | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export interface TagDependantUIElementConstructor { | export interface TagDependantUIElementConstructor { | ||||||
| 
 | 
 | ||||||
|     construct(dependencies: {tags: UIEventSource<any>, changes: Changes}): TagDependantUIElement; |     construct(dependencies: Dependencies): TagDependantUIElement; | ||||||
|     IsKnown(properties: any): boolean; |     IsKnown(properties: any): boolean; | ||||||
|     IsQuestioning(properties: any): boolean; |     IsQuestioning(properties: any): boolean; | ||||||
|     Priority(): number; |     Priority(): number; | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								Helpers.ts
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								Helpers.ts
									
										
									
									
									
								
							|  | @ -1,5 +1,6 @@ | ||||||
| import {UIEventSource} from "./UI/UIEventSource"; | import {UIEventSource} from "./UI/UIEventSource"; | ||||||
| import {Changes} from "./Logic/Osm/Changes"; | import {Changes} from "./Logic/Osm/Changes"; | ||||||
|  | import {State} from "./State"; | ||||||
| 
 | 
 | ||||||
| export class Helpers { | export class Helpers { | ||||||
| 
 | 
 | ||||||
|  | @ -13,9 +14,11 @@ export class Helpers { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     static SetupAutoSave(changes: Changes, millisTillChangesAreSaved: UIEventSource<number>, saveAfterXMillis: number) { |     static SetupAutoSave() { | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  |         const changes = State.state.changes; | ||||||
|  |         const millisTillChangesAreSaved = State.state.secondsTillChangesAreSaved; | ||||||
|  |         const saveAfterXMillis = State.state.secondsTillChangesAreSaved.data * 1000; | ||||||
|         changes.pendingChangesES.addCallback(function () { |         changes.pendingChangesES.addCallback(function () { | ||||||
| 
 | 
 | ||||||
|             var c = changes.pendingChangesES.data; |             var c = changes.pendingChangesES.data; | ||||||
|  | @ -53,7 +56,8 @@ export class Helpers { | ||||||
|     * -> Asks the user not to close. The 'not to close' dialog should profide enough time to upload |     * -> Asks the user not to close. The 'not to close' dialog should profide enough time to upload | ||||||
|     * -> WHen uploading is done, the window is closed anyway |     * -> WHen uploading is done, the window is closed anyway | ||||||
|      */ |      */ | ||||||
|     static LastEffortSave(changes: Changes) { |     static LastEffortSave() { | ||||||
|  |         const changes = State.state.changes; | ||||||
|         window.addEventListener("beforeunload", function (e) { |         window.addEventListener("beforeunload", function (e) { | ||||||
|             // Quickly save everyting!
 |             // Quickly save everyting!
 | ||||||
|             if (changes.pendingChangesES.data == 0) { |             if (changes.pendingChangesES.data == 0) { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import {Layout, WelcomeMessage} from "./Customizations/Layout"; | import {Layout} from "./Customizations/Layout"; | ||||||
| import Locale from "./UI/i18n/Locale"; | import Locale from "./UI/i18n/Locale"; | ||||||
| import Translations from "./UI/i18n/Translations"; | import Translations from "./UI/i18n/Translations"; | ||||||
| import {TabbedComponent} from "./UI/Base/TabbedComponent"; | import {TabbedComponent} from "./UI/Base/TabbedComponent"; | ||||||
|  | @ -17,6 +17,8 @@ import {Preset} from "./UI/SimpleAddUI"; | ||||||
| import {Changes} from "./Logic/Osm/Changes"; | import {Changes} from "./Logic/Osm/Changes"; | ||||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||||
| import {Basemap} from "./Logic/Leaflet/Basemap"; | import {Basemap} from "./Logic/Leaflet/Basemap"; | ||||||
|  | import {State} from "./State"; | ||||||
|  | import {WelcomeMessage} from "./UI/WelcomeMessage"; | ||||||
| 
 | 
 | ||||||
| export class InitUiElements { | export class InitUiElements { | ||||||
| 
 | 
 | ||||||
|  | @ -36,17 +38,16 @@ export class InitUiElements { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private static CreateWelcomePane(layoutToUse: Layout, osmConnection: OsmConnection, bm: Basemap) { |     private static CreateWelcomePane() { | ||||||
| 
 | 
 | ||||||
|         const welcome = new WelcomeMessage(layoutToUse, |         const welcome = new WelcomeMessage() | ||||||
|             Locale.CreateLanguagePicker(layoutToUse, Translations.t.general.pickLanguage), |  | ||||||
|             osmConnection) |  | ||||||
| 
 | 
 | ||||||
|  |         const layoutToUse = State.state.layoutToUse.data; | ||||||
|         const fullOptions = new TabbedComponent([ |         const fullOptions = new TabbedComponent([ | ||||||
|             {header: `<img src='${layoutToUse.icon}'>`, content: welcome}, |             {header: `<img src='${layoutToUse.icon}'>`, content: welcome}, | ||||||
|             {header: `<img src='${'./assets/osm-logo.svg'}'>`, content: Translations.t.general.openStreetMapIntro}, |             {header: `<img src='${'./assets/osm-logo.svg'}'>`, content: Translations.t.general.openStreetMapIntro}, | ||||||
|             {header: `<img src='${'./assets/share.svg'}'>`, content: new ShareScreen(layoutToUse, bm.Location)}, |             {header: `<img src='${'./assets/share.svg'}'>`, content: new ShareScreen()}, | ||||||
|             {header: `<img src='${'./assets/add.svg'}'>`, content: new MoreScreen(layoutToUse.name, bm.Location)} |             {header: `<img src='${'./assets/add.svg'}'>`, content: new MoreScreen()} | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|         return fullOptions; |         return fullOptions; | ||||||
|  | @ -54,10 +55,9 @@ export class InitUiElements { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     static InitWelcomeMessage(layoutToUse: Layout, osmConnection: OsmConnection, bm: Basemap, |     static InitWelcomeMessage() { | ||||||
|                               fullScreenMessage: UIEventSource<UIElement>) { |  | ||||||
| 
 | 
 | ||||||
|         const fullOptions = this.CreateWelcomePane(layoutToUse, osmConnection, bm); |         const fullOptions = this.CreateWelcomePane(); | ||||||
| 
 | 
 | ||||||
|         const help = new FixedUiElement(`<div class='collapse-button-img'><img src='assets/help.svg'  alt='help'></div>`); |         const help = new FixedUiElement(`<div class='collapse-button-img'><img src='assets/help.svg'  alt='help'></div>`); | ||||||
|         const close = new FixedUiElement(`<div class='collapse-button-img'><img src='assets/close.svg'  alt='close'></div>`); |         const close = new FixedUiElement(`<div class='collapse-button-img'><img src='assets/close.svg'  alt='close'></div>`); | ||||||
|  | @ -70,31 +70,27 @@ export class InitUiElements { | ||||||
|             , true |             , true | ||||||
|         ).AttachTo("messagesbox"); |         ).AttachTo("messagesbox"); | ||||||
|         let dontCloseYet = true; |         let dontCloseYet = true; | ||||||
|         bm.Location.addCallback(() => { |         const openedTime = new Date().getTime(); | ||||||
|             if(dontCloseYet){ |         State.state.locationControl.addCallback(() => { | ||||||
|                 dontCloseYet = false; |             if (new Date().getTime() - openedTime < 15 * 1000) { | ||||||
|  |                 // Don't autoclose the first 15 secs
 | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             checkbox.isEnabled.setData(false); |             checkbox.isEnabled.setData(false); | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         const fullOptions2 = this.CreateWelcomePane(layoutToUse, osmConnection, bm); |         const fullOptions2 = this.CreateWelcomePane(); | ||||||
|         fullScreenMessage.setData(fullOptions2) |         State.state.fullScreenMessage.setData(fullOptions2) | ||||||
|         new FixedUiElement(`<div class='collapse-button-img' class="shadow"><img src='assets/help.svg'  alt='help'></div>`).onClick(() => { |         new FixedUiElement(`<div class='collapse-button-img' class="shadow"><img src='assets/help.svg'  alt='help'></div>`).onClick(() => { | ||||||
|             fullScreenMessage.setData(fullOptions2) |             State.state.fullScreenMessage.setData(fullOptions2) | ||||||
|         }).AttachTo("help-button-mobile"); |         }).AttachTo("help-button-mobile"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     static InitLayers(layoutToUse: Layout, osmConnection: OsmConnection, |     static InitLayers(): { | ||||||
|                       changes: Changes, |  | ||||||
|                       allElements: ElementStorage, |  | ||||||
|                       bm: Basemap, |  | ||||||
|                       fullScreenMessage: UIEventSource<UIElement>, |  | ||||||
|                       selectedElement: UIEventSource<any>): { |  | ||||||
|         minZoom: number |         minZoom: number | ||||||
|         flayers: FilteredLayer[], |         flayers: FilteredLayer[], | ||||||
|         presets: Preset[] |         presets: Preset[] | ||||||
|  | @ -105,8 +101,8 @@ export class InitUiElements { | ||||||
|         const flayers: FilteredLayer[] = [] |         const flayers: FilteredLayer[] = [] | ||||||
| 
 | 
 | ||||||
|         let minZoom = 0; |         let minZoom = 0; | ||||||
| 
 |         const state = State.state; | ||||||
|         for (const layer of layoutToUse.layers) { |         for (const layer of state.layoutToUse.data.layers) { | ||||||
| 
 | 
 | ||||||
|             const generateInfo = (tagsES, feature) => { |             const generateInfo = (tagsES, feature) => { | ||||||
| 
 | 
 | ||||||
|  | @ -115,14 +111,12 @@ export class InitUiElements { | ||||||
|                     tagsES, |                     tagsES, | ||||||
|                     layer.title, |                     layer.title, | ||||||
|                     layer.elementsToShow, |                     layer.elementsToShow, | ||||||
|                     changes, |  | ||||||
|                     osmConnection.userDetails |  | ||||||
|                 ) |                 ) | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             minZoom = Math.max(minZoom, layer.minzoom); |             minZoom = Math.max(minZoom, layer.minzoom); | ||||||
| 
 | 
 | ||||||
|             const flayer = FilteredLayer.fromDefinition(layer, bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); |             const flayer = FilteredLayer.fromDefinition(layer, generateInfo); | ||||||
| 
 | 
 | ||||||
|             for (const preset of layer.presets ?? []) { |             for (const preset of layer.presets ?? []) { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import codegrid from "codegrid-js"; | ||||||
| import {Changes} from "./Osm/Changes"; | import {Changes} from "./Osm/Changes"; | ||||||
| import {UserDetails} from "./Osm/OsmConnection"; | import {UserDetails} from "./Osm/OsmConnection"; | ||||||
| import {Basemap} from "./Leaflet/Basemap"; | import {Basemap} from "./Leaflet/Basemap"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| /*** | /*** | ||||||
|  * A filtered layer is a layer which offers a 'set-data' function |  * A filtered layer is a layer which offers a 'set-data' function | ||||||
|  | @ -25,12 +26,10 @@ export class FilteredLayer { | ||||||
|     public readonly filters: TagsFilter; |     public readonly filters: TagsFilter; | ||||||
|     public readonly isDisplayed: UIEventSource<boolean> = new UIEventSource(true); |     public readonly isDisplayed: UIEventSource<boolean> = new UIEventSource(true); | ||||||
|     public readonly layerDef: LayerDefinition; |     public readonly layerDef: LayerDefinition; | ||||||
|     private readonly _map: Basemap; |  | ||||||
|     private readonly _maxAllowedOverlap: number; |     private readonly _maxAllowedOverlap: number; | ||||||
| 
 | 
 | ||||||
|     private readonly _style: (properties) => { color: string, weight?: number, icon: { iconUrl: string, iconSize? : number[], popupAnchor?: number[], iconAnchor?:number[] } }; |     private readonly _style: (properties) => { color: string, weight?: number, icon: { iconUrl: string, iconSize? : number[], popupAnchor?: number[], iconAnchor?:number[] } }; | ||||||
| 
 | 
 | ||||||
|     private readonly _storage: ElementStorage; |  | ||||||
| 
 | 
 | ||||||
|     /** The featurecollection from overpass |     /** The featurecollection from overpass | ||||||
|      */ |      */ | ||||||
|  | @ -43,22 +42,17 @@ export class FilteredLayer { | ||||||
|      * The leaflet layer object which should be removed on rerendering |      * The leaflet layer object which should be removed on rerendering | ||||||
|      */ |      */ | ||||||
|     private _geolayer; |     private _geolayer; | ||||||
|     private _selectedElement: UIEventSource<{ feature: any }>; |  | ||||||
|     private _showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement; |     private _showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement; | ||||||
| 
 | 
 | ||||||
|     private static readonly grid = codegrid.CodeGrid(); |     private static readonly grid = codegrid.CodeGrid(); | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         layerDef: LayerDefinition, |         layerDef: LayerDefinition, | ||||||
|         map: Basemap, storage: ElementStorage, |  | ||||||
|         changes: Changes, |  | ||||||
|         selectedElement: UIEventSource<any>, |  | ||||||
|         showOnPopup: ((tags: UIEventSource<any>, feature: any) => UIElement) |         showOnPopup: ((tags: UIEventSource<any>, feature: any) => UIElement) | ||||||
|     ) { |     ) { | ||||||
|         this.layerDef = layerDef; |         this.layerDef = layerDef; | ||||||
| 
 | 
 | ||||||
|         this._wayHandling = layerDef.wayHandling; |         this._wayHandling = layerDef.wayHandling; | ||||||
|         this._selectedElement = selectedElement; |  | ||||||
|         this._showOnPopup = showOnPopup; |         this._showOnPopup = showOnPopup; | ||||||
|         this._style = layerDef.style; |         this._style = layerDef.style; | ||||||
|         if (this._style === undefined) { |         if (this._style === undefined) { | ||||||
|  | @ -67,17 +61,16 @@ export class FilteredLayer { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         this.name = name; |         this.name = name; | ||||||
|         this._map = map; |  | ||||||
|         this.filters = layerDef.overpassFilter; |         this.filters = layerDef.overpassFilter; | ||||||
|         this._storage = storage; |  | ||||||
|         this._maxAllowedOverlap = layerDef.maxAllowedOverlapPercentage; |         this._maxAllowedOverlap = layerDef.maxAllowedOverlapPercentage; | ||||||
|         const self = this; |         const self = this; | ||||||
|         this.isDisplayed.addCallback(function (isDisplayed) { |         this.isDisplayed.addCallback(function (isDisplayed) { | ||||||
|  |             const map = State.state.bm.map; | ||||||
|             if (self._geolayer !== undefined && self._geolayer !== null) { |             if (self._geolayer !== undefined && self._geolayer !== null) { | ||||||
|                 if (isDisplayed) { |                 if (isDisplayed) { | ||||||
|                     self._geolayer.addTo(self._map.map); |                     self._geolayer.addTo(map); | ||||||
|                 } else { |                 } else { | ||||||
|                     self._map.map.removeLayer(self._geolayer); |                     map.removeLayer(self._geolayer); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|  | @ -85,15 +78,10 @@ export class FilteredLayer { | ||||||
|      |      | ||||||
|     static fromDefinition( |     static fromDefinition( | ||||||
|         definition,  |         definition,  | ||||||
|         basemap: Basemap, allElements: ElementStorage, changes: Changes, userDetails: UIEventSource<UserDetails>, |  | ||||||
|                  selectedElement: UIEventSource<{feature: any}>, |  | ||||||
|                  showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement): |                  showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement): | ||||||
|         FilteredLayer { |         FilteredLayer { | ||||||
|         return new FilteredLayer( |         return new FilteredLayer( | ||||||
|             definition, |             definition, showOnPopup); | ||||||
|             basemap, allElements, changes, |  | ||||||
|             selectedElement, |  | ||||||
|             showOnPopup); |  | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -170,7 +158,7 @@ export class FilteredLayer { | ||||||
|         let self = this; |         let self = this; | ||||||
| 
 | 
 | ||||||
|         if (this._geolayer !== undefined && this._geolayer !== null) { |         if (this._geolayer !== undefined && this._geolayer !== null) { | ||||||
|             this._map.map.removeLayer(this._geolayer); |             State.state.bm.map.removeLayer(this._geolayer); | ||||||
|         } |         } | ||||||
|         this._dataFromOverpass = data; |         this._dataFromOverpass = data; | ||||||
|         const fusedFeatures = []; |         const fusedFeatures = []; | ||||||
|  | @ -227,7 +215,7 @@ export class FilteredLayer { | ||||||
|                         icon: new L.icon(style.icon), |                         icon: new L.icon(style.icon), | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|                 let eventSource = self._storage.addOrGetElement(feature); |                 let eventSource = State.state.allElements.addOrGetElement(feature); | ||||||
|                 const uiElement = self._showOnPopup(eventSource, feature); |                 const uiElement = self._showOnPopup(eventSource, feature); | ||||||
|                 const popup = L.popup({}, marker).setContent(uiElement.Render()); |                 const popup = L.popup({}, marker).setContent(uiElement.Render()); | ||||||
|                 marker.bindPopup(popup) |                 marker.bindPopup(popup) | ||||||
|  | @ -246,7 +234,7 @@ export class FilteredLayer { | ||||||
|                     } else { |                     } else { | ||||||
|                         self._geolayer.setStyle(function (feature) { |                         self._geolayer.setStyle(function (feature) { | ||||||
|                             const style = self._style(feature.properties); |                             const style = self._style(feature.properties); | ||||||
|                             if (self._selectedElement.data?.feature === feature) { |                             if (State.state.selectedElement.data?.feature === feature) { | ||||||
|                                 if (style.weight !== undefined) { |                                 if (style.weight !== undefined) { | ||||||
|                                     style.weight = style.weight * 2; |                                     style.weight = style.weight * 2; | ||||||
|                                 }else{ |                                 }else{ | ||||||
|  | @ -258,14 +246,14 @@ export class FilteredLayer { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 let eventSource = self._storage.addOrGetElement(feature); |                 let eventSource = State.state.allElements.addOrGetElement(feature); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|                 eventSource.addCallback(feature.updateStyle); |                 eventSource.addCallback(feature.updateStyle); | ||||||
| 
 | 
 | ||||||
|                 layer.on("click", function (e) { |                 layer.on("click", function (e) { | ||||||
|                     const previousFeature = self._selectedElement.data?.feature; |                     const previousFeature =State.state.selectedElement.data?.feature; | ||||||
|                     self._selectedElement.setData({feature: feature}); |                     State.state.selectedElement.setData({feature: feature}); | ||||||
|                     feature.updateStyle(); |                     feature.updateStyle(); | ||||||
|                     previousFeature?.updateStyle(); |                     previousFeature?.updateStyle(); | ||||||
| 
 | 
 | ||||||
|  | @ -281,7 +269,7 @@ export class FilteredLayer { | ||||||
|                     }) |                     }) | ||||||
|                         .setContent(uiElement.Render()) |                         .setContent(uiElement.Render()) | ||||||
|                         .setLatLng(e.latlng) |                         .setLatLng(e.latlng) | ||||||
|                         .openOn(self._map.map); |                         .openOn(State.state.bm.map); | ||||||
|                     uiElement.Update(); |                     uiElement.Update(); | ||||||
|                     uiElement.Activate(); |                     uiElement.Activate(); | ||||||
|                     L.DomEvent.stop(e); // Marks the event as consumed
 |                     L.DomEvent.stop(e); // Marks the event as consumed
 | ||||||
|  | @ -290,7 +278,7 @@ export class FilteredLayer { | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         if (this.isDisplayed.data) { |         if (this.isDisplayed.data) { | ||||||
|             this._geolayer.addTo(this._map.map); |             this._geolayer.addTo(State.state.bm.map); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import {SimpleImageElement} from "../UI/Image/SimpleImageElement"; | ||||||
| import {UIElement} from "../UI/UIElement"; | import {UIElement} from "../UI/UIElement"; | ||||||
| import {Changes} from "./Osm/Changes"; | import {Changes} from "./Osm/Changes"; | ||||||
| import {ImgurImage} from "../UI/Image/ImgurImage"; | import {ImgurImage} from "../UI/Image/ImgurImage"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * There are multiple way to fetch images for an object |  * There are multiple way to fetch images for an object | ||||||
|  | @ -27,16 +28,13 @@ export class ImageSearcher extends UIEventSource<string[]> { | ||||||
|     private readonly _wdItem = new UIEventSource<string>(""); |     private readonly _wdItem = new UIEventSource<string>(""); | ||||||
|     private readonly _commons = new UIEventSource<string>(""); |     private readonly _commons = new UIEventSource<string>(""); | ||||||
|     private _activated: boolean = false; |     private _activated: boolean = false; | ||||||
|     private _changes: Changes; |  | ||||||
|     public _deletedImages = new UIEventSource<string[]>([]); |     public _deletedImages = new UIEventSource<string[]>([]); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(tags: UIEventSource<any>, |     constructor(tags: UIEventSource<any>) { | ||||||
|                 changes: Changes) { |  | ||||||
|         super([]); |         super([]); | ||||||
| 
 | 
 | ||||||
|         this._tags = tags; |         this._tags = tags; | ||||||
|         this._changes = changes; |  | ||||||
| 
 | 
 | ||||||
|         const self = this; |         const self = this; | ||||||
|         this._wdItem.addCallback(() => { |         this._wdItem.addCallback(() => { | ||||||
|  | @ -119,7 +117,7 @@ export class ImageSearcher extends UIEventSource<string[]> { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         console.log("Deleting image...", key, " --> ", url); |         console.log("Deleting image...", key, " --> ", url); | ||||||
|         this._changes.addChange(this._tags.data.id, key, ""); |         State.state.changes.addChange(this._tags.data.id, key, ""); | ||||||
|         this._deletedImages.data.push(url); |         this._deletedImages.data.push(url); | ||||||
|         this._deletedImages.ping(); |         this._deletedImages.ping(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -4,9 +4,9 @@ import {FilteredLayer} from "./FilteredLayer"; | ||||||
| import {Bounds} from "./Bounds"; | import {Bounds} from "./Bounds"; | ||||||
| import {Overpass} from "./Osm/Overpass"; | import {Overpass} from "./Osm/Overpass"; | ||||||
| import {Basemap} from "./Leaflet/Basemap"; | import {Basemap} from "./Leaflet/Basemap"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class LayerUpdater { | export class LayerUpdater { | ||||||
|     private _map: Basemap; |  | ||||||
|     private _layers: FilteredLayer[]; |     private _layers: FilteredLayer[]; | ||||||
|     private widenFactor: number; |     private widenFactor: number; | ||||||
| 
 | 
 | ||||||
|  | @ -26,12 +26,10 @@ export class LayerUpdater { | ||||||
|      * @param minzoom |      * @param minzoom | ||||||
|      * @param layers |      * @param layers | ||||||
|      */ |      */ | ||||||
|     constructor(map: Basemap, |     constructor(minzoom: number, | ||||||
|                 minzoom: number, |  | ||||||
|                 widenFactor: number, |                 widenFactor: number, | ||||||
|                 layers: FilteredLayer[]) { |                 layers: FilteredLayer[]) { | ||||||
|         this.widenFactor = widenFactor; |         this.widenFactor = widenFactor; | ||||||
|         this._map = map; |  | ||||||
|         this._layers = layers; |         this._layers = layers; | ||||||
|         this._minzoom = minzoom; |         this._minzoom = minzoom; | ||||||
|         var filters: TagsFilter[] = []; |         var filters: TagsFilter[] = []; | ||||||
|  | @ -41,7 +39,7 @@ export class LayerUpdater { | ||||||
|         this._overpass = new Overpass(new Or(filters)); |         this._overpass = new Overpass(new Or(filters)); | ||||||
| 
 | 
 | ||||||
|         const self = this; |         const self = this; | ||||||
|         map.Location.addCallback(function () { |         State.state.locationControl.addCallback(function () { | ||||||
|             self.update(); |             self.update(); | ||||||
|         }); |         }); | ||||||
|         self.update(); |         self.update(); | ||||||
|  | @ -67,9 +65,7 @@ export class LayerUpdater { | ||||||
|                 renderLayers(rest); |                 renderLayers(rest); | ||||||
|             }, 50) |             }, 50) | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         renderLayers(this._layers); |         renderLayers(this._layers); | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private handleFail(reason: any) { |     private handleFail(reason: any) { | ||||||
|  | @ -89,8 +85,8 @@ export class LayerUpdater { | ||||||
|         if (this.IsInBounds()) { |         if (this.IsInBounds()) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         console.log("Zoom level: ",this._map.map.getZoom(), "Least needed zoom:", this._minzoom) |         console.log("Zoom level: ",State.state.bm.map.getZoom(), "Least needed zoom:", this._minzoom) | ||||||
|         if (this._map.map.getZoom() < this._minzoom || this._map.Location.data.zoom < this._minzoom) { |         if (State.state.bm.map.getZoom() < this._minzoom || State.state.bm.Location.data.zoom < this._minzoom) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -98,7 +94,7 @@ export class LayerUpdater { | ||||||
|             console.log("Still running a query, skip"); |             console.log("Still running a query, skip"); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         const bounds = this._map.map.getBounds(); |         const bounds = State.state.bm.map.getBounds(); | ||||||
| 
 | 
 | ||||||
|         const diff = this.widenFactor; |         const diff = this.widenFactor; | ||||||
| 
 | 
 | ||||||
|  | @ -131,7 +127,7 @@ export class LayerUpdater { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         const b = this._map.map.getBounds(); |         const b = State.state.bm.map.getBounds(); | ||||||
|         if (b.getSouth() < this.previousBounds.south) { |         if (b.getSouth() < this.previousBounds.south) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,25 +1,20 @@ | ||||||
| import {Basemap} from "./Basemap"; |  | ||||||
| import L from "leaflet"; | import L from "leaflet"; | ||||||
| import {UIEventSource} from "../../UI/UIEventSource"; | import {UIEventSource} from "../../UI/UIEventSource"; | ||||||
| import {UIElement} from "../../UI/UIElement"; | import {UIElement} from "../../UI/UIElement"; | ||||||
| import {Helpers} from "../../Helpers"; | import {Helpers} from "../../Helpers"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| export class GeoLocationHandler extends UIElement { | export class GeoLocationHandler extends UIElement { | ||||||
| 
 | 
 | ||||||
|     currentLocation: UIEventSource<{ |  | ||||||
|         latlng: number, |  | ||||||
|         accuracy: number |  | ||||||
|     }> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined); |  | ||||||
| 
 |  | ||||||
|     private _isActive: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     private _isActive: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|     private _permission: UIEventSource<string> = new UIEventSource<string>(""); |     private _permission: UIEventSource<string> = new UIEventSource<string>(""); | ||||||
|     private _map: Basemap; |  | ||||||
|     private _marker: any; |     private _marker: any; | ||||||
|  |     private _hasLocation: UIEventSource<boolean>; | ||||||
| 
 | 
 | ||||||
|     constructor(map: Basemap) { |     constructor() { | ||||||
|         super(undefined); |         super(undefined); | ||||||
|         this._map = map; |         this._hasLocation = State.state.currentGPSLocation.map((location) => location !== undefined); | ||||||
|         this.ListenTo(this.currentLocation); |         this.ListenTo(this._hasLocation); | ||||||
|         this.ListenTo(this._isActive); |         this.ListenTo(this._isActive); | ||||||
|         this.ListenTo(this._permission); |         this.ListenTo(this._permission); | ||||||
| 
 | 
 | ||||||
|  | @ -29,13 +24,13 @@ export class GeoLocationHandler extends UIElement { | ||||||
|         function onAccuratePositionProgress(e) { |         function onAccuratePositionProgress(e) { | ||||||
|             console.log(e.accuracy); |             console.log(e.accuracy); | ||||||
|             console.log(e.latlng); |             console.log(e.latlng); | ||||||
|             self.currentLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); |             State.state.currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         function onAccuratePositionFound(e) { |         function onAccuratePositionFound(e) { | ||||||
|             console.log(e.accuracy); |             console.log(e.accuracy); | ||||||
|             console.log(e.latlng); |             console.log(e.latlng); | ||||||
|             self.currentLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); |             State.state.currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         function onAccuratePositionError(e) { |         function onAccuratePositionError(e) { | ||||||
|  | @ -43,9 +38,10 @@ export class GeoLocationHandler extends UIElement { | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         map.map.on('accuratepositionprogress', onAccuratePositionProgress); |         const map = State.state.bm.map; | ||||||
|         map.map.on('accuratepositionfound', onAccuratePositionFound); |         map.on('accuratepositionprogress', onAccuratePositionProgress); | ||||||
|         map.map.on('accuratepositionerror', onAccuratePositionError); |         map.on('accuratepositionfound', onAccuratePositionFound); | ||||||
|  |         map.on('accuratepositionerror', onAccuratePositionError); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         const icon = L.icon( |         const icon = L.icon( | ||||||
|  | @ -55,12 +51,12 @@ export class GeoLocationHandler extends UIElement { | ||||||
|                 iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
 |                 iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
 | ||||||
|             }) |             }) | ||||||
| 
 | 
 | ||||||
|         this.currentLocation.addCallback((location) => { |         State.state.currentGPSLocation.addCallback((location) => { | ||||||
|             const newMarker = L.marker(location.latlng, {icon: icon}); |             const newMarker = L.marker(location.latlng, {icon: icon}); | ||||||
|             newMarker.addTo(map.map); |             newMarker.addTo(map.map); | ||||||
| 
 | 
 | ||||||
|             if (self._marker !== undefined) { |             if (self._marker !== undefined) { | ||||||
|                 map.map.removeLayer(self._marker); |                 map.removeLayer(self._marker); | ||||||
|             } |             } | ||||||
|             self._marker = newMarker; |             self._marker = newMarker; | ||||||
|         }); |         }); | ||||||
|  | @ -81,7 +77,7 @@ export class GeoLocationHandler extends UIElement { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
|         if (this.currentLocation.data) { |         if (this._hasLocation.data) { | ||||||
|             return "<img src='assets/crosshair-blue.png' alt='locate me'>"; |             return "<img src='assets/crosshair-blue.png' alt='locate me'>"; | ||||||
|         } |         } | ||||||
|         if (this._isActive.data) { |         if (this._isActive.data) { | ||||||
|  | @ -94,17 +90,17 @@ export class GeoLocationHandler extends UIElement { | ||||||
|      |      | ||||||
|     private StartGeolocating() { |     private StartGeolocating() { | ||||||
|         const self = this; |         const self = this; | ||||||
| 
 |         const map = State.state.bm.map; | ||||||
|         if (self._permission.data === "denied") { |         if (self._permission.data === "denied") { | ||||||
|             return ""; |             return ""; | ||||||
|         } |         } | ||||||
|         if (self.currentLocation.data !== undefined) { |         if (State.state.currentGPSLocation.data !== undefined) { | ||||||
|             self._map.map.flyTo(self.currentLocation.data.latlng, 18); |             map.flyTo(State.state.currentGPSLocation.data.latlng, 18); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         console.log("Searching location using GPS") |         console.log("Searching location using GPS") | ||||||
|         self._map.map.findAccuratePosition({ |         map.findAccuratePosition({ | ||||||
|             maxWait: 10000, // defaults to 10000
 |             maxWait: 10000, // defaults to 10000
 | ||||||
|             desiredAccuracy: 50 // defaults to 20
 |             desiredAccuracy: 50 // defaults to 20
 | ||||||
|         }); |         }); | ||||||
|  | @ -119,7 +115,7 @@ export class GeoLocationHandler extends UIElement { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 self._map.map.findAccuratePosition({ |                 map.findAccuratePosition({ | ||||||
|                     maxWait: 10000, // defaults to 10000
 |                     maxWait: 10000, // defaults to 10000
 | ||||||
|                     desiredAccuracy: 50 // defaults to 20
 |                     desiredAccuracy: 50 // defaults to 20
 | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  | @ -2,29 +2,23 @@ import {Basemap} from "./Basemap"; | ||||||
| import L from "leaflet"; | import L from "leaflet"; | ||||||
| import {UIEventSource} from "../../UI/UIEventSource"; | import {UIEventSource} from "../../UI/UIEventSource"; | ||||||
| import {UIElement} from "../../UI/UIElement"; | import {UIElement} from "../../UI/UIElement"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * The stray-click-hanlders adds a marker to the map if no feature was clicked. |  * The stray-click-hanlders adds a marker to the map if no feature was clicked. | ||||||
|  * Shows the given uiToShow-element in the messagebox |  * Shows the given uiToShow-element in the messagebox | ||||||
|  */ |  */ | ||||||
| export class StrayClickHandler { | export class StrayClickHandler { | ||||||
|     private _basemap: Basemap; |  | ||||||
|     private _lastMarker; |     private _lastMarker; | ||||||
|     private _fullScreenMessage: UIEventSource<UIElement>; |  | ||||||
|     private _uiToShow: (() => UIElement); |     private _uiToShow: (() => UIElement); | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         basemap: Basemap, |  | ||||||
|         selectElement: UIEventSource<{ feature: any }>, |  | ||||||
|         fullScreenMessage: UIEventSource<UIElement>,  |  | ||||||
|         uiToShow: (() => UIElement)) { |         uiToShow: (() => UIElement)) { | ||||||
|         this._basemap = basemap; |  | ||||||
|         this._fullScreenMessage = fullScreenMessage; |  | ||||||
|         this._uiToShow = uiToShow; |         this._uiToShow = uiToShow; | ||||||
|         const self = this; |         const self = this; | ||||||
|         const map = basemap.map; |         const map = State.state.bm.map; | ||||||
|         basemap.LastClickLocation.addCallback(function (lastClick) { |         State.state.bm.LastClickLocation.addCallback(function (lastClick) { | ||||||
|             selectElement.setData(undefined); |             State.state.selectedElement.setData(undefined); | ||||||
| 
 | 
 | ||||||
|             if (self._lastMarker !== undefined) { |             if (self._lastMarker !== undefined) { | ||||||
|                 map.removeLayer(self._lastMarker); |                 map.removeLayer(self._lastMarker); | ||||||
|  | @ -45,13 +39,13 @@ export class StrayClickHandler { | ||||||
|             self._lastMarker.bindPopup(popup).openPopup(); |             self._lastMarker.bindPopup(popup).openPopup(); | ||||||
| 
 | 
 | ||||||
|             self._lastMarker.on("click", () => { |             self._lastMarker.on("click", () => { | ||||||
|                 fullScreenMessage.setData(self._uiToShow()); |                 State.state.fullScreenMessage.setData(self._uiToShow()); | ||||||
|             }); |             }); | ||||||
|             uiElement.Update(); |             uiElement.Update(); | ||||||
|             uiElement.Activate(); |             uiElement.Activate(); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         selectElement.addCallback(() => { |         State.state.selectedElement.addCallback(() => { | ||||||
|             if (self._lastMarker !== undefined) { |             if (self._lastMarker !== undefined) { | ||||||
|                 map.removeLayer(self._lastMarker); |                 map.removeLayer(self._lastMarker); | ||||||
|                 this._lastMarker = undefined; |                 this._lastMarker = undefined; | ||||||
|  |  | ||||||
|  | @ -7,14 +7,12 @@ import {OsmConnection} from "./OsmConnection"; | ||||||
| import {OsmNode, OsmObject} from "./OsmObject"; | import {OsmNode, OsmObject} from "./OsmObject"; | ||||||
| import {And, Tag, TagsFilter} from "../TagsFilter"; | import {And, Tag, TagsFilter} from "../TagsFilter"; | ||||||
| import {ElementStorage} from "../ElementStorage"; | import {ElementStorage} from "../ElementStorage"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| export class Changes { | export class Changes { | ||||||
| 
 | 
 | ||||||
|     private static _nextId = -1; // New assined ID's are negative
 |     private static _nextId = -1; // New assined ID's are negative
 | ||||||
| 
 | 
 | ||||||
|     public readonly login: OsmConnection; |  | ||||||
|     public readonly _allElements: ElementStorage; |  | ||||||
| 
 |  | ||||||
|     private _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
 |     private _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
 | ||||||
|     private newElements: OsmObject[] = []; // Gets reset on uploadAll
 |     private newElements: OsmObject[] = []; // Gets reset on uploadAll
 | ||||||
| 
 | 
 | ||||||
|  | @ -27,8 +25,6 @@ export class Changes { | ||||||
|         login: OsmConnection, |         login: OsmConnection, | ||||||
|         allElements: ElementStorage) { |         allElements: ElementStorage) { | ||||||
|         this._changesetComment = changesetComment; |         this._changesetComment = changesetComment; | ||||||
|         this.login = login; |  | ||||||
|         this._allElements = allElements; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     addTag(elementId: string, tagsFilter : TagsFilter){ |     addTag(elementId: string, tagsFilter : TagsFilter){ | ||||||
|  | @ -66,7 +62,7 @@ console.log("Received change",key, value) | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const eventSource = this._allElements.getElement(elementId); |         const eventSource = State.state.allElements.getElement(elementId); | ||||||
| 
 | 
 | ||||||
|         eventSource.data[key] = value; |         eventSource.data[key] = value; | ||||||
|         eventSource.ping(); |         eventSource.ping(); | ||||||
|  | @ -104,7 +100,7 @@ console.log("Received change",key, value) | ||||||
|                 ] |                 ] | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         this._allElements.addOrGetElement(geojson); |         State.state.allElements.addOrGetElement(geojson); | ||||||
| 
 | 
 | ||||||
|         // The basictags are COPIED, the id is included in the properties
 |         // The basictags are COPIED, the id is included in the properties
 | ||||||
|         // The tags are not yet written into the OsmObject, but this is applied onto a 
 |         // The tags are not yet written into the OsmObject, but this is applied onto a 
 | ||||||
|  | @ -208,16 +204,16 @@ console.log("Received change",key, value) | ||||||
|                 for (const oldId in idMapping) { |                 for (const oldId in idMapping) { | ||||||
|                     const newId = idMapping[oldId]; |                     const newId = idMapping[oldId]; | ||||||
| 
 | 
 | ||||||
|                     const element = self._allElements.getElement(oldId); |                     const element = State.state.allElements.getElement(oldId); | ||||||
|                     element.data.id = newId; |                     element.data.id = newId; | ||||||
|                     self._allElements.addElementById(newId, element); |                     State.state.allElements.addElementById(newId, element); | ||||||
|                     element.ping(); |                     element.ping(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             console.log("Beginning upload..."); |             console.log("Beginning upload..."); | ||||||
|             // At last, we build the changeset and upload
 |             // At last, we build the changeset and upload
 | ||||||
|             self.login.UploadChangeset(self._changesetComment, |             State.state.osmConnection.UploadChangeset(self._changesetComment, | ||||||
|                 function (csId) { |                 function (csId) { | ||||||
| 
 | 
 | ||||||
|                     let modifications = ""; |                     let modifications = ""; | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| import {Basemap} from "../Leaflet/Basemap"; | import {Basemap} from "../Leaflet/Basemap"; | ||||||
| import $ from "jquery" | import $ from "jquery" | ||||||
|  | import {State} from "../../State"; | ||||||
| export class Geocoding { | export class Geocoding { | ||||||
| 
 | 
 | ||||||
|     private static readonly host = "https://nominatim.openstreetmap.org/search?"; |     private static readonly host = "https://nominatim.openstreetmap.org/search?"; | ||||||
| 
 | 
 | ||||||
|     static Search(query: string, |     static Search(query: string, | ||||||
|                   basemap: Basemap, |  | ||||||
|                   handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox: number[] }[]) => void), |                   handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox: number[] }[]) => void), | ||||||
|                   onFail: (() => void)) { |                   onFail: (() => void)) { | ||||||
|         const b = basemap.map.getBounds(); |         const b = State.state.bm.map.getBounds(); | ||||||
|         console.log(b); |         console.log(b); | ||||||
|         $.getJSON( |         $.getJSON( | ||||||
|             Geocoding.host + "format=json&limit=1&viewbox=" +  |             Geocoding.host + "format=json&limit=1&viewbox=" +  | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ export class UserDetails { | ||||||
|     public img: string; |     public img: string; | ||||||
|     public unreadMessages = 0; |     public unreadMessages = 0; | ||||||
|     public totalMessages = 0; |     public totalMessages = 0; | ||||||
|     public osmConnection: OsmConnection; |  | ||||||
|     public dryRun: boolean; |     public dryRun: boolean; | ||||||
|     home: { lon: number; lat: number }; |     home: { lon: number; lat: number }; | ||||||
| } | } | ||||||
|  | @ -23,19 +22,38 @@ export class OsmConnection { | ||||||
| 
 | 
 | ||||||
|     constructor(dryRun: boolean, oauth_token: UIEventSource<string>) { |     constructor(dryRun: boolean, oauth_token: UIEventSource<string>) { | ||||||
| 
 | 
 | ||||||
|  |         let pwaStandAloneMode = false; | ||||||
|  |         try { | ||||||
|  | 
 | ||||||
|  |             if (window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: fullscreen)').matches) { | ||||||
|  |                 pwaStandAloneMode = true; | ||||||
|  |             } | ||||||
|  |         } catch (e) { | ||||||
|  |             console.warn("Detecting standalone mode failed", e, ". Assuming in browser and not worrying furhter") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         if (pwaStandAloneMode) { | ||||||
|  |             // In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
 | ||||||
|  |             this.auth = new osmAuth({ | ||||||
|  |                 oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', | ||||||
|  |                 oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', | ||||||
|  |                 singlepage: false, | ||||||
|  |                 auto: true | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  | 
 | ||||||
|             this.auth = new osmAuth({ |             this.auth = new osmAuth({ | ||||||
|                 oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', |                 oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', | ||||||
|                 oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', |                 oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', | ||||||
|                 singlepage: true, |                 singlepage: true, | ||||||
|                 landing: window.location.href, |                 landing: window.location.href, | ||||||
|             auto: true // show a login form if the user is not authenticated and
 |                 auto: true | ||||||
|                        // you try to do a call
 |  | ||||||
| 
 |  | ||||||
|             }); |             }); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         this.userDetails = new UIEventSource<UserDetails>(new UserDetails()); |         this.userDetails = new UIEventSource<UserDetails>(new UserDetails()); | ||||||
|         this.userDetails.data.osmConnection = this; |  | ||||||
|         this.userDetails.data.dryRun = dryRun; |         this.userDetails.data.dryRun = dryRun; | ||||||
|         this._dryRun = dryRun; |         this._dryRun = dryRun; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,27 +6,19 @@ import {UIEventSource} from "../../UI/UIEventSource"; | ||||||
| import {ImageUploadFlow} from "../../UI/ImageUploadFlow"; | import {ImageUploadFlow} from "../../UI/ImageUploadFlow"; | ||||||
| import {UserDetails} from "./OsmConnection"; | import {UserDetails} from "./OsmConnection"; | ||||||
| import {SlideShow} from "../../UI/SlideShow"; | import {SlideShow} from "../../UI/SlideShow"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| export class OsmImageUploadHandler { | export class OsmImageUploadHandler { | ||||||
|     private _tags: UIEventSource<any>; |     private _tags: UIEventSource<any>; | ||||||
|     private _changeHandler: Changes; |  | ||||||
|     private _userdetails: UIEventSource<UserDetails>; |  | ||||||
|     private _slideShow: SlideShow; |     private _slideShow: SlideShow; | ||||||
|     private _preferedLicense: UIEventSource<string>; |     private _preferedLicense: UIEventSource<string>; | ||||||
| 
 | 
 | ||||||
|     constructor(tags: UIEventSource<any>, |     constructor(tags: UIEventSource<any>, | ||||||
|                 userdetails: UIEventSource<UserDetails>, |  | ||||||
|                 preferedLicense: UIEventSource<string>, |                 preferedLicense: UIEventSource<string>, | ||||||
|                 changeHandler: Changes, |  | ||||||
|                 slideShow : SlideShow |                 slideShow : SlideShow | ||||||
|     ) { |     ) { | ||||||
|         this._slideShow = slideShow; // To move the slideshow (if any) to the last, just added element
 |         this._slideShow = slideShow; // To move the slideshow (if any) to the last, just added element
 | ||||||
|         if (tags === undefined || userdetails === undefined || changeHandler === undefined) { |  | ||||||
|             throw "Something is undefined" |  | ||||||
|         } |  | ||||||
|         this._tags = tags; |         this._tags = tags; | ||||||
|         this._changeHandler = changeHandler; |  | ||||||
|         this._userdetails = userdetails; |  | ||||||
|         this._preferedLicense = preferedLicense; |         this._preferedLicense = preferedLicense; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -36,14 +28,14 @@ export class OsmImageUploadHandler { | ||||||
| 
 | 
 | ||||||
|         const title = tags.name ?? "Unknown area"; |         const title = tags.name ?? "Unknown area"; | ||||||
|         const description = [ |         const description = [ | ||||||
|             "author:" + this._userdetails.data.name, |             "author:" + State.state.osmConnection.userDetails.data.name, | ||||||
|             "license:" + license, |             "license:" + license, | ||||||
|             "wikidata:" + tags.wikidata, |             "wikidata:" + tags.wikidata, | ||||||
|             "osmid:" + tags.id, |             "osmid:" + tags.id, | ||||||
|             "name:" + tags.name |             "name:" + tags.name | ||||||
|         ].join("\n"); |         ].join("\n"); | ||||||
| 
 | 
 | ||||||
|         const changes = this._changeHandler; |         const changes = State.state.changes; | ||||||
|         return { |         return { | ||||||
|             title: title, |             title: title, | ||||||
|             description: description, |             description: description, | ||||||
|  | @ -73,7 +65,6 @@ export class OsmImageUploadHandler { | ||||||
|     getUI(): ImageUploadFlow { |     getUI(): ImageUploadFlow { | ||||||
|         const self = this; |         const self = this; | ||||||
|         return new ImageUploadFlow( |         return new ImageUploadFlow( | ||||||
|             this._userdetails, |  | ||||||
|             this._preferedLicense, |             this._preferedLicense, | ||||||
|             function (license) { |             function (license) { | ||||||
|                 return self.generateOptions(license) |                 return self.generateOptions(license) | ||||||
|  |  | ||||||
|  | @ -51,7 +51,10 @@ export class QueryParameters { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static GetQueryParameter(key: string, deflt: string): UIEventSource<string> { |     public static GetQueryParameter(key: string, deflt: string): UIEventSource<string> { | ||||||
|  |         if (deflt !== undefined) { | ||||||
|  |             console.log(key, "-->", deflt) | ||||||
|             QueryParameters.defaults[key] = deflt; |             QueryParameters.defaults[key] = deflt; | ||||||
|  |         } | ||||||
|         if (QueryParameters.knownSources[key] !== undefined) { |         if (QueryParameters.knownSources[key] !== undefined) { | ||||||
|             return QueryParameters.knownSources[key]; |             return QueryParameters.knownSources[key]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								State.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								State.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | import {UIEventSource} from "./UI/UIEventSource"; | ||||||
|  | import {UIElement} from "./UI/UIElement"; | ||||||
|  | import {QueryParameters} from "./Logic/QueryParameters"; | ||||||
|  | import {LocalStorageSource} from "./Logic/LocalStorageSource"; | ||||||
|  | import {Layout} from "./Customizations/Layout"; | ||||||
|  | import {Utils} from "./Utils"; | ||||||
|  | import {LayerDefinition} from "./Customizations/LayerDefinition"; | ||||||
|  | import {ElementStorage} from "./Logic/ElementStorage"; | ||||||
|  | import {Changes} from "./Logic/Osm/Changes"; | ||||||
|  | import {Basemap} from "./Logic/Leaflet/Basemap"; | ||||||
|  | import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Contains the global state: a bunch of UI-event sources | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | export class State { | ||||||
|  | 
 | ||||||
|  |     // The singleton of the global state
 | ||||||
|  |     public static state: State; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      THe layout to use | ||||||
|  |      */ | ||||||
|  |     public readonly layoutToUse = new UIEventSource<Layout>(undefined); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      The mapping from id -> UIEventSource<properties> | ||||||
|  |      */ | ||||||
|  |     public allElements: ElementStorage; | ||||||
|  |     /** | ||||||
|  |      THe change handler | ||||||
|  |      */ | ||||||
|  |     public changes: Changes; | ||||||
|  |     /** | ||||||
|  |      THe basemap with leaflet instance | ||||||
|  |      */ | ||||||
|  |     public bm: Basemap; | ||||||
|  |     /** | ||||||
|  |      The user crednetials | ||||||
|  |      */ | ||||||
|  |     public osmConnection: OsmConnection; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      *  The message that should be shown at the center of the screen | ||||||
|  |      */ | ||||||
|  |     public readonly centerMessage = new UIEventSource<string>(""); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource | ||||||
|  |      */ | ||||||
|  |     public readonly secondsTillChangesAreSaved = new UIEventSource<number>(0); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      This message is shown full screen on mobile devices | ||||||
|  |      */ | ||||||
|  |     public readonly fullScreenMessage = new UIEventSource<UIElement>(undefined); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      The latest element that was selected - used to generate the right UI at the right place | ||||||
|  |      */ | ||||||
|  |     public readonly selectedElement = new UIEventSource<{ feature: any }>(undefined); | ||||||
|  | 
 | ||||||
|  |     public readonly zoom = QueryParameters.GetQueryParameter("z", undefined) | ||||||
|  |         .syncWith(LocalStorageSource.Get("zoom")); | ||||||
|  |     public readonly lat = QueryParameters.GetQueryParameter("lat", undefined) | ||||||
|  |         .syncWith(LocalStorageSource.Get("lat")); | ||||||
|  |     public readonly lon = QueryParameters.GetQueryParameter("lon", undefined) | ||||||
|  |         .syncWith(LocalStorageSource.Get("lon")); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public readonly featureSwitchUserbadge: UIEventSource<boolean>; | ||||||
|  |     public readonly featureSwitchSearch: UIEventSource<boolean>; | ||||||
|  |     public readonly featureSwitchLayers: UIEventSource<boolean>; | ||||||
|  |     public readonly featureSwitchAddNew: UIEventSource<boolean>; | ||||||
|  |     public readonly featureSwitchWelcomeMessage: UIEventSource<boolean>; | ||||||
|  |     public readonly featureSwitchIframe: UIEventSource<boolean>; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The map location: currently centered lat, lon and zoom | ||||||
|  |      */ | ||||||
|  |     public readonly locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>(undefined); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The location as delivered by the GPS | ||||||
|  |      */ | ||||||
|  |     public currentGPSLocation: UIEventSource<{ | ||||||
|  |         latlng: number, | ||||||
|  |         accuracy: number | ||||||
|  |     }> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined); | ||||||
|  | 
 | ||||||
|  |     // After this many milliseconds without changes, saves are sent of to OSM
 | ||||||
|  |     public readonly saveTimeout = new UIEventSource<number>(30 * 1000); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     constructor(layoutToUse: Layout) { | ||||||
|  |         this.layoutToUse = new UIEventSource<Layout>(layoutToUse); | ||||||
|  |         this.locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({ | ||||||
|  |             zoom: Utils.asFloat(this.zoom.data) ?? layoutToUse.startzoom, | ||||||
|  |             lat: Utils.asFloat(this.lat.data) ?? layoutToUse.startLat, | ||||||
|  |             lon: Utils.asFloat(this.lon.data) ?? layoutToUse.startLon | ||||||
|  |         }).addCallback((latlonz) => { | ||||||
|  |             this.zoom.setData(latlonz.zoom.toString()); | ||||||
|  |             this.lat.setData(latlonz.lat.toString().substr(0, 6)); | ||||||
|  |             this.lon.setData(latlonz.lon.toString().substr(0, 6)); | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         const self = this; | ||||||
|  | 
 | ||||||
|  |         function featSw(key: string, deflt: (layout: Layout) => boolean): UIEventSource<boolean> { | ||||||
|  |             const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); | ||||||
|  |             // I'm so sorry about someone trying to decipher this
 | ||||||
|  |              | ||||||
|  |             // It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened
 | ||||||
|  |             return UIEventSource.flatten( | ||||||
|  |                 self.layoutToUse.map((layout) => | ||||||
|  |                     QueryParameters.GetQueryParameter(key, "" + deflt(layout)).map((str) => str === undefined ? deflt(layout) : str !== "false")), [queryParameterSource]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge); | ||||||
|  |         this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch); | ||||||
|  |         this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers); | ||||||
|  |         this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAdd); | ||||||
|  |         this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true); | ||||||
|  |         this.featureSwitchIframe = featSw("fs-iframe", () => false); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -2,75 +2,60 @@ import {UIElement} from "./UIElement"; | ||||||
| import {UIEventSource} from "./UIEventSource"; | import {UIEventSource} from "./UIEventSource"; | ||||||
| import {OsmConnection} from "../Logic/Osm/OsmConnection"; | import {OsmConnection} from "../Logic/Osm/OsmConnection"; | ||||||
| import Translations from "./i18n/Translations"; | import Translations from "./i18n/Translations"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class CenterMessageBox extends UIElement { | export class CenterMessageBox extends UIElement { | ||||||
| 
 | 
 | ||||||
|     private readonly _location: UIEventSource<{ zoom: number }>; |  | ||||||
|     private readonly _zoomInMore = new UIEventSource<boolean>(true); |  | ||||||
|     private readonly _centermessage: UIEventSource<string>; |  | ||||||
|     private readonly _osmConnection: OsmConnection; |  | ||||||
|     private readonly _queryRunning: UIEventSource<boolean>; |     private readonly _queryRunning: UIEventSource<boolean>; | ||||||
|  |     private startZoom: number; | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         startZoom: number, |         startZoom: number, | ||||||
|         centermessage: UIEventSource<string>, |  | ||||||
|         osmConnection: OsmConnection, |  | ||||||
|         location: UIEventSource<{ zoom: number }>, |  | ||||||
|         queryRunning: UIEventSource<boolean> |         queryRunning: UIEventSource<boolean> | ||||||
|     ) { |     ) { | ||||||
|         super(centermessage); |         super(State.state.centerMessage); | ||||||
|  |         this.startZoom = startZoom; | ||||||
| 
 | 
 | ||||||
|         this._centermessage = centermessage; |         this.ListenTo(State.state.locationControl); | ||||||
|         this._location = location; |  | ||||||
|         this._osmConnection = osmConnection; |  | ||||||
|         this._queryRunning = queryRunning; |  | ||||||
|         this.ListenTo(queryRunning); |         this.ListenTo(queryRunning); | ||||||
| 
 | 
 | ||||||
|  |         this._queryRunning = queryRunning; | ||||||
| 
 | 
 | ||||||
|         const self = this; |  | ||||||
|         location.addCallback(function () { |  | ||||||
|             self._zoomInMore.setData(location.data.zoom < startZoom); |  | ||||||
|         }); |  | ||||||
|         this.ListenTo(this._zoomInMore); |  | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private prep(): { innerHtml: string, done: boolean } { | ||||||
|  |         if (State.state.centerMessage.data != "") { | ||||||
|  |             return {innerHtml: State.state.centerMessage.data, done: false}; | ||||||
|  |         } | ||||||
|  |         if (this._queryRunning.data) { | ||||||
|  |             return {innerHtml: Translations.t.centerMessage.loadingData.Render(), done: false}; | ||||||
|  |         } else if (State.state.locationControl.data.zoom < this.startZoom) { | ||||||
|  |             return {innerHtml: Translations.t.centerMessage.zoomIn.Render(), done: false}; | ||||||
|  |         } else { | ||||||
|  |             return {innerHtml: Translations.t.centerMessage.ready.Render(), done: true}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
| 
 |         return this.prep().innerHtml; | ||||||
|         if (this._centermessage.data != "") { |  | ||||||
|             return this._centermessage.data; |  | ||||||
|         } |  | ||||||
|         if (this._queryRunning.data) { |  | ||||||
|             return Translations.t.centerMessage.loadingData.Render(); |  | ||||||
|         } else if (this._zoomInMore.data) { |  | ||||||
|             return Translations.t.centerMessage.zoomIn.Render(); |  | ||||||
|         } |  | ||||||
|         return Translations.t.centerMessage.ready.Render(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private ShouldShowSomething() : boolean{ |  | ||||||
|         if (this._queryRunning.data) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         return this._zoomInMore.data; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     InnerUpdate(htmlElement: HTMLElement) { |     InnerUpdate(htmlElement: HTMLElement) { | ||||||
|         const pstyle = htmlElement.parentElement.style; |         const pstyle = htmlElement.parentElement.style; | ||||||
|         if (this._centermessage.data != "") { |         if (State.state.centerMessage.data != "") { | ||||||
|             pstyle.opacity = "1"; |             pstyle.opacity = "1"; | ||||||
|             pstyle.pointerEvents = "all"; |             pstyle.pointerEvents = "all"; | ||||||
|             this._osmConnection.registerActivateOsmAUthenticationClass(); |             State.state.osmConnection.registerActivateOsmAUthenticationClass(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         pstyle.pointerEvents = "none"; |         pstyle.pointerEvents = "none"; | ||||||
| 
 | 
 | ||||||
| 
 |         if (this.prep().done) { | ||||||
|         if (this.ShouldShowSomething()) { |  | ||||||
|             pstyle.opacity = "0.5"; |  | ||||||
|         } else { |  | ||||||
|             pstyle.opacity = "0"; |             pstyle.opacity = "0"; | ||||||
|  |         } else { | ||||||
|  |             pstyle.opacity = "0.5"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import Translations from "./i18n/Translations"; | ||||||
| import {Changes} from "../Logic/Osm/Changes"; | import {Changes} from "../Logic/Osm/Changes"; | ||||||
| import {UserDetails} from "../Logic/Osm/OsmConnection"; | import {UserDetails} from "../Logic/Osm/OsmConnection"; | ||||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | import {FixedUiElement} from "./Base/FixedUiElement"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class FeatureInfoBox extends UIElement { | export class FeatureInfoBox extends UIElement { | ||||||
| 
 | 
 | ||||||
|  | @ -23,14 +24,11 @@ export class FeatureInfoBox extends UIElement { | ||||||
|      */ |      */ | ||||||
|     private _tagsES: UIEventSource<any>; |     private _tagsES: UIEventSource<any>; | ||||||
|     private _changes: Changes; |     private _changes: Changes; | ||||||
|     private _userDetails: UIEventSource<UserDetails>; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     private _title: UIElement; |     private _title: UIElement; | ||||||
|     private _osmLink: UIElement; |     private _osmLink: UIElement; | ||||||
|     private _wikipedialink: UIElement; |     private _wikipedialink: UIElement; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     private _infoboxes: TagDependantUIElement[]; |     private _infoboxes: TagDependantUIElement[]; | ||||||
| 
 | 
 | ||||||
|     private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone(); |     private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone(); | ||||||
|  | @ -41,15 +39,11 @@ export class FeatureInfoBox extends UIElement { | ||||||
|         tagsES: UIEventSource<any>, |         tagsES: UIEventSource<any>, | ||||||
|         title: TagRenderingOptions | UIElement | string, |         title: TagRenderingOptions | UIElement | string, | ||||||
|         elementsToShow: TagDependantUIElementConstructor[], |         elementsToShow: TagDependantUIElementConstructor[], | ||||||
|         changes: Changes, |  | ||||||
|         userDetails: UIEventSource<UserDetails> |  | ||||||
|     ) { |     ) { | ||||||
|         super(tagsES); |         super(tagsES); | ||||||
|         this._feature = feature; |         this._feature = feature; | ||||||
|         this._tagsES = tagsES; |         this._tagsES = tagsES; | ||||||
|         this._changes = changes; |         this.ListenTo(State.state.osmConnection.userDetails); | ||||||
|         this._userDetails = userDetails; |  | ||||||
|         this.ListenTo(userDetails); |  | ||||||
| 
 | 
 | ||||||
|         const deps = {tags: this._tagsES, changes: this._changes} |         const deps = {tags: this._tagsES, changes: this._changes} | ||||||
| 
 | 
 | ||||||
|  | @ -112,7 +106,7 @@ export class FeatureInfoBox extends UIElement { | ||||||
| 
 | 
 | ||||||
|         let questionsHtml = ""; |         let questionsHtml = ""; | ||||||
| 
 | 
 | ||||||
|         if (this._userDetails.data.loggedIn && questions.length > 0) { |         if (State.state.osmConnection.userDetails.data.loggedIn && questions.length > 0) { | ||||||
|             // We select the most important question and render that one
 |             // We select the most important question and render that one
 | ||||||
|             let mostImportantQuestion; |             let mostImportantQuestion; | ||||||
|             let score = -1000; |             let score = -1000; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import {UIEventSource} from "./UIEventSource"; | ||||||
| import {UIElement} from "./UIElement"; | import {UIElement} from "./UIElement"; | ||||||
| import {VariableUiElement} from "./Base/VariableUIElement"; | import {VariableUiElement} from "./Base/VariableUIElement"; | ||||||
| import Translations from "./i18n/Translations"; | import Translations from "./i18n/Translations"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handles the full screen popup on mobile |  * Handles the full screen popup on mobile | ||||||
|  | @ -10,17 +11,20 @@ export class FullScreenMessageBoxHandler { | ||||||
| 
 | 
 | ||||||
|     private _uielement: UIEventSource<UIElement>; |     private _uielement: UIEventSource<UIElement>; | ||||||
| 
 | 
 | ||||||
|     constructor(uielement: UIEventSource<UIElement>, |     constructor(onClear: (() => void)) { | ||||||
|                 onClear: (() => void)) { |         this._uielement = State.state.fullScreenMessage; | ||||||
|         this._uielement = uielement; |         const self = this; | ||||||
|         this.listenTo(uielement); |         this._uielement.addCallback(function () { | ||||||
|  |             self.update(); | ||||||
|  |         }); | ||||||
|  |          | ||||||
|         this.update(); |         this.update(); | ||||||
| 
 | 
 | ||||||
|         if (window !== undefined) { |         if (window !== undefined) { | ||||||
|             window.onhashchange = function () { |             window.onhashchange = function () { | ||||||
|                 if (location.hash === "") { |                 if (location.hash === "") { | ||||||
|                     // No more element: back to the map!
 |                     // No more element: back to the map!
 | ||||||
|                     uielement.setData(undefined); |                     self._uielement.setData(undefined); | ||||||
|                     onClear(); |                     onClear(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -28,7 +32,7 @@ export class FullScreenMessageBoxHandler { | ||||||
| 
 | 
 | ||||||
|         Translations.t.general.returnToTheMap |         Translations.t.general.returnToTheMap | ||||||
|             .onClick(() => { |             .onClick(() => { | ||||||
|                 uielement.setData(undefined); |                 self._uielement.setData(undefined); | ||||||
|                 onClear(); |                 onClear(); | ||||||
|             }) |             }) | ||||||
|             .AttachTo("to-the-map"); |             .AttachTo("to-the-map"); | ||||||
|  | @ -36,13 +40,6 @@ export class FullScreenMessageBoxHandler { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     listenTo(uiEventSource: UIEventSource<any>) { |  | ||||||
|         const self = this; |  | ||||||
|         uiEventSource.addCallback(function () { |  | ||||||
|             self.update(); |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     update() { |     update() { | ||||||
|         const wrapper = document.getElementById("messagesboxmobilewrapper"); |         const wrapper = document.getElementById("messagesboxmobilewrapper"); | ||||||
|  |  | ||||||
|  | @ -3,12 +3,15 @@ import {ImageSearcher} from "../../Logic/ImageSearcher"; | ||||||
| import {UIEventSource} from "../UIEventSource"; | import {UIEventSource} from "../UIEventSource"; | ||||||
| import {SlideShow} from "../SlideShow"; | import {SlideShow} from "../SlideShow"; | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | import {FixedUiElement} from "../Base/FixedUiElement"; | ||||||
| import {VerticalCombine} from "../Base/VerticalCombine"; |  | ||||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
| import {ConfirmDialog} from "../ConfirmDialog"; | import {ConfirmDialog} from "../ConfirmDialog"; | ||||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; | import { | ||||||
|  |     Dependencies, | ||||||
|  |     TagDependantUIElement, | ||||||
|  |     TagDependantUIElementConstructor | ||||||
|  | } from "../../Customizations/UIElementConstructor"; | ||||||
| import {Changes} from "../../Logic/Osm/Changes"; | import {Changes} from "../../Logic/Osm/Changes"; | ||||||
| import {UserDetails} from "../../Logic/Osm/OsmConnection"; | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| export class ImageCarouselConstructor implements TagDependantUIElementConstructor{ | export class ImageCarouselConstructor implements TagDependantUIElementConstructor{ | ||||||
|     IsKnown(properties: any): boolean { |     IsKnown(properties: any): boolean { | ||||||
|  | @ -23,8 +26,8 @@ export class ImageCarouselConstructor implements TagDependantUIElementConstructo | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     construct(dependencies: { tags: UIEventSource<any>, changes: Changes }): TagDependantUIElement { |     construct(dependencies: Dependencies): TagDependantUIElement { | ||||||
|         return new ImageCarousel(dependencies.tags, dependencies.changes); |         return new ImageCarousel(dependencies.tags); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -41,14 +44,11 @@ export class ImageCarousel extends TagDependantUIElement { | ||||||
|     private readonly _deleteButton: UIElement; |     private readonly _deleteButton: UIElement; | ||||||
|     private readonly _isDeleted: UIElement; |     private readonly _isDeleted: UIElement; | ||||||
|      |      | ||||||
|     private readonly _userDetails : UIEventSource<UserDetails>; |     constructor(tags: UIEventSource<any>) { | ||||||
| 
 |  | ||||||
|     constructor(tags: UIEventSource<any>, changes: Changes) { |  | ||||||
|         super(tags); |         super(tags); | ||||||
|         this._userDetails = changes.login.userDetails; |  | ||||||
|          |          | ||||||
|         const self = this; |         const self = this; | ||||||
|         this.searcher = new ImageSearcher(tags, changes); |         this.searcher = new ImageSearcher(tags); | ||||||
| 
 | 
 | ||||||
|         this._uiElements = this.searcher.map((imageURLS: string[]) => { |         this._uiElements = this.searcher.map((imageURLS: string[]) => { | ||||||
|             const uiElements: UIElement[] = []; |             const uiElements: UIElement[] = []; | ||||||
|  | @ -65,11 +65,11 @@ export class ImageCarousel extends TagDependantUIElement { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         const showDeleteButton = this.slideshow._currentSlide.map((i) => { |         const showDeleteButton = this.slideshow._currentSlide.map((i) => { | ||||||
|             if(!self._userDetails.data.loggedIn){ |             if(!State.state.osmConnection.userDetails.data.loggedIn){ | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             return self.searcher.IsDeletable(self.searcher.data[i]); |             return self.searcher.IsDeletable(self.searcher.data[i]); | ||||||
|         }, [this.searcher, this._userDetails]); |         }, [this.searcher, State.state.osmConnection.userDetails]); | ||||||
|         this.slideshow._currentSlide.addCallback(() => { |         this.slideshow._currentSlide.addCallback(() => { | ||||||
|             showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons
 |             showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons
 | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|  | @ -1,9 +1,12 @@ | ||||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; | import { | ||||||
|  |     Dependencies, | ||||||
|  |     TagDependantUIElement, | ||||||
|  |     TagDependantUIElementConstructor | ||||||
|  | } from "../../Customizations/UIElementConstructor"; | ||||||
| import {ImageCarousel} from "./ImageCarousel"; | import {ImageCarousel} from "./ImageCarousel"; | ||||||
| import {UIEventSource} from "../UIEventSource"; |  | ||||||
| import {ImageUploadFlow} from "../ImageUploadFlow"; | import {ImageUploadFlow} from "../ImageUploadFlow"; | ||||||
| import {Changes} from "../../Logic/Osm/Changes"; |  | ||||||
| import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler"; | import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ | export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ | ||||||
|     IsKnown(properties: any): boolean { |     IsKnown(properties: any): boolean { | ||||||
|  | @ -27,16 +30,13 @@ class ImageCarouselWithUpload extends TagDependantUIElement { | ||||||
|     private _imageElement: ImageCarousel; |     private _imageElement: ImageCarousel; | ||||||
|     private _pictureUploader: ImageUploadFlow; |     private _pictureUploader: ImageUploadFlow; | ||||||
| 
 | 
 | ||||||
|     constructor(dependencies: {tags: UIEventSource<any>, changes: Changes}) { |     constructor(dependencies: Dependencies) { | ||||||
|         super(dependencies.tags); |         super(dependencies.tags); | ||||||
|         const tags = dependencies.tags; |         const tags = dependencies.tags; | ||||||
|         const changes = dependencies.changes; |         this._imageElement = new ImageCarousel(tags); | ||||||
|         this._imageElement = new ImageCarousel(tags, changes); |         const userDetails = State.state.osmConnection.userDetails; | ||||||
|         const userDetails = changes.login.userDetails; |         const license = State.state.osmConnection.GetPreference( "pictures-license"); | ||||||
|         const license = changes.login.GetPreference( "pictures-license"); |         this._pictureUploader = new OsmImageUploadHandler(tags, license, this._imageElement.slideshow).getUI(); | ||||||
|         this._pictureUploader = new OsmImageUploadHandler(tags, |  | ||||||
|             userDetails, license, |  | ||||||
|             changes, this._imageElement.slideshow).getUI(); |  | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import Translations from "./i18n/Translations"; | ||||||
| import {fail} from "assert"; | import {fail} from "assert"; | ||||||
| import Combine from "./Base/Combine"; | import Combine from "./Base/Combine"; | ||||||
| import {VerticalCombine} from "./Base/VerticalCombine"; | import {VerticalCombine} from "./Base/VerticalCombine"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class ImageUploadFlow extends UIElement { | export class ImageUploadFlow extends UIElement { | ||||||
|     private _licensePicker: UIElement; |     private _licensePicker: UIElement; | ||||||
|  | @ -17,10 +18,8 @@ export class ImageUploadFlow extends UIElement { | ||||||
|     private _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     private _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|     private _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     private _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|     private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) }; |     private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) }; | ||||||
|     private _userdetails: UIEventSource<UserDetails>; |  | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         userInfo: UIEventSource<UserDetails>, |  | ||||||
|         preferedLicense: UIEventSource<string>, |         preferedLicense: UIEventSource<string>, | ||||||
|         uploadOptions: ((license: string) => |         uploadOptions: ((license: string) => | ||||||
|             { |             { | ||||||
|  | @ -30,9 +29,7 @@ export class ImageUploadFlow extends UIElement { | ||||||
|                 allDone: (() => void) |                 allDone: (() => void) | ||||||
|             }) |             }) | ||||||
|     ) { |     ) { | ||||||
|         super(undefined); |         super(State.state.osmConnection.userDetails); | ||||||
|         this._userdetails = userInfo; |  | ||||||
|         this.ListenTo(userInfo); |  | ||||||
|         this._uploadOptions = uploadOptions; |         this._uploadOptions = uploadOptions; | ||||||
|         this.ListenTo(this._isUploading); |         this.ListenTo(this._isUploading); | ||||||
|         this.ListenTo(this._didFail); |         this.ListenTo(this._didFail); | ||||||
|  | @ -56,11 +53,11 @@ export class ImageUploadFlow extends UIElement { | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
| 
 | 
 | ||||||
|         const t = Translations.t.image; |         const t = Translations.t.image; | ||||||
|         if (this._userdetails === undefined) { |         if (State.state.osmConnection.userDetails === undefined) { | ||||||
|             return ""; // No user details -> logging in is probably disabled or smthing
 |             return ""; // No user details -> logging in is probably disabled or smthing
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!this._userdetails.data.loggedIn) { |         if (!State.state.osmConnection.userDetails.data.loggedIn) { | ||||||
|             return `<div class='activate-osm-authentication'>${t.pleaseLogin.Render()}</div>`; |             return `<div class='activate-osm-authentication'>${t.pleaseLogin.Render()}</div>`; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -79,6 +76,16 @@ export class ImageUploadFlow extends UIElement { | ||||||
|             currentState.push(t.uploadDone) |             currentState.push(t.uploadDone) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         let currentStateHtml = ""; | ||||||
|  |         if (currentState.length > 0) { | ||||||
|  |             currentStateHtml = new VerticalCombine(currentState).Render(); | ||||||
|  |             if (!this._allDone.data) { | ||||||
|  |                 currentStateHtml = "<span class='alert'>" + | ||||||
|  |                     currentStateHtml + | ||||||
|  |                     "</span>"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return "" + |         return "" + | ||||||
|             "<div class='imageflow'>" + |             "<div class='imageflow'>" + | ||||||
| 
 | 
 | ||||||
|  | @ -89,9 +96,9 @@ export class ImageUploadFlow extends UIElement { | ||||||
|             `<span class='imageflow-add-picture'>${Translations.t.image.addPicture.R()}</span>` + |             `<span class='imageflow-add-picture'>${Translations.t.image.addPicture.R()}</span>` + | ||||||
|             "<div class='break'></div>" + |             "<div class='break'></div>" + | ||||||
|             "</div>" + |             "</div>" + | ||||||
|  |             currentStateHtml + | ||||||
|             Translations.t.image.respectPrivacy.Render() + "<br/>" + |             Translations.t.image.respectPrivacy.Render() + "<br/>" + | ||||||
|             this._licensePicker.Render() + "<br/>" + |             this._licensePicker.Render() + "<br/>" + | ||||||
|             new VerticalCombine(currentState).Render() + |  | ||||||
|             "</label>" + |             "</label>" + | ||||||
|             "<form id='fileselector-form-" + this.id + "'>" + |             "<form id='fileselector-form-" + this.id + "'>" + | ||||||
|             "<input id='fileselector-" + this.id + "' " + |             "<input id='fileselector-" + this.id + "' " + | ||||||
|  | @ -106,11 +113,11 @@ export class ImageUploadFlow extends UIElement { | ||||||
| 
 | 
 | ||||||
|     InnerUpdate(htmlElement: HTMLElement) { |     InnerUpdate(htmlElement: HTMLElement) { | ||||||
|         super.InnerUpdate(htmlElement); |         super.InnerUpdate(htmlElement); | ||||||
|         const user = this._userdetails.data; |         const user = State.state.osmConnection.userDetails.data; | ||||||
| 
 | 
 | ||||||
|         htmlElement.onclick = function () { |         htmlElement.onclick = function () { | ||||||
|             if (!user.loggedIn) { |             if (!user.loggedIn) { | ||||||
|                 user.osmConnection.AttemptLogin(); |                 State.state.osmConnection.AttemptLogin(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,16 +9,13 @@ import {UIEventSource} from "./UIEventSource"; | ||||||
| import {VariableUiElement} from "./Base/VariableUIElement"; | import {VariableUiElement} from "./Base/VariableUIElement"; | ||||||
| import Combine from "./Base/Combine"; | import Combine from "./Base/Combine"; | ||||||
| import {SubtleButton} from "./Base/SubtleButton"; | import {SubtleButton} from "./Base/SubtleButton"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export class MoreScreen extends UIElement { | export class MoreScreen extends UIElement { | ||||||
|     private currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>; |  | ||||||
|     private currentLayout: string; |  | ||||||
| 
 | 
 | ||||||
|     constructor(currentLayout: string, currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>) { |     constructor() { | ||||||
|         super(currentLocation); |         super(State.state.locationControl); | ||||||
|         this.currentLayout = currentLayout; |  | ||||||
|         this.currentLocation = currentLocation; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
|  | @ -30,12 +27,13 @@ export class MoreScreen extends UIElement { | ||||||
|             if (layout.hideFromOverview) { |             if (layout.hideFromOverview) { | ||||||
|                 continue |                 continue | ||||||
|             } |             } | ||||||
|             if (layout.name === this.currentLayout) { |             if (layout.name === State.state.layoutToUse.data.name) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             const currentLocation = State.state.locationControl.data; | ||||||
|             const linkText = |             const linkText = | ||||||
|                 `https://pietervdvn.github.io/MapComplete/${layout.name}.html?z=${this.currentLocation.data.zoom}&lat=${this.currentLocation.data.lat}&lon=${this.currentLocation.data.lon}` |                 `https://pietervdvn.github.io/MapComplete/${layout.name}.html?z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}` | ||||||
|             const link = |             const link = | ||||||
|                 new SubtleButton(layout.icon, |                 new SubtleButton(layout.icon, | ||||||
|                     new Combine([ |                     new Combine([ | ||||||
|  |  | ||||||
|  | @ -1,23 +1,21 @@ | ||||||
| import {UIElement} from "./UIElement"; | import {UIElement} from "./UIElement"; | ||||||
| import {UIEventSource} from "./UIEventSource"; | import {UIEventSource} from "./UIEventSource"; | ||||||
| import {Changes} from "../Logic/Osm/Changes"; | import {Changes} from "../Logic/Osm/Changes"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class PendingChanges extends UIElement { | export class PendingChanges extends UIElement { | ||||||
|     private _pendingChangesCount: UIEventSource<number>; |     private _pendingChangesCount: UIEventSource<number>; | ||||||
|     private _countdown: UIEventSource<number>; |  | ||||||
|     private _isSaving: UIEventSource<boolean>; |     private _isSaving: UIEventSource<boolean>; | ||||||
| 
 | 
 | ||||||
|     constructor(changes: Changes, |     constructor() { | ||||||
|                 countdown: UIEventSource<number>) { |         super(State.state.changes.pendingChangesES); | ||||||
|         super(changes.pendingChangesES); |         this.ListenTo(State.state.changes.isSaving); | ||||||
|         this.ListenTo(changes.isSaving); |         this.ListenTo(State.state.secondsTillChangesAreSaved); | ||||||
|         this.ListenTo(countdown); |         this._pendingChangesCount = State.state.changes.pendingChangesES; | ||||||
|         this._pendingChangesCount = changes.pendingChangesES; |         this._isSaving = State.state.changes.isSaving; | ||||||
|         this._countdown = countdown; |  | ||||||
|         this._isSaving = changes.isSaving; |  | ||||||
| 
 | 
 | ||||||
|         this.onClick(() => { |         this.onClick(() => { | ||||||
|             changes.uploadAll(); |             State.state.changes.uploadAll(); | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -29,7 +27,7 @@ export class PendingChanges extends UIElement { | ||||||
|             return ""; |             return ""; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var restingSeconds = this._countdown.data / 1000; |         var restingSeconds =State.state.secondsTillChangesAreSaved.data / 1000; | ||||||
|         var dots = ""; |         var dots = ""; | ||||||
|         while (restingSeconds > 0) { |         while (restingSeconds > 0) { | ||||||
|             dots += "."; |             dots += "."; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import {TextField} from "./Input/TextField"; | ||||||
| import {Geocoding} from "../Logic/Osm/Geocoding"; | import {Geocoding} from "../Logic/Osm/Geocoding"; | ||||||
| import Translations from "./i18n/Translations"; | import Translations from "./i18n/Translations"; | ||||||
| import {Basemap} from "../Logic/Leaflet/Basemap"; | import {Basemap} from "../Logic/Leaflet/Basemap"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export class SearchAndGo extends UIElement { | export class SearchAndGo extends UIElement { | ||||||
|  | @ -23,12 +24,10 @@ export class SearchAndGo extends UIElement { | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     private _foundEntries = new UIEventSource([]); |     private _foundEntries = new UIEventSource([]); | ||||||
|     private _map: Basemap; |  | ||||||
|     private _goButton = new FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>"); |     private _goButton = new FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>"); | ||||||
| 
 | 
 | ||||||
|     constructor(map: Basemap) { |     constructor() { | ||||||
|         super(undefined); |         super(undefined); | ||||||
|         this._map = map; |  | ||||||
|         this.ListenTo(this._foundEntries); |         this.ListenTo(this._foundEntries); | ||||||
| 
 | 
 | ||||||
|         const self = this; |         const self = this; | ||||||
|  | @ -48,7 +47,7 @@ export class SearchAndGo extends UIElement { | ||||||
|         this._searchField.Clear(); |         this._searchField.Clear(); | ||||||
|         this._placeholder.setData(Translations.t.general.search.searching); |         this._placeholder.setData(Translations.t.general.search.searching); | ||||||
|         const self = this; |         const self = this; | ||||||
|         Geocoding.Search(searchString, this._map, (result) => { |         Geocoding.Search(searchString,  (result) => { | ||||||
| 
 | 
 | ||||||
|                 if (result.length == 0) { |                 if (result.length == 0) { | ||||||
|                     this._placeholder.setData(Translations.t.general.search.nothing); |                     this._placeholder.setData(Translations.t.general.search.nothing); | ||||||
|  | @ -60,7 +59,7 @@ export class SearchAndGo extends UIElement { | ||||||
|                     [bb[0], bb[2]], |                     [bb[0], bb[2]], | ||||||
|                     [bb[1], bb[3]] |                     [bb[1], bb[3]] | ||||||
|                 ] |                 ] | ||||||
|                 self._map.map.fitBounds(bounds); |                 State.state.bm.map.fitBounds(bounds); | ||||||
|                 this._placeholder.setData(Translations.t.general.search.search); |                 this._placeholder.setData(Translations.t.general.search.search); | ||||||
|             }, |             }, | ||||||
|             () => { |             () => { | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import {CheckBox} from "./Input/CheckBox"; | ||||||
| import {VerticalCombine} from "./Base/VerticalCombine"; | import {VerticalCombine} from "./Base/VerticalCombine"; | ||||||
| import {QueryParameters} from "../Logic/QueryParameters"; | import {QueryParameters} from "../Logic/QueryParameters"; | ||||||
| import {Img} from "./Img"; | import {Img} from "./Img"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export class ShareScreen extends UIElement { | export class ShareScreen extends UIElement { | ||||||
| 
 | 
 | ||||||
|  | @ -19,7 +20,7 @@ export class ShareScreen extends UIElement { | ||||||
|     private _link: UIElement; |     private _link: UIElement; | ||||||
|     private _linkStatus: UIElement; |     private _linkStatus: UIElement; | ||||||
| 
 | 
 | ||||||
|     constructor(layout: Layout, currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>) { |     constructor() { | ||||||
|         super(undefined) |         super(undefined) | ||||||
|         const tr = Translations.t.general.sharescreen; |         const tr = Translations.t.general.sharescreen; | ||||||
| 
 | 
 | ||||||
|  | @ -32,6 +33,10 @@ export class ShareScreen extends UIElement { | ||||||
|             true |             true | ||||||
|         ) |         ) | ||||||
|         optionCheckboxes.push(includeLocation); |         optionCheckboxes.push(includeLocation); | ||||||
|  |          | ||||||
|  |         const currentLocation = State.state.locationControl; | ||||||
|  |         const layout = State.state.layoutToUse.data; | ||||||
|  |          | ||||||
|         optionParts.push(includeLocation.isEnabled.map((includeL) => { |         optionParts.push(includeLocation.isEnabled.map((includeL) => { | ||||||
|             if (includeL) { |             if (includeL) { | ||||||
|                 return `z=${currentLocation.data.zoom}&lat=${currentLocation.data.lat}&lon=${currentLocation.data.lon}` |                 return `z=${currentLocation.data.zoom}&lat=${currentLocation.data.lat}&lon=${currentLocation.data.lon}` | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import {VerticalCombine} from "./Base/VerticalCombine"; | ||||||
| import Locale from "./i18n/Locale"; | import Locale from "./i18n/Locale"; | ||||||
| import {Changes} from "../Logic/Osm/Changes"; | import {Changes} from "../Logic/Osm/Changes"; | ||||||
| import {UserDetails} from "../Logic/Osm/OsmConnection"; | import {UserDetails} from "../Logic/Osm/OsmConnection"; | ||||||
|  | import {State} from "../State"; | ||||||
| 
 | 
 | ||||||
| export interface Preset { | export interface Preset { | ||||||
|     description: string | UIElement, |     description: string | UIElement, | ||||||
|  | @ -24,13 +25,9 @@ export interface Preset { | ||||||
|  * Asks to add a feature at the last clicked location, at least if zoom is sufficient |  * Asks to add a feature at the last clicked location, at least if zoom is sufficient | ||||||
|  */ |  */ | ||||||
| export class SimpleAddUI extends UIElement { | export class SimpleAddUI extends UIElement { | ||||||
|     private _zoomlevel: UIEventSource<{ zoom: number }>; |  | ||||||
|     private _addButtons: UIElement[]; |     private _addButtons: UIElement[]; | ||||||
|     private _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; |      | ||||||
|     private _changes: Changes; |  | ||||||
|     private _selectedElement: UIEventSource<{ feature: any }>; |  | ||||||
|     private _dataIsLoading: UIEventSource<boolean>; |     private _dataIsLoading: UIEventSource<boolean>; | ||||||
|     private _userDetails: UIEventSource<UserDetails>; |  | ||||||
| 
 | 
 | ||||||
|     private _confirmPreset: UIEventSource<Preset> |     private _confirmPreset: UIEventSource<Preset> | ||||||
|         = new UIEventSource<Preset>(undefined); |         = new UIEventSource<Preset>(undefined); | ||||||
|  | @ -39,24 +36,17 @@ export class SimpleAddUI extends UIElement { | ||||||
|     private goToInboxButton: UIElement = new SubtleButton("./assets/envelope.svg",  |     private goToInboxButton: UIElement = new SubtleButton("./assets/envelope.svg",  | ||||||
|         Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false}); |         Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false}); | ||||||
| 
 | 
 | ||||||
|     constructor(zoomlevel: UIEventSource<{ zoom: number }>, |     constructor( | ||||||
|                 lastClickLocation: UIEventSource<{ lat: number, lon: number }>, |  | ||||||
|                 changes: Changes, |  | ||||||
|                 selectedElement: UIEventSource<{ feature: any }>, |  | ||||||
|                 dataIsLoading: UIEventSource<boolean>, |                 dataIsLoading: UIEventSource<boolean>, | ||||||
|                 userDetails: UIEventSource<UserDetails>, |  | ||||||
|                 addButtons: { description: string | UIElement, name: string | UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[], |                 addButtons: { description: string | UIElement, name: string | UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[], | ||||||
|     ) { |     ) { | ||||||
|         super(zoomlevel); |         super(State.state.locationControl); | ||||||
|         this.ListenTo(Locale.language); |         this.ListenTo(Locale.language); | ||||||
|         this._zoomlevel = zoomlevel; |         this.ListenTo(State.state.osmConnection.userDetails); | ||||||
|         this._lastClickLocation = lastClickLocation; |          | ||||||
|         this._changes = changes; |  | ||||||
|         this._selectedElement = selectedElement; |  | ||||||
|         this._dataIsLoading = dataIsLoading; |         this._dataIsLoading = dataIsLoading; | ||||||
|         this._userDetails = userDetails; |  | ||||||
|         this.ListenTo(userDetails); |  | ||||||
|         this.ListenTo(dataIsLoading); |         this.ListenTo(dataIsLoading); | ||||||
|  |          | ||||||
|         this._addButtons = []; |         this._addButtons = []; | ||||||
|         this.ListenTo(this._confirmPreset); |         this.ListenTo(this._confirmPreset); | ||||||
|         this.clss = "add-ui" |         this.clss = "add-ui" | ||||||
|  | @ -102,26 +92,27 @@ export class SimpleAddUI extends UIElement { | ||||||
|         const self = this; |         const self = this; | ||||||
|         return () => { |         return () => { | ||||||
| 
 | 
 | ||||||
|             const loc = self._lastClickLocation.data; |             const loc = State.state.bm.lastClickLocation.data; | ||||||
|             let feature = self._changes.createElement(option.tags, loc.lat, loc.lon); |             let feature = State.state.changes.createElement(option.tags, loc.lat, loc.lon); | ||||||
|             option.layerToAddTo.AddNewElement(feature); |             option.layerToAddTo.AddNewElement(feature); | ||||||
|             self._selectedElement.setData({feature: feature}); |             State.state.selectedElement.setData({feature: feature}); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
| 
 | 
 | ||||||
|  |         const userDetails = State.state.osmConnection.userDetails; | ||||||
| 
 | 
 | ||||||
|         if (this._confirmPreset.data !== undefined) { |         if (this._confirmPreset.data !== undefined) { | ||||||
| 
 | 
 | ||||||
|             if(this._userDetails.data.dryRun){ |             if(userDetails.data.dryRun){ | ||||||
|                 this.CreatePoint(this._confirmPreset.data)(); |                 this.CreatePoint(this._confirmPreset.data)(); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return new Combine([ |             return new Combine([ | ||||||
|                 Translations.t.general.add.confirmIntro.Subs({title: this._confirmPreset.data.name}), |                 Translations.t.general.add.confirmIntro.Subs({title: this._confirmPreset.data.name}), | ||||||
|                 this._userDetails.data.dryRun ? "<span class='alert'>TESTING - changes won't be saved</span>":"", |                 userDetails.data.dryRun ? "<span class='alert'>TESTING - changes won't be saved</span>":"", | ||||||
|                 this.confirmButton, |                 this.confirmButton, | ||||||
|                 this.cancelButton |                 this.cancelButton | ||||||
| 
 | 
 | ||||||
|  | @ -134,15 +125,15 @@ export class SimpleAddUI extends UIElement { | ||||||
|         let header: UIElement = Translations.t.general.add.header; |         let header: UIElement = Translations.t.general.add.header; | ||||||
| 
 | 
 | ||||||
|          |          | ||||||
|         if(this._userDetails === undefined){ |         if(userDetails === undefined){ | ||||||
|             return header.Render(); |             return header.Render(); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         if (!this._userDetails.data.loggedIn) { |         if (!userDetails.data.loggedIn) { | ||||||
|             return new Combine([header, Translations.t.general.add.pleaseLogin]).Render() |             return new Combine([header, Translations.t.general.add.pleaseLogin]).Render() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this._userDetails.data.unreadMessages > 0) { |         if (userDetails.data.unreadMessages > 0) { | ||||||
|             return new Combine([header, "<span class='alert'>", |             return new Combine([header, "<span class='alert'>", | ||||||
|                 Translations.t.general.readYourMessages, |                 Translations.t.general.readYourMessages, | ||||||
|                 "</span>", |                 "</span>", | ||||||
|  | @ -150,7 +141,7 @@ export class SimpleAddUI extends UIElement { | ||||||
|             ]).Render(); |             ]).Render(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this._userDetails.data.dryRun) { |         if (userDetails.data.dryRun) { | ||||||
|             header = new Combine([header, |             header = new Combine([header, | ||||||
|                 "<span class='alert'>", |                 "<span class='alert'>", | ||||||
|                 "Test mode - changes won't be saved", |                 "Test mode - changes won't be saved", | ||||||
|  | @ -158,13 +149,13 @@ export class SimpleAddUI extends UIElement { | ||||||
|             ]); |             ]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this._userDetails.data.csCount < 5) { |         if (userDetails.data.csCount < 5) { | ||||||
|             return new Combine([header, "<span class='alert'>", |             return new Combine([header, "<span class='alert'>", | ||||||
|                 Translations.t.general.fewChangesBefore, |                 Translations.t.general.fewChangesBefore, | ||||||
|                 "</span>"]).Render(); |                 "</span>"]).Render(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this._zoomlevel.data.zoom < 19) { |         if (State.state.locationControl.data.zoom < 19) { | ||||||
|             return new Combine([header, Translations.t.general.add.zoomInFurther]).Render() |             return new Combine([header, Translations.t.general.add.zoomInFurther]).Render() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -183,7 +174,7 @@ export class SimpleAddUI extends UIElement { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerUpdate(htmlElement: HTMLElement) { |     InnerUpdate(htmlElement: HTMLElement) { | ||||||
|         this._userDetails.data.osmConnection.registerActivateOsmAUthenticationClass(); |        State.state.osmConnection.registerActivateOsmAUthenticationClass(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -6,6 +6,9 @@ import {VariableUiElement} from "./Base/VariableUIElement"; | ||||||
| import Translations from "./i18n/Translations"; | import Translations from "./i18n/Translations"; | ||||||
| import {UserDetails} from "../Logic/Osm/OsmConnection"; | import {UserDetails} from "../Logic/Osm/OsmConnection"; | ||||||
| import {Basemap} from "../Logic/Leaflet/Basemap"; | import {Basemap} from "../Logic/Leaflet/Basemap"; | ||||||
|  | import {State} from "../State"; | ||||||
|  | import {PendingChanges} from "./PendingChanges"; | ||||||
|  | import Locale from "./i18n/Locale"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handles and updates the user badge |  * Handles and updates the user badge | ||||||
|  | @ -14,27 +17,22 @@ export class UserBadge extends UIElement { | ||||||
|     private _userDetails: UIEventSource<UserDetails>; |     private _userDetails: UIEventSource<UserDetails>; | ||||||
|     private _pendingChanges: UIElement; |     private _pendingChanges: UIElement; | ||||||
|     private _logout: UIElement; |     private _logout: UIElement; | ||||||
|     private _basemap: Basemap; |  | ||||||
|     private _homeButton: UIElement; |     private _homeButton: UIElement; | ||||||
|     private _languagePicker: UIElement; |     private _languagePicker: UIElement; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(userDetails: UIEventSource<UserDetails>, |     constructor() { | ||||||
|                 pendingChanges: UIElement, |         super(State.state.osmConnection.userDetails); | ||||||
|                 languagePicker: UIElement, |         this._userDetails = State.state.osmConnection.userDetails; | ||||||
|                 basemap: Basemap) { |         this._pendingChanges = new PendingChanges(); | ||||||
|         super(userDetails); |         this._languagePicker = Locale.CreateLanguagePicker(); | ||||||
|         this._userDetails = userDetails; |  | ||||||
|         this._pendingChanges = pendingChanges; |  | ||||||
|         this._basemap = basemap; |  | ||||||
|         this._languagePicker = languagePicker; |  | ||||||
| 
 | 
 | ||||||
|         this._logout = new FixedUiElement("<img src='assets/logout.svg' class='small-userbadge-icon' alt='logout'>") |         this._logout = new FixedUiElement("<img src='assets/logout.svg' class='small-userbadge-icon' alt='logout'>") | ||||||
|             .onClick(() => { |             .onClick(() => { | ||||||
|                 userDetails.data.osmConnection.LogOut(); |                 State.state.osmConnection.LogOut(); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         userDetails.addCallback(function () { |         this._userDetails.addCallback(function () { | ||||||
|             const profilePic = document.getElementById("profile-pic"); |             const profilePic = document.getElementById("profile-pic"); | ||||||
|             if (profilePic) { |             if (profilePic) { | ||||||
| 
 | 
 | ||||||
|  | @ -45,18 +43,18 @@ export class UserBadge extends UIElement { | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         this._homeButton = new VariableUiElement( |         this._homeButton = new VariableUiElement( | ||||||
|             userDetails.map((userinfo) => { |             this._userDetails.map((userinfo) => { | ||||||
|                 if (userinfo.home) { |                 if (userinfo.home) { | ||||||
|                     return "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'> "; |                     return "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'> "; | ||||||
|                 } |                 } | ||||||
|                 return ""; |                 return ""; | ||||||
|             }) |             }) | ||||||
|         ).onClick(() => { |         ).onClick(() => { | ||||||
|             const home = userDetails.data?.home; |             const home = State.state.osmConnection.userDetails.data?.home; | ||||||
|             if (home === undefined) { |             if (home === undefined) { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             basemap.map.flyTo([home.lat, home.lon], 18); |             State.state.bm.map.flyTo([home.lat, home.lon], 18); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | @ -91,7 +89,7 @@ export class UserBadge extends UIElement { | ||||||
|                 iconSize: [20, 20], |                 iconSize: [20, 20], | ||||||
|                 iconAnchor: [10, 10] |                 iconAnchor: [10, 10] | ||||||
|             }); |             }); | ||||||
|             L.marker([user.home.lat, user.home.lon], {icon: icon}).addTo(this._basemap.map); |             L.marker([user.home.lat, user.home.lon], {icon: icon}).addTo(State.state.bm.map); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const settings = |         const settings = | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								UI/WelcomeMessage.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								UI/WelcomeMessage.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | import {UIElement} from "../UI/UIElement"; | ||||||
|  | import {UIEventSource} from "../UI/UIEventSource"; | ||||||
|  | import {OsmConnection, UserDetails} from "../Logic/Osm/OsmConnection"; | ||||||
|  | import Locale from "../UI/i18n/Locale"; | ||||||
|  | import {State} from "../State"; | ||||||
|  | import {Layout} from "../Customizations/Layout"; | ||||||
|  | import Translations from "./i18n/Translations"; | ||||||
|  | import {VariableUiElement} from "./Base/VariableUIElement"; | ||||||
|  | 
 | ||||||
|  | export class WelcomeMessage extends UIElement { | ||||||
|  |     private readonly layout: Layout; | ||||||
|  |     private languagePicker: UIElement; | ||||||
|  |     private osmConnection: OsmConnection; | ||||||
|  | 
 | ||||||
|  |     private readonly description: UIElement; | ||||||
|  |     private readonly plzLogIn: UIElement; | ||||||
|  |     private readonly welcomeBack: UIElement; | ||||||
|  |     private readonly tail: UIElement; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         super(State.state.osmConnection.userDetails); | ||||||
|  |         this.languagePicker = Locale.CreateLanguagePicker(Translations.t.general.pickLanguage); | ||||||
|  |         this.ListenTo(Locale.language); | ||||||
|  | 
 | ||||||
|  |         function fromLayout(f: (layout: Layout) => (string | UIElement)): UIElement { | ||||||
|  |             return new VariableUiElement( | ||||||
|  |                 State.state.layoutToUse.map((layout) => Translations.W(f(layout)).Render()) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.description = fromLayout((layout) => layout.welcomeMessage); | ||||||
|  |         this.plzLogIn = fromLayout((layout) => layout.gettingStartedPlzLogin); | ||||||
|  |         this.welcomeBack = fromLayout((layout) => layout.welcomeBackMessage); | ||||||
|  |         this.tail = fromLayout((layout) => layout.welcomeTail); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     InnerRender(): string { | ||||||
|  | 
 | ||||||
|  |         let loginStatus = ""; | ||||||
|  |         if (State.state.featureSwitchUserbadge.data) { | ||||||
|  |             loginStatus = (State.state.osmConnection.userDetails.data.loggedIn ? this.welcomeBack : this.plzLogIn).Render(); | ||||||
|  |             loginStatus = loginStatus + "<br/>" | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return "<span>" + | ||||||
|  |             this.description.Render() + | ||||||
|  |             "<br/>" + | ||||||
|  |             loginStatus + | ||||||
|  |             this.tail.Render() + | ||||||
|  |             "<br/>" + | ||||||
|  |             this.languagePicker.Render() + | ||||||
|  |             "</span>"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected InnerUpdate(htmlElement: HTMLElement) { | ||||||
|  |         this.osmConnection?.registerActivateOsmAUthenticationClass() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -3,14 +3,15 @@ import {LocalStorageSource} from "../../Logic/LocalStorageSource"; | ||||||
| import {DropDown} from "../Input/DropDown"; | import {DropDown} from "../Input/DropDown"; | ||||||
| import {Layout} from "../../Customizations/Layout"; | import {Layout} from "../../Customizations/Layout"; | ||||||
| import {UIElement} from "../UIElement"; | import {UIElement} from "../UIElement"; | ||||||
|  | import {State} from "../../State"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export default class Locale { | export default class Locale { | ||||||
|     public static language: UIEventSource<string> = LocalStorageSource.Get('language', "en"); |     public static language: UIEventSource<string> = LocalStorageSource.Get('language', "en"); | ||||||
| 
 | 
 | ||||||
|     public static CreateLanguagePicker(layoutToUse: Layout, label: string | UIElement = "") { |     public static CreateLanguagePicker(label: string | UIElement = "") { | ||||||
| 
 | 
 | ||||||
|         return new DropDown(label, layoutToUse.supportedLanguages.map(lang => { |         return new DropDown(label, State.state.layoutToUse.data.supportedLanguages.map(lang => { | ||||||
|                 return {value: lang, shown: lang} |                 return {value: lang, shown: lang} | ||||||
|             } |             } | ||||||
|         ), Locale.language); |         ), Locale.language); | ||||||
|  |  | ||||||
							
								
								
									
										146
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										146
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -31,6 +31,7 @@ import {BaseLayers, Basemap} from "./Logic/Leaflet/Basemap"; | ||||||
| import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler"; | import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler"; | ||||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||||
| import {Changes} from "./Logic/Osm/Changes"; | import {Changes} from "./Logic/Osm/Changes"; | ||||||
|  | import {State} from "./State"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // --------------------- Special actions based on the parameters -----------------
 | // --------------------- Special actions based on the parameters -----------------
 | ||||||
|  | @ -83,66 +84,19 @@ if(layoutToUse === undefined){ | ||||||
|     console.log("Incorrect layout") |     console.log("Incorrect layout") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | // Setup the global state
 | ||||||
| // ----------------- Setup a few event sources -------------
 | State.state = new State(layoutToUse); | ||||||
| 
 | const state = State.state; | ||||||
| 
 |  | ||||||
| // The message that should be shown at the center of the screen
 |  | ||||||
| const centerMessage = new UIEventSource<string>(""); |  | ||||||
| 
 |  | ||||||
| // The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource
 |  | ||||||
| const secondsTillChangesAreSaved = new UIEventSource<number>(0); |  | ||||||
| 
 |  | ||||||
| // const leftMessage = new UIEventSource<() => UIElement>(undefined);
 |  | ||||||
| 
 |  | ||||||
| // This message is shown full screen on mobile devices
 |  | ||||||
| const fullScreenMessage = new UIEventSource<UIElement>(undefined); |  | ||||||
| 
 |  | ||||||
| // The latest element that was selected - used to generate the right UI at the right place
 |  | ||||||
| const selectedElement = new UIEventSource<{ feature: any }>(undefined); |  | ||||||
| 
 |  | ||||||
| const zoom = QueryParameters.GetQueryParameter("z", undefined) |  | ||||||
|     .syncWith(LocalStorageSource.Get("zoom")); |  | ||||||
| const lat = QueryParameters.GetQueryParameter("lat", undefined) |  | ||||||
|     .syncWith(LocalStorageSource.Get("lat")); |  | ||||||
| const lon = QueryParameters.GetQueryParameter("lon", undefined) |  | ||||||
|     .syncWith(LocalStorageSource.Get("lon")); |  | ||||||
| 
 |  | ||||||
| function featSw(key: string, deflt: boolean): UIEventSource<boolean> { |  | ||||||
|     return QueryParameters.GetQueryParameter(key, "" + deflt).map((str) => { |  | ||||||
|         return str !== "false"; |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const featureSwitchUserbadge = featSw("fs-userbadge", layoutToUse.enableUserBadge); |  | ||||||
| const featureSwitchSearch = featSw("fs-search", layoutToUse.enableSearch); |  | ||||||
| const featureSwitchLayers = featSw("fs-layers", layoutToUse.enableLayers); |  | ||||||
| const featureSwitchAddNew = featSw("fs-add-new", layoutToUse.enableAdd); |  | ||||||
| const featureSwitchWelcomeMessage = featSw("fs-welcome-message", true); |  | ||||||
| const featureSwitchIframe = featSw("fs-iframe", false); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| const locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({ |  | ||||||
|     zoom: Utils.asFloat(zoom.data) ?? layoutToUse.startzoom, |  | ||||||
|     lat: Utils.asFloat(lat.data) ?? layoutToUse.startLat, |  | ||||||
|     lon: Utils.asFloat(lon.data) ?? layoutToUse.startLon |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| locationControl.addCallback((latlonz) => { |  | ||||||
|     zoom.setData(latlonz.zoom.toString()); |  | ||||||
|     lat.setData(latlonz.lat.toString().substr(0, 6)); |  | ||||||
|     lon.setData(latlonz.lon.toString().substr(0, 6)); |  | ||||||
| }) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // ----------------- Prepare the important objects -----------------
 | // ----------------- Prepare the important objects -----------------
 | ||||||
| const osmConnection: OsmConnection =  new OsmConnection( | state.osmConnection = new OsmConnection( | ||||||
|     QueryParameters.GetQueryParameter("test", "false").data === "true", |     QueryParameters.GetQueryParameter("test", "false").data === "true", | ||||||
|     QueryParameters.GetQueryParameter("oauth_token", undefined) |     QueryParameters.GetQueryParameter("oauth_token", undefined) | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Locale.language.syncWith(osmConnection.GetPreference("language")); | Locale.language.syncWith(state.osmConnection.GetPreference("language")); | ||||||
| 
 | 
 | ||||||
| // @ts-ignore
 | // @ts-ignore
 | ||||||
| window.setLanguage = function (language: string) { | window.setLanguage = function (language: string) { | ||||||
|  | @ -158,13 +112,12 @@ Locale.language.addCallback((currentLanguage) => { | ||||||
| }).ping() | }).ping() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const saveTimeout = 30000; // After this many milliseconds without changes, saves are sent of to OSM
 | state.allElements = new ElementStorage(); | ||||||
| const allElements = new ElementStorage(); | state.changes = new Changes( | ||||||
| const changes = new Changes( |     "Beantwoorden van vragen met #MapComplete voor vragenset #" + state.layoutToUse.data.name, | ||||||
|     "Beantwoorden van vragen met #MapComplete voor vragenset #" + layoutToUse.name, |     state.osmConnection, state.allElements); | ||||||
|     osmConnection, allElements); | state.bm = new Basemap("leafletDiv", state.locationControl, new VariableUiElement( | ||||||
| const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement( |     state.locationControl.map((location) => { | ||||||
|     locationControl.map((location) => { |  | ||||||
|         const mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " + |         const mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " + | ||||||
|             " " + |             " " + | ||||||
|             "<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug'  class='small-userbadge-icon'></a>"; |             "<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug'  class='small-userbadge-icon'></a>"; | ||||||
|  | @ -184,9 +137,9 @@ const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement( | ||||||
| 
 | 
 | ||||||
| // ------------- Setup the layers -------------------------------
 | // ------------- Setup the layers -------------------------------
 | ||||||
| 
 | 
 | ||||||
| const layerSetup = InitUiElements.InitLayers(layoutToUse, osmConnection, changes, allElements, bm, fullScreenMessage, selectedElement); | const layerSetup = InitUiElements.InitLayers(); | ||||||
| 
 | 
 | ||||||
| const layerUpdater = new LayerUpdater(bm, layerSetup.minZoom, layoutToUse.widenFactor, layerSetup.flayers); | const layerUpdater = new LayerUpdater(layerSetup.minZoom, layoutToUse.widenFactor, layerSetup.flayers); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // --------------- Setting up layer selection ui --------
 | // --------------- Setting up layer selection ui --------
 | ||||||
|  | @ -196,19 +149,21 @@ const closedFilterButton = `<button id="filter__button" class="filter__button sh | ||||||
| const openFilterButton = ` | const openFilterButton = ` | ||||||
| <button id="filter__button" class="filter__button">${Img.openFilterButton}</button>`;
 | <button id="filter__button" class="filter__button">${Img.openFilterButton}</button>`;
 | ||||||
| 
 | 
 | ||||||
| let baseLayerOptions =  BaseLayers.baseLayers.map((layer) => {return {value: layer, shown: layer.name}}); | let baseLayerOptions = BaseLayers.baseLayers.map((layer) => { | ||||||
| const backgroundMapPicker = new Combine([new DropDown(`Background map`, baseLayerOptions, bm.CurrentLayer), openFilterButton]); |     return {value: layer, shown: layer.name} | ||||||
|  | }); | ||||||
|  | const backgroundMapPicker = new Combine([new DropDown(`Background map`, baseLayerOptions, State.state.bm.CurrentLayer), openFilterButton]); | ||||||
| const layerSelection = new Combine([`<p class="filter__label">Maplayers</p>`, new LayerSelection(layerSetup.flayers)]); | const layerSelection = new Combine([`<p class="filter__label">Maplayers</p>`, new LayerSelection(layerSetup.flayers)]); | ||||||
| let layerControl = backgroundMapPicker; | let layerControl = backgroundMapPicker; | ||||||
| if (layerSetup.flayers.length > 1) { | if (layerSetup.flayers.length > 1) { | ||||||
|     layerControl = new Combine([layerSelection, backgroundMapPicker]); |     layerControl = new Combine([layerSelection, backgroundMapPicker]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| InitUiElements.OnlyIf(featureSwitchLayers, () => { | InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => { | ||||||
| 
 | 
 | ||||||
|     const checkbox = new CheckBox(layerControl, closedFilterButton); |     const checkbox = new CheckBox(layerControl, closedFilterButton); | ||||||
|     checkbox.AttachTo("filter__selection"); |     checkbox.AttachTo("filter__selection"); | ||||||
|     bm.Location.addCallback(() => { |     State.state.bm.Location.addCallback(() => { | ||||||
|         checkbox.isEnabled.setData(false); |         checkbox.isEnabled.setData(false); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -224,14 +179,10 @@ Locale.language.addCallback(e => { | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| InitUiElements.OnlyIf(featureSwitchAddNew, () => { | InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => { | ||||||
|     new StrayClickHandler(bm, selectedElement, fullScreenMessage, () => { |     new StrayClickHandler(() => { | ||||||
|             return new SimpleAddUI(bm.Location, |             return new SimpleAddUI( | ||||||
|                 bm.LastClickLocation, |  | ||||||
|                 changes, |  | ||||||
|                 selectedElement, |  | ||||||
|                 layerUpdater.runningQuery, |                 layerUpdater.runningQuery, | ||||||
|                 osmConnection.userDetails, |  | ||||||
|                 layerSetup.presets); |                 layerSetup.presets); | ||||||
|         } |         } | ||||||
|     ); |     ); | ||||||
|  | @ -242,7 +193,7 @@ InitUiElements.OnlyIf(featureSwitchAddNew, () => { | ||||||
|  * Show the questions and information for the selected element |  * Show the questions and information for the selected element | ||||||
|  * This is given to the div which renders fullscreen on mobile devices |  * This is given to the div which renders fullscreen on mobile devices | ||||||
|  */ |  */ | ||||||
| selectedElement.addCallback((feature) => { | State.state.selectedElement.addCallback((feature) => { | ||||||
|     if (feature?.feature?.properties === undefined) { |     if (feature?.feature?.properties === undefined) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -256,67 +207,54 @@ selectedElement.addCallback((feature) => { | ||||||
| 
 | 
 | ||||||
|             const featureBox = new FeatureInfoBox( |             const featureBox = new FeatureInfoBox( | ||||||
|                 feature.feature, |                 feature.feature, | ||||||
|                 allElements.getElement(data.id), |                 State.state.allElements.getElement(data.id), | ||||||
|                 layer.title, |                 layer.title, | ||||||
|                 layer.elementsToShow, |                 layer.elementsToShow, | ||||||
|                 changes, |  | ||||||
|                 osmConnection.userDetails |  | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             fullScreenMessage.setData(featureBox); |             State.state.fullScreenMessage.setData(featureBox); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     } |     } | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| 
 | console.log("Enable new:",State.state.featureSwitchAddNew.data,"deafult", layoutToUse.enableAdd) | ||||||
| const pendingChanges = new PendingChanges(changes, secondsTillChangesAreSaved,); | InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => { | ||||||
| 
 |     new UserBadge().AttachTo('userbadge'); | ||||||
| InitUiElements.OnlyIf(featureSwitchUserbadge, () => { |  | ||||||
| 
 |  | ||||||
|     new UserBadge(osmConnection.userDetails, |  | ||||||
|         pendingChanges, |  | ||||||
|         Locale.CreateLanguagePicker(layoutToUse), |  | ||||||
|         bm) |  | ||||||
|         .AttachTo('userbadge'); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| InitUiElements.OnlyIf((featureSwitchSearch), () => { | InitUiElements.OnlyIf((State.state.featureSwitchSearch), () => { | ||||||
|     new SearchAndGo(bm).AttachTo("searchbox"); |     new SearchAndGo().AttachTo("searchbox"); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| new FullScreenMessageBoxHandler(fullScreenMessage, () => { | new FullScreenMessageBoxHandler(() => { | ||||||
|     selectedElement.setData(undefined) |     State.state.selectedElement.setData(undefined) | ||||||
| }).update(); | }).update(); | ||||||
| 
 | 
 | ||||||
| InitUiElements.OnlyIf(featureSwitchWelcomeMessage, () => { | InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => { | ||||||
|     InitUiElements.InitWelcomeMessage(layoutToUse, |     InitUiElements.InitWelcomeMessage() | ||||||
|         featureSwitchUserbadge.data ? osmConnection : undefined, bm, fullScreenMessage) |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| if ((window != window.top && !featureSwitchWelcomeMessage.data) || featureSwitchIframe.data) { | if ((window != window.top && !State.state.featureSwitchWelcomeMessage) || State.state.featureSwitchIframe.data) { | ||||||
|     new FixedUiElement(`<a href='${window.location}' target='_blank'><span class='iframe-escape'><img src='assets/pop-out.svg'></span></a>`).AttachTo("top-right") |     new FixedUiElement(`<a href='${window.location}' target='_blank'><span class='iframe-escape'><img src='assets/pop-out.svg'></span></a>`).AttachTo("top-right") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| new CenterMessageBox( | new CenterMessageBox( | ||||||
|     layerSetup.minZoom, |     layerSetup.minZoom, | ||||||
|     centerMessage, |  | ||||||
|     osmConnection, |  | ||||||
|     locationControl, |  | ||||||
|     layerUpdater.runningQuery) |     layerUpdater.runningQuery) | ||||||
|     .AttachTo("centermessage"); |     .AttachTo("centermessage"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved, saveTimeout); | Helpers.SetupAutoSave(); | ||||||
| Helpers.LastEffortSave(changes); | Helpers.LastEffortSave(); | ||||||
| 
 |  | ||||||
| osmConnection.registerActivateOsmAUthenticationClass(); |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| new GeoLocationHandler(bm).AttachTo("geolocate-button"); | 
 | ||||||
|  | new GeoLocationHandler().AttachTo("geolocate-button"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| locationControl.ping() | State.state.osmConnection.registerActivateOsmAUthenticationClass(); | ||||||
|  | State.state.locationControl.ping() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue