Custom themes now stick to the user account and can be revisited, small improvements
This commit is contained in:
parent
bf6eae9af1
commit
4a0970a71f
23 changed files with 556 additions and 1748 deletions
|
@ -1,7 +1,6 @@
|
||||||
import {LayerDefinition} from "./LayerDefinition";
|
import {LayerDefinition} from "./LayerDefinition";
|
||||||
import {Layout} from "./Layout";
|
import {Layout} from "./Layout";
|
||||||
import {All} from "./Layouts/All";
|
import {All} from "./Layouts/All";
|
||||||
import {CustomLayout} from "../Logic/CustomLayers";
|
|
||||||
import {Groen} from "./Layouts/Groen";
|
import {Groen} from "./Layouts/Groen";
|
||||||
import Cyclofix from "./Layouts/Cyclofix";
|
import Cyclofix from "./Layouts/Cyclofix";
|
||||||
import {StreetWidth} from "./Layouts/StreetWidth";
|
import {StreetWidth} from "./Layouts/StreetWidth";
|
||||||
|
@ -16,13 +15,14 @@ import * as bookcases from "../assets/themes/bookcases/Bookcases.json";
|
||||||
import * as aed from "../assets/themes/aed/aed.json";
|
import * as aed from "../assets/themes/aed/aed.json";
|
||||||
import * as toilets from "../assets/themes/toilets/toilets.json";
|
import * as toilets from "../assets/themes/toilets/toilets.json";
|
||||||
import * as artworks from "../assets/themes/artwork/artwork.json";
|
import * as artworks from "../assets/themes/artwork/artwork.json";
|
||||||
|
import {PersonalLayout} from "../Logic/PersonalLayout";
|
||||||
|
|
||||||
export class AllKnownLayouts {
|
export class AllKnownLayouts {
|
||||||
|
|
||||||
public static allLayers: Map<string, LayerDefinition> = undefined;
|
public static allLayers: Map<string, LayerDefinition> = undefined;
|
||||||
|
|
||||||
public static layoutsList: Layout[] = [
|
public static layoutsList: Layout[] = [
|
||||||
new CustomLayout(),
|
new PersonalLayout(),
|
||||||
new Natuurpunt(),
|
new Natuurpunt(),
|
||||||
new GRB(),
|
new GRB(),
|
||||||
new Cyclofix(),
|
new Cyclofix(),
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
|
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
|
||||||
import {Map} from "../Layers/Map";
|
import {Map} from "../Layers/Map";
|
||||||
import {UIElement} from "../../UI/UIElement";
|
import {UIElement} from "../../UI/UIElement";
|
||||||
|
import Translations from "../../UI/i18n/Translations";
|
||||||
|
|
||||||
|
|
||||||
export interface TagRenderingConfigJson {
|
export interface TagRenderingConfigJson {
|
||||||
|
@ -246,7 +247,7 @@ export class CustomLayoutFromJSON {
|
||||||
json.id,
|
json.id,
|
||||||
{
|
{
|
||||||
description: t(json.description),
|
description: t(json.description),
|
||||||
name: t(json.title.render),
|
name: Translations.WT(t(json.title.render)).txt.replace(/[^a-zA-Z0-9-_]/g, ''),
|
||||||
icon: icon,
|
icon: icon,
|
||||||
minzoom: parseInt(""+json.minzoom),
|
minzoom: parseInt(""+json.minzoom),
|
||||||
title: tr(json.title),
|
title: tr(json.title),
|
||||||
|
|
|
@ -14,6 +14,7 @@ export class Layout {
|
||||||
public icon: string = "./assets/logo.svg";
|
public icon: string = "./assets/logo.svg";
|
||||||
public title: UIElement;
|
public title: UIElement;
|
||||||
public maintainer: string;
|
public maintainer: string;
|
||||||
|
public version: string;
|
||||||
public description: string | UIElement;
|
public description: string | UIElement;
|
||||||
public socialImage: string = "";
|
public socialImage: string = "";
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,13 @@ import {WelcomeMessage} from "./UI/WelcomeMessage";
|
||||||
import {Img} from "./UI/Img";
|
import {Img} from "./UI/Img";
|
||||||
import {DropDown} from "./UI/Input/DropDown";
|
import {DropDown} from "./UI/Input/DropDown";
|
||||||
import {LayerSelection} from "./UI/LayerSelection";
|
import {LayerSelection} from "./UI/LayerSelection";
|
||||||
import {CustomLayersPanel} from "./Logic/CustomLayersPanel";
|
|
||||||
import {CustomLayout} from "./Logic/CustomLayers";
|
|
||||||
import {Preset} from "./Customizations/LayerDefinition";
|
import {Preset} from "./Customizations/LayerDefinition";
|
||||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||||
import {LayerUpdater} from "./Logic/LayerUpdater";
|
import {LayerUpdater} from "./Logic/LayerUpdater";
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import {QueryParameters} from "./Logic/Web/QueryParameters";
|
import {QueryParameters} from "./Logic/Web/QueryParameters";
|
||||||
|
import {PersonalLayout} from "./Logic/PersonalLayout";
|
||||||
|
import {PersonalLayersPanel} from "./Logic/PersonalLayersPanel";
|
||||||
|
|
||||||
export class InitUiElements {
|
export class InitUiElements {
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ export class InitUiElements {
|
||||||
|
|
||||||
const layoutToUse = State.state.layoutToUse.data;
|
const layoutToUse = State.state.layoutToUse.data;
|
||||||
let welcome: UIElement = new WelcomeMessage();
|
let welcome: UIElement = new WelcomeMessage();
|
||||||
if (layoutToUse.name === CustomLayout.NAME) {
|
if (layoutToUse.name === PersonalLayout.NAME) {
|
||||||
welcome = new CustomLayersPanel();
|
welcome = new PersonalLayersPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
import {State} from "../State";
|
|
||||||
|
|
||||||
export class CustomLayersState {
|
|
||||||
static RemoveFavouriteLayer(layer: string) {
|
|
||||||
|
|
||||||
State.state.GetFilteredLayerFor(layer)?.isDisplayed?.setData(false);
|
|
||||||
|
|
||||||
const favs = State.state.favourteLayers.data;
|
|
||||||
const ind = favs.indexOf(layer);
|
|
||||||
if (ind < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
favs.splice(ind, 1);
|
|
||||||
|
|
||||||
|
|
||||||
const osmConnection = State.state.osmConnection;
|
|
||||||
const count = osmConnection.GetPreference("mapcomplete-custom-layer-count");
|
|
||||||
for (let i = 0; i < favs.length; i++) {
|
|
||||||
const layerIDescr = osmConnection.GetPreference("mapcomplete-custom-layer-" + i);
|
|
||||||
layerIDescr.setData(favs[i]);
|
|
||||||
}
|
|
||||||
count.setData("" + favs.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
static AddFavouriteLayer(layer: string) {
|
|
||||||
State.state.GetFilteredLayerFor(layer)?.isDisplayed?.setData(true);
|
|
||||||
|
|
||||||
const favs = State.state.favourteLayers.data;
|
|
||||||
const ind = favs.indexOf(layer);
|
|
||||||
if (ind >= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("Adding fav layer", layer);
|
|
||||||
favs.push(layer);
|
|
||||||
|
|
||||||
|
|
||||||
const osmConnection = State.state.osmConnection;
|
|
||||||
const count = osmConnection.GetPreference("mapcomplete-custom-layer-count");
|
|
||||||
if (count.data === undefined || isNaN(Number(count.data))) {
|
|
||||||
count.data = "0";
|
|
||||||
}
|
|
||||||
const lastId = Number(count.data);
|
|
||||||
|
|
||||||
for (let i = 0; i < lastId; i++) {
|
|
||||||
const layerIDescr = osmConnection.GetPreference("mapcomplete-custom-layer-" + i);
|
|
||||||
if (layerIDescr.data === undefined || layerIDescr.data === "") {
|
|
||||||
// An earlier item was removed -> overwrite it
|
|
||||||
layerIDescr.setData(layer);
|
|
||||||
count.ping();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No empty slot found -> create a new one
|
|
||||||
const layerIDescr = osmConnection.GetPreference("mapcomplete-custom-layer-" + lastId);
|
|
||||||
layerIDescr.setData(layer);
|
|
||||||
count.setData((lastId + 1) + "");
|
|
||||||
}
|
|
||||||
|
|
||||||
static InitFavouriteLayers(state: State) {
|
|
||||||
const osmConnection = state.osmConnection;
|
|
||||||
const count = osmConnection.GetPreference("mapcomplete-custom-layer-count");
|
|
||||||
const favs = state.favourteLayers.data;
|
|
||||||
let changed = false;
|
|
||||||
count.addCallback((countStr) => {
|
|
||||||
console.log("Updating favourites")
|
|
||||||
if (countStr === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let countI = Number(countStr);
|
|
||||||
if (isNaN(countI)) {
|
|
||||||
countI = 999;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < countI; i++) {
|
|
||||||
const layerId = osmConnection.GetPreference("mapcomplete-custom-layer-" + i).data;
|
|
||||||
if (layerId !== undefined && layerId !== "" && favs.indexOf(layerId) < 0) {
|
|
||||||
state.favourteLayers.data.push(layerId);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
state.favourteLayers.ping();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
131
Logic/Osm/ChangesetHandler.ts
Normal file
131
Logic/Osm/ChangesetHandler.ts
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import {State} from "../../State";
|
||||||
|
import {UserDetails} from "./OsmConnection";
|
||||||
|
import {UIEventSource} from "../UIEventSource";
|
||||||
|
|
||||||
|
export class ChangesetHandler {
|
||||||
|
|
||||||
|
private _dryRun: boolean;
|
||||||
|
private userDetails: UIEventSource<UserDetails>;
|
||||||
|
private auth: any;
|
||||||
|
|
||||||
|
constructor(dryRun: boolean, userDetails: UIEventSource<UserDetails>, auth) {
|
||||||
|
this._dryRun = dryRun;
|
||||||
|
this.userDetails = userDetails;
|
||||||
|
this.auth = auth;
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
console.log("DRYRUN ENABLED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UploadChangeset(generateChangeXML: (csid: string) => string,
|
||||||
|
handleMapping: (idMapping: any) => void,
|
||||||
|
continuation: () => void) {
|
||||||
|
|
||||||
|
if (this._dryRun) {
|
||||||
|
console.log("NOT UPLOADING as dryrun is true");
|
||||||
|
var changesetXML = generateChangeXML("123456");
|
||||||
|
console.log(changesetXML);
|
||||||
|
continuation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
this.OpenChangeset(
|
||||||
|
function (csId) {
|
||||||
|
var changesetXML = generateChangeXML(csId);
|
||||||
|
self.AddChange(csId, changesetXML,
|
||||||
|
function (csId, mapping) {
|
||||||
|
self.CloseChangeset(csId, continuation);
|
||||||
|
handleMapping(mapping);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.userDetails.data.csCount++;
|
||||||
|
this.userDetails.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private OpenChangeset(continuation: (changesetId: string) => void) {
|
||||||
|
|
||||||
|
const layout = State.state.layoutToUse.data;
|
||||||
|
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/api/0.6/changeset/create',
|
||||||
|
options: {header: {'Content-Type': 'text/xml'}},
|
||||||
|
content: [`<osm><changeset>`,
|
||||||
|
`<tag k="created_by" v="MapComplete ${State.vNumber}" />`,
|
||||||
|
`<tag k="comment" v="Adding data with #MapComplete"/>`,
|
||||||
|
`<tag k="theme" v="${layout.name}"/>`,
|
||||||
|
layout.maintainer !== undefined ? `<tag k="theme-creator" v="${layout.maintainer}"/>` : "",
|
||||||
|
`</changeset></osm>`].join("")
|
||||||
|
}, function (err, response) {
|
||||||
|
if (response === undefined) {
|
||||||
|
console.log("err", err);
|
||||||
|
alert("Could not upload change (opening failed). Please file a bug report")
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
continuation(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private AddChange(changesetId: string,
|
||||||
|
changesetXML: string,
|
||||||
|
continuation: ((changesetId: string, idMapping: any) => void)) {
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'POST',
|
||||||
|
options: {header: {'Content-Type': 'text/xml'}},
|
||||||
|
path: '/api/0.6/changeset/' + changesetId + '/upload',
|
||||||
|
content: changesetXML
|
||||||
|
}, function (err, response) {
|
||||||
|
if (response == null) {
|
||||||
|
console.log("err", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const mapping = ChangesetHandler.parseUploadChangesetResponse(response);
|
||||||
|
console.log("Uploaded changeset ", changesetId);
|
||||||
|
continuation(changesetId, mapping);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private CloseChangeset(changesetId: string, continuation: (() => void)) {
|
||||||
|
console.log("closing");
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/api/0.6/changeset/' + changesetId + '/close',
|
||||||
|
}, function (err, response) {
|
||||||
|
if (response == null) {
|
||||||
|
|
||||||
|
console.log("err", err);
|
||||||
|
}
|
||||||
|
console.log("Closed changeset ", changesetId);
|
||||||
|
|
||||||
|
if (continuation !== undefined) {
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static parseUploadChangesetResponse(response: XMLDocument) {
|
||||||
|
const nodes = response.getElementsByTagName("node");
|
||||||
|
const mapping = {};
|
||||||
|
// @ts-ignore
|
||||||
|
for (const node of nodes) {
|
||||||
|
const oldId = parseInt(node.attributes.old_id.value);
|
||||||
|
const newId = parseInt(node.attributes.new_id.value);
|
||||||
|
if (oldId !== undefined && newId !== undefined &&
|
||||||
|
!isNaN(oldId) && !isNaN(newId)) {
|
||||||
|
mapping["node/" + oldId] = "node/" + newId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import osmAuth from "osm-auth";
|
import osmAuth from "osm-auth";
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
import {CustomLayersState} from "../CustomLayersState";
|
|
||||||
import {State} from "../../State";
|
import {State} from "../../State";
|
||||||
|
import {All} from "../../Customizations/Layouts/All";
|
||||||
|
import {OsmPreferences} from "./OsmPreferences";
|
||||||
|
import {ChangesetHandler} from "./ChangesetHandler";
|
||||||
|
|
||||||
export class UserDetails {
|
export class UserDetails {
|
||||||
|
|
||||||
|
@ -22,6 +24,11 @@ export class OsmConnection {
|
||||||
public userDetails: UIEventSource<UserDetails>;
|
public userDetails: UIEventSource<UserDetails>;
|
||||||
private _dryRun: boolean;
|
private _dryRun: boolean;
|
||||||
|
|
||||||
|
public _preferencesHandler: OsmPreferences;
|
||||||
|
private _changesetHandler: ChangesetHandler;
|
||||||
|
|
||||||
|
private _onLoggedIn : ((userDetails: UserDetails) => void)[] = [];
|
||||||
|
|
||||||
constructor(dryRun: boolean, oauth_token: UIEventSource<string>, singlePage: boolean = true) {
|
constructor(dryRun: boolean, oauth_token: UIEventSource<string>, singlePage: boolean = true) {
|
||||||
|
|
||||||
let pwaStandAloneMode = false;
|
let pwaStandAloneMode = false;
|
||||||
|
@ -61,7 +68,9 @@ export class OsmConnection {
|
||||||
this.userDetails.data.dryRun = dryRun;
|
this.userDetails.data.dryRun = dryRun;
|
||||||
this._dryRun = dryRun;
|
this._dryRun = dryRun;
|
||||||
|
|
||||||
|
this._preferencesHandler = new OsmPreferences(this.auth, this);
|
||||||
|
|
||||||
|
this._changesetHandler = new ChangesetHandler(dryRun, this.userDetails, this.auth);
|
||||||
if (oauth_token.data !== undefined) {
|
if (oauth_token.data !== undefined) {
|
||||||
console.log(oauth_token.data)
|
console.log(oauth_token.data)
|
||||||
const self = this;
|
const self = this;
|
||||||
|
@ -79,13 +88,25 @@ export class OsmConnection {
|
||||||
} else {
|
} else {
|
||||||
console.log("Not authenticated");
|
console.log("Not authenticated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (dryRun) {
|
|
||||||
console.log("DRYRUN ENABLED");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UploadChangeset(generateChangeXML: (csid: string) => string,
|
||||||
|
handleMapping: (idMapping: any) => void,
|
||||||
|
continuation: () => void) {
|
||||||
|
this._changesetHandler.UploadChangeset(generateChangeXML, handleMapping, continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
|
return this._preferencesHandler.GetPreference(key, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
|
return this._preferencesHandler.GetLongPreference(key, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnLoggedIn(action: (userDetails: UserDetails) => void){
|
||||||
|
this._onLoggedIn.push(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogOut() {
|
public LogOut() {
|
||||||
|
@ -112,7 +133,6 @@ export class OsmConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.UpdatePreferences();
|
|
||||||
self.CheckForMessagesContinuously();
|
self.CheckForMessagesContinuously();
|
||||||
|
|
||||||
// details is an XML DOM of user details
|
// details is an XML DOM of user details
|
||||||
|
@ -143,8 +163,12 @@ export class OsmConnection {
|
||||||
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
|
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
|
||||||
data.unreadMessages = parseInt(messages.getAttribute("unread"));
|
data.unreadMessages = parseInt(messages.getAttribute("unread"));
|
||||||
data.totalMessages = parseInt(messages.getAttribute("count"));
|
data.totalMessages = parseInt(messages.getAttribute("count"));
|
||||||
self.userDetails.ping();
|
|
||||||
|
|
||||||
|
for (const action of self._onLoggedIn) {
|
||||||
|
action(self.userDetails.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.userDetails.ping();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,208 +183,5 @@ export class OsmConnection {
|
||||||
}, 5 * 60 * 1000);
|
}, 5 * 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public preferences = new UIEventSource<any>({});
|
|
||||||
public preferenceSources : any = {}
|
|
||||||
|
|
||||||
public GetPreference(key: string, prefix : string = "mapcomplete-") : UIEventSource<string>{
|
|
||||||
key = prefix+key;
|
|
||||||
if (this.preferenceSources[key] !== undefined) {
|
|
||||||
return this.preferenceSources[key];
|
|
||||||
}
|
|
||||||
if (this.userDetails.data.loggedIn && this.preferences.data[key] === undefined) {
|
|
||||||
this.UpdatePreferences();
|
|
||||||
}
|
|
||||||
const pref = new UIEventSource<string>(this.preferences.data[key]);
|
|
||||||
pref.addCallback((v) => {
|
|
||||||
this.SetPreference(key, v);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.preferences.addCallback((prefs) => {
|
|
||||||
if (prefs[key] !== undefined) {
|
|
||||||
pref.setData(prefs[key]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.preferenceSources[key] = pref;
|
|
||||||
return pref;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UpdatePreferences() {
|
|
||||||
const self = this;
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/0.6/user/preferences'
|
|
||||||
}, function (error, value: XMLDocument) {
|
|
||||||
if(error){
|
|
||||||
console.log("Could not load preferences", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const prefs = value.getElementsByTagName("preference");
|
|
||||||
for (let i = 0; i < prefs.length; i++) {
|
|
||||||
const pref = prefs[i];
|
|
||||||
const k = pref.getAttribute("k");
|
|
||||||
const v = pref.getAttribute("v");
|
|
||||||
self.preferences.data[k] = v;
|
|
||||||
}
|
|
||||||
self.preferences.ping();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private SetPreference(k:string, v:string) {
|
|
||||||
if(!this.userDetails.data.loggedIn){
|
|
||||||
console.log("Not saving preference: user not logged in");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.preferences.data[k] === v) {
|
|
||||||
console.log("Not updating preference", k, " to ", v, "not changed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("Updating preference", k, " to ", v);
|
|
||||||
|
|
||||||
this.preferences.data[k] = v;
|
|
||||||
this.preferences.ping();
|
|
||||||
|
|
||||||
if(v === ""){
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'DELETE',
|
|
||||||
path: '/api/0.6/user/preferences/' + k,
|
|
||||||
options: {header: {'Content-Type': 'text/plain'}},
|
|
||||||
}, function (error, result) {
|
|
||||||
if (error) {
|
|
||||||
console.log("Could not remove preference", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Preference removed!", result == "" ? "OK" : result);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'PUT',
|
|
||||||
path: '/api/0.6/user/preferences/' + k,
|
|
||||||
options: {header: {'Content-Type': 'text/plain'}},
|
|
||||||
content: v
|
|
||||||
}, function (error, result) {
|
|
||||||
if (error) {
|
|
||||||
console.log("Could not set preference", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Preference written!", result == "" ? "OK" : result);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static parseUploadChangesetResponse(response: XMLDocument) {
|
|
||||||
const nodes = response.getElementsByTagName("node");
|
|
||||||
const mapping = {};
|
|
||||||
// @ts-ignore
|
|
||||||
for (const node of nodes) {
|
|
||||||
const oldId = parseInt(node.attributes.old_id.value);
|
|
||||||
const newId = parseInt(node.attributes.new_id.value);
|
|
||||||
if (oldId !== undefined && newId !== undefined &&
|
|
||||||
!isNaN(oldId) && !isNaN(newId)) {
|
|
||||||
mapping["node/" + oldId] = "node/" + newId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public UploadChangeset(generateChangeXML: (csid: string) => string,
|
|
||||||
handleMapping: (idMapping: any) => void,
|
|
||||||
continuation: () => void) {
|
|
||||||
|
|
||||||
if (this._dryRun) {
|
|
||||||
console.log("NOT UPLOADING as dryrun is true");
|
|
||||||
var changesetXML = generateChangeXML("123456");
|
|
||||||
console.log(changesetXML);
|
|
||||||
continuation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = this;
|
|
||||||
this.OpenChangeset(
|
|
||||||
function (csId) {
|
|
||||||
var changesetXML = generateChangeXML(csId);
|
|
||||||
self.AddChange(csId, changesetXML,
|
|
||||||
function (csId, mapping) {
|
|
||||||
self.CloseChangeset(csId, continuation);
|
|
||||||
handleMapping(mapping);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.userDetails.data.csCount++;
|
|
||||||
this.userDetails.ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private OpenChangeset(continuation: (changesetId: string) => void) {
|
|
||||||
|
|
||||||
const layout = State.state.layoutToUse.data;
|
|
||||||
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'PUT',
|
|
||||||
path: '/api/0.6/changeset/create',
|
|
||||||
options: {header: {'Content-Type': 'text/xml'}},
|
|
||||||
content: [`<osm><changeset>`,
|
|
||||||
`<tag k="created_by" v="MapComplete ${State.vNumber}" />`,
|
|
||||||
`<tag k="comment" v="Adding data with #MapComplete"/>`,
|
|
||||||
`<tag k="theme" v="${layout.name}"/>`,
|
|
||||||
layout.maintainer !== undefined ? `<tag k="theme-creator" v="${layout.maintainer}"/>` : "",
|
|
||||||
`</changeset></osm>`].join("")
|
|
||||||
}, function (err, response) {
|
|
||||||
if (response === undefined) {
|
|
||||||
console.log("err", err);
|
|
||||||
alert("Could not upload change (opening failed). Please file a bug report")
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
continuation(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private AddChange(changesetId: string,
|
|
||||||
changesetXML: string,
|
|
||||||
continuation: ((changesetId: string, idMapping: any) => void)){
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'POST',
|
|
||||||
options: { header: { 'Content-Type': 'text/xml' } },
|
|
||||||
path: '/api/0.6/changeset/'+changesetId+'/upload',
|
|
||||||
content: changesetXML
|
|
||||||
}, function (err, response) {
|
|
||||||
if (response == null) {
|
|
||||||
console.log("err", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const mapping = OsmConnection.parseUploadChangesetResponse(response);
|
|
||||||
console.log("Uploaded changeset ", changesetId);
|
|
||||||
continuation(changesetId, mapping);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloseChangeset(changesetId: string, continuation : (() => void)) {
|
|
||||||
console.log("closing");
|
|
||||||
this.auth.xhr({
|
|
||||||
method: 'PUT',
|
|
||||||
path: '/api/0.6/changeset/'+changesetId+'/close',
|
|
||||||
}, function (err, response) {
|
|
||||||
if (response == null) {
|
|
||||||
|
|
||||||
console.log("err", err);
|
|
||||||
}
|
|
||||||
console.log("Closed changeset ", changesetId);
|
|
||||||
|
|
||||||
if(continuation !== undefined){
|
|
||||||
continuation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
176
Logic/Osm/OsmPreferences.ts
Normal file
176
Logic/Osm/OsmPreferences.ts
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
import {UIEventSource} from "../UIEventSource";
|
||||||
|
import {OsmConnection, UserDetails} from "./OsmConnection";
|
||||||
|
import {All} from "../../Customizations/Layouts/All";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
|
||||||
|
export class OsmPreferences {
|
||||||
|
|
||||||
|
private auth: any;
|
||||||
|
private userDetails: UIEventSource<UserDetails>;
|
||||||
|
|
||||||
|
public preferences = new UIEventSource<any>({});
|
||||||
|
public preferenceSources: any = {}
|
||||||
|
|
||||||
|
constructor(auth, osmConnection: OsmConnection) {
|
||||||
|
this.auth = auth;
|
||||||
|
this.userDetails = osmConnection.userDetails;
|
||||||
|
const self = this;
|
||||||
|
osmConnection.OnLoggedIn(() => self.UpdatePreferences());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSM preferences can be at most 255 chars
|
||||||
|
* @param key
|
||||||
|
* @param prefix
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
|
const source = new UIEventSource<string>(undefined);
|
||||||
|
|
||||||
|
const allStartWith = prefix + key + "-combined";
|
||||||
|
// Gives the number of combined preferences
|
||||||
|
const length = this.GetPreference(allStartWith + "-length", "");
|
||||||
|
|
||||||
|
console.log("Getting long pref " + prefix + key);
|
||||||
|
const self = this;
|
||||||
|
source.addCallback(str => {
|
||||||
|
if (str === undefined) {
|
||||||
|
for (const prefKey in self.preferenceSources) {
|
||||||
|
if (prefKey.startsWith(allStartWith)) {
|
||||||
|
self.GetPreference(prefKey, "").setData(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (str !== "") {
|
||||||
|
self.GetPreference(allStartWith + "-" + i, "").setData(str.substr(0, 255));
|
||||||
|
str = str.substr(255);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
length.setData("" + i);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function updateData(l: number) {
|
||||||
|
if (l === undefined) {
|
||||||
|
source.setData(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const length = Number(l);
|
||||||
|
let str = "";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
str += self.GetPreference(allStartWith + "-" + i, "").data;
|
||||||
|
}
|
||||||
|
source.setData(str);
|
||||||
|
source.ping();
|
||||||
|
console.log("Long preference ", key, " has ", str.length, " chars");
|
||||||
|
}
|
||||||
|
|
||||||
|
length.addCallback(l => {
|
||||||
|
updateData(Number(l));
|
||||||
|
});
|
||||||
|
updateData(Number(length.data));
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
|
key = prefix + key;
|
||||||
|
if(key.length >= 255){
|
||||||
|
throw "Preferences: key length to big";
|
||||||
|
}
|
||||||
|
if (this.preferenceSources[key] !== undefined) {
|
||||||
|
return this.preferenceSources[key];
|
||||||
|
}
|
||||||
|
if (this.userDetails.data.loggedIn && this.preferences.data[key] === undefined) {
|
||||||
|
this.UpdatePreferences();
|
||||||
|
}
|
||||||
|
const pref = new UIEventSource<string>(this.preferences.data[key]);
|
||||||
|
pref.addCallback((v) => {
|
||||||
|
this.SetPreference(key, v);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.preferences.addCallback((prefs) => {
|
||||||
|
if (prefs[key] !== undefined) {
|
||||||
|
pref.setData(prefs[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.preferenceSources[key] = pref;
|
||||||
|
return pref;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdatePreferences() {
|
||||||
|
const self = this;
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/0.6/user/preferences'
|
||||||
|
}, function (error, value: XMLDocument) {
|
||||||
|
if (error) {
|
||||||
|
console.log("Could not load preferences", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prefs = value.getElementsByTagName("preference");
|
||||||
|
for (let i = 0; i < prefs.length; i++) {
|
||||||
|
const pref = prefs[i];
|
||||||
|
const k = pref.getAttribute("k");
|
||||||
|
const v = pref.getAttribute("v");
|
||||||
|
self.preferences.data[k] = v;
|
||||||
|
}
|
||||||
|
self.preferences.ping();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private SetPreference(k: string, v: string) {
|
||||||
|
if (!this.userDetails.data.loggedIn) {
|
||||||
|
console.log("Not saving preference: user not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.preferences.data[k] === v) {
|
||||||
|
console.log("Not updating preference", k, " to ", v, "not changed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15));
|
||||||
|
|
||||||
|
this.preferences.data[k] = v;
|
||||||
|
this.preferences.ping();
|
||||||
|
|
||||||
|
if (v === "") {
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'DELETE',
|
||||||
|
path: '/api/0.6/user/preferences/' + k,
|
||||||
|
options: {header: {'Content-Type': 'text/plain'}},
|
||||||
|
}, function (error, result) {
|
||||||
|
if (error) {
|
||||||
|
console.log("Could not remove preference", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Preference removed!", result == "" ? "OK" : result);
|
||||||
|
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.auth.xhr({
|
||||||
|
method: 'PUT',
|
||||||
|
path: '/api/0.6/user/preferences/' + k,
|
||||||
|
options: {header: {'Content-Type': 'text/plain'}},
|
||||||
|
content: v
|
||||||
|
}, function (error, result) {
|
||||||
|
if (error) {
|
||||||
|
console.log("Could not set preference", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Preference written!", result == "" ? "OK" : result);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -6,37 +6,35 @@ import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
import {Img} from "../UI/Img";
|
import {Img} from "../UI/Img";
|
||||||
import {CheckBox} from "../UI/Input/CheckBox";
|
import {CheckBox} from "../UI/Input/CheckBox";
|
||||||
import {CustomLayersState} from "./CustomLayersState";
|
|
||||||
import {VerticalCombine} from "../UI/Base/VerticalCombine";
|
import {VerticalCombine} from "../UI/Base/VerticalCombine";
|
||||||
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
import {CustomLayout} from "./CustomLayers";
|
|
||||||
import {SubtleButton} from "../UI/Base/SubtleButton";
|
import {SubtleButton} from "../UI/Base/SubtleButton";
|
||||||
|
import {PersonalLayout} from "./PersonalLayout";
|
||||||
|
|
||||||
export class CustomLayersPanel extends UIElement {
|
export class PersonalLayersPanel extends UIElement {
|
||||||
private checkboxes: UIElement[] = [];
|
private checkboxes: UIElement[] = [];
|
||||||
|
|
||||||
private updateButton : UIElement;
|
private updateButton: UIElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(State.state.favourteLayers);
|
super(State.state.favouriteLayers);
|
||||||
|
|
||||||
this.ListenTo(State.state.osmConnection.userDetails);
|
this.ListenTo(State.state.osmConnection.userDetails);
|
||||||
|
|
||||||
|
|
||||||
const t = Translations.t.favourite;
|
const t = Translations.t.favourite;
|
||||||
const favs = State.state.favourteLayers.data;
|
const favs = State.state.favouriteLayers.data ?? [];
|
||||||
|
|
||||||
this.updateButton = new SubtleButton("./assets/reload.svg", t.reload)
|
this.updateButton = new SubtleButton("./assets/reload.svg", t.reload)
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
State.state.layerUpdater.ForceRefresh();
|
State.state.layerUpdater.ForceRefresh();
|
||||||
CustomLayersState.InitFavouriteLayers(State.state);
|
|
||||||
State.state.layoutToUse.ping();
|
State.state.layoutToUse.ping();
|
||||||
})
|
})
|
||||||
|
|
||||||
const controls = new Map<string, UIEventSource<boolean>>();
|
const controls = new Map<string, UIEventSource<boolean>>();
|
||||||
for (const layout of AllKnownLayouts.layoutsList) {
|
for (const layout of AllKnownLayouts.layoutsList) {
|
||||||
|
|
||||||
if(layout.name === CustomLayout.NAME){
|
if (layout.name === PersonalLayout.NAME) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (layout.hideFromOverview &&
|
if (layout.hideFromOverview &&
|
||||||
|
@ -86,18 +84,20 @@ export class CustomLayersPanel extends UIElement {
|
||||||
controls[layer.id] = cb.isEnabled;
|
controls[layer.id] = cb.isEnabled;
|
||||||
|
|
||||||
cb.isEnabled.addCallback((isEnabled) => {
|
cb.isEnabled.addCallback((isEnabled) => {
|
||||||
|
const favs = State.state.favouriteLayers;
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
CustomLayersState.AddFavouriteLayer(layer.id)
|
favs.data.push(layer.id);
|
||||||
} else {
|
} else {
|
||||||
CustomLayersState.RemoveFavouriteLayer(layer.id);
|
favs.data.splice(favs.data.indexOf(layer.id), 1);
|
||||||
}
|
}
|
||||||
|
favs.ping();
|
||||||
})
|
})
|
||||||
|
|
||||||
this.checkboxes.push(cb);
|
this.checkboxes.push(cb);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State.state.favourteLayers.addCallback((layers) => {
|
State.state.favouriteLayers.addCallback((layers) => {
|
||||||
for (const layerId of layers) {
|
for (const layerId of layers) {
|
||||||
controls[layerId]?.setData(true);
|
controls[layerId]?.setData(true);
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
import {Layout} from "../Customizations/Layout";
|
import {Layout} from "../Customizations/Layout";
|
||||||
import Translations from "../UI/i18n/Translations";
|
import Translations from "../UI/i18n/Translations";
|
||||||
|
|
||||||
export class CustomLayout extends Layout {
|
export class PersonalLayout extends Layout {
|
||||||
|
|
||||||
public static NAME: string = "personal";
|
public static NAME: string = "personal";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
CustomLayout.NAME,
|
PersonalLayout.NAME,
|
||||||
["en"],
|
["en"],
|
||||||
Translations.t.favourite.title,
|
Translations.t.favourite.title,
|
||||||
[],
|
[],
|
||||||
|
@ -21,6 +21,3 @@ export class CustomLayout extends Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,9 +68,9 @@ A typical user journey would be:
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
GPL + pingback.
|
GPLv3.0 + recommended pingback.
|
||||||
|
|
||||||
I love it to see where the project ends up. You are free to reuse the software (under GPL) but, when you have made your own change and are using it, I would like to know about it. Drop me a line, give a pingback in the issues, ...
|
I love it to see where the project ends up. You are free to reuse the software (under GPL) but, when you have made your own change and are using it, I would like to know about it. Drop me a line, give a pingback in the issues,...
|
||||||
|
|
||||||
## Dev
|
## Dev
|
||||||
|
|
||||||
|
|
84
State.ts
84
State.ts
|
@ -8,7 +8,6 @@ import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
||||||
import Locale from "./UI/i18n/Locale";
|
import Locale from "./UI/i18n/Locale";
|
||||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||||
import Translations from "./UI/i18n/Translations";
|
import Translations from "./UI/i18n/Translations";
|
||||||
import {CustomLayersState} from "./Logic/CustomLayersState";
|
|
||||||
import {FilteredLayer} from "./Logic/FilteredLayer";
|
import {FilteredLayer} from "./Logic/FilteredLayer";
|
||||||
import {LayerUpdater} from "./Logic/LayerUpdater";
|
import {LayerUpdater} from "./Logic/LayerUpdater";
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
|
@ -24,7 +23,7 @@ export class State {
|
||||||
// The singleton of the global state
|
// The singleton of the global state
|
||||||
public static state: State;
|
public static state: State;
|
||||||
|
|
||||||
public static vNumber = "0.0.6c";
|
public static vNumber = "0.0.6d";
|
||||||
|
|
||||||
// The user journey states thresholds when a new feature gets unlocked
|
// The user journey states thresholds when a new feature gets unlocked
|
||||||
public static userJourney = {
|
public static userJourney = {
|
||||||
|
@ -38,11 +37,7 @@ export class State {
|
||||||
|
|
||||||
public static runningFromConsole: boolean = false;
|
public static runningFromConsole: boolean = false;
|
||||||
|
|
||||||
/**
|
|
||||||
THe layout to use
|
|
||||||
*/
|
|
||||||
public readonly layoutToUse = new UIEventSource<Layout>(undefined);
|
public readonly layoutToUse = new UIEventSource<Layout>(undefined);
|
||||||
public layoutDefinition : string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The mapping from id -> UIEventSource<properties>
|
The mapping from id -> UIEventSource<properties>
|
||||||
|
@ -61,7 +56,9 @@ export class State {
|
||||||
*/
|
*/
|
||||||
public osmConnection: OsmConnection;
|
public osmConnection: OsmConnection;
|
||||||
|
|
||||||
public layerUpdater : LayerUpdater;
|
public favouriteLayers: UIEventSource<string[]>;
|
||||||
|
|
||||||
|
public layerUpdater: LayerUpdater;
|
||||||
|
|
||||||
|
|
||||||
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([])
|
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([])
|
||||||
|
@ -123,27 +120,30 @@ export class State {
|
||||||
/** After this many milliseconds without changes, saves are sent of to OSM
|
/** After this many milliseconds without changes, saves are sent of to OSM
|
||||||
*/
|
*/
|
||||||
public readonly saveTimeout = new UIEventSource<number>(30 * 1000);
|
public readonly saveTimeout = new UIEventSource<number>(30 * 1000);
|
||||||
|
public layoutDefinition: string;
|
||||||
/**
|
|
||||||
* Layers can be marked as favourites, they show up in a custom layout
|
|
||||||
*/
|
|
||||||
public favourteLayers: UIEventSource<string[]> = new UIEventSource<string[]>([])
|
|
||||||
|
|
||||||
|
|
||||||
constructor(layoutToUse: Layout) {
|
constructor(layoutToUse: Layout) {
|
||||||
this.layoutToUse = new UIEventSource<Layout>(layoutToUse);
|
const self = this;
|
||||||
|
this.layoutToUse.setData(layoutToUse)
|
||||||
this.locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({
|
this.locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({
|
||||||
zoom: Utils.asFloat(this.zoom.data) ?? layoutToUse.startzoom,
|
zoom: Utils.asFloat(this.zoom.data),
|
||||||
lat: Utils.asFloat(this.lat.data) ?? layoutToUse.startLat,
|
lat: Utils.asFloat(this.lat.data),
|
||||||
lon: Utils.asFloat(this.lon.data) ?? layoutToUse.startLon
|
lon: Utils.asFloat(this.lon.data),
|
||||||
}).addCallback((latlonz) => {
|
}).addCallback((latlonz) => {
|
||||||
this.zoom.setData(latlonz.zoom.toString());
|
this.zoom.setData(latlonz.zoom.toString());
|
||||||
this.lat.setData(latlonz.lat.toString().substr(0, 6));
|
this.lat.setData(latlonz.lat.toString().substr(0, 6));
|
||||||
this.lon.setData(latlonz.lon.toString().substr(0, 6));
|
this.lon.setData(latlonz.lon.toString().substr(0, 6));
|
||||||
})
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
function featSw(key: string, deflt: (layout: Layout) => boolean): UIEventSource<boolean> {
|
function featSw(key: string, deflt: (layout: Layout) => boolean): UIEventSource<boolean> {
|
||||||
const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined);
|
const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined);
|
||||||
|
@ -151,49 +151,65 @@ export class State {
|
||||||
|
|
||||||
// It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened
|
// 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(
|
return UIEventSource.flatten(
|
||||||
self.layoutToUse.map((layout) =>
|
self.layoutToUse.map((layout) => {
|
||||||
QueryParameters.GetQueryParameter(key, "" + deflt(layout)).map((str) => str === undefined ? deflt(layout) : str !== "false")), [queryParameterSource]);
|
const defaultValue = deflt(layout);
|
||||||
|
const queryParam = QueryParameters.GetQueryParameter(key, "" + defaultValue)
|
||||||
|
return queryParam.map((str) => str === undefined ? defaultValue : (str !== "false"));
|
||||||
|
}), [queryParameterSource]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge);
|
this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true);
|
||||||
this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch);
|
this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true);
|
||||||
this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers);
|
this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true);
|
||||||
this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAdd);
|
this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAdd ?? true);
|
||||||
this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true);
|
this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true);
|
||||||
this.featureSwitchIframe = featSw("fs-iframe", () => false);
|
this.featureSwitchIframe = featSw("fs-iframe", () => false);
|
||||||
this.featureSwitchMoreQuests = featSw("fs-more-quests", () => layoutToUse?.enableMoreQuests);
|
this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true);
|
||||||
this.featureSwitchShareScreen = featSw("fs-share-screen", () => layoutToUse?.enableShareScreen);
|
this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true);
|
||||||
this.featureSwitchGeolocation = featSw("fs-geolocation", () => layoutToUse?.enableGeolocation);
|
this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true);
|
||||||
|
|
||||||
this.osmConnection = new OsmConnection(
|
this.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)
|
||||||
);
|
);
|
||||||
|
|
||||||
CustomLayersState.InitFavouriteLayers(this);
|
|
||||||
|
this.favouriteLayers = this.osmConnection.GetLongPreference("favouriteLayers").map(
|
||||||
|
str => Utils.Dedup(str?.split(";")) ?? [],
|
||||||
|
[], layers => Utils.Dedup(layers)?.join(";")
|
||||||
|
);
|
||||||
|
|
||||||
Locale.language.syncWith(this.osmConnection.GetPreference("language"));
|
Locale.language.syncWith(this.osmConnection.GetPreference("language"));
|
||||||
|
|
||||||
|
|
||||||
Locale.language.addCallback((currentLanguage) => {
|
Locale.language.addCallback((currentLanguage) => {
|
||||||
if (layoutToUse.supportedLanguages.indexOf(currentLanguage) < 0) {
|
const layoutToUse = self.layoutToUse.data;
|
||||||
|
if (layoutToUse === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.layoutToUse.data.supportedLanguages.indexOf(currentLanguage) < 0) {
|
||||||
console.log("Resetting language to", layoutToUse.supportedLanguages[0], "as", currentLanguage, " is unsupported")
|
console.log("Resetting language to", layoutToUse.supportedLanguages[0], "as", currentLanguage, " is unsupported")
|
||||||
// The current language is not supported -> switch to a supported one
|
// The current language is not supported -> switch to a supported one
|
||||||
Locale.language.setData(layoutToUse.supportedLanguages[0]);
|
Locale.language.setData(layoutToUse.supportedLanguages[0]);
|
||||||
}
|
}
|
||||||
}).ping()
|
}).ping()
|
||||||
|
|
||||||
document.title = Translations.W(layoutToUse.title).InnerRender();
|
this.layoutToUse.map((layoutToUse) => {
|
||||||
Locale.language.addCallback(e => {
|
if (layoutToUse === undefined) {
|
||||||
document.title = Translations.W(layoutToUse.title).InnerRender();
|
return "MapComplete";
|
||||||
})
|
}
|
||||||
|
return Translations.W(layoutToUse.title).InnerRender()
|
||||||
|
}, [Locale.language]
|
||||||
|
).addCallback((title) => {
|
||||||
|
document.title = title
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.allElements = new ElementStorage();
|
this.allElements = new ElementStorage();
|
||||||
this.changes = new Changes(this);
|
this.changes = new Changes(this);
|
||||||
|
|
||||||
if(State.runningFromConsole){
|
if (State.runningFromConsole) {
|
||||||
console.warn("running from console - not initializing map. Assuming test.html");
|
console.warn("running from console - not initializing map. Assuming test.html");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class VerticalCombine extends UIElement {
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
let html = "";
|
let html = "";
|
||||||
for (const element of this._elements) {
|
for (const element of this._elements) {
|
||||||
if (!element.IsEmpty()) {
|
if (element!== undefined && !element.IsEmpty()) {
|
||||||
html += "<div>" + element.Render() + "</div>";
|
html += "<div>" + element.Render() + "</div>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
114
UI/MoreScreen.ts
114
UI/MoreScreen.ts
|
@ -5,8 +5,12 @@ import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||||
import Combine from "./Base/Combine";
|
import Combine from "./Base/Combine";
|
||||||
import {SubtleButton} from "./Base/SubtleButton";
|
import {SubtleButton} from "./Base/SubtleButton";
|
||||||
import {State} from "../State";
|
import {State} from "../State";
|
||||||
import {CustomLayout} from "../Logic/CustomLayers";
|
|
||||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||||
|
import {PersonalLayout} from "../Logic/PersonalLayout";
|
||||||
|
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||||
|
import {Layout} from "../Customizations/Layout";
|
||||||
|
import {CustomLayoutFromJSON} from "../Customizations/JSON/CustomLayoutFromJSON";
|
||||||
|
import {All} from "../Customizations/Layouts/All";
|
||||||
|
|
||||||
|
|
||||||
export class MoreScreen extends UIElement {
|
export class MoreScreen extends UIElement {
|
||||||
|
@ -14,6 +18,48 @@ export class MoreScreen extends UIElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(State.state.locationControl);
|
super(State.state.locationControl);
|
||||||
this.ListenTo(State.state.osmConnection.userDetails);
|
this.ListenTo(State.state.osmConnection.userDetails);
|
||||||
|
this.ListenTo(State.state.osmConnection._preferencesHandler.preferences);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private createLinkButton(layout: Layout, customThemeDefinition: string = undefined) {
|
||||||
|
if (layout.hideFromOverview && State.state.osmConnection.userDetails.data.name !== "Pieter Vander Vennet") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (layout.name === State.state.layoutToUse.data.name) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout.name === PersonalLayout.NAME) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLocation = State.state.locationControl.data;
|
||||||
|
let linkText =
|
||||||
|
`./${layout.name}.html?z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}`
|
||||||
|
|
||||||
|
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
||||||
|
linkText = `./index.html?layout=${layout.name}&z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customThemeDefinition) {
|
||||||
|
linkText = `./index.html?userlayout=${layout.name}&z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}#${customThemeDefinition}`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let description = Translations.W(layout.description);
|
||||||
|
if (description !== undefined) {
|
||||||
|
description = new Combine(["<br/>", description]);
|
||||||
|
}
|
||||||
|
const link =
|
||||||
|
new SubtleButton(layout.icon,
|
||||||
|
new Combine([
|
||||||
|
"<b>",
|
||||||
|
Translations.W(layout.title),
|
||||||
|
"</b>",
|
||||||
|
description ?? "",
|
||||||
|
]), {url: linkText, newTab: false})
|
||||||
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
|
@ -28,7 +74,7 @@ export class MoreScreen extends UIElement {
|
||||||
return tr.requestATheme.Render();
|
return tr.requestATheme.Render();
|
||||||
}
|
}
|
||||||
return new SubtleButton("./assets/pencil.svg", tr.createYourOwnTheme, {
|
return new SubtleButton("./assets/pencil.svg", tr.createYourOwnTheme, {
|
||||||
url: "https://pietervdvn.github.io/MapComplete/customGenerator.html",
|
url: "./customGenerator.html",
|
||||||
newTab: false
|
newTab: false
|
||||||
}).Render();
|
}).Render();
|
||||||
})
|
})
|
||||||
|
@ -53,40 +99,44 @@ export class MoreScreen extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
for (const k in AllKnownLayouts.allSets) {
|
for (const k in AllKnownLayouts.allSets) {
|
||||||
const layout = AllKnownLayouts.allSets[k]
|
els.push(this.createLinkButton(AllKnownLayouts.allSets[k]));
|
||||||
if (layout.hideFromOverview && State.state.osmConnection.userDetails.data.name !== "Pieter Vander Vennet") {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if (layout.name === State.state.layoutToUse.data.name) {
|
|
||||||
|
const installedThemes = State.state.osmConnection._preferencesHandler.preferences.map(allPreferences => {
|
||||||
|
const installedThemes = [];
|
||||||
|
if(allPreferences === undefined){
|
||||||
|
return installedThemes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const allPreferencesKey in allPreferences) {
|
||||||
|
"mapcomplete-installed-theme-Superficie-combined-length"
|
||||||
|
const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/);
|
||||||
|
if(themename){
|
||||||
|
installedThemes.push(themename[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return installedThemes;
|
||||||
|
|
||||||
|
})
|
||||||
|
const customThemesNames = installedThemes.data ?? [];
|
||||||
|
if (customThemesNames !== []) {
|
||||||
|
els.push(Translations.t.general.customThemeIntro)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(customThemesNames);
|
||||||
|
for (const installedThemeName of customThemesNames) {
|
||||||
|
if(installedThemeName === ""){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const customThemeDefinition = State.state.osmConnection.GetLongPreference("installed-theme-" + installedThemeName);
|
||||||
if (layout.name === CustomLayout.NAME) {
|
try {
|
||||||
continue;
|
const layout = CustomLayoutFromJSON.FromQueryParam(customThemeDefinition.data);
|
||||||
|
els.push(this.createLinkButton(layout, customThemeDefinition.data));
|
||||||
|
} catch (e) {
|
||||||
|
console.log(customThemeDefinition.data);
|
||||||
|
console.warn("Could not parse custom layout from preferences: ", installedThemeName, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentLocation = State.state.locationControl.data;
|
|
||||||
let linkText =
|
|
||||||
`./${layout.name}.html?z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}`
|
|
||||||
|
|
||||||
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
|
||||||
linkText = `./index.html?layout=${layout.name}&z=${currentLocation.zoom}&lat=${currentLocation.lat}&lon=${currentLocation.lon}`
|
|
||||||
}
|
|
||||||
|
|
||||||
let description = Translations.W(layout.description);
|
|
||||||
if (description !== undefined) {
|
|
||||||
description = new Combine(["<br/>", description]);
|
|
||||||
}
|
|
||||||
const link =
|
|
||||||
new SubtleButton(layout.icon,
|
|
||||||
new Combine([
|
|
||||||
"<b>",
|
|
||||||
Translations.W(layout.title),
|
|
||||||
"</b>",
|
|
||||||
description ?? "",
|
|
||||||
]), {url: linkText, newTab: false});
|
|
||||||
|
|
||||||
els.push(link)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ export class WelcomeMessage extends UIElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(State.state.osmConnection.userDetails);
|
super(State.state.osmConnection.userDetails);
|
||||||
this.languagePicker = Utils.CreateLanguagePicker(Translations.t.general.pickLanguage);
|
|
||||||
this.ListenTo(Locale.language);
|
this.ListenTo(Locale.language);
|
||||||
|
this.languagePicker = Utils.CreateLanguagePicker(Translations.t.general.pickLanguage);
|
||||||
|
|
||||||
function fromLayout(f: (layout: Layout) => (string | UIElement)): UIElement {
|
function fromLayout(f: (layout: Layout) => (string | UIElement)): UIElement {
|
||||||
return Translations.W(f(State.state.layoutToUse.data))
|
return Translations.W(f(State.state.layoutToUse.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.description = fromLayout((layout) => layout.welcomeMessage);
|
this.description = fromLayout((layout) => layout.welcomeMessage);
|
||||||
|
|
|
@ -901,7 +901,8 @@ export default class Translations {
|
||||||
nl: " of <a href='https://www.openstreetmap.org/user/new' target='_blank'>maak een nieuwe account aan</a> ",
|
nl: " of <a href='https://www.openstreetmap.org/user/new' target='_blank'>maak een nieuwe account aan</a> ",
|
||||||
fr: " ou <a href='https://www.openstreetmap.org/user/new' target='_blank'>registrez vous</a>"
|
fr: " ou <a href='https://www.openstreetmap.org/user/new' target='_blank'>registrez vous</a>"
|
||||||
}),
|
}),
|
||||||
noTagsSelected: new T({en: "No tags selected"})
|
noTagsSelected: new T({en: "No tags selected"}),
|
||||||
|
customThemeIntro: new T({en:"<h3>Custom themes</h3>These are previously visited user-generated themes."})
|
||||||
|
|
||||||
},
|
},
|
||||||
favourite: {
|
favourite: {
|
||||||
|
|
13
Utils.ts
13
Utils.ts
|
@ -73,4 +73,17 @@ export class Utils {
|
||||||
return str.substr(0, l - 3)+"...";
|
return str.substr(0, l - 3)+"...";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dedup(arr: string[]):string[]{
|
||||||
|
if(arr === undefined){
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const newArr = [];
|
||||||
|
for (const string of arr) {
|
||||||
|
if(newArr.indexOf(string) < 0){
|
||||||
|
newArr.push(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
|
||||||
|
mkdir assets/generated
|
||||||
ts-node createLayouts.ts
|
ts-node createLayouts.ts
|
||||||
|
find -name '*.png' | parallel optipng '{}'
|
||||||
npm run build
|
npm run build
|
||||||
rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/*
|
rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/*
|
||||||
cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/
|
cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/
|
||||||
|
|
14
index.ts
14
index.ts
|
@ -13,10 +13,11 @@ import {InitUiElements} from "./InitUiElements";
|
||||||
import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler";
|
import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler";
|
||||||
import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler";
|
import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler";
|
||||||
import {State} from "./State";
|
import {State} from "./State";
|
||||||
import {CustomLayout} from "./Logic/CustomLayers";
|
|
||||||
import {CustomLayoutFromJSON} from "./Customizations/JSON/CustomLayoutFromJSON";
|
import {CustomLayoutFromJSON} from "./Customizations/JSON/CustomLayoutFromJSON";
|
||||||
import {QueryParameters} from "./Logic/Web/QueryParameters";
|
import {QueryParameters} from "./Logic/Web/QueryParameters";
|
||||||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
||||||
|
import {PersonalLayout} from "./Logic/PersonalLayout";
|
||||||
|
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
||||||
|
|
||||||
TagRendering.injectFunction();
|
TagRendering.injectFunction();
|
||||||
|
|
||||||
|
@ -109,7 +110,9 @@ console.log("Using layout: ", layoutToUse.name);
|
||||||
State.state = new State(layoutToUse);
|
State.state = new State(layoutToUse);
|
||||||
if (layoutFromBase64 !== "false") {
|
if (layoutFromBase64 !== "false") {
|
||||||
State.state.layoutDefinition = hash.substr(1);
|
State.state.layoutDefinition = hash.substr(1);
|
||||||
console.log(State.state.layoutDefinition)
|
State.state.osmConnection.OnLoggedIn(() => {
|
||||||
|
State.state.osmConnection.GetLongPreference("installed-theme-"+layoutToUse.name).setData(State.state.layoutDefinition);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
InitUiElements.InitBaseMap();
|
InitUiElements.InitBaseMap();
|
||||||
|
|
||||||
|
@ -152,8 +155,8 @@ function setupAllLayerElements() {
|
||||||
|
|
||||||
setupAllLayerElements();
|
setupAllLayerElements();
|
||||||
|
|
||||||
if (layoutToUse === AllKnownLayouts.allSets[CustomLayout.NAME]) {
|
if (layoutToUse === AllKnownLayouts.allSets[PersonalLayout.NAME]) {
|
||||||
State.state.favourteLayers.addCallback((favs) => {
|
State.state.favouriteLayers.addCallback((favs: string[]) => {
|
||||||
layoutToUse.layers = [];
|
layoutToUse.layers = [];
|
||||||
for (const fav of favs) {
|
for (const fav of favs) {
|
||||||
const layer = AllKnownLayouts.allLayers[fav];
|
const layer = AllKnownLayouts.allLayers[fav];
|
||||||
|
@ -161,7 +164,8 @@ if (layoutToUse === AllKnownLayouts.allSets[CustomLayout.NAME]) {
|
||||||
layoutToUse.layers.push(layer);
|
layoutToUse.layers.push(layer);
|
||||||
}
|
}
|
||||||
setupAllLayerElements();
|
setupAllLayerElements();
|
||||||
};
|
}
|
||||||
|
;
|
||||||
State.state.locationControl.ping();
|
State.state.locationControl.ping();
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
1323
package-lock.json
generated
1323
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -39,6 +39,7 @@
|
||||||
"@babel/polyfill": "^7.10.4",
|
"@babel/polyfill": "^7.10.4",
|
||||||
"@types/node": "^7.0.5",
|
"@types/node": "^7.0.5",
|
||||||
"assert": "^2.0.0",
|
"assert": "^2.0.0",
|
||||||
|
"canvas": "^2.6.1",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"fs": "0.0.1-security",
|
"fs": "0.0.1-security",
|
||||||
"marked": "^1.1.1",
|
"marked": "^1.1.1",
|
||||||
|
|
|
@ -57,5 +57,5 @@ function createTable(preferences: any) {
|
||||||
el.AttachTo("maindiv");
|
el.AttachTo("maindiv");
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.preferences.addCallback((prefs) => createTable(prefs))
|
connection._preferencesHandler.preferences.addCallback((prefs) => createTable(prefs))
|
||||||
|
|
||||||
|
|
23
test.ts
23
test.ts
|
@ -1,10 +1,15 @@
|
||||||
import {TextField, ValidatedTextField} from "./UI/Input/TextField";
|
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
||||||
import {CustomLayoutFromJSON} from "./Customizations/JSON/CustomLayoutFromJSON";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import {And} from "./Logic/TagsFilter";
|
|
||||||
|
|
||||||
const tags = CustomLayoutFromJSON.TagsFromJson("indoor=yes&access!=private");
|
const conn = new OsmConnection(true, new UIEventSource<string>(undefined));
|
||||||
console.log(tags);
|
conn.AttemptLogin();
|
||||||
const m0 = new And(tags).matches([{k:"indoor",v:"yes"}, {k:"access",v: "yes"}]);
|
|
||||||
console.log("Matches 0", m0)
|
conn.userDetails.addCallback(userDetails => {
|
||||||
const m1 = new And(tags).matches([{k:"indoor",v:"yes"}, {k:"access",v: "private"}]);
|
if (!userDetails.loggedIn) {
|
||||||
console.log("Matches 1", m1)
|
return;
|
||||||
|
}
|
||||||
|
const str = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
|
||||||
|
console.log(str.length);
|
||||||
|
conn.GetLongPreference("test").setData(str);
|
||||||
|
// console.log(got.length)
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue