forked from MapComplete/MapComplete
		
	Formatting (pre-merge)
This commit is contained in:
		
							parent
							
								
									135219b5dc
								
							
						
					
					
						commit
						17b35e731e
					
				
					 2 changed files with 374 additions and 382 deletions
				
			
		
							
								
								
									
										752
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										752
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -1,13 +1,13 @@ | |||
| import { Utils } from "./Utils"; | ||||
| import { ElementStorage } from "./Logic/ElementStorage"; | ||||
| import { Changes } from "./Logic/Osm/Changes"; | ||||
| import { OsmConnection } from "./Logic/Osm/OsmConnection"; | ||||
| import {Utils} from "./Utils"; | ||||
| import {ElementStorage} from "./Logic/ElementStorage"; | ||||
| import {Changes} from "./Logic/Osm/Changes"; | ||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||
| import Locale from "./UI/i18n/Locale"; | ||||
| import { UIEventSource } from "./Logic/UIEventSource"; | ||||
| import { LocalStorageSource } from "./Logic/Web/LocalStorageSource"; | ||||
| import { QueryParameters } from "./Logic/Web/QueryParameters"; | ||||
| import {UIEventSource} from "./Logic/UIEventSource"; | ||||
| import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; | ||||
| import {QueryParameters} from "./Logic/Web/QueryParameters"; | ||||
| import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | ||||
| import { MangroveIdentity } from "./Logic/Web/MangroveReviews"; | ||||
| import {MangroveIdentity} from "./Logic/Web/MangroveReviews"; | ||||
| import InstalledThemes from "./Logic/Actors/InstalledThemes"; | ||||
| import BaseLayer from "./Models/BaseLayer"; | ||||
| import Loc from "./Models/Loc"; | ||||
|  | @ -17,7 +17,7 @@ import OverpassFeatureSource from "./Logic/Actors/OverpassFeatureSource"; | |||
| import LayerConfig from "./Customizations/JSON/LayerConfig"; | ||||
| import TitleHandler from "./Logic/Actors/TitleHandler"; | ||||
| import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; | ||||
| import { Relation } from "./Logic/Osm/ExtractRelations"; | ||||
| import {Relation} from "./Logic/Osm/ExtractRelations"; | ||||
| import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; | ||||
| 
 | ||||
| /** | ||||
|  | @ -25,402 +25,394 @@ import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; | |||
|  */ | ||||
| 
 | ||||
