Add custom theme for advanced users

This commit is contained in:
Pieter Vander Vennet 2020-07-31 04:58:58 +02:00
parent 004eead4ee
commit 9c42839f01
44 changed files with 635 additions and 326 deletions

26
Logic/CustomLayers.ts Normal file
View file

@ -0,0 +1,26 @@
import {Layout} from "../Customizations/Layout";
import Translations from "../UI/i18n/Translations";
export class CustomLayers extends Layout {
public static NAME: string = "personal";
constructor() {
super(
CustomLayers.NAME,
["en"],
Translations.t.favourite.title,
[],
12,
0,
0,
Translations.t.favourite.description,
);
this.icon = "./assets/star.svg"
}
}

View file

@ -0,0 +1,96 @@
import {UIElement} from "../UI/UIElement";
import {State} from "../State";
import Translations from "../UI/i18n/Translations";
import {UIEventSource} from "../UI/UIEventSource";
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
import Combine from "../UI/Base/Combine";
import {Img} from "../UI/Img";
import {CheckBox} from "../UI/Input/CheckBox";
import {CustomLayersState} from "./CustomLayersState";
import {VerticalCombine} from "../UI/Base/VerticalCombine";
import {FixedUiElement} from "../UI/Base/FixedUiElement";
export class CustomLayersPanel extends UIElement {
private checkboxes: UIElement[];
constructor() {
super(State.state.favourteLayers);
this.ListenTo(State.state.osmConnection.userDetails);
const t = Translations.t.favourite;
this.checkboxes = [];
const controls = new Map<string, UIEventSource<boolean>>();
const favs = State.state.favourteLayers.data;
for (const layout of AllKnownLayouts.layoutsList) {
const header =
new Combine([
`<div class="custom-layer-panel-header-img"><img src='${layout.icon}'></div>`,
"<span><b>",
layout.title,
"</b><br/>",
layout.description ?? "",
"</span>",
], 'custom-layer-panel-header')
this.checkboxes.push(header);
for (const layer of layout.layers) {
const image = (layer.icon ? `<img src='${layer.icon}'>` : Img.checkmark);
const cb = new CheckBox(
new Combine([
image,
"<b>", layer.name ?? "", "</b> ", layer.description ?? ""
]),
new Combine([
"<span style='opacity: 0'>",
image, "</span>", "<b>", layer.name ?? "", "</b> ", layer.description ?? ""
]),
controls[layer.id] ?? (favs.indexOf(layer.id) >= 0)
);
cb.clss = "custom-layer-checkbox"
controls[layer.id] = cb.isEnabled;
cb.isEnabled.addCallback((isEnabled) => {
if (isEnabled) {
CustomLayersState.AddFavouriteLayer(layer.id)
} else {
CustomLayersState.RemoveFavouriteLayer(layer.id);
}
})
this.checkboxes.push(cb);
}
}
State.state.favourteLayers.addCallback((layers) => {
for (const layerId of layers) {
controls[layerId].setData(true);
}
})
}
InnerRender(): string {
const t = Translations.t.favourite;
const userDetails = State.state.osmConnection.userDetails.data;
if(!userDetails.loggedIn){
return "";
}
if(userDetails.csCount <= 100){
return "";
}
return new VerticalCombine([
t.panelIntro,
new FixedUiElement("<a href='./index.html?layout=personal'>GO</a>"),
...this.checkboxes
], "custom-layer-panel").Render();
}
}

View file

@ -0,0 +1,94 @@
import {State} from "../State";
export class CustomLayersState {
static RemoveFavouriteLayer(layer: string) {
const favs = State.state.favourteLayers.data;
const ind = favs.indexOf(layer);
if (ind < 0) {
return;
}
console.log("REmovign fav layer", layer);
favs.splice(ind, 1);
State.state.favourteLayers.ping();
const osmConnection = State.state.osmConnection;
const count = osmConnection.GetPreference("mapcomplete-custom-layer-count");
if (favs.length === 0) {
count.setData("0")
} else 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 === layer) {
// We found the value to remove - mark with a tombstone
layerIDescr.setData("-");
return;
}
}
}
static AddFavouriteLayer(layer: string) {
const favs = State.state.favourteLayers.data;
const ind = favs.indexOf(layer);
if (ind >= 0) {
return;
}
console.log("Adding fav layer", layer);
favs.push(layer);
State.state.favourteLayers.ping();
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 InitFavouriteLayer() {
const osmConnection = State.state.osmConnection;
const count = osmConnection.GetPreference("mapcomplete-custom-layer-count");
const favs = State.state.favourteLayers.data;
let changed = false;
count.addCallback((countStr) => {
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.state.favourteLayers.data.push(layerId);
changed = true;
}
}
if (changed) {
State.state.favourteLayers.ping();
}
})
}
}

View file

@ -1,8 +1,8 @@
import L from "leaflet";
import {UIEventSource} from "../../UI/UIEventSource";
import {UIElement} from "../../UI/UIElement";
import {Helpers} from "../../Helpers";
import {State} from "../../State";
import {Utils} from "../../Utils";
export class GeoLocationHandler extends UIElement {
@ -108,7 +108,7 @@ export class GeoLocationHandler extends UIElement {
if (!self._isActive.data) {
self._isActive.setData(true);
Helpers.DoEvery(60000, () => {
Utils.DoEvery(60000, () => {
if (document.visibilityState !== "visible") {
console.log("Not starting gps: document not visible")

View file

@ -8,6 +8,7 @@ import {OsmNode, OsmObject} from "./OsmObject";
import {And, Tag, TagsFilter} from "../TagsFilter";
import {ElementStorage} from "../ElementStorage";
import {State} from "../../State";
import {Utils} from "../../Utils";
export class Changes {
@ -22,9 +23,11 @@ export class Changes {
constructor(
changesetComment: string,
login: OsmConnection,
allElements: ElementStorage) {
state: State) {
this._changesetComment = changesetComment;
this.SetupAutoSave(state);
this.LastEffortSave();
}
addTag(elementId: string, tagsFilter : TagsFilter){
@ -52,7 +55,6 @@ export class Changes {
* @param value
*/
addChange(elementId: string, key: string, value: string) {
console.log("Received change",key, value)
if (key === undefined || key === null) {
console.log("Invalid key");
return;
@ -256,5 +258,78 @@ console.log("Received change",key, value)
optionalContinuationWrapped);
});
}
/*
* Registers an action that:
* -> Upload everything to OSM
* -> Asks the user not to close. The 'not to close' dialog should profide enough time to upload
* -> WHen uploading is done, the window is closed anyway
*/
private LastEffortSave() {
const self = this;
window.addEventListener("beforeunload", function (e) {
// Quickly save everyting!
if (self.pendingChangesES.data == 0) {
return "";
}
self.uploadAll(function () {
window.close()
});
var confirmationMessage = "Nog even geduld - je laatset wijzigingen worden opgeslaan!";
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Webkit, Safari, Chrome
});
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === "visible") {
return;
}
if (this.pendingChangesES.data == 0) {
return;
}
console.log("Upmoading: loss of focus")
this.uploadAll(function () {
window.close()
});
})
}
private SetupAutoSave(state: State) {
const millisTillChangesAreSaved = state.secondsTillChangesAreSaved;
const saveAfterXMillis = state.secondsTillChangesAreSaved.data * 1000;
this.pendingChangesES.addCallback(function () {
var c = this.pendingChangesES.data;
if (c > 10) {
millisTillChangesAreSaved.setData(0);
this.uploadAll(undefined);
return;
}
if (c > 0) {
millisTillChangesAreSaved.setData(saveAfterXMillis);
}
});
millisTillChangesAreSaved.addCallback((time) => {
if (time <= 0 && this.pendingChangesES.data > 0) {
this.uploadAll(undefined);
}
}
)
Utils.DoEvery(
1000,
() => {
millisTillChangesAreSaved
.setData(millisTillChangesAreSaved.data - 1000)
});
}
}

View file

@ -1,6 +1,7 @@
// @ts-ignore
import osmAuth from "osm-auth";
import {UIEventSource} from "../../UI/UIEventSource";
import {CustomLayersState} from "../CustomLayersState";
export class UserDetails {
@ -215,6 +216,7 @@ export class OsmConnection {
self.preferences.data[k] = v;
}
self.preferences.ping();
CustomLayersState.InitFavouriteLayer();
});
}

View file

@ -52,7 +52,6 @@ export class QueryParameters {
public static GetQueryParameter(key: string, deflt: string): UIEventSource<string> {
if (deflt !== undefined) {
console.log(key, "-->", deflt)
QueryParameters.defaults[key] = deflt;
}
if (QueryParameters.knownSources[key] !== undefined) {