forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			211 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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, Preset} from "./Customizations/LayerDefinition";
 | 
						|
import {ElementStorage} from "./Logic/ElementStorage";
 | 
						|
import {Changes} from "./Logic/Osm/Changes";
 | 
						|
import {OsmConnection} from "./Logic/Osm/OsmConnection";
 | 
						|
import Locale from "./UI/i18n/Locale";
 | 
						|
import {VariableUiElement} from "./UI/Base/VariableUIElement";
 | 
						|
import Translations from "./UI/i18n/Translations";
 | 
						|
import {CustomLayersState} from "./Logic/CustomLayersState";
 | 
						|
import {FilteredLayer} from "./Logic/FilteredLayer";
 | 
						|
import {LayerUpdater} from "./Logic/LayerUpdater";
 | 
						|
 | 
						|
/**
 | 
						|
 * Contains the global state: a bunch of UI-event sources
 | 
						|
 */
 | 
						|
 | 
						|
export class State {
 | 
						|
 | 
						|
    // The singleton of the global state
 | 
						|
    public static state: State;
 | 
						|
    
 | 
						|
    public static vNumber = "0.0.4";
 | 
						|
 | 
						|
    public static runningFromConsole: boolean = false; 
 | 
						|
 | 
						|
    /**
 | 
						|
     THe layout to use
 | 
						|
     */
 | 
						|
    public readonly layoutToUse = new UIEventSource<Layout>(undefined);
 | 
						|
    public layoutDefinition : string;
 | 
						|
 | 
						|
    /**
 | 
						|
     The mapping from id -> UIEventSource<properties>
 | 
						|
     */
 | 
						|
    public allElements: ElementStorage;
 | 
						|
    /**
 | 
						|
     THe change handler
 | 
						|
     */
 | 
						|
    public changes: Changes;
 | 
						|
    /**
 | 
						|
     THe basemap with leaflet instance
 | 
						|
     */
 | 
						|
    public bm;
 | 
						|
    /**
 | 
						|
     The user crednetials
 | 
						|
     */
 | 
						|
    public osmConnection: OsmConnection;
 | 
						|
    
 | 
						|
    public layerUpdater : LayerUpdater;
 | 
						|
    
 | 
						|
    
 | 
						|
    public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([])
 | 
						|
    public presets: UIEventSource<Preset[]> = new UIEventSource<Preset[]>([])
 | 
						|
    
 | 
						|
    /**
 | 
						|
     *  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>;
 | 
						|
    public readonly featureSwitchMoreQuests: UIEventSource<boolean>;
 | 
						|
    public readonly featureSwitchShareScreen: UIEventSource<boolean>;
 | 
						|
    public readonly featureSwitchGeolocation: 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);
 | 
						|
 | 
						|
    /**
 | 
						|
     * Layers can be marked as favourites, they show up in a custom layout
 | 
						|
     */
 | 
						|
    public favourteLayers: UIEventSource<string[]> = new UIEventSource<string[]>([])
 | 
						|
 | 
						|
 | 
						|
    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);
 | 
						|
        this.featureSwitchMoreQuests = featSw("fs-more-quests", () => layoutToUse?.enableMoreQuests);
 | 
						|
        this.featureSwitchShareScreen = featSw("fs-share-screen", () => layoutToUse?.enableShareScreen);
 | 
						|
        this.featureSwitchGeolocation = featSw("fs-geolocation", () => layoutToUse?.enableGeolocation);
 | 
						|
 | 
						|
        this.osmConnection = new OsmConnection(
 | 
						|
            QueryParameters.GetQueryParameter("test", "false").data === "true",
 | 
						|
            QueryParameters.GetQueryParameter("oauth_token", undefined)
 | 
						|
        );
 | 
						|
        
 | 
						|
        CustomLayersState.InitFavouriteLayers(this);
 | 
						|
       
 | 
						|
        Locale.language.syncWith(this.osmConnection.GetPreference("language"));
 | 
						|
 | 
						|
 | 
						|
        Locale.language.addCallback((currentLanguage) => {
 | 
						|
            if (layoutToUse.supportedLanguages.indexOf(currentLanguage) < 0) {
 | 
						|
                console.log("Resetting language to", layoutToUse.supportedLanguages[0], "as", currentLanguage, " is unsupported")
 | 
						|
                // The current language is not supported -> switch to a supported one
 | 
						|
                Locale.language.setData(layoutToUse.supportedLanguages[0]);
 | 
						|
            }
 | 
						|
        }).ping()
 | 
						|
 | 
						|
        document.title = Translations.W(layoutToUse.title).InnerRender();
 | 
						|
        Locale.language.addCallback(e => {
 | 
						|
            document.title = Translations.W(layoutToUse.title).InnerRender();
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
        this.allElements = new ElementStorage();
 | 
						|
        this.changes = new Changes(
 | 
						|
            "Beantwoorden van vragen met #MapComplete voor vragenset #" + this.layoutToUse.data.name,
 | 
						|
           this);
 | 
						|
 | 
						|
        if(State.runningFromConsole){
 | 
						|
            console.warn("running from console - not initializing map. Assuming test.html");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        if (document.getElementById("leafletDiv") === null) {
 | 
						|
            console.warn("leafletDiv not found - not initializing map. Assuming test.html");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
       
 | 
						|
 | 
						|
    }
 | 
						|
    
 | 
						|
    public GetFilteredLayerFor(id: string) : FilteredLayer{
 | 
						|
        for (const flayer of this.filteredLayers.data) {
 | 
						|
            if(flayer.layerDef.id === id){
 | 
						|
                return flayer;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return undefined;
 | 
						|
    }
 | 
						|
}
 |