| export default class State { | ||||
|   // The singleton of the global state
 | ||||
|   public static state: State; | ||||
|     // The singleton of the global state
 | ||||
|     public static state: State; | ||||
| 
 | ||||
|   public readonly layoutToUse = new UIEventSource<LayoutConfig>(undefined); | ||||
|     public readonly layoutToUse = new UIEventSource<LayoutConfig>(undefined); | ||||
| 
 | ||||
|   /** | ||||
|     /** | ||||
|      The mapping from id -> UIEventSource<properties> | ||||
|      */ | ||||
|   public allElements: ElementStorage; | ||||
|   /** | ||||
|     public allElements: ElementStorage; | ||||
|     /** | ||||
|      THe change handler | ||||
|      */ | ||||
|   public changes: Changes; | ||||
|   /** | ||||
|     public changes: Changes; | ||||
|     /** | ||||
|      The leaflet instance of the big basemap | ||||
|      */ | ||||
|   public leafletMap = new UIEventSource<L.Map>(undefined); | ||||
|   /** | ||||
|    * Background layer id | ||||
|    */ | ||||
|   public availableBackgroundLayers: UIEventSource<BaseLayer[]>; | ||||
|   /** | ||||
|     public leafletMap = new UIEventSource<L.Map>(undefined); | ||||
|     /** | ||||
|      * Background layer id | ||||
|      */ | ||||
|     public availableBackgroundLayers: UIEventSource<BaseLayer[]>; | ||||
|     /** | ||||
|      The user credentials | ||||
|      */ | ||||
|   public osmConnection: OsmConnection; | ||||
|     public osmConnection: OsmConnection; | ||||
| 
 | ||||
|   public mangroveIdentity: MangroveIdentity; | ||||
|     public mangroveIdentity: MangroveIdentity; | ||||
| 
 | ||||
|   public favouriteLayers: UIEventSource<string[]>; | ||||
|     public favouriteLayers: UIEventSource<string[]>; | ||||
| 
 | ||||
|   public layerUpdater: OverpassFeatureSource; | ||||
|     public layerUpdater: OverpassFeatureSource; | ||||
| 
 | ||||
|   public osmApiFeatureSource: OsmApiFeatureSource; | ||||
|     public osmApiFeatureSource: OsmApiFeatureSource; | ||||
| 
 | ||||
|   public filteredLayers: UIEventSource< | ||||
|     { | ||||
|       readonly isDisplayed: UIEventSource<boolean>; | ||||
|       readonly layerDef: LayerConfig; | ||||
|     }[] | ||||
|   > = new UIEventSource< | ||||
|     { | ||||
|       readonly isDisplayed: UIEventSource<boolean>; | ||||
|       readonly layerDef: LayerConfig; | ||||
|     }[] | ||||
|   >([]); | ||||
|     public filteredLayers: UIEventSource<{ | ||||
|         readonly isDisplayed: UIEventSource<boolean>; | ||||
|         readonly layerDef: LayerConfig; | ||||
|     }[]> = new UIEventSource<{ | ||||
|         readonly isDisplayed: UIEventSource<boolean>; | ||||
|         readonly layerDef: LayerConfig; | ||||
|     }[]>([]); | ||||
| 
 | ||||
|   /** | ||||
|     /** | ||||
|      The latest element that was selected | ||||
|      */ | ||||
|   public readonly selectedElement = new UIEventSource<any>( | ||||
|     undefined, | ||||
|     "Selected element" | ||||
|   ); | ||||
| 
 | ||||
|   /** | ||||
|    * Keeps track of relations: which way is part of which other way? | ||||
|    * Set by the overpass-updater; used in the metatagging | ||||
|    */ | ||||
|   public readonly knownRelations = new UIEventSource< | ||||
|     Map<string, { role: string; relation: Relation }[]> | ||||
|   >(undefined, "Relation memberships"); | ||||
| 
 | ||||
|   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>; | ||||
|   public readonly featureSwitchMoreQuests: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchShareScreen: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchGeolocation: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchIsTesting: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchIsDebugging: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>; | ||||
|   public readonly featureSwitchApiURL: UIEventSource<string>; | ||||
|   public readonly featureSwitchFilter: UIEventSource<boolean>; | ||||
| 
 | ||||
|   /** | ||||
|    * The map location: currently centered lat, lon and zoom | ||||
|    */ | ||||
|   public readonly locationControl = new UIEventSource<Loc>(undefined); | ||||
|   public backgroundLayer; | ||||
|   public readonly backgroundLayerId: UIEventSource<string>; | ||||
| 
 | ||||
|   /* Last location where a click was registered | ||||
|    */ | ||||
|   public readonly LastClickLocation: UIEventSource<{ | ||||
|     lat: number; | ||||
|     lon: number; | ||||
|   }> = new UIEventSource<{ lat: number; lon: number }>(undefined); | ||||
| 
 | ||||
|   /** | ||||
|    * The location as delivered by the GPS | ||||
|    */ | ||||
|   public currentGPSLocation: UIEventSource<{ | ||||
|     latlng: { lat: number; lng: number }; | ||||
|     accuracy: number; | ||||
|   }> = new UIEventSource<{ | ||||
|     latlng: { lat: number; lng: number }; | ||||
|     accuracy: number; | ||||
|   }>(undefined); | ||||
|   public layoutDefinition: string; | ||||
|   public installedThemes: UIEventSource< | ||||
|     { layout: LayoutConfig; definition: string }[] | ||||
|   >; | ||||
| 
 | ||||
|   public layerControlIsOpened: UIEventSource<boolean> = | ||||
|     QueryParameters.GetQueryParameter( | ||||
|       "layer-control-toggle", | ||||
|       "false", | ||||
|       "Whether or not the layer control is shown" | ||||
|     ).map<boolean>( | ||||
|       (str) => str !== "false", | ||||
|       [], | ||||
|       (b) => "" + b | ||||
|     ); | ||||
| 
 | ||||
|   public FilterIsOpened: UIEventSource<boolean> = | ||||
|     QueryParameters.GetQueryParameter( | ||||
|       "filter-toggle", | ||||
|       "false", | ||||
|       "Whether or not the filter is shown" | ||||
|     ).map<boolean>( | ||||
|       (str) => str !== "false", | ||||
|       [], | ||||
|       (b) => "" + b | ||||
|     ); | ||||
| 
 | ||||
|   public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter( | ||||
|     "tab", | ||||
|     "0", | ||||
|     `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` | ||||
|   ).map<number>( | ||||
|     (str) => (isNaN(Number(str)) ? 0 : Number(str)), | ||||
|     [], | ||||
|     (n) => "" + n | ||||
|   ); | ||||
| 
 | ||||
|   constructor(layoutToUse: LayoutConfig) { | ||||
|     const self = this; | ||||
| 
 | ||||
|     this.layoutToUse.setData(layoutToUse); | ||||
| 
 | ||||
|     // -- Location control initialization
 | ||||
|     { | ||||
|       const zoom = State.asFloat( | ||||
|         QueryParameters.GetQueryParameter( | ||||
|           "z", | ||||
|           "" + (layoutToUse?.startZoom ?? 1), | ||||
|           "The initial/current zoom level" | ||||
|         ).syncWith(LocalStorageSource.Get("zoom")) | ||||
|       ); | ||||
|       const lat = State.asFloat( | ||||
|         QueryParameters.GetQueryParameter( | ||||
|           "lat", | ||||
|           "" + (layoutToUse?.startLat ?? 0), | ||||
|           "The initial/current latitude" | ||||
|         ).syncWith(LocalStorageSource.Get("lat")) | ||||
|       ); | ||||
|       const lon = State.asFloat( | ||||
|         QueryParameters.GetQueryParameter( | ||||
|           "lon", | ||||
|           "" + (layoutToUse?.startLon ?? 0), | ||||
|           "The initial/current longitude of the app" | ||||
|         ).syncWith(LocalStorageSource.Get("lon")) | ||||
|       ); | ||||
| 
 | ||||
|       this.locationControl = new UIEventSource<Loc>({ | ||||
|         zoom: Utils.asFloat(zoom.data), | ||||
|         lat: Utils.asFloat(lat.data), | ||||
|         lon: Utils.asFloat(lon.data), | ||||
|       }).addCallback((latlonz) => { | ||||
|         zoom.setData(latlonz.zoom); | ||||
|         lat.setData(latlonz.lat); | ||||
|         lon.setData(latlonz.lon); | ||||
|       }); | ||||
| 
 | ||||
|       this.layoutToUse.addCallback((layoutToUse) => { | ||||
|         const lcd = self.locationControl.data; | ||||
|         lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; | ||||
|         lcd.lat = lcd.lat ?? layoutToUse?.startLat; | ||||
|         lcd.lon = lcd.lon ?? layoutToUse?.startLon; | ||||
|         self.locationControl.ping(); | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     // Helper function to initialize feature switches
 | ||||
|     function featSw( | ||||
|       key: string, | ||||
|       deflt: (layout: LayoutConfig) => boolean, | ||||
|       documentation: string | ||||
|     ): UIEventSource<boolean> { | ||||
|       const queryParameterSource = QueryParameters.GetQueryParameter( | ||||
|         key, | ||||
|     public readonly selectedElement = new UIEventSource<any>( | ||||
|         undefined, | ||||
|         documentation | ||||
|       ); | ||||
|       // I'm so sorry about someone trying to decipher this
 | ||||
| 
 | ||||
|       // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
 | ||||
|       return UIEventSource.flatten( | ||||
|         self.layoutToUse.map((layout) => { | ||||
|           const defaultValue = deflt(layout); | ||||
|           const queryParam = QueryParameters.GetQueryParameter( | ||||
|             key, | ||||
|             "" + defaultValue, | ||||
|             documentation | ||||
|           ); | ||||
|           return queryParam.map((str) => | ||||
|             str === undefined ? defaultValue : str !== "false" | ||||
|           ); | ||||
|         }), | ||||
|         [queryParameterSource] | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     // Feature switch initialization - not as a function as the UIEventSources are readonly
 | ||||
|     { | ||||
|       this.featureSwitchUserbadge = featSw( | ||||
|         "fs-userbadge", | ||||
|         (layoutToUse) => layoutToUse?.enableUserBadge ?? true, | ||||
|         "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode." | ||||
|       ); | ||||
|       this.featureSwitchSearch = featSw( | ||||
|         "fs-search", | ||||
|         (layoutToUse) => layoutToUse?.enableSearch ?? true, | ||||
|         "Disables/Enables the search bar" | ||||
|       ); | ||||
|       this.featureSwitchLayers = featSw( | ||||
|         "fs-layers", | ||||
|         (layoutToUse) => layoutToUse?.enableLayers ?? true, | ||||
|         "Disables/Enables the layer control" | ||||
|       ); | ||||
|       this.featureSwitchFilter = featSw( | ||||
|         "fs-filter", | ||||
|         (layoutToUse) => layoutToUse?.enableLayers ?? true, | ||||
|         "Disables/Enables the filter" | ||||
|       ); | ||||
|       this.featureSwitchAddNew = featSw( | ||||
|         "fs-add-new", | ||||
|         (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, | ||||
|         "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)" | ||||
|       ); | ||||
|       this.featureSwitchWelcomeMessage = featSw( | ||||
|         "fs-welcome-message", | ||||
|         () => true, | ||||
|         "Disables/enables the help menu or welcome message" | ||||
|       ); | ||||
|       this.featureSwitchIframe = featSw( | ||||
|         "fs-iframe", | ||||
|         () => false, | ||||
|         "Disables/Enables the iframe-popup" | ||||
|       ); | ||||
|       this.featureSwitchMoreQuests = featSw( | ||||
|         "fs-more-quests", | ||||
|         (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, | ||||
|         "Disables/Enables the 'More Quests'-tab in the welcome message" | ||||
|       ); | ||||
|       this.featureSwitchShareScreen = featSw( | ||||
|         "fs-share-screen", | ||||
|         (layoutToUse) => layoutToUse?.enableShareScreen ?? true, | ||||
|         "Disables/Enables the 'Share-screen'-tab in the welcome message" | ||||
|       ); | ||||
|       this.featureSwitchGeolocation = featSw( | ||||
|         "fs-geolocation", | ||||
|         (layoutToUse) => layoutToUse?.enableGeolocation ?? true, | ||||
|         "Disables/Enables the geolocation button" | ||||
|       ); | ||||
|       this.featureSwitchShowAllQuestions = featSw( | ||||
|         "fs-all-questions", | ||||
|         (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false, | ||||
|         "Always show all questions" | ||||
|       ); | ||||
| 
 | ||||
|       this.featureSwitchIsTesting = QueryParameters.GetQueryParameter( | ||||
|         "test", | ||||
|         "false", | ||||
|         "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org" | ||||
|       ).map( | ||||
|         (str) => str === "true", | ||||
|         [], | ||||
|         (b) => "" + b | ||||
|       ); | ||||
| 
 | ||||
|       this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter( | ||||
|         "debug", | ||||
|         "false", | ||||
|         "If true, shows some extra debugging help such as all the available tags on every object" | ||||
|       ).map( | ||||
|         (str) => str === "true", | ||||
|         [], | ||||
|         (b) => "" + b | ||||
|       ); | ||||
| 
 | ||||
|       this.featureSwitchApiURL = QueryParameters.GetQueryParameter( | ||||
|         "backend", | ||||
|         "osm", | ||||
|         "The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'" | ||||
|       ); | ||||
|     } | ||||
|     { | ||||
|       // Some other feature switches
 | ||||
|       const customCssQP = QueryParameters.GetQueryParameter( | ||||
|         "custom-css", | ||||
|         "", | ||||
|         "If specified, the custom css from the given link will be loaded additionaly" | ||||
|       ); | ||||
|       if (customCssQP.data !== undefined && customCssQP.data !== "") { | ||||
|         Utils.LoadCustomCss(customCssQP.data); | ||||
|       } | ||||
| 
 | ||||
|       this.backgroundLayerId = QueryParameters.GetQueryParameter( | ||||
|         "background", | ||||
|         layoutToUse?.defaultBackgroundId ?? "osm", | ||||
|         "The id of the background layer to start with" | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (Utils.runningFromConsole) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     this.osmConnection = new OsmConnection( | ||||
|       this.featureSwitchIsTesting.data, | ||||
|       QueryParameters.GetQueryParameter( | ||||
|         "oauth_token", | ||||
|         undefined, | ||||
|         "Used to complete the login" | ||||
|       ), | ||||
|       layoutToUse?.id, | ||||
|       true, | ||||
|       // @ts-ignore
 | ||||
|       this.featureSwitchApiURL.data | ||||
|         "Selected element" | ||||
|     ); | ||||
| 
 | ||||
|     this.allElements = new ElementStorage(); | ||||
|     this.changes = new Changes(); | ||||
|     this.osmApiFeatureSource = new OsmApiFeatureSource(); | ||||
|     /** | ||||
|      * Keeps track of relations: which way is part of which other way? | ||||
|      * Set by the overpass-updater; used in the metatagging | ||||
|      */ | ||||
|     public readonly knownRelations = new UIEventSource<Map<string, { role: string; relation: Relation }[]>>(undefined, "Relation memberships"); | ||||
| 
 | ||||
|     new PendingChangesUploader(this.changes, this.selectedElement); | ||||
|     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>; | ||||
|     public readonly featureSwitchMoreQuests: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchShareScreen: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchGeolocation: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchIsTesting: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchIsDebugging: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>; | ||||
|     public readonly featureSwitchApiURL: UIEventSource<string>; | ||||
|     public readonly featureSwitchFilter: UIEventSource<boolean>; | ||||
| 
 | ||||
|     this.mangroveIdentity = new MangroveIdentity( | ||||
|       this.osmConnection.GetLongPreference("identity", "mangrove") | ||||
|     ); | ||||
|     /** | ||||
|      * The map location: currently centered lat, lon and zoom | ||||
|      */ | ||||
|     public readonly locationControl = new UIEventSource<Loc>(undefined); | ||||
|     public backgroundLayer; | ||||
|     public readonly backgroundLayerId: UIEventSource<string>; | ||||
| 
 | ||||
|     this.installedThemes = new InstalledThemes( | ||||
|       this.osmConnection | ||||
|     ).installedThemes; | ||||
|     /* Last location where a click was registered | ||||
|      */ | ||||
|     public readonly LastClickLocation: UIEventSource<{ | ||||
|         lat: number; | ||||
|         lon: number; | ||||
|     }> = new UIEventSource<{ lat: number; lon: number }>(undefined); | ||||
| 
 | ||||
|     // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 | ||||
|     this.favouriteLayers = LocalStorageSource.Get("favouriteLayers") | ||||
|       .syncWith(this.osmConnection.GetLongPreference("favouriteLayers")) | ||||
|       .map( | ||||
|         (str) => Utils.Dedup(str?.split(";")) ?? [], | ||||
|     /** | ||||
|      * The location as delivered by the GPS | ||||
|      */ | ||||
|     public currentGPSLocation: UIEventSource<{ | ||||
|         latlng: { lat: number; lng: number }; | ||||
|         accuracy: number; | ||||
|     }> = new UIEventSource<{ | ||||
|         latlng: { lat: number; lng: number }; | ||||
|         accuracy: number; | ||||
|     }>(undefined); | ||||
|     public layoutDefinition: string; | ||||
|     public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; | ||||
| 
 | ||||
|     public layerControlIsOpened: UIEventSource<boolean> = | ||||
|         QueryParameters.GetQueryParameter( | ||||
|             "layer-control-toggle", | ||||
|             "false", | ||||
|             "Whether or not the layer control is shown" | ||||
|         ).map<boolean>( | ||||
|             (str) => str !== "false", | ||||
|             [], | ||||
|             (b) => "" + b | ||||
|         ); | ||||
| 
 | ||||
|     public FilterIsOpened: UIEventSource<boolean> = | ||||
|         QueryParameters.GetQueryParameter( | ||||
|             "filter-toggle", | ||||
|             "false", | ||||
|             "Whether or not the filter is shown" | ||||
|         ).map<boolean>( | ||||
|             (str) => str !== "false", | ||||
|             [], | ||||
|             (b) => "" + b | ||||
|         ); | ||||
| 
 | ||||
|     public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter( | ||||
|         "tab", | ||||
|         "0", | ||||
|         `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` | ||||
|     ).map<number>( | ||||
|         (str) => (isNaN(Number(str)) ? 0 : Number(str)), | ||||
|         [], | ||||
|         (layers) => Utils.Dedup(layers)?.join(";") | ||||
|       ); | ||||
| 
 | ||||
|     Locale.language.syncWith(this.osmConnection.GetPreference("language")); | ||||
| 
 | ||||
|     Locale.language | ||||
|       .addCallback((currentLanguage) => { | ||||
|         const layoutToUse = self.layoutToUse.data; | ||||
|         if (layoutToUse === undefined) { | ||||
|           return; | ||||
|         } | ||||
|         if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { | ||||
|           console.log( | ||||
|             "Resetting language to", | ||||
|             layoutToUse.language[0], | ||||
|             "as", | ||||
|             currentLanguage, | ||||
|             " is unsupported" | ||||
|           ); | ||||
|           // The current language is not supported -> switch to a supported one
 | ||||
|           Locale.language.setData(layoutToUse.language[0]); | ||||
|         } | ||||
|       }) | ||||
|       .ping(); | ||||
| 
 | ||||
|     new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); | ||||
|   } | ||||
| 
 | ||||
|   private static asFloat(source: UIEventSource<string>): UIEventSource<number> { | ||||
|     return source.map( | ||||
|       (str) => { | ||||
|         let parsed = parseFloat(str); | ||||
|         return isNaN(parsed) ? undefined : parsed; | ||||
|       }, | ||||
|       [], | ||||
|       (fl) => { | ||||
|         if (fl === undefined || isNaN(fl)) { | ||||
|           return undefined; | ||||
|         } | ||||
|         return ("" + fl).substr(0, 8); | ||||
|       } | ||||
|         (n) => "" + n | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|     constructor(layoutToUse: LayoutConfig) { | ||||
|         const self = this; | ||||
| 
 | ||||
|         this.layoutToUse.setData(layoutToUse); | ||||
| 
 | ||||
|         // -- Location control initialization
 | ||||
|         { | ||||
|             const zoom = State.asFloat( | ||||
|                 QueryParameters.GetQueryParameter( | ||||
|                     "z", | ||||
|                     "" + (layoutToUse?.startZoom ?? 1), | ||||
|                     "The initial/current zoom level" | ||||
|                 ).syncWith(LocalStorageSource.Get("zoom")) | ||||
|             ); | ||||
|             const lat = State.asFloat( | ||||
|                 QueryParameters.GetQueryParameter( | ||||
|                     "lat", | ||||
|                     "" + (layoutToUse?.startLat ?? 0), | ||||
|                     "The initial/current latitude" | ||||
|                 ).syncWith(LocalStorageSource.Get("lat")) | ||||
|             ); | ||||
|             const lon = State.asFloat( | ||||
|                 QueryParameters.GetQueryParameter( | ||||
|                     "lon", | ||||
|                     "" + (layoutToUse?.startLon ?? 0), | ||||
|                     "The initial/current longitude of the app" | ||||
|                 ).syncWith(LocalStorageSource.Get("lon")) | ||||
|             ); | ||||
| 
 | ||||
|             this.locationControl = new UIEventSource<Loc>({ | ||||
|                 zoom: Utils.asFloat(zoom.data), | ||||
|                 lat: Utils.asFloat(lat.data), | ||||
|                 lon: Utils.asFloat(lon.data), | ||||
|             }).addCallback((latlonz) => { | ||||
|                 zoom.setData(latlonz.zoom); | ||||
|                 lat.setData(latlonz.lat); | ||||
|                 lon.setData(latlonz.lon); | ||||
|             }); | ||||
| 
 | ||||
|             this.layoutToUse.addCallback((layoutToUse) => { | ||||
|                 const lcd = self.locationControl.data; | ||||
|                 lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; | ||||
|                 lcd.lat = lcd.lat ?? layoutToUse?.startLat; | ||||
|                 lcd.lon = lcd.lon ?? layoutToUse?.startLon; | ||||
|                 self.locationControl.ping(); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         // Helper function to initialize feature switches
 | ||||
|         function featSw( | ||||
|             key: string, | ||||
|             deflt: (layout: LayoutConfig) => boolean, | ||||
|             documentation: string | ||||
|         ): UIEventSource<boolean> { | ||||
|             const queryParameterSource = QueryParameters.GetQueryParameter( | ||||
|                 key, | ||||
|                 undefined, | ||||
|                 documentation | ||||
|             ); | ||||
|             // I'm so sorry about someone trying to decipher this
 | ||||
| 
 | ||||
|             // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
 | ||||
|             return UIEventSource.flatten( | ||||
|                 self.layoutToUse.map((layout) => { | ||||
|                     const defaultValue = deflt(layout); | ||||
|                     const queryParam = QueryParameters.GetQueryParameter( | ||||
|                         key, | ||||
|                         "" + defaultValue, | ||||
|                         documentation | ||||
|                     ); | ||||
|                     return queryParam.map((str) => | ||||
|                         str === undefined ? defaultValue : str !== "false" | ||||
|                     ); | ||||
|                 }), | ||||
|                 [queryParameterSource] | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         // Feature switch initialization - not as a function as the UIEventSources are readonly
 | ||||
|         { | ||||
|             this.featureSwitchUserbadge = featSw( | ||||
|                 "fs-userbadge", | ||||
|                 (layoutToUse) => layoutToUse?.enableUserBadge ?? true, | ||||
|                 "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode." | ||||
|             ); | ||||
|             this.featureSwitchSearch = featSw( | ||||
|                 "fs-search", | ||||
|                 (layoutToUse) => layoutToUse?.enableSearch ?? true, | ||||
|                 "Disables/Enables the search bar" | ||||
|             ); | ||||
|             this.featureSwitchLayers = featSw( | ||||
|                 "fs-layers", | ||||
|                 (layoutToUse) => layoutToUse?.enableLayers ?? true, | ||||
|                 "Disables/Enables the layer control" | ||||
|             ); | ||||
|             this.featureSwitchFilter = featSw( | ||||
|                 "fs-filter", | ||||
|                 (layoutToUse) => layoutToUse?.enableLayers ?? true, | ||||
|                 "Disables/Enables the filter" | ||||
|             ); | ||||
|             this.featureSwitchAddNew = featSw( | ||||
|                 "fs-add-new", | ||||
|                 (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, | ||||
|                 "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)" | ||||
|             ); | ||||
|             this.featureSwitchWelcomeMessage = featSw( | ||||
|                 "fs-welcome-message", | ||||
|                 () => true, | ||||
|                 "Disables/enables the help menu or welcome message" | ||||
|             ); | ||||
|             this.featureSwitchIframe = featSw( | ||||
|                 "fs-iframe", | ||||
|                 () => false, | ||||
|                 "Disables/Enables the iframe-popup" | ||||
|             ); | ||||
|             this.featureSwitchMoreQuests = featSw( | ||||
|                 "fs-more-quests", | ||||
|                 (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, | ||||
|                 "Disables/Enables the 'More Quests'-tab in the welcome message" | ||||
|             ); | ||||
|             this.featureSwitchShareScreen = featSw( | ||||
|                 "fs-share-screen", | ||||
|                 (layoutToUse) => layoutToUse?.enableShareScreen ?? true, | ||||
|                 "Disables/Enables the 'Share-screen'-tab in the welcome message" | ||||
|             ); | ||||
|             this.featureSwitchGeolocation = featSw( | ||||
|                 "fs-geolocation", | ||||
|                 (layoutToUse) => layoutToUse?.enableGeolocation ?? true, | ||||
|                 "Disables/Enables the geolocation button" | ||||
|             ); | ||||
|             this.featureSwitchShowAllQuestions = featSw( | ||||
|                 "fs-all-questions", | ||||
|                 (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false, | ||||
|                 "Always show all questions" | ||||
|             ); | ||||
| 
 | ||||
|             this.featureSwitchIsTesting = QueryParameters.GetQueryParameter( | ||||
|                 "test", | ||||
|                 "false", | ||||
|                 "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org" | ||||
|             ).map( | ||||
|                 (str) => str === "true", | ||||
|                 [], | ||||
|                 (b) => "" + b | ||||
|             ); | ||||
| 
 | ||||
|             this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter( | ||||
|                 "debug", | ||||
|                 "false", | ||||
|                 "If true, shows some extra debugging help such as all the available tags on every object" | ||||
|             ).map( | ||||
|                 (str) => str === "true", | ||||
|                 [], | ||||
|                 (b) => "" + b | ||||
|             ); | ||||
| 
 | ||||
|             this.featureSwitchApiURL = QueryParameters.GetQueryParameter( | ||||
|                 "backend", | ||||
|                 "osm", | ||||
|                 "The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'" | ||||
|             ); | ||||
|         } | ||||
|         { | ||||
|             // Some other feature switches
 | ||||
|             const customCssQP = QueryParameters.GetQueryParameter( | ||||
|                 "custom-css", | ||||
|                 "", | ||||
|                 "If specified, the custom css from the given link will be loaded additionaly" | ||||
|             ); | ||||
|             if (customCssQP.data !== undefined && customCssQP.data !== "") { | ||||
|                 Utils.LoadCustomCss(customCssQP.data); | ||||
|             } | ||||
| 
 | ||||
|             this.backgroundLayerId = QueryParameters.GetQueryParameter( | ||||
|                 "background", | ||||
|                 layoutToUse?.defaultBackgroundId ?? "osm", | ||||
|                 "The id of the background layer to start with" | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (Utils.runningFromConsole) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.osmConnection = new OsmConnection( | ||||
|             this.featureSwitchIsTesting.data, | ||||
|             QueryParameters.GetQueryParameter( | ||||
|                 "oauth_token", | ||||
|                 undefined, | ||||
|                 "Used to complete the login" | ||||
|             ), | ||||
|             layoutToUse?.id, | ||||
|             true, | ||||
|             // @ts-ignore
 | ||||
|             this.featureSwitchApiURL.data | ||||
|         ); | ||||
| 
 | ||||
|         this.allElements = new ElementStorage(); | ||||
|         this.changes = new Changes(); | ||||
|         this.osmApiFeatureSource = new OsmApiFeatureSource(); | ||||
| 
 | ||||
|         new PendingChangesUploader(this.changes, this.selectedElement); | ||||
| 
 | ||||
|         this.mangroveIdentity = new MangroveIdentity( | ||||
|             this.osmConnection.GetLongPreference("identity", "mangrove") | ||||
|         ); | ||||
| 
 | ||||
|         this.installedThemes = new InstalledThemes( | ||||
|             this.osmConnection | ||||
|         ).installedThemes; | ||||
| 
 | ||||
|         // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 | ||||
|         this.favouriteLayers = LocalStorageSource.Get("favouriteLayers") | ||||
|             .syncWith(this.osmConnection.GetLongPreference("favouriteLayers")) | ||||
|             .map( | ||||
|                 (str) => Utils.Dedup(str?.split(";")) ?? [], | ||||
|                 [], | ||||
|                 (layers) => Utils.Dedup(layers)?.join(";") | ||||
|             ); | ||||
| 
 | ||||
|         Locale.language.syncWith(this.osmConnection.GetPreference("language")); | ||||
| 
 | ||||
|         Locale.language | ||||
|             .addCallback((currentLanguage) => { | ||||
|                 const layoutToUse = self.layoutToUse.data; | ||||
|                 if (layoutToUse === undefined) { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { | ||||
|                     console.log( | ||||
|                         "Resetting language to", | ||||
|                         layoutToUse.language[0], | ||||
|                         "as", | ||||
|                         currentLanguage, | ||||
|                         " is unsupported" | ||||
|                     ); | ||||
|                     // The current language is not supported -> switch to a supported one
 | ||||
|                     Locale.language.setData(layoutToUse.language[0]); | ||||
|                 } | ||||
|             }) | ||||
|             .ping(); | ||||
| 
 | ||||
|         new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); | ||||
|     } | ||||
| 
 | ||||
|     private static asFloat(source: UIEventSource<string>): UIEventSource<number> { | ||||
|         return source.map( | ||||
|             (str) => { | ||||
|                 let parsed = parseFloat(str); | ||||
|                 return isNaN(parsed) ? undefined : parsed; | ||||
|             }, | ||||
|             [], | ||||
|             (fl) => { | ||||
|                 if (fl === undefined || isNaN(fl)) { | ||||
|                     return undefined; | ||||
|                 } | ||||
|                 return ("" + fl).substr(0, 8); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue