Refactoring: introduction of global state to simplify getting common objects

This commit is contained in:
Pieter Vander Vennet 2020-07-31 01:45:54 +02:00
parent afaaaaadb1
commit 004eead4ee
34 changed files with 532 additions and 506 deletions

View file

@ -2,75 +2,60 @@ import {UIElement} from "./UIElement";
import {UIEventSource} from "./UIEventSource";
import {OsmConnection} from "../Logic/Osm/OsmConnection";
import Translations from "./i18n/Translations";
import {State} from "../State";
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 startZoom: number;
constructor(
startZoom: number,
centermessage: UIEventSource<string>,
osmConnection: OsmConnection,
location: UIEventSource<{ zoom: number }>,
queryRunning: UIEventSource<boolean>
) {
super(centermessage);
super(State.state.centerMessage);
this.startZoom = startZoom;
this._centermessage = centermessage;
this._location = location;
this._osmConnection = osmConnection;
this._queryRunning = queryRunning;
this.ListenTo(State.state.locationControl);
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 {
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();
return this.prep().innerHtml;
}
private ShouldShowSomething() : boolean{
if (this._queryRunning.data) {
return true;
}
return this._zoomInMore.data;
}
InnerUpdate(htmlElement: HTMLElement) {
const pstyle = htmlElement.parentElement.style;
if (this._centermessage.data != "") {
if (State.state.centerMessage.data != "") {
pstyle.opacity = "1";
pstyle.pointerEvents = "all";
this._osmConnection.registerActivateOsmAUthenticationClass();
State.state.osmConnection.registerActivateOsmAUthenticationClass();
return;
}
pstyle.pointerEvents = "none";
if (this.ShouldShowSomething()) {
pstyle.opacity = "0.5";
} else {
if (this.prep().done) {
pstyle.opacity = "0";
} else {
pstyle.opacity = "0.5";
}
}

View file

@ -11,6 +11,7 @@ import Translations from "./i18n/Translations";
import {Changes} from "../Logic/Osm/Changes";
import {UserDetails} from "../Logic/Osm/OsmConnection";
import {FixedUiElement} from "./Base/FixedUiElement";
import {State} from "../State";
export class FeatureInfoBox extends UIElement {
@ -23,14 +24,11 @@ export class FeatureInfoBox extends UIElement {
*/
private _tagsES: UIEventSource<any>;
private _changes: Changes;
private _userDetails: UIEventSource<UserDetails>;
private _title: UIElement;
private _osmLink: UIElement;
private _wikipedialink: UIElement;
private _infoboxes: TagDependantUIElement[];
private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone();
@ -41,15 +39,11 @@ export class FeatureInfoBox extends UIElement {
tagsES: UIEventSource<any>,
title: TagRenderingOptions | UIElement | string,
elementsToShow: TagDependantUIElementConstructor[],
changes: Changes,
userDetails: UIEventSource<UserDetails>
) {
super(tagsES);
this._feature = feature;
this._tagsES = tagsES;
this._changes = changes;
this._userDetails = userDetails;
this.ListenTo(userDetails);
this.ListenTo(State.state.osmConnection.userDetails);
const deps = {tags: this._tagsES, changes: this._changes}
@ -112,7 +106,7 @@ export class FeatureInfoBox extends UIElement {
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
let mostImportantQuestion;
let score = -1000;

View file

@ -2,25 +2,29 @@ import {UIEventSource} from "./UIEventSource";
import {UIElement} from "./UIElement";
import {VariableUiElement} from "./Base/VariableUIElement";
import Translations from "./i18n/Translations";
import {State} from "../State";
/**
* Handles the full screen popup on mobile
*/
export class FullScreenMessageBoxHandler {
private _uielement: UIEventSource<UIElement>;
constructor(uielement: UIEventSource<UIElement>,
onClear: (() => void)) {
this._uielement = uielement;
this.listenTo(uielement);
constructor(onClear: (() => void)) {
this._uielement = State.state.fullScreenMessage;
const self = this;
this._uielement.addCallback(function () {
self.update();
});
this.update();
if (window !== undefined) {
window.onhashchange = function () {
if (location.hash === "") {
// No more element: back to the map!
uielement.setData(undefined);
self._uielement.setData(undefined);
onClear();
}
}
@ -28,7 +32,7 @@ export class FullScreenMessageBoxHandler {
Translations.t.general.returnToTheMap
.onClick(() => {
uielement.setData(undefined);
self._uielement.setData(undefined);
onClear();
})
.AttachTo("to-the-map");
@ -36,13 +40,6 @@ export class FullScreenMessageBoxHandler {
}
listenTo(uiEventSource: UIEventSource<any>) {
const self = this;
uiEventSource.addCallback(function () {
self.update();
})
}
update() {
const wrapper = document.getElementById("messagesboxmobilewrapper");

View file

@ -3,12 +3,15 @@ import {ImageSearcher} from "../../Logic/ImageSearcher";
import {UIEventSource} from "../UIEventSource";
import {SlideShow} from "../SlideShow";
import {FixedUiElement} from "../Base/FixedUiElement";
import {VerticalCombine} from "../Base/VerticalCombine";
import {VariableUiElement} from "../Base/VariableUIElement";
import {ConfirmDialog} from "../ConfirmDialog";
import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor";
import {
Dependencies,
TagDependantUIElement,
TagDependantUIElementConstructor
} from "../../Customizations/UIElementConstructor";
import {Changes} from "../../Logic/Osm/Changes";
import {UserDetails} from "../../Logic/Osm/OsmConnection";
import {State} from "../../State";
export class ImageCarouselConstructor implements TagDependantUIElementConstructor{
IsKnown(properties: any): boolean {
@ -23,8 +26,8 @@ export class ImageCarouselConstructor implements TagDependantUIElementConstructo
return 0;
}
construct(dependencies: { tags: UIEventSource<any>, changes: Changes }): TagDependantUIElement {
return new ImageCarousel(dependencies.tags, dependencies.changes);
construct(dependencies: Dependencies): TagDependantUIElement {
return new ImageCarousel(dependencies.tags);
}
}
@ -41,14 +44,11 @@ export class ImageCarousel extends TagDependantUIElement {
private readonly _deleteButton: UIElement;
private readonly _isDeleted: UIElement;
private readonly _userDetails : UIEventSource<UserDetails>;
constructor(tags: UIEventSource<any>, changes: Changes) {
constructor(tags: UIEventSource<any>) {
super(tags);
this._userDetails = changes.login.userDetails;
const self = this;
this.searcher = new ImageSearcher(tags, changes);
this.searcher = new ImageSearcher(tags);
this._uiElements = this.searcher.map((imageURLS: string[]) => {
const uiElements: UIElement[] = [];
@ -65,11 +65,11 @@ export class ImageCarousel extends TagDependantUIElement {
const showDeleteButton = this.slideshow._currentSlide.map((i) => {
if(!self._userDetails.data.loggedIn){
if(!State.state.osmConnection.userDetails.data.loggedIn){
return false;
}
return self.searcher.IsDeletable(self.searcher.data[i]);
}, [this.searcher, this._userDetails]);
}, [this.searcher, State.state.osmConnection.userDetails]);
this.slideshow._currentSlide.addCallback(() => {
showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons
})

View file

@ -1,9 +1,12 @@
import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor";
import {
Dependencies,
TagDependantUIElement,
TagDependantUIElementConstructor
} from "../../Customizations/UIElementConstructor";
import {ImageCarousel} from "./ImageCarousel";
import {UIEventSource} from "../UIEventSource";
import {ImageUploadFlow} from "../ImageUploadFlow";
import {Changes} from "../../Logic/Osm/Changes";
import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler";
import {State} from "../../State";
export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{
IsKnown(properties: any): boolean {
@ -27,16 +30,13 @@ class ImageCarouselWithUpload extends TagDependantUIElement {
private _imageElement: ImageCarousel;
private _pictureUploader: ImageUploadFlow;
constructor(dependencies: {tags: UIEventSource<any>, changes: Changes}) {
constructor(dependencies: Dependencies) {
super(dependencies.tags);
const tags = dependencies.tags;
const changes = dependencies.changes;
this._imageElement = new ImageCarousel(tags, changes);
const userDetails = changes.login.userDetails;
const license = changes.login.GetPreference( "pictures-license");
this._pictureUploader = new OsmImageUploadHandler(tags,
userDetails, license,
changes, this._imageElement.slideshow).getUI();
this._imageElement = new ImageCarousel(tags);
const userDetails = State.state.osmConnection.userDetails;
const license = State.state.osmConnection.GetPreference( "pictures-license");
this._pictureUploader = new OsmImageUploadHandler(tags, license, this._imageElement.slideshow).getUI();
}

View file

@ -9,6 +9,7 @@ import Translations from "./i18n/Translations";
import {fail} from "assert";
import Combine from "./Base/Combine";
import {VerticalCombine} from "./Base/VerticalCombine";
import {State} from "../State";
export class ImageUploadFlow extends UIElement {
private _licensePicker: UIElement;
@ -17,10 +18,8 @@ export class ImageUploadFlow extends UIElement {
private _didFail: 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 _userdetails: UIEventSource<UserDetails>;
constructor(
userInfo: UIEventSource<UserDetails>,
preferedLicense: UIEventSource<string>,
uploadOptions: ((license: string) =>
{
@ -30,9 +29,7 @@ export class ImageUploadFlow extends UIElement {
allDone: (() => void)
})
) {
super(undefined);
this._userdetails = userInfo;
this.ListenTo(userInfo);
super(State.state.osmConnection.userDetails);
this._uploadOptions = uploadOptions;
this.ListenTo(this._isUploading);
this.ListenTo(this._didFail);
@ -56,11 +53,11 @@ export class ImageUploadFlow extends UIElement {
InnerRender(): string {
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
}
if (!this._userdetails.data.loggedIn) {
if (!State.state.osmConnection.userDetails.data.loggedIn) {
return `<div class='activate-osm-authentication'>${t.pleaseLogin.Render()}</div>`;
}
@ -79,6 +76,16 @@ export class ImageUploadFlow extends UIElement {
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 "" +
"<div class='imageflow'>" +
@ -89,9 +96,9 @@ export class ImageUploadFlow extends UIElement {
`<span class='imageflow-add-picture'>${Translations.t.image.addPicture.R()}</span>` +
"<div class='break'></div>" +
"</div>" +
currentStateHtml +
Translations.t.image.respectPrivacy.Render() + "<br/>" +
this._licensePicker.Render() + "<br/>" +
new VerticalCombine(currentState).Render() +
"</label>" +
"<form id='fileselector-form-" + this.id + "'>" +
"<input id='fileselector-" + this.id + "' " +
@ -106,11 +113,11 @@ export class ImageUploadFlow extends UIElement {
InnerUpdate(htmlElement: HTMLElement) {
super.InnerUpdate(htmlElement);
const user = this._userdetails.data;
const user = State.state.osmConnection.userDetails.data;
htmlElement.onclick = function () {
if (!user.loggedIn) {
user.osmConnection.AttemptLogin();
State.state.osmConnection.AttemptLogin();
}
}

View file

@ -9,16 +9,13 @@ import {UIEventSource} from "./UIEventSource";
import {VariableUiElement} from "./Base/VariableUIElement";
import Combine from "./Base/Combine";
import {SubtleButton} from "./Base/SubtleButton";
import {State} from "../State";
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 }>) {
super(currentLocation);
this.currentLayout = currentLayout;
this.currentLocation = currentLocation;
constructor() {
super(State.state.locationControl);
}
InnerRender(): string {
@ -30,12 +27,13 @@ export class MoreScreen extends UIElement {
if (layout.hideFromOverview) {
continue
}
if (layout.name === this.currentLayout) {
if (layout.name === State.state.layoutToUse.data.name) {
continue;
}
const currentLocation = State.state.locationControl.data;
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 =
new SubtleButton(layout.icon,
new Combine([

View file

@ -1,23 +1,21 @@
import {UIElement} from "./UIElement";
import {UIEventSource} from "./UIEventSource";
import {Changes} from "../Logic/Osm/Changes";
import {State} from "../State";
export class PendingChanges extends UIElement {
private _pendingChangesCount: UIEventSource<number>;
private _countdown: UIEventSource<number>;
private _isSaving: UIEventSource<boolean>;
constructor(changes: Changes,
countdown: UIEventSource<number>) {
super(changes.pendingChangesES);
this.ListenTo(changes.isSaving);
this.ListenTo(countdown);
this._pendingChangesCount = changes.pendingChangesES;
this._countdown = countdown;
this._isSaving = changes.isSaving;
constructor() {
super(State.state.changes.pendingChangesES);
this.ListenTo(State.state.changes.isSaving);
this.ListenTo(State.state.secondsTillChangesAreSaved);
this._pendingChangesCount = State.state.changes.pendingChangesES;
this._isSaving = State.state.changes.isSaving;
this.onClick(() => {
changes.uploadAll();
State.state.changes.uploadAll();
})
}
@ -29,7 +27,7 @@ export class PendingChanges extends UIElement {
return "";
}
var restingSeconds = this._countdown.data / 1000;
var restingSeconds =State.state.secondsTillChangesAreSaved.data / 1000;
var dots = "";
while (restingSeconds > 0) {
dots += ".";

View file

@ -8,6 +8,7 @@ import {TextField} from "./Input/TextField";
import {Geocoding} from "../Logic/Osm/Geocoding";
import Translations from "./i18n/Translations";
import {Basemap} from "../Logic/Leaflet/Basemap";
import {State} from "../State";
export class SearchAndGo extends UIElement {
@ -23,12 +24,10 @@ export class SearchAndGo extends UIElement {
);
private _foundEntries = new UIEventSource([]);
private _map: Basemap;
private _goButton = new FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>");
constructor(map: Basemap) {
constructor() {
super(undefined);
this._map = map;
this.ListenTo(this._foundEntries);
const self = this;
@ -48,7 +47,7 @@ export class SearchAndGo extends UIElement {
this._searchField.Clear();
this._placeholder.setData(Translations.t.general.search.searching);
const self = this;
Geocoding.Search(searchString, this._map, (result) => {
Geocoding.Search(searchString, (result) => {
if (result.length == 0) {
this._placeholder.setData(Translations.t.general.search.nothing);
@ -60,7 +59,7 @@ export class SearchAndGo extends UIElement {
[bb[0], bb[2]],
[bb[1], bb[3]]
]
self._map.map.fitBounds(bounds);
State.state.bm.map.fitBounds(bounds);
this._placeholder.setData(Translations.t.general.search.search);
},
() => {

View file

@ -9,6 +9,7 @@ import {CheckBox} from "./Input/CheckBox";
import {VerticalCombine} from "./Base/VerticalCombine";
import {QueryParameters} from "../Logic/QueryParameters";
import {Img} from "./Img";
import {State} from "../State";
export class ShareScreen extends UIElement {
@ -19,7 +20,7 @@ export class ShareScreen extends UIElement {
private _link: UIElement;
private _linkStatus: UIElement;
constructor(layout: Layout, currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>) {
constructor() {
super(undefined)
const tr = Translations.t.general.sharescreen;
@ -32,6 +33,10 @@ export class ShareScreen extends UIElement {
true
)
optionCheckboxes.push(includeLocation);
const currentLocation = State.state.locationControl;
const layout = State.state.layoutToUse.data;
optionParts.push(includeLocation.isEnabled.map((includeL) => {
if (includeL) {
return `z=${currentLocation.data.zoom}&lat=${currentLocation.data.lat}&lon=${currentLocation.data.lon}`

View file

@ -11,6 +11,7 @@ import {VerticalCombine} from "./Base/VerticalCombine";
import Locale from "./i18n/Locale";
import {Changes} from "../Logic/Osm/Changes";
import {UserDetails} from "../Logic/Osm/OsmConnection";
import {State} from "../State";
export interface Preset {
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
*/
export class SimpleAddUI extends UIElement {
private _zoomlevel: UIEventSource<{ zoom: number }>;
private _addButtons: UIElement[];
private _lastClickLocation: UIEventSource<{ lat: number; lon: number }>;
private _changes: Changes;
private _selectedElement: UIEventSource<{ feature: any }>;
private _dataIsLoading: UIEventSource<boolean>;
private _userDetails: UIEventSource<UserDetails>;
private _confirmPreset: UIEventSource<Preset>
= new UIEventSource<Preset>(undefined);
@ -39,24 +36,17 @@ export class SimpleAddUI extends UIElement {
private goToInboxButton: UIElement = new SubtleButton("./assets/envelope.svg",
Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false});
constructor(zoomlevel: UIEventSource<{ zoom: number }>,
lastClickLocation: UIEventSource<{ lat: number, lon: number }>,
changes: Changes,
selectedElement: UIEventSource<{ feature: any }>,
constructor(
dataIsLoading: UIEventSource<boolean>,
userDetails: UIEventSource<UserDetails>,
addButtons: { description: string | UIElement, name: string | UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[],
) {
super(zoomlevel);
super(State.state.locationControl);
this.ListenTo(Locale.language);
this._zoomlevel = zoomlevel;
this._lastClickLocation = lastClickLocation;
this._changes = changes;
this._selectedElement = selectedElement;
this.ListenTo(State.state.osmConnection.userDetails);
this._dataIsLoading = dataIsLoading;
this._userDetails = userDetails;
this.ListenTo(userDetails);
this.ListenTo(dataIsLoading);
this._addButtons = [];
this.ListenTo(this._confirmPreset);
this.clss = "add-ui"
@ -102,26 +92,27 @@ export class SimpleAddUI extends UIElement {
const self = this;
return () => {
const loc = self._lastClickLocation.data;
let feature = self._changes.createElement(option.tags, loc.lat, loc.lon);
const loc = State.state.bm.lastClickLocation.data;
let feature = State.state.changes.createElement(option.tags, loc.lat, loc.lon);
option.layerToAddTo.AddNewElement(feature);
self._selectedElement.setData({feature: feature});
State.state.selectedElement.setData({feature: feature});
}
}
InnerRender(): string {
const userDetails = State.state.osmConnection.userDetails;
if (this._confirmPreset.data !== undefined) {
if(this._userDetails.data.dryRun){
if(userDetails.data.dryRun){
this.CreatePoint(this._confirmPreset.data)();
return;
}
return new Combine([
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.cancelButton
@ -134,15 +125,15 @@ export class SimpleAddUI extends UIElement {
let header: UIElement = Translations.t.general.add.header;
if(this._userDetails === undefined){
if(userDetails === undefined){
return header.Render();
}
if (!this._userDetails.data.loggedIn) {
if (!userDetails.data.loggedIn) {
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'>",
Translations.t.general.readYourMessages,
"</span>",
@ -150,7 +141,7 @@ export class SimpleAddUI extends UIElement {
]).Render();
}
if (this._userDetails.data.dryRun) {
if (userDetails.data.dryRun) {
header = new Combine([header,
"<span class='alert'>",
"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'>",
Translations.t.general.fewChangesBefore,
"</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()
}
@ -183,7 +174,7 @@ export class SimpleAddUI extends UIElement {
}
InnerUpdate(htmlElement: HTMLElement) {
this._userDetails.data.osmConnection.registerActivateOsmAUthenticationClass();
State.state.osmConnection.registerActivateOsmAUthenticationClass();
}
}

View file

@ -6,6 +6,9 @@ import {VariableUiElement} from "./Base/VariableUIElement";
import Translations from "./i18n/Translations";
import {UserDetails} from "../Logic/Osm/OsmConnection";
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
@ -14,27 +17,22 @@ export class UserBadge extends UIElement {
private _userDetails: UIEventSource<UserDetails>;
private _pendingChanges: UIElement;
private _logout: UIElement;
private _basemap: Basemap;
private _homeButton: UIElement;
private _languagePicker: UIElement;
constructor(userDetails: UIEventSource<UserDetails>,
pendingChanges: UIElement,
languagePicker: UIElement,
basemap: Basemap) {
super(userDetails);
this._userDetails = userDetails;
this._pendingChanges = pendingChanges;
this._basemap = basemap;
this._languagePicker = languagePicker;
constructor() {
super(State.state.osmConnection.userDetails);
this._userDetails = State.state.osmConnection.userDetails;
this._pendingChanges = new PendingChanges();
this._languagePicker = Locale.CreateLanguagePicker();
this._logout = new FixedUiElement("<img src='assets/logout.svg' class='small-userbadge-icon' alt='logout'>")
.onClick(() => {
userDetails.data.osmConnection.LogOut();
State.state.osmConnection.LogOut();
});
userDetails.addCallback(function () {
this._userDetails.addCallback(function () {
const profilePic = document.getElementById("profile-pic");
if (profilePic) {
@ -45,18 +43,18 @@ export class UserBadge extends UIElement {
});
this._homeButton = new VariableUiElement(
userDetails.map((userinfo) => {
this._userDetails.map((userinfo) => {
if (userinfo.home) {
return "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'> ";
}
return "";
})
).onClick(() => {
const home = userDetails.data?.home;
const home = State.state.osmConnection.userDetails.data?.home;
if (home === undefined) {
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],
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 =

60
UI/WelcomeMessage.ts Normal file
View 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()
}
}

View file

@ -3,14 +3,15 @@ import {LocalStorageSource} from "../../Logic/LocalStorageSource";
import {DropDown} from "../Input/DropDown";
import {Layout} from "../../Customizations/Layout";
import {UIElement} from "../UIElement";
import {State} from "../../State";
export default class Locale {
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}
}
), Locale.language);