diff --git a/InitUiElements.ts b/InitUiElements.ts index ab9037b10..876a2f16e 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -39,7 +39,42 @@ import {Utils} from "./Utils"; export class InitUiElements { - static InitAll(layoutToUse: Layout, layoutFromBase64: string, testing: UIEventSource, layoutName: string ) { + private static setupAllLayerElements() { + + // ------------- Setup the layers ------------------------------- + + InitUiElements.InitLayers(); + InitUiElements.InitLayerSelection(); + + + // ------------------ Setup various other UI elements ------------ + + + InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => { + + let presetCount = 0; + for (const layer of State.state.filteredLayers.data) { + for (const preset of layer.layerDef.presets) { + presetCount++; + } + } + if (presetCount == 0) { + return; + } + + + new StrayClickHandler(() => { + return new SimpleAddUI(); + } + ); + }); + + new CenterMessageBox().AttachTo("centermessage"); + + } + + static InitAll(layoutToUse: Layout, layoutFromBase64: string, testing: UIEventSource, layoutName: string, + layoutDefinition: string = "") { if (layoutToUse === undefined) { console.log("Incorrect layout") new FixedUiElement("Error: incorrect layout " + layoutName + "
Go back").AttachTo("centermessage").onClick(() => { @@ -47,7 +82,6 @@ export class InitUiElements { throw "Incorrect layout" } - const hash = location.hash.substr(1); console.log("Using layout: ", layoutToUse.id, "LayoutFromBase64 is ", layoutFromBase64); State.state = new State(layoutToUse); @@ -60,8 +94,8 @@ export class InitUiElements { } if (layoutFromBase64 !== "false") { - State.state.layoutDefinition = hash; - console.log("Layout definition:",Utils.EllipsesAfter(State.state.layoutDefinition, 100)) + State.state.layoutDefinition = layoutDefinition; + console.log("Layout definition:", Utils.EllipsesAfter(State.state.layoutDefinition, 100)) if (testing.data !== "true") { State.state.osmConnection.OnLoggedIn(() => { State.state.osmConnection.GetLongPreference("installed-theme-" + layoutToUse.id).setData(State.state.layoutDefinition); @@ -74,41 +108,7 @@ export class InitUiElements { new FixedUiElement("").AttachTo("decoration-desktop"); // Remove the decoration - function setupAllLayerElements() { - - // ------------- Setup the layers ------------------------------- - - InitUiElements.InitLayers(); - InitUiElements.InitLayerSelection(); - - - // ------------------ Setup various other UI elements ------------ - - - InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => { - - let presetCount = 0; - for (const layer of State.state.filteredLayers.data) { - for (const preset of layer.layerDef.presets) { - presetCount++; - } - } - if (presetCount == 0) { - return; - } - - - new StrayClickHandler(() => { - return new SimpleAddUI(); - } - ); - }); - - new CenterMessageBox().AttachTo("centermessage"); - - } - - setupAllLayerElements(); + InitUiElements.setupAllLayerElements(); function updateFavs() { @@ -133,9 +133,10 @@ export class InitUiElements { } } - setupAllLayerElements(); + InitUiElements.setupAllLayerElements(); State.state.layerUpdater.ForceRefresh(); - State.state.locationControl.ping(); + State.state.layoutToUse.ping(); + } if (layoutToUse === AllKnownLayouts.allSets[PersonalLayout.NAME]) { diff --git a/Logic/LayerUpdater.ts b/Logic/LayerUpdater.ts index f7537fd40..6473e7eff 100644 --- a/Logic/LayerUpdater.ts +++ b/Logic/LayerUpdater.ts @@ -20,11 +20,13 @@ export class LayerUpdater { */ private readonly previousBounds: Map = new Map(); + private readonly state: State; + /** * The most important layer should go first, as that one gets first pick for the questions */ constructor(state: State) { - + this.state = state; const self = this; this.sufficentlyZoomed = State.state.locationControl.map(location => { @@ -178,6 +180,7 @@ export class LayerUpdater { for (let i = 0; i < 25; i++) { this.previousBounds.set(i, []); } + this.update(this.state); } } \ No newline at end of file diff --git a/Logic/Osm/ChangesetHandler.ts b/Logic/Osm/ChangesetHandler.ts index fb16944ec..4314dec60 100644 --- a/Logic/Osm/ChangesetHandler.ts +++ b/Logic/Osm/ChangesetHandler.ts @@ -6,11 +6,11 @@ import {State} from "../../State"; export class ChangesetHandler { - private _dryRun: boolean; - private userDetails: UIEventSource; - private auth: any; + private readonly _dryRun: boolean; + private readonly userDetails: UIEventSource; + private readonly auth: any; - public currentChangeset: UIEventSource; + public readonly currentChangeset: UIEventSource; constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, auth) { this._dryRun = dryRun; @@ -29,6 +29,12 @@ export class ChangesetHandler { allElements: ElementStorage, generateChangeXML: (csid: string) => string, continuation: () => void) { + + if(this.userDetails.data.csCount == 0){ + // The user became a contributor! + this.userDetails.data.csCount = 1; + this.userDetails.ping(); + } if (this._dryRun) { const changesetXML = generateChangeXML("123456"); diff --git a/Logic/Osm/OsmPreferences.ts b/Logic/Osm/OsmPreferences.ts index dc7a36b55..d57b93ddf 100644 --- a/Logic/Osm/OsmPreferences.ts +++ b/Logic/Osm/OsmPreferences.ts @@ -65,17 +65,16 @@ export class OsmPreferences { source.setData(undefined); return; } - if (l > 25) { + const prefsCount = Number(l); + if (prefsCount > 100) { throw "Length to long"; } - const prefsCount = Number(l); let str = ""; for (let i = 0; i < prefsCount; i++) { str += self.GetPreference(allStartWith + "-" + i, "").data; } source.setData(str); - source.ping(); console.log("Long preference", key, "has", str.length, "chars"); } diff --git a/Logic/PersonalLayersPanel.ts b/Logic/PersonalLayersPanel.ts index 1537aa8fc..5c673e69c 100644 --- a/Logic/PersonalLayersPanel.ts +++ b/Logic/PersonalLayersPanel.ts @@ -4,19 +4,18 @@ import Translations from "../UI/i18n/Translations"; import {UIEventSource} from "./UIEventSource"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; import Combine from "../UI/Base/Combine"; -import {Img} from "../UI/Img"; import {CheckBox} from "../UI/Input/CheckBox"; import {PersonalLayout} from "./PersonalLayout"; import {Layout} from "../Customizations/Layout"; +import {SubtleButton} from "../UI/Base/SubtleButton"; +import {FixedUiElement} from "../UI/Base/FixedUiElement"; export class PersonalLayersPanel extends UIElement { private checkboxes: UIElement[] = []; constructor() { super(State.state.favouriteLayers); - this.ListenTo(State.state.osmConnection.userDetails); - this.ListenTo(State.state.favouriteLayers); this.UpdateView([]); const self = this; @@ -32,7 +31,6 @@ export class PersonalLayersPanel extends UIElement { const favs = State.state.favouriteLayers.data ?? []; const controls = new Map>(); const allLayouts = AllKnownLayouts.layoutsList.concat(extraThemes); - console.log("ALL LAYOUTS", allLayouts) for (const layout of allLayouts) { if (layout.id === PersonalLayout.NAME) { continue; @@ -40,50 +38,41 @@ export class PersonalLayersPanel extends UIElement { const header = new Combine([ - `
`, - "", + ``, + "", layout.title, "
", - layout.description ?? "", - "
", - ], 'custom-layer-panel-header') + layout.description ?? "" + ]).SetStyle("background: #eee; display: block; padding: 0.5em; border-radius:0.5em; overflow:auto;") this.checkboxes.push(header); for (const layer of layout.layers) { - if(typeof layer === "string"){ + if (typeof layer === "string") { continue; } - let icon = layer.icon; - if (icon !== undefined && typeof (icon) !== "string") { - icon = icon.GetContent({"id": "node/-1"}).txt ?? "./assets/bug.svg"; + let icon = layer.icon ?? "./assets/checkmark.svg"; + if (typeof (icon) !== "string") { + icon = icon.GetContent({"id": "node/-1"}).txt ?? "./assets/checkmark.svg"; } - const image = (layer.icon ? `` : Img.checkmark); - const noimage = (layer.icon ? `` : Img.no_checkmark); let name = layer.name ?? layer.id; if (name === undefined) { continue; } - if (typeof (name) !== "string") { - name = name.InnerRender(); - } - const content = new Combine([ - "", - "", name ?? "", " ", + "", + name, + " ", layer.description !== undefined ? new Combine(["
", layer.description]) : "", - "
"]) + ]) const cb = new CheckBox( - new Combine([ - image, content - ]), - new Combine([ - "", - noimage, "", - "", - content, - "" - ]), + new SubtleButton(icon ?? "./assets/checkmark.svg", content), + new SubtleButton( + new FixedUiElement(``).SetStyle("opacity:0.1"), + new Combine(["", + content, + "" + ])), controls[layer.id] ?? (favs.indexOf(layer.id) >= 0) ); cb.SetClass("custom-layer-checkbox"); @@ -92,6 +81,9 @@ export class PersonalLayersPanel extends UIElement { cb.isEnabled.addCallback((isEnabled) => { const favs = State.state.favouriteLayers; if (isEnabled) { + if(favs.data.indexOf(layer.id)>= 0){ + return; // Already added + } favs.data.push(layer.id); } else { favs.data.splice(favs.data.indexOf(layer.id), 1); @@ -103,13 +95,14 @@ export class PersonalLayersPanel extends UIElement { } - State.state.favouriteLayers.addCallback((layers) => { - for (const layerId of layers) { - controls[layerId]?.setData(true); - } - }) - } + + State.state.favouriteLayers.addCallback((layers) => { + for (const layerId of layers) { + controls[layerId]?.setData(true); + } + }); + } InnerRender(): string { @@ -122,7 +115,7 @@ export class PersonalLayersPanel extends UIElement { return new Combine([ t.panelIntro, ...this.checkboxes - ], "custom-layer-panel").Render(); + ]).Render(); } diff --git a/UI/Base/SubtleButton.ts b/UI/Base/SubtleButton.ts index d99aa83df..5821d29bb 100644 --- a/UI/Base/SubtleButton.ts +++ b/UI/Base/SubtleButton.ts @@ -1,40 +1,51 @@ import {UIElement} from "../UIElement"; import Translations from "../i18n/Translations"; import Combine from "./Combine"; +import {FixedUiElement} from "./FixedUiElement"; export class SubtleButton extends UIElement{ - private readonly imageUrl: string; + private readonly image: UIElement; private readonly message: UIElement; private readonly linkTo: { url: string, newTab?: boolean } = undefined; - constructor(imageUrl: string, message: string | UIElement, linkTo: { url: string, newTab?: boolean } = undefined) { + constructor(imageUrl: string | UIElement, message: string | UIElement, linkTo: { url: string, newTab?: boolean } = undefined) { super(undefined); this.linkTo = linkTo; this.message = Translations.W(message); - this.imageUrl = imageUrl; + if(this.message !== null){ + this.message.dumbMode = false; + } + if ((imageUrl ?? "") === "") { + this.image = new FixedUiElement(""); + } else if (typeof (imageUrl) === "string") { + this.image = new FixedUiElement(``); + } else { + this.image = imageUrl; + } } InnerRender(): string { if(this.message !== null && this.message.IsEmpty()){ + // Message == null: special case to force empty text return ""; } if(this.linkTo != undefined){ return new Combine([ ``, - this.imageUrl !== undefined ? `` : "", - this.message ?? "", + this.image, + this.message, '' ]).Render(); } return new Combine([ '', - this.imageUrl !== undefined ? `` : "", - this.message ?? "", + this.image, + this.message, '' ]).Render(); } diff --git a/UI/CustomGenerator/GenerateEmpty.ts b/UI/CustomGenerator/GenerateEmpty.ts index 84274645f..7c13fa291 100644 --- a/UI/CustomGenerator/GenerateEmpty.ts +++ b/UI/CustomGenerator/GenerateEmpty.ts @@ -6,7 +6,7 @@ export class GenerateEmpty { public static createEmptyLayer(): LayerConfigJson { return { id: "yourlayer", - name: "Layer", + name: {}, minzoom: 12, overpassTags: {and: [""]}, title: {}, diff --git a/UI/CustomGenerator/SavePanel.ts b/UI/CustomGenerator/SavePanel.ts index 9367621fe..5893514ab 100644 --- a/UI/CustomGenerator/SavePanel.ts +++ b/UI/CustomGenerator/SavePanel.ts @@ -7,6 +7,7 @@ import {OsmConnection} from "../../Logic/Osm/OsmConnection"; import {FixedUiElement} from "../Base/FixedUiElement"; import {TextField} from "../Input/TextField"; import {SubtleButton} from "../Base/SubtleButton"; +import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; export default class SavePanel extends UIElement { private json: UIElement; @@ -45,7 +46,8 @@ export default class SavePanel extends UIElement { this.loadFromJson = new SubtleButton("./assets/reload.svg", "Load the JSON file below") .onClick(() => { const json = jsonTextField.GetValue().data; - config.setData(JSON.parse(json)); + const parsed : LayoutConfigJson = JSON.parse(json); + config.setData(parsed); }); } diff --git a/UI/Input/MultiLingualTextFields.ts b/UI/Input/MultiLingualTextFields.ts index 244ed6f5e..7d864a077 100644 --- a/UI/Input/MultiLingualTextFields.ts +++ b/UI/Input/MultiLingualTextFields.ts @@ -12,15 +12,23 @@ export default class MultiLingualTextFields extends InputElement { value: UIEventSource>> = undefined) { super(undefined); this._value = value ?? new UIEventSource({}); - const self = this; + this._value.addCallbackAndRun(latestData => { + if(typeof(latestData) === "string"){ + console.warn("Refusing string for multilingual input",latestData); + self._value.setData({}); + } + }) + + const self = this; + function setup(languages: string[]) { - if(languages === undefined){ + if (languages === undefined) { return; } const newFields = new Map>(); for (const language of languages) { - if(language.length != 2){ + if (language.length != 2) { continue; } diff --git a/assets/checkmark.svg b/assets/checkmark.svg new file mode 100644 index 000000000..59d073ff9 --- /dev/null +++ b/assets/checkmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.ts b/index.ts index d547dd0b8..f31c2cd96 100644 --- a/index.ts +++ b/index.ts @@ -82,7 +82,7 @@ if (layoutFromBase64.startsWith("wiki:")) { console.log("DOWNLOADED:",layoutJson); const layout = FromJSON.LayoutFromJSON(JSON.parse(layoutJson)); layout.id = layoutFromBase64; - InitUiElements.InitAll(layout, layoutFromBase64, testing, layoutFromBase64); + InitUiElements.InitAll(layout, layoutFromBase64, testing, layoutFromBase64, btoa(layoutJson)); } catch (e) { new FixedUiElement(`${themeName} is invalid:
${e}`) .SetClass("clickable") @@ -98,7 +98,7 @@ if (layoutFromBase64.startsWith("wiki:")) { } else if (layoutFromBase64 !== "false") { layoutToUse = InitUiElements.LoadLayoutFromHash(userLayoutParam); - InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout); + InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout, location.hash.substr(1)); } else { InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout); }