These are previously visited user-generated themes.","nl":"
Onofficiële thema's
De onderstaande thema's heb je eerder bezocht en zijn gemaakt door andere OpenStreetMappers.","fr":"
Thèmes personnalisés
Vous avez déjà visité ces thèmes personnalisés.","gl":"
Temas personalizados
Estes son temas xerados por usuarios previamente visitados.","de":"
Kundenspezifische Themen
Dies sind zuvor besuchte benutzergenerierte Themen"} ),
aboutMapcomplete: new Translation( {"en":"
About MapComplete
MapComplete is an OpenStreetMap editor that is meant to help everyone to easily add information on a single theme.
Only features relevant to a single theme are shown with a few predefined questions, in order to keep things simple and extremly user-friendly.The theme maintainer can also choose a language for the interface, choose to disable elements or even to embed it into a different website without any UI-element at all.
However, another important part of MapComplete is to always offer the next step to learn more about OpenStreetMap:
An iframe without UI-elements will link to a full-screen version
The fullscreen version offers information about OpenStreetMap
If you're not logged in, you're asked to log in
If you answered a single question, you are allowed to add points
At a certain point, the actual added tags appear which later get linked to the wiki...
Do you notice an issue with MapComplete? Do you have a feature request? Do you want to help translating? Head over to the source code or issue tracker. Follow the edit count on OsmCha
","nl":"
Over MapComplete
MapComplete is een OpenStreetMap-editor om eenvoudig informatie toe te voegen over één enkel onderwerp.
Om de editor zo simpel en gebruiksvriendelijk mogelijk te houden, worden enkel objecten relevant voor het thema getoond.Voor deze objecten kunnen dan vragen beantwoord worden, of men kan een nieuw punt van dit thema toevoegen.De maker van het thema kan er ook voor opteren om een aantal elementen van de gebruikersinterface uit te schakelen of de taal ervan in te stellen.
Een ander belangrijk aspect is om bezoekers stap voor stap meer te leren over OpenStreetMap:
Een iframe zonder verdere uitleg linkt naar de volledige versie van MapComplete
De volledige versie heeft uitleg over OpenStreetMap
Als je niet aangemeld bent, wordt er je gevraagd dit te doen
Als je minstens één vraag hebt beantwoord, kan je punten gaan toevoegen.
Heb je genoeg changesets, dan verschijnen de tags die wat later doorlinken naar de wiki
Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker. Volg de edits op OsmCha
","de":"
Über MapComplete
MapComplete ist ein OpenStreetMap-Editor, der jedem helfen soll, auf einfache Weise Informationen zu einem Einzelthema hinzuzufügen.
Nur Merkmale, die für ein einzelnes Thema relevant sind, werden mit einigen vordefinierten Fragen gezeigt, um die Dinge einfach und extrem benutzerfreundlich zu halten.Der Themen-Betreuer kann auch eine Sprache für die Schnittstelle wählen, Elemente deaktivieren oder sogar in eine andere Website ohne jegliches UI-Element einbetten.
Ein weiterer wichtiger Teil von MapComplete ist jedoch, immer den nächsten Schritt anzubietenum mehr über OpenStreetMap zu erfahren:
Ein iframe ohne UI-Elemente verlinkt zu einer Vollbildversion
Die Vollbildversion bietet Informationen über OpenStreetMap
Wenn Sie nicht eingeloggt sind, werden Sie gebeten, sich einzuloggen
Wenn Sie eine einzige Frage beantwortet haben, dürfen Sie Punkte hinzufügen
An einem bestimmten Punkt erscheinen die tatsächlich hinzugefügten Tags, die später mit dem Wiki verlinkt werden...
Fällt Ihnen ein Problem mit MapComplete auf? Haben Sie einen Feature-Wunsch? Wollen Sie beim Übersetzen helfen? Gehen Sie zum Quellcode oder zur Problemverfolgung.
"} ),
backgroundMap: new Translation( {"en":"Background map","ca":"Mapa de fons","es":"Mapa de fondo","nl":"Achtergrondkaart","fr":"Carte de fonds","de":"Hintergrundkarte"} ),
- zoomInToSeeThisLayer: new Translation( {"en":"Zoom in to see this layer","ca":"Amplia per veure aquesta capa","es":"Amplía para ver esta capa","nl":"Vergroot de kaart om deze laag te zien","fr":"Aggrandissez la carte pour voir cette couche","de":"Vergrößern, um diese Ebene zu sehen"} ),
+ layerSelection: { zoomInToSeeThisLayer: new Translation( {"en":"Zoom in to see this layer","ca":"Amplia per veure aquesta capa","es":"Amplía para ver esta capa","nl":"Vergroot de kaart om deze laag te zien","fr":"Aggrandissez la carte pour voir cette couche","de":"Vergrößern, um diese Ebene zu sehen"} ),
+ title: new Translation( {"en":"Select layers","nl":"Selecteer lagen"} ),
+},
weekdays: { abbreviations: { monday: new Translation( {"en":"Mon","ca":"Dil","es":"Lun","nl":"Maan","fr":"Lun"} ),
tuesday: new Translation( {"en":"Tue","ca":"Dim","es":"Mar","nl":"Din","fr":"Mar"} ),
wednesday: new Translation( {"en":"Wed","ca":"Dic","es":"Mie","nl":"Woe","fr":"Mer"} ),
diff --git a/Models/Constants.ts b/Models/Constants.ts
index 24d7f72cb..106bec851 100644
--- a/Models/Constants.ts
+++ b/Models/Constants.ts
@@ -1,7 +1,7 @@
import { Utils } from "../Utils";
export default class Constants {
- public static vNumber = "0.4.2";
+ public static vNumber = "0.4.3";
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {
diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts
new file mode 100644
index 000000000..f5770d70a
--- /dev/null
+++ b/UI/Base/ScrollableFullScreen.ts
@@ -0,0 +1,34 @@
+import {UIElement} from "../UIElement";
+import Svg from "../../Svg";
+import State from "../../State";
+import Combine from "./Combine";
+
+/**
+ * Wraps some contents into a panel that scrolls the content _under_ the title
+ */
+export default class ScrollableFullScreen extends UIElement{
+ private _component: Combine;
+
+
+ constructor(title: UIElement, content: UIElement) {
+ super();
+ const returnToTheMap = Svg.back_svg().onClick(() => {
+ State.state.fullScreenMessage.setData(undefined);
+ State.state.selectedElement.setData(undefined);
+ }).SetClass("only-on-mobile")
+ .SetClass("featureinfobox-back-to-the-map")
+
+ this._component = new Combine([
+ new Combine([returnToTheMap, title]).SetClass("featureinfobox-titlebar"),
+ new Combine([content]).SetClass("featureinfobox-content")
+ ])
+ this.SetClass("featureinfobox");
+
+ }
+
+ InnerRender(): string {
+ return this._component.Render();
+ }
+
+
+}
\ No newline at end of file
diff --git a/UI/BigComponents/LayerControlPanel.ts b/UI/BigComponents/LayerControlPanel.ts
index 2330beec2..76f7ac4cf 100644
--- a/UI/BigComponents/LayerControlPanel.ts
+++ b/UI/BigComponents/LayerControlPanel.ts
@@ -3,6 +3,8 @@ import State from "../../State";
import BackgroundSelector from "./BackgroundSelector";
import LayerSelection from "./LayerSelection";
import Combine from "../Base/Combine";
+import {FixedUiElement} from "../Base/FixedUiElement";
+import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
export default class LayerControlPanel extends UIElement {
@@ -11,7 +13,7 @@ export default class LayerControlPanel extends UIElement {
constructor() {
super();
- let layerControlPanel: UIElement = undefined;
+ let layerControlPanel: UIElement = new FixedUiElement("");
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
layerControlPanel = new BackgroundSelector();
layerControlPanel.SetStyle("margin:1em");
@@ -27,16 +29,9 @@ export default class LayerControlPanel extends UIElement {
}
- const backButton = new Combine([
- new Combine([Translations.t.general.returnToTheMap.Clone().SetClass("to-the-map")])
- .SetClass("to-the-map-inner")
+ const title =Translations.t.general.layerSelection.title.SetClass("featureinfobox-title")
- ]).SetClass("only-on-mobile")
- .onClick(() => State.state.fullScreenMessage.setData(undefined));
-
- layerControlPanel = new Combine([layerControlPanel , backButton]);
-
- this._panel = layerControlPanel;
+ this._panel = new ScrollableFullScreen(title, layerControlPanel);
}
InnerRender(): string {
diff --git a/UI/BigComponents/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts
index f914a18bc..f38b8ca5b 100644
--- a/UI/BigComponents/LayerSelection.ts
+++ b/UI/BigComponents/LayerSelection.ts
@@ -36,14 +36,14 @@ export default class LayerSelection extends UIElement {
const zoomStatus = new VariableUiElement(State.state.locationControl.map(location => {
if (location.zoom < layer.layerDef.minzoom) {
- return Translations.t.general.zoomInToSeeThisLayer
+ return Translations.t.general.layerSelection.zoomInToSeeThisLayer
.SetClass("alert")
.SetStyle("display: block ruby;width:min-content;")
.Render();
}
return ""
}))
- const style = "display:flex;align-items:center;"
+ const style = "display:flex;align-items:center;flex-wrap:wrap;"
this._checkboxes.push(new CheckBox(
new Combine([icon, name, zoomStatus]).SetStyle(style),
new Combine([iconUnselected, "", name, "", zoomStatus]).SetStyle(style),
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index f97e97a27..01c29e649 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -10,17 +10,10 @@ import {FixedUiElement} from "../Base/FixedUiElement";
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
import Svg from "../../Svg";
import Ornament from "../Base/Ornament";
+import ScrollableFullScreen from "../Base/ScrollableFullScreen";
export default class FeatureInfoBox extends UIElement {
- private _tags: UIEventSource;
- private _layerConfig: LayerConfig;
-
- private _title: UIElement;
- private _titleIcons: UIElement;
- private _renderings: UIElement[];
- private _questionBox: UIElement;
- private _returnToTheMap: UIElement;
- private _tail: UIElement;
+ private _component: UIElement;
constructor(
tags: UIEventSource,
@@ -30,18 +23,11 @@ export default class FeatureInfoBox extends UIElement {
if (layerConfig === undefined) {
throw "Undefined layerconfig"
}
- this._tags = tags;
- this._layerConfig = layerConfig;
- this._returnToTheMap = Svg.back_svg().onClick(() => {
- State.state.fullScreenMessage.setData(undefined);
- State.state.selectedElement.setData(undefined);
- }).SetClass("only-on-mobile")
- .SetClass("featureinfobox-back-to-the-map")
- this._title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI"))
+ const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI"))
.SetClass("featureinfobox-title");
- this._titleIcons = new Combine(
+ const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
.SetClass("featureinfobox-icons");
@@ -51,7 +37,7 @@ export default class FeatureInfoBox extends UIElement {
}
let questionBoxIsUsed = false;
- this._renderings = layerConfig.tagRenderings.map(tr => {
+ const renderings = layerConfig.tagRenderings.map(tr => {
if (tr.question === null) {
questionBoxIsUsed = true;
// This is the question box!
@@ -59,27 +45,27 @@ export default class FeatureInfoBox extends UIElement {
}
return new EditableTagRendering(tags, tr);
});
- this._renderings[0]?.SetClass("first-rendering");
+ renderings[0]?.SetClass("first-rendering");
if (!questionBoxIsUsed) {
- this._renderings.push(questionBox);
+ renderings.push(questionBox);
}
- this._tail = new Combine([new Ornament()]).SetClass("only-on-mobile");
+ const tail = new Combine([new Ornament()]).SetClass("only-on-mobile");
+
+ const content = new Combine([
+ ...renderings,
+ questionBox,
+ tail.SetClass("featureinfobox-tail")
+ ]
+ )
+ const titleBar = new Combine([
+ new Combine([title, titleIcons]).SetClass("featureinfobox-titlebar-title")
+ ])
+
+ this._component = new ScrollableFullScreen(titleBar, content)
}
InnerRender(): string {
- return new Combine([
- new Combine([
- this._returnToTheMap, new Combine([this._title, this._titleIcons]).SetClass("featureinfobox-titlebar-title")
- ]).SetClass("featureinfobox-titlebar"),
- new Combine([
- ...this._renderings,
- this._questionBox,
- this._tail.SetClass("featureinfobox-tail")
- ]
- ).SetClass("featureinfobox-content"),
- ]).SetClass("featureinfobox")
- .Render();
+ return this._component.Render();
}
-
}
diff --git a/assets/translations.json b/assets/translations.json
index b5be39a60..65dc28304 100644
--- a/assets/translations.json
+++ b/assets/translations.json
@@ -744,14 +744,21 @@
"fr": "Carte de fonds",
"de": "Hintergrundkarte"
},
- "zoomInToSeeThisLayer": {
- "en": "Zoom in to see this layer",
- "ca": "Amplia per veure aquesta capa",
- "es": "Amplía para ver esta capa",
- "nl": "Vergroot de kaart om deze laag te zien",
- "fr": "Aggrandissez la carte pour voir cette couche",
- "de": "Vergrößern, um diese Ebene zu sehen"
+ "layerSelection": {
+ "zoomInToSeeThisLayer": {
+ "en": "Zoom in to see this layer",
+ "ca": "Amplia per veure aquesta capa",
+ "es": "Amplía para ver esta capa",
+ "nl": "Vergroot de kaart om deze laag te zien",
+ "fr": "Aggrandissez la carte pour voir cette couche",
+ "de": "Vergrößern, um diese Ebene zu sehen"
+ },
+ "title": {
+ "en": "Select layers",
+ "nl": "Selecteer lagen"
+ }
},
+
"weekdays": {
"abbreviations": {
"monday": {
diff --git a/css/tagrendering.css b/css/tagrendering.css
index bab0741ca..f99be5789 100644
--- a/css/tagrendering.css
+++ b/css/tagrendering.css
@@ -25,9 +25,11 @@
.featureinfobox-titlebar {
border-bottom: 2px solid var(--foreground-color);
- box-shadow: 0 10px 10px -10px lightgray;
+ box-shadow: 0 10px 10px -10px var(--shadow-color);
display: flex;
justify-content: space-between;
+ max-width: 95vw;
+ overflow-x: hidden;
}
.featureinfobox-titlebar-title {
@@ -65,8 +67,9 @@
display: block;
max-height: 75vh;
overflow-y: auto;
- overflow-x: hidden;
padding-top: 1em;
+ max-width: 95vw;
+ overflow-x: hidden;
}
@media only screen and (max-width: 600px), only screen and (max-height: 600px) {
From 2a31badd3d8dbbf8ebfb065620c820a0d1303a40 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 03:57:18 +0100
Subject: [PATCH 15/25] New roaming rendering system which allows layers to
push questions, badges and title-icons to all the other layers, improve
bike-clean-services
---
Customizations/JSON/LayerConfig.ts | 139 +++++----
Customizations/JSON/LayoutConfig.ts | 18 +-
Customizations/JSON/TagRenderingConfig.ts | 37 ++-
Customizations/JSON/TagRenderingConfigJson.ts | 6 +
Customizations/SharedLayers.ts | 2 +-
UI/CustomGenerator/AllLayersPanel.ts | 2 +-
UI/CustomGenerator/TagRenderingPanel.ts | 2 +-
UI/CustomGenerator/TagRenderingPreview.ts | 2 +-
UI/Popup/FeatureInfoBox.ts | 3 +-
UI/Popup/TagRenderingQuestion.ts | 2 +-
UI/UIElement.ts | 6 -
.../layers/bike_cleaning/bike_cleaning.json | 81 ++++-
.../bike_cleaning/bike_cleaning_icon.svg | 204 ++++++++++++
assets/layers/bike_shop/bike_cleaning.svg | 292 ------------------
assets/layers/bike_shop/bike_shop.json | 9 -
test/Tag.spec.ts | 16 +-
16 files changed, 427 insertions(+), 394 deletions(-)
create mode 100644 assets/layers/bike_cleaning/bike_cleaning_icon.svg
delete mode 100644 assets/layers/bike_shop/bike_cleaning.svg
diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 79891f166..53825613d 100644
--- a/Customizations/JSON/LayerConfig.ts
+++ b/Customizations/JSON/LayerConfig.ts
@@ -19,22 +19,18 @@ import {UIElement} from "../../UI/UIElement";
export default class LayerConfig {
+ static WAYHANDLING_DEFAULT = 0;
+ static WAYHANDLING_CENTER_ONLY = 1;
+ static WAYHANDLING_CENTER_AND_WAY = 2;
id: string;
-
name: Translation
-
description: Translation;
overpassTags: TagsFilter;
doNotDownload: boolean;
-
passAllFeatures: boolean;
-
minzoom: number;
-
title?: TagRenderingConfig;
-
titleIcons: TagRenderingConfig[];
-
icon: TagRenderingConfig;
iconOverlays: { if: TagsFilter, then: TagRenderingConfig, badge: boolean }[]
iconSize: TagRenderingConfig;
@@ -42,14 +38,7 @@ export default class LayerConfig {
color: TagRenderingConfig;
width: TagRenderingConfig;
dashArray: TagRenderingConfig;
-
-
wayHandling: number;
-
- static WAYHANDLING_DEFAULT = 0;
- static WAYHANDLING_CENTER_ONLY = 1;
- static WAYHANDLING_CENTER_AND_WAY = 2;
-
hideUnderlayingFeaturesMinPercentage?: number;
presets: {
@@ -60,10 +49,10 @@ export default class LayerConfig {
tagRenderings: TagRenderingConfig [];
- constructor(json: LayerConfigJson, roamingRenderings: TagRenderingConfig[],
+ constructor(json: LayerConfigJson,
context?: string) {
context = context + "." + json.id;
-
+ const self = this;
this.id = json.id;
this.name = Translations.T(json.name);
this.description = Translations.T(json.description);
@@ -81,6 +70,28 @@ export default class LayerConfig {
}))
+ /** Given a key, gets the corresponding property from the json (or the default if not found
+ *
+ * The found value is interpreted as a tagrendering and fetched/parsed
+ * */
+ function tr(key: string, deflt) {
+ const v = json[key];
+ if (v === undefined || v === null) {
+ if (deflt === undefined) {
+ return undefined;
+ }
+ return new TagRenderingConfig(deflt, self.overpassTags, `${context}.${key}.default value`);
+ }
+ if (typeof v === "string") {
+ const shared = SharedTagRenderings.SharedTagRendering[v];
+ if (shared) {
+ console.log("Got shared TR:", v, "-->", shared)
+ return shared;
+ }
+ }
+ return new TagRenderingConfig(v, self.overpassTags, `${context}.${key}`);
+ }
+
/**
* Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig
* A string is interpreted as a name to call
@@ -90,26 +101,27 @@ export default class LayerConfig {
if (tagRenderings === undefined) {
return [];
}
+
return tagRenderings.map(
(renderingJson, i) => {
if (typeof renderingJson === "string") {
-
- if(renderingJson === "questions"){
- return new TagRenderingConfig("questions")
+
+ if (renderingJson === "questions") {
+ return new TagRenderingConfig("questions", undefined)
}
-
-
+
+
const shared = SharedTagRenderings.SharedTagRendering[renderingJson];
if (shared !== undefined) {
return shared;
}
throw `Predefined tagRendering ${renderingJson} not found in ${context}`;
}
- return new TagRenderingConfig(renderingJson, `${context}.tagrendering[${i}]`);
+ return new TagRenderingConfig(renderingJson, self.overpassTags, `${context}.tagrendering[${i}]`);
});
}
- this.tagRenderings = trs(json.tagRenderings).concat(roamingRenderings);
+ this.tagRenderings = trs(json.tagRenderings);
const titleIcons = [];
@@ -125,29 +137,10 @@ export default class LayerConfig {
this.titleIcons = trs(titleIcons);
- function tr(key, deflt) {
- const v = json[key];
- if (v === undefined || v === null) {
- if (deflt === undefined) {
- return undefined;
- }
- return new TagRenderingConfig(deflt);
- }
- if (typeof v === "string") {
- const shared = SharedTagRenderings.SharedTagRendering[v];
- if (shared) {
- console.log("Got shared TR:", v, "-->", shared)
- return shared;
- }
- }
- return new TagRenderingConfig(v, context + "." + key);
- }
-
-
this.title = tr("title", undefined);
this.icon = tr("icon", Img.AsData(Svg.pin));
- this.iconOverlays = (json.iconOverlays ?? []).map(overlay => {
- let tr = new TagRenderingConfig(overlay.then);
+ this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => {
+ let tr = new TagRenderingConfig(overlay.then, self.overpassTags, `iconoverlays.${i}`);
if (typeof overlay.then === "string" && SharedTagRenderings.SharedIcons[overlay.then] !== undefined) {
tr = SharedTagRenderings.SharedIcons[overlay.then];
}
@@ -175,18 +168,54 @@ export default class LayerConfig {
}
+ public AddRoamingRenderings(addAll: {
+ tagRenderings: TagRenderingConfig[],
+ titleIcons: TagRenderingConfig[],
+ iconOverlays: { "if": TagsFilter, then: TagRenderingConfig, badge: boolean }[]
+
+ }): LayerConfig {
+ this.tagRenderings.push(...addAll.tagRenderings);
+ this.iconOverlays.push(...addAll.iconOverlays);
+ for (const icon of addAll.titleIcons) {
+ console.log("Adding ",icon, "to", this.id)
+ this.titleIcons.splice(0,0, icon);
+ }
+ return this;
+ }
+
+ public GetRoamingRenderings(): {
+ tagRenderings: TagRenderingConfig[],
+ titleIcons: TagRenderingConfig[],
+ iconOverlays: { "if": TagsFilter, then: TagRenderingConfig, badge: boolean }[]
+
+ } {
+
+ const tagRenderings = this.tagRenderings.filter(tr => tr.roaming);
+ const titleIcons = this.titleIcons.filter(tr => tr.roaming);
+ const iconOverlays = this.iconOverlays.filter(io => io.then.roaming)
+
+ return {
+ tagRenderings: tagRenderings,
+ titleIcons: titleIcons,
+ iconOverlays: iconOverlays
+ }
+
+ }
+
public GenerateLeafletStyle(tags: UIEventSource, clickable: boolean):
{
- color: string;
- icon: {
- iconUrl: string,
- popupAnchor: [number, number];
- iconAnchor: [number, number];
- iconSize: [number, number];
- html: UIElement;
- className?: string;
- };
- weight: number; dashArray: number[]
+ icon:
+ {
+ html: UIElement,
+ iconSize: [number, number],
+ iconAnchor: [number, number],
+ popupAnchor: [number, number],
+ iconUrl: string,
+ className: string
+ },
+ color: string,
+ weight: number,
+ dashArray: number[]
} {
function num(str, deflt = 40) {
@@ -259,7 +288,7 @@ export default class LayerConfig {
if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
html = new Combine([
(Svg.All[match[1] + ".svg"] as string)
- .replace(/#000000/g, match[2])
+ .replace(/#000000/g, match[2])
]).SetStyle(style);
}
return html;
diff --git a/Customizations/JSON/LayoutConfig.ts b/Customizations/JSON/LayoutConfig.ts
index 23a2264b5..81be65c8e 100644
--- a/Customizations/JSON/LayoutConfig.ts
+++ b/Customizations/JSON/LayoutConfig.ts
@@ -75,7 +75,7 @@ export default class LayoutConfig {
return SharedTagRenderings.SharedTagRendering[tr];
}
}
- return new TagRenderingConfig(tr, `${this.id}.roaming_renderings[${i}]`);
+ return new TagRenderingConfig(tr, undefined,`${this.id}.roaming_renderings[${i}]`);
}
);
this.defaultBackgroundId = json.defaultBackgroundId;
@@ -102,9 +102,23 @@ export default class LayoutConfig {
}
// @ts-ignore
- return new LayerConfig(layer, this.roamingRenderings, `${this.id}.layers[${i}]`);
+ return new LayerConfig(layer, `${this.id}.layers[${i}]`)
});
+
+ // ALl the layers are constructed, let them share tags in piece now!
+ const roaming : {r, source: LayerConfig}[] = []
+ for (const layer of this.layers) {
+ roaming.push({r: layer.GetRoamingRenderings(), source:layer});
+ }
+ for (const layer of this.layers) {
+ for (const r of roaming) {
+ if(r.source == layer){
+ continue;
+ }
+ layer.AddRoamingRenderings(r.r);
+ }
+ }
this.clustering = {
maxZoom: 16,
diff --git a/Customizations/JSON/TagRenderingConfig.ts b/Customizations/JSON/TagRenderingConfig.ts
index add0f72c2..5817bb5e2 100644
--- a/Customizations/JSON/TagRenderingConfig.ts
+++ b/Customizations/JSON/TagRenderingConfig.ts
@@ -1,4 +1,4 @@
-import {TagsFilter} from "../../Logic/Tags";
+import {And, TagsFilter} from "../../Logic/Tags";
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
import Translations from "../../UI/i18n/Translations";
import {FromJSON} from "./FromJSON";
@@ -11,25 +11,26 @@ import {Translation} from "../../UI/i18n/Translation";
*/
export default class TagRenderingConfig {
- render?: Translation;
- question?: Translation;
- condition?: TagsFilter;
+ readonly render?: Translation;
+ readonly question?: Translation;
+ readonly condition?: TagsFilter;
- freeform?: {
- key: string,
- type: string,
- addExtraTags: TagsFilter[];
+ readonly freeform?: {
+ readonly key: string,
+ readonly type: string,
+ readonly addExtraTags: TagsFilter[];
};
readonly multiAnswer: boolean;
- mappings?: {
- if: TagsFilter,
- then: Translation
- hideInAnswer: boolean | TagsFilter
+ readonly mappings?: {
+ readonly if: TagsFilter,
+ readonly then: Translation
+ readonly hideInAnswer: boolean | TagsFilter
}[]
+ readonly roaming: boolean;
- constructor(json: string | TagRenderingConfigJson, context?: string) {
+ constructor(json: string | TagRenderingConfigJson, conditionIfRoaming: TagsFilter, context?: string) {
if (json === "questions") {
// Very special value
@@ -46,10 +47,16 @@ export default class TagRenderingConfig {
this.multiAnswer = false;
return;
}
-
+
this.render = Translations.T(json.render);
this.question = Translations.T(json.question);
- this.condition = FromJSON.Tag(json.condition ?? {"and": []}, `${context}.condition`);
+ this.roaming = json.roaming ?? false;
+ const condition = FromJSON.Tag(json.condition ?? {"and": []}, `${context}.condition`);
+ if (this.roaming && conditionIfRoaming !== undefined) {
+ this.condition = new And([condition, conditionIfRoaming]);
+ } else {
+ this.condition = condition;
+ }
if (json.freeform) {
this.freeform = {
key: json.freeform.key,
diff --git a/Customizations/JSON/TagRenderingConfigJson.ts b/Customizations/JSON/TagRenderingConfigJson.ts
index 7f2541b2f..441530dfd 100644
--- a/Customizations/JSON/TagRenderingConfigJson.ts
+++ b/Customizations/JSON/TagRenderingConfigJson.ts
@@ -53,4 +53,10 @@ export interface TagRenderingConfigJson {
then: string | any
hideInAnswer?: boolean
}[]
+
+ /**
+ * If set to true, this tagRendering will escape the current layer and attach itself to all the other layers too.
+ * However, it will _only_ be shown if it matches the overpass-tags of the layer it was originally defined in.
+ */
+ roaming?: boolean
}
\ No newline at end of file
diff --git a/Customizations/SharedLayers.ts b/Customizations/SharedLayers.ts
index 6aed204c7..72427998f 100644
--- a/Customizations/SharedLayers.ts
+++ b/Customizations/SharedLayers.ts
@@ -56,7 +56,7 @@ export default class SharedLayers {
private static getSharedLayers(): Map {
const sharedLayers = new Map();
for (const layer of SharedLayers.sharedLayersListRaw) {
- const parsed = new LayerConfig(layer, [], "shared_layers")
+ const parsed = new LayerConfig(layer, "shared_layers")
sharedLayers.set(layer.id, parsed);
sharedLayers[layer.id] = parsed;
}
diff --git a/UI/CustomGenerator/AllLayersPanel.ts b/UI/CustomGenerator/AllLayersPanel.ts
index 588af7f2e..dcf0c633c 100644
--- a/UI/CustomGenerator/AllLayersPanel.ts
+++ b/UI/CustomGenerator/AllLayersPanel.ts
@@ -68,7 +68,7 @@ export default class AllLayersPanel extends UIElement {
const layer = config.layers[i];
if (typeof layer !== "string") {
try {
- const iconTagRendering = new TagRenderingConfig(layer.icon, "icon")
+ const iconTagRendering = new TagRenderingConfig(layer.icon, undefined, "icon")
const icon = iconTagRendering.GetRenderValue({"id": "node/-1"}).txt;
return ``
} catch (e) {
diff --git a/UI/CustomGenerator/TagRenderingPanel.ts b/UI/CustomGenerator/TagRenderingPanel.ts
index 211400498..89b6cd3f2 100644
--- a/UI/CustomGenerator/TagRenderingPanel.ts
+++ b/UI/CustomGenerator/TagRenderingPanel.ts
@@ -111,7 +111,7 @@ export default class TagRenderingPanel extends InputElement {
try{
- new TagRenderingConfig(json, options?.title ?? "");
+ new TagRenderingConfig(json,undefined, options?.title ?? "");
return "";
}catch(e){
return ""+e+""
diff --git a/UI/CustomGenerator/TagRenderingPreview.ts b/UI/CustomGenerator/TagRenderingPreview.ts
index a884b3493..d758682b1 100644
--- a/UI/CustomGenerator/TagRenderingPreview.ts
+++ b/UI/CustomGenerator/TagRenderingPreview.ts
@@ -40,7 +40,7 @@ export default class TagRenderingPreview extends UIElement {
rendering =
new VariableUiElement(es.map(tagRenderingConfig => {
try {
- const tr = new EditableTagRendering(self.previewTagValue, new TagRenderingConfig(tagRenderingConfig, "preview"));
+ const tr = new EditableTagRendering(self.previewTagValue, new TagRenderingConfig(tagRenderingConfig, undefined,"preview"));
return tr.Render();
} catch (e) {
return new Combine(["Could not show this tagrendering:", e.message]).Render();
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index 01c29e649..57e7e99da 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -25,7 +25,7 @@ export default class FeatureInfoBox extends UIElement {
}
- const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI"))
+ const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
.SetClass("featureinfobox-title");
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
@@ -53,7 +53,6 @@ export default class FeatureInfoBox extends UIElement {
const content = new Combine([
...renderings,
- questionBox,
tail.SetClass("featureinfobox-tail")
]
)
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index 7fd00a7b7..b6ba2c39d 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -67,7 +67,7 @@ export default class TagRenderingQuestion extends UIElement {
}
- this._saveButton = new SaveButton(this._inputElement.GetValue(), State.state.osmConnection)
+ this._saveButton = new SaveButton(this._inputElement.GetValue(), State.state?.osmConnection)
.onClick(save)
diff --git a/UI/UIElement.ts b/UI/UIElement.ts
index d1fda3f87..947e616f3 100644
--- a/UI/UIElement.ts
+++ b/UI/UIElement.ts
@@ -4,12 +4,6 @@ import {Utils} from "../Utils";
export abstract class UIElement extends UIEventSource {
- /**
- * In the 'deploy'-step, some code needs to be run by ts-node.
- * However, ts-node crashes when it sees 'document'. When running from console, we flag this and disable all code where document is needed.
- * This is a workaround and yet another hack
- */
- public static runningFromConsole = false;
private static nextId: number = 0;
public readonly id: string;
public readonly _source: UIEventSource;
diff --git a/assets/layers/bike_cleaning/bike_cleaning.json b/assets/layers/bike_cleaning/bike_cleaning.json
index ea66f4dbe..2665ea102 100644
--- a/assets/layers/bike_cleaning/bike_cleaning.json
+++ b/assets/layers/bike_cleaning/bike_cleaning.json
@@ -26,6 +26,7 @@
"overpassTags": {
"or": [
"service:bicycle:cleaning=yes",
+ "service:bicycle:cleaning=diy",
"amenity=bicycle_wash"
]
},
@@ -38,13 +39,87 @@
"nl": "Fietsschoonmaakpunt"
},
"tags": [
- "amenity=bicycle_wash",
- "service:bicycle:cleaning=yes"
+ "amenity=bicycle_wash"
]
}
],
"color": "#6bc4f7",
+ "iconOverlays": [
+ {
+ "if": {
+ "and": [
+ "service:bicycle:cleaning~*",
+ "amenity!=bike_wash"
+ ]
+ },
+ "then": {
+ "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg",
+ "roaming": true
+ },
+ "badge": true
+ }
+ ],
+ "titleIcons": [
+ {
+ "render": "",
+ "roaming": true
+ }
+ ],
"tagRenderings": [
- "images"
+ "images",
+ {
+ "question": "How much does it cost to use the cleaning service?",
+ "render": "Using the cleaning service costs {charge}",
+ "condition": "amenity!=bike_wash",
+ "freeform": {
+ "key": "service:bicycle:cleaning:charge",
+ "addExtraTags": [
+ "service:bicycle:cleaning:fee=yes"
+ ]
+ },
+ "mappings": [
+ {
+ "if": "service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge=",
+ "then": "The cleaning service is free to use"
+ },
+ {
+ "if": "service:bicycle:cleaning:fee=no&",
+ "then": "Free to use",
+ "hideInAnswer": true
+ },
+ {
+ "if": "service:bicycle:cleaning:fee=yes",
+ "then": "The cleaning service has a fee"
+ }
+ ],
+ "roaming": true
+ },
+ {
+ "question": "How much does it cost to use the cleaning service?",
+ "render": "Using the cleaning service costs {charge}",
+ "condition": "amenity=bike_wash",
+ "freeform": {
+ "key": "charge",
+ "addExtraTags": [
+ "fee=yes"
+ ]
+ },
+ "mappings": [
+ {
+ "if": "fee=no&charge=",
+ "then": "Free to use cleaning service"
+ },
+ {
+ "if": "fee=no&",
+ "then": "Free to use",
+ "hideInAnswer": true
+ },
+ {
+ "if": "fee=yes",
+ "then": "The cleaning service has a fee"
+ }
+ ],
+ "roaming": false
+ }
]
}
\ No newline at end of file
diff --git a/assets/layers/bike_cleaning/bike_cleaning_icon.svg b/assets/layers/bike_cleaning/bike_cleaning_icon.svg
new file mode 100644
index 000000000..e6b5c0169
--- /dev/null
+++ b/assets/layers/bike_cleaning/bike_cleaning_icon.svg
@@ -0,0 +1,204 @@
+
+
diff --git a/assets/layers/bike_shop/bike_cleaning.svg b/assets/layers/bike_shop/bike_cleaning.svg
deleted file mode 100644
index 96c03eed4..000000000
--- a/assets/layers/bike_shop/bike_cleaning.svg
+++ /dev/null
@@ -1,292 +0,0 @@
-
-
diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json
index 9a54711c6..f8676ce3a 100644
--- a/assets/layers/bike_shop/bike_shop.json
+++ b/assets/layers/bike_shop/bike_shop.json
@@ -186,15 +186,6 @@
"condition": "service:bicycle:diy=yes",
"render": ""
},
- {
- "condition": {
- "or": [
- "service:bicycle:cleaning=yes",
- "service:bicycle:cleaning=diy"
- ]
- },
- "render": ""
- },
"defaults"
],
"description": {
diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts
index 4ef6ddf7d..5c4f8af55 100644
--- a/test/Tag.spec.ts
+++ b/test/Tag.spec.ts
@@ -1,5 +1,5 @@
-import {UIElement} from "../UI/UIElement";
-UIElement.runningFromConsole = true;
+import {Utils} from "../Utils";
+Utils.runningFromConsole = true;
import {equal} from "assert";
import T from "./TestHelper";
import {FromJSON} from "../Customizations/JSON/FromJSON";
@@ -10,7 +10,6 @@ import {UIEventSource} from "../Logic/UIEventSource";
import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig";
import EditableTagRendering from "../UI/Popup/EditableTagRendering";
import {SubstitutedTranslation} from "../UI/SpecialVisualizations";
-import {Utils} from "../Utils";
import {Translation} from "../UI/i18n/Translation";
import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours";
import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput";
@@ -40,6 +39,13 @@ new T([
equal(notReg.matches([{k:"x",v:""}]), true)
equal(notReg.matches([]), true)
+
+
+ const noMatch = FromJSON.Tag("key!=value") as Tag;
+ equal(noMatch.matches([{k:"key",v:"value"}]), false)
+ equal(noMatch.matches([{k:"key",v:"otherValue"}]), true)
+ equal(noMatch.matches([{k:"key",v:""}]), true)
+ equal(noMatch.matches([{k:"otherKey",v:""}]), true)
})],
@@ -92,7 +98,7 @@ new T([
}
],
condition: "x="
- }, "");
+ }, undefined,"");
equal(undefined, tr.GetRenderValue({"foo": "bar"}));
equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt);
@@ -145,7 +151,7 @@ new T([
]
};
- const constr = new TagRenderingConfig(def, "test");
+ const constr = new TagRenderingConfig(def, undefined,"test");
const uiEl = new EditableTagRendering(new UIEventSource(
{leisure: "park", "access": "no"}), constr
);
From 52f1d5511d6cd662905114f0f45cfe9d2e9306b0 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 04:06:10 +0100
Subject: [PATCH 16/25] Layer filtering now allows items that would show up on
another layer
---
Logic/FeatureSource/FilteringFeatureSource.ts | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts
index 49fd2004b..9d684248e 100644
--- a/Logic/FeatureSource/FilteringFeatureSource.ts
+++ b/Logic/FeatureSource/FilteringFeatureSource.ts
@@ -32,7 +32,20 @@ export default class FilteringFeatureSource implements FeatureSource {
if (layer === undefined) {
throw "No layer found with id " + layerId;
}
- return layer.isDisplayed.data && (layer.layerDef.minzoom <= location.data.zoom);
+ if(layer.isDisplayed.data && (layer.layerDef.minzoom <= location.data.zoom)){
+ return true;
+ }
+ // Does it match any other layer?
+ for (const toCheck of layers) {
+ if(!toCheck.isDisplayed.data){
+ continue;
+ }
+ if(toCheck.layerDef.overpassTags.matchesProperties(f.feature.properties)){
+ return true;
+ }
+ }
+ return false;
+
});
self.features.setData(newFeatures);
}
From ddea3d2fc1e348a3325856bd9ba7c05a69e02296 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 14:23:12 +0100
Subject: [PATCH 17/25] Small css tweaks, fix layer filtering
---
Logic/FeatureSource/FilteringFeatureSource.ts | 25 ++++++++++++-------
UI/BigComponents/LayerSelection.ts | 9 ++++---
css/tagrendering.css | 4 +--
index.css | 1 +
4 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts
index 9d684248e..cfc9c9480 100644
--- a/Logic/FeatureSource/FilteringFeatureSource.ts
+++ b/Logic/FeatureSource/FilteringFeatureSource.ts
@@ -32,20 +32,20 @@ export default class FilteringFeatureSource implements FeatureSource {
if (layer === undefined) {
throw "No layer found with id " + layerId;
}
- if(layer.isDisplayed.data && (layer.layerDef.minzoom <= location.data.zoom)){
+ if (FilteringFeatureSource.showLayer(layer, location)) {
return true;
}
// Does it match any other layer?
for (const toCheck of layers) {
- if(!toCheck.isDisplayed.data){
+ if (!FilteringFeatureSource.showLayer(toCheck, location)) {
continue;
}
- if(toCheck.layerDef.overpassTags.matchesProperties(f.feature.properties)){
+ if (toCheck.layerDef.overpassTags.matchesProperties(f.feature.properties)) {
return true;
}
}
return false;
-
+
});
self.features.setData(newFeatures);
}
@@ -54,15 +54,16 @@ export default class FilteringFeatureSource implements FeatureSource {
layerDict[layer.layerDef.id] = layer;
}
upstream.features.addCallback(() => {
- update()});
+ update()
+ });
location.map(l => {
// We want something that is stable for the shown layers
const displayedLayerIndexes = [];
for (let i = 0; i < layers.length; i++) {
- if(l.zoom < layers[i].layerDef.minzoom){
+ if (l.zoom < layers[i].layerDef.minzoom) {
continue;
}
- if(!layers[i].isDisplayed.data){
+ if (!layers[i].isDisplayed.data) {
continue;
}
displayedLayerIndexes.push(i);
@@ -70,10 +71,16 @@ export default class FilteringFeatureSource implements FeatureSource {
return displayedLayerIndexes.join(",")
}, layers.map(l => l.isDisplayed))
.addCallback(() => {
- update();});
+ update();
+ });
}
-
+ private static showLayer(layer: {
+ isDisplayed: UIEventSource,
+ layerDef: LayerConfig
+ }, location: UIEventSource) {
+ return layer.isDisplayed.data && (layer.layerDef.minzoom <= location.data.zoom)
+ }
}
\ No newline at end of file
diff --git a/UI/BigComponents/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts
index f38b8ca5b..d812b6810 100644
--- a/UI/BigComponents/LayerSelection.ts
+++ b/UI/BigComponents/LayerSelection.ts
@@ -43,10 +43,13 @@ export default class LayerSelection extends UIElement {
}
return ""
}))
- const style = "display:flex;align-items:center;flex-wrap:wrap;"
+ const style = "display:flex;align-items:center;"
+ const styleWhole = "display:flex; flex-wrap: wrap"
this._checkboxes.push(new CheckBox(
- new Combine([icon, name, zoomStatus]).SetStyle(style),
- new Combine([iconUnselected, "", name, "", zoomStatus]).SetStyle(style),
+ new Combine([new Combine([icon, name]).SetStyle(style), zoomStatus])
+ .SetStyle(styleWhole),
+ new Combine([new Combine([iconUnselected, "", name, ""]).SetStyle(style), zoomStatus])
+ .SetStyle(styleWhole),
layer.isDisplayed)
.SetStyle("margin:0.3em;")
);
diff --git a/css/tagrendering.css b/css/tagrendering.css
index f99be5789..eb3290bda 100644
--- a/css/tagrendering.css
+++ b/css/tagrendering.css
@@ -28,7 +28,7 @@
box-shadow: 0 10px 10px -10px var(--shadow-color);
display: flex;
justify-content: space-between;
- max-width: 95vw;
+ width: 100%;
overflow-x: hidden;
}
@@ -68,7 +68,7 @@
max-height: 75vh;
overflow-y: auto;
padding-top: 1em;
- max-width: 95vw;
+ width:100%;
overflow-x: hidden;
}
diff --git a/index.css b/index.css
index 74584f272..486bdbd11 100644
--- a/index.css
+++ b/index.css
@@ -103,6 +103,7 @@ a {
position: relative;
width: 2em;
height: 2em;
+ flex-shrink: 0;
}
.single-layer-selection-toggle img {
From 6e775048541fc896da5a5589605aa67b5d6f5ad7 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 16:49:42 +0100
Subject: [PATCH 18/25] Add back button on simpleAddUI
---
AllTranslationAssets.ts | 3 +-
Logic/Web/QueryParameters.ts | 1 +
Models/Constants.ts | 2 +-
UI/Base/ScrollableFullScreen.ts | 7 +-
UI/BigComponents/SimpleAddUI.ts | 273 ++++++++++++++++++--------------
UI/Popup/FeatureInfoBox.ts | 5 +-
assets/translations.json | 26 ++-
7 files changed, 178 insertions(+), 139 deletions(-)
diff --git a/AllTranslationAssets.ts b/AllTranslationAssets.ts
index d2b4dd4b1..ac65d3d9a 100644
--- a/AllTranslationAssets.ts
+++ b/AllTranslationAssets.ts
@@ -39,7 +39,8 @@ export default class AllTranslationAssets {
number: new Translation( {"en":"number","ca":"nombre","es":"número","nl":"getal","fr":"nombre","gl":"número","de":"Zahl"} ),
osmLinkTooltip: new Translation( {"en":"See this object on OpenStreetMap for history and more editing options","ca":"Mira aquest objecte a OpenStreetMap per veure historial i altres opcions d'edició","es":"Mira este objeto en OpenStreetMap para ver historial y otras opciones de edición","nl":"Bekijk dit object op OpenStreetMap waar geschiedenis en meer aanpasopties zijn","fr":"Voir l'historique de cet objet sur OpenStreetMap et plus d'options d'édition","gl":"Ollar este obxecto no OpenStreetMap para ollar o historial e outras opcións de edición","de":"Dieses Objekt auf OpenStreetMap anschauen für die Geschichte und weitere Bearbeitungsmöglichkeiten"} ),
add: { addNew: new Translation( {"en":"Add a new {category} here","ca":"Afegir {category} aquí","es":"Añadir {category} aquí","nl":"Voeg hier een {category} toe","fr":"Ajouter un/une {category} ici","gl":"Engadir {category} aquí","de":"Hier eine neue {category} hinzufügen"} ),
- header: new Translation( {"en":"
Add a point?
You clicked somewhere where no data is known yet. ","ca":"
Vols afegir un punt?
Has marcat un lloc on no coneixem les dades. ","es":"
Quieres añadir un punto?
Has marcado un lugar del que no conocemos los datos. ","nl":"
Punt toevoegen?
Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen ","fr":"
Pas de données
Vous avez cliqué sur un endroit où il n'y a pas encore de données. ","gl":"
Queres engadir un punto?
Marcaches un lugar onde non coñecemos os datos. ","de":"
Punkt hinzufügen?
Sie haben irgendwo geklickt, wo noch keine Daten bekannt sind. "} ),
+ title: new Translation( {"en":"Add a point?","ca":"Vols afegir un punt?","es":"Quieres añadir un punto?","nl":"Punt toevoegen?","fr":"Pas de données","gl":"Queres engadir un punto?","de":"Punkt hinzufügen?"} ),
+ intro: new Translation( {"en":"You clicked somewhere where no data is known yet. ","ca":"Has marcat un lloc on no coneixem les dades. ","es":"Has marcado un lugar del que no conocemos los datos. ","nl":"Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen ","fr":"Vous avez cliqué sur un endroit où il n'y a pas encore de données. ","gl":"Marcaches un lugar onde non coñecemos os datos. ","de":"Sie haben irgendwo geklickt, wo noch keine Daten bekannt sind. "} ),
pleaseLogin: new Translation( {"en":"Please log in to add a new point","ca":"Entra per afegir un nou punt","es":"Entra para añadir un nuevo punto","nl":"Gelieve je aan te melden om een punt to te voegen","fr":"Vous devez vous connecter pour ajouter un point","gl":"Inicia a sesión para engadir un novo punto","de":"Bitte loggen Sie sich ein, um einen neuen Punkt hinzuzufügen"} ),
zoomInFurther: new Translation( {"en":"Zoom in further to add a point.","ca":"Apropa per afegir un punt.","es":"Acerca para añadir un punto.","nl":"Gelieve verder in te zoomen om een punt toe te voegen.","fr":"Rapprochez vous pour ajouter un point.","gl":"Achégate para engadir un punto.","de":"Weiter einzoomen, um einen Punkt hinzuzufügen."} ),
stillLoading: new Translation( {"en":"The data is still loading. Please wait a bit before you add a new point.","ca":"Les dades es segueixen carregant. Espera una mica abans d'afegir cap punt.","es":"Los datos se siguen cargando. Espera un poco antes de añadir ningún punto.","nl":"De data wordt nog geladen. Nog even geduld en dan kan je een punt toevoegen.","fr":"Chargement des données en cours. Patientez un instant avant d'ajouter un nouveau point.","gl":"Os datos seguen a cargarse. Agarda un intre antes de engadir ningún punto.","de":"Die Daten werden noch geladen. Bitte warten Sie etwas, bevor Sie einen neuen Punkt hinzufügen."} ),
diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts
index 715ee32a8..7c3458325 100644
--- a/Logic/Web/QueryParameters.ts
+++ b/Logic/Web/QueryParameters.ts
@@ -62,6 +62,7 @@ export class QueryParameters {
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data))
}
+ // Don't pollute the history every time a parameter changes
history.replaceState(null, "", "?" + parts.join("&") + "#" + Hash.hash.data);
}
diff --git a/Models/Constants.ts b/Models/Constants.ts
index 106bec851..dcd75e0ee 100644
--- a/Models/Constants.ts
+++ b/Models/Constants.ts
@@ -1,7 +1,7 @@
import { Utils } from "../Utils";
export default class Constants {
- public static vNumber = "0.4.3";
+ public static vNumber = "0.4.4";
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {
diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts
index f5770d70a..6c27d1210 100644
--- a/UI/Base/ScrollableFullScreen.ts
+++ b/UI/Base/ScrollableFullScreen.ts
@@ -2,6 +2,7 @@ import {UIElement} from "../UIElement";
import Svg from "../../Svg";
import State from "../../State";
import Combine from "./Combine";
+import Ornament from "./Ornament";
/**
* Wraps some contents into a panel that scrolls the content _under_ the title
@@ -17,10 +18,12 @@ export default class ScrollableFullScreen extends UIElement{
State.state.selectedElement.setData(undefined);
}).SetClass("only-on-mobile")
.SetClass("featureinfobox-back-to-the-map")
-
+ title.SetClass("featureinfobox-title")
this._component = new Combine([
new Combine([returnToTheMap, title]).SetClass("featureinfobox-titlebar"),
- new Combine([content]).SetClass("featureinfobox-content")
+ new Combine([content]).SetClass("featureinfobox-content"),
+ new Combine([ new Ornament().SetStyle("height:5em;")]).SetClass("only-on-mobile")
+ // We add an ornament which takes around 5em. This is in order to make sure the Web UI doesn't hide
])
this.SetClass("featureinfobox");
diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts
index c2c73aed6..cecafdb91 100644
--- a/UI/BigComponents/SimpleAddUI.ts
+++ b/UI/BigComponents/SimpleAddUI.ts
@@ -13,47 +13,175 @@ import {FixedUiElement} from "../Base/FixedUiElement";
import Translations from "../i18n/Translations";
import Constants from "../../Models/Constants";
import LayerConfig from "../../Customizations/JSON/LayerConfig";
+import ScrollableFullScreen from "../Base/ScrollableFullScreen";
export default class SimpleAddUI extends UIElement {
- private readonly _addButtons: UIElement[];
-
- private _loginButton : UIElement;
-
- private _confirmPreset: UIEventSource<{
+ private readonly _loginButton: UIElement;
+
+ private readonly _confirmPreset: UIEventSource<{
description: string | UIElement,
name: string | UIElement,
icon: UIElement,
tags: Tag[],
layerToAddTo: {
layerDef: LayerConfig,
- isDisplayed: UIEventSource }
+ isDisplayed: UIEventSource
+ }
}>
= new UIEventSource(undefined);
- private confirmButton: UIElement = undefined;
- private _confirmDescription: UIElement = undefined;
- private openLayerControl: UIElement;
- private cancelButton: UIElement;
- private goToInboxButton: UIElement = new SubtleButton(Svg.envelope_ui(),
- Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false});
+
+ private _component: UIElement;
+
+ private readonly openLayerControl: UIElement;
+ private readonly cancelButton: UIElement;
+ private readonly goToInboxButton: UIElement = new SubtleButton(Svg.envelope_ui(),
+ Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false});
constructor() {
- super(State.state.locationControl);
+ super(State.state.locationControl.map(loc => loc.zoom));
+ const self = this;
this.ListenTo(Locale.language);
this.ListenTo(State.state.osmConnection.userDetails);
this.ListenTo(State.state.layerUpdater.runningQuery);
this.ListenTo(this._confirmPreset);
this.ListenTo(State.state.locationControl);
-
+ State.state.filteredLayers.data?.map(layer => {
+ self.ListenTo(layer.isDisplayed)
+ })
+
this._loginButton = Translations.t.general.add.pleaseLogin.Clone().onClick(() => State.state.osmConnection.AttemptLogin());
-
- this._addButtons = [];
+
this.SetStyle("font-size:large");
-
+ this.cancelButton = new SubtleButton(Svg.close_ui(),
+ Translations.t.general.cancel
+ ).onClick(() => {
+ self._confirmPreset.setData(undefined);
+ })
+
+ this.openLayerControl = new SubtleButton(Svg.layers_ui(),
+ Translations.t.general.add.openLayerControl
+ ).onClick(() => {
+ State.state.fullScreenMessage.setData(undefined);
+ State.state.layerControlIsOpened.setData(true);
+ })
+ }
+
+ InnerRender(): string {
+
+ this._component = new ScrollableFullScreen(
+ Translations.t.general.add.title,
+ this.CreateContent()
+ )
+ return this._component.Render();
+
+ }
+
+ private CreatePresetsPanel(): UIElement {
+ const userDetails = State.state.osmConnection.userDetails;
+ if (userDetails === undefined) {
+ return undefined;
+ }
+
+ if (!userDetails.data.loggedIn) {
+ return this._loginButton;
+ }
+
+ if (userDetails.data.unreadMessages > 0 && userDetails.data.csCount < Constants.userJourney.addNewPointWithUnreadMessagesUnlock) {
+ return new Combine([
+ Translations.t.general.readYourMessages.Clone().SetClass("alert"),
+ this.goToInboxButton
+ ]);
+
+ }
+
+ if (userDetails.data.csCount < Constants.userJourney.addNewPointsUnlock) {
+ return new Combine(["",
+ Translations.t.general.fewChangesBefore,
+ ""]);
+ }
+
+ if (State.state.locationControl.data.zoom < Constants.userJourney.minZoomLevelToAddNewPoints) {
+ return Translations.t.general.add.zoomInFurther.SetClass("alert")
+ }
+
+ if (State.state.layerUpdater.runningQuery.data) {
+ return Translations.t.general.add.stillLoading
+ }
+
+ const presetButtons = this.CreatePresetButtons()
+ return new Combine(presetButtons).SetClass("add-popup-all-buttons")
+ }
+
+
+ private CreateContent(): UIElement {
+ const confirmPanel = this.CreateConfirmPanel();
+ if (confirmPanel !== undefined) {
+ return confirmPanel;
+ }
+
+ let intro: UIElement = Translations.t.general.add.intro;
+
+ let testMode: UIElement = undefined;
+ if (State.state.osmConnection?.userDetails?.data?.dryRun) {
+ testMode = new Combine([
+ "",
+ "Test mode - changes won't be saved",
+ ""
+ ]);
+ }
+
+ let presets = this.CreatePresetsPanel();
+ return new Combine([intro, testMode, presets])
+
+
+ }
+
+ private CreateConfirmPanel(): UIElement {
+ const preset = this._confirmPreset.data;
+ if (preset === undefined) {
+ return undefined;
+ }
+
+ const confirmButton = new SubtleButton(preset.icon,
+ new Combine([
+ "",
+ Translations.t.general.add.confirmButton.Subs({category: preset.name}),
+ ""]));
+ confirmButton.onClick(this.CreatePoint(preset.tags));
+
+ if (!this._confirmPreset.data.layerToAddTo.isDisplayed.data) {
+ return new Combine([
+ Translations.t.general.add.layerNotEnabled.Subs({layer: this._confirmPreset.data.layerToAddTo.layerDef.name})
+ .SetClass("alert"),
+ this.openLayerControl,
+
+ this.cancelButton
+ ]);
+ }
+
+ let tagInfo = "";
+ const csCount = State.state.osmConnection.userDetails.data.csCount;
+ if (csCount > Constants.userJourney.tagsVisibleAt) {
+ tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&");
+ tagInfo = ` More information about the preset: ${tagInfo}`
+ }
+
+ return new Combine([
+ Translations.t.general.add.confirmIntro.Subs({title: this._confirmPreset.data.name}),
+ State.state.osmConnection.userDetails.data.dryRun ? "TESTING - changes won't be saved" : "",
+ confirmButton,
+ this.cancelButton,
+ preset.description,
+ tagInfo
+
+ ])
+
+ }
+
+ private CreatePresetButtons() {
+ const allButtons = [];
const self = this;
for (const layer of State.state.filteredLayers.data) {
-
- this.ListenTo(layer.isDisplayed);
-
const presets = layer.layerDef.presets;
for (const preset of presets) {
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
@@ -77,13 +205,6 @@ export default class SimpleAddUI extends UIElement {
])
).onClick(
() => {
- self.confirmButton = new SubtleButton(icon,
- new Combine([
- "",
- Translations.t.general.add.confirmButton.Subs({category: preset.title}),
- ""]));
- self.confirmButton.onClick(self.CreatePoint(preset.tags));
- self._confirmDescription = preset.description;
self._confirmPreset.setData({
tags: preset.tags,
layerToAddTo: layer,
@@ -94,23 +215,10 @@ export default class SimpleAddUI extends UIElement {
self.Update();
}
)
-
-
- this._addButtons.push(button);
+ allButtons.push(button);
}
}
-
- this.cancelButton = new SubtleButton(Svg.close_ui(),
- Translations.t.general.cancel
- ).onClick(() => {
- self._confirmPreset.setData(undefined);
- })
-
- this.openLayerControl = new SubtleButton(Svg.layers_ui(),
- Translations.t.general.add.openLayerControl
- ).onClick(() => {
- State.state.layerControlIsOpened.setData(true);
- })
+ return allButtons;
}
private CreatePoint(tags: Tag[]) {
@@ -121,86 +229,5 @@ export default class SimpleAddUI extends UIElement {
}
}
- InnerRender(): string {
-
- const userDetails = State.state.osmConnection.userDetails;
-
- if (this._confirmPreset.data !== undefined) {
-
- if(!this._confirmPreset.data.layerToAddTo.isDisplayed.data){
- return new Combine([
- Translations.t.general.add.layerNotEnabled.Subs({layer: this._confirmPreset.data.layerToAddTo.layerDef.name})
- .SetClass("alert"),
- this.openLayerControl,
-
- this.cancelButton
- ]).Render();
- }
-
- let tagInfo = "";
- const csCount = State.state.osmConnection.userDetails.data.csCount;
- if (csCount > Constants.userJourney.tagsVisibleAt) {
- tagInfo = this._confirmPreset.data .tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&");
- tagInfo = ` More information about the preset: ${tagInfo}`
- }
-
- return new Combine([
- Translations.t.general.add.confirmIntro.Subs({title: this._confirmPreset.data.name}),
- userDetails.data.dryRun ? "TESTING - changes won't be saved" : "",
- this.confirmButton,
- this.cancelButton,
- this._confirmDescription,
- tagInfo
-
- ]).Render();
-
-
- }
-
-
- let header: UIElement = Translations.t.general.add.header;
-
-
- if (userDetails === undefined) {
- return header.Render();
- }
-
- if (!userDetails.data.loggedIn) {
- return new Combine([header, this._loginButton]).Render()
- }
-
- if (userDetails.data.unreadMessages > 0 && userDetails.data.csCount < Constants.userJourney.addNewPointWithUnreadMessagesUnlock) {
- return new Combine([header,
- Translations.t.general.readYourMessages.Clone().SetClass("alert"),
- this.goToInboxButton
- ]).Render();
-
- }
-
- if (userDetails.data.dryRun) {
- header = new Combine([header,
- "",
- "Test mode - changes won't be saved",
- ""
- ]);
- }
-
- if (userDetails.data.csCount < Constants.userJourney.addNewPointsUnlock) {
- return new Combine([header, "",
- Translations.t.general.fewChangesBefore,
- ""]).Render();
- }
-
- if (State.state.locationControl.data.zoom < Constants.userJourney.minZoomLevelToAddNewPoints) {
- return new Combine([header, Translations.t.general.add.zoomInFurther.SetClass("alert")]).Render()
- }
-
- if (State.state.layerUpdater.runningQuery.data) {
- return new Combine([header, Translations.t.general.add.stillLoading]).Render()
- }
-
- return header.Render() + new Combine(this._addButtons).SetClass("add-popup-all-buttons").Render();
- }
-
}
\ No newline at end of file
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index 57e7e99da..9ad6f9dfc 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -6,10 +6,7 @@ import QuestionBox from "./QuestionBox";
import Combine from "../Base/Combine";
import TagRenderingAnswer from "./TagRenderingAnswer";
import State from "../../State";
-import {FixedUiElement} from "../Base/FixedUiElement";
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
-import Svg from "../../Svg";
-import Ornament from "../Base/Ornament";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
export default class FeatureInfoBox extends UIElement {
@@ -49,7 +46,7 @@ export default class FeatureInfoBox extends UIElement {
if (!questionBoxIsUsed) {
renderings.push(questionBox);
}
- const tail = new Combine([new Ornament()]).SetClass("only-on-mobile");
+ const tail = new Combine([]).SetClass("only-on-mobile");
const content = new Combine([
...renderings,
diff --git a/assets/translations.json b/assets/translations.json
index 65dc28304..77cbb72c1 100644
--- a/assets/translations.json
+++ b/assets/translations.json
@@ -309,14 +309,24 @@
"gl": "Engadir {category} aquí",
"de": "Hier eine neue {category} hinzufügen"
},
- "header": {
- "en": "
Add a point?
You clicked somewhere where no data is known yet. ",
- "ca": "
Vols afegir un punt?
Has marcat un lloc on no coneixem les dades. ",
- "es": "
Quieres añadir un punto?
Has marcado un lugar del que no conocemos los datos. ",
- "nl": "
Punt toevoegen?
Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen ",
- "fr": "
Pas de données
Vous avez cliqué sur un endroit où il n'y a pas encore de données. ",
- "gl": "
Queres engadir un punto?
Marcaches un lugar onde non coñecemos os datos. ",
- "de": "
Punkt hinzufügen?
Sie haben irgendwo geklickt, wo noch keine Daten bekannt sind. "
+ "title": {
+ "en": "Add a new point?",
+ "ca": "Vols afegir un punt?",
+ "es": "Quieres añadir un punto?",
+ "nl": "Nieuw punt toevoegen?",
+ "fr": "Pas de données",
+ "gl": "Queres engadir un punto?",
+ "de": "Punkt hinzufügen?"
+
+ },
+ "intro": {
+ "en": "You clicked somewhere where no data is known yet. ",
+ "ca": "Has marcat un lloc on no coneixem les dades. ",
+ "es": "Has marcado un lugar del que no conocemos los datos. ",
+ "nl": "Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen ",
+ "fr": "Vous avez cliqué sur un endroit où il n'y a pas encore de données. ",
+ "gl": "Marcaches un lugar onde non coñecemos os datos. ",
+ "de": "Sie haben irgendwo geklickt, wo noch keine Daten bekannt sind. "
},
"pleaseLogin": {
"en": "Please log in to add a new point",
From 2f570102022e34598a9f16ca579361ac4546fb60 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 18:02:07 +0100
Subject: [PATCH 19/25] Fix back button; add title
---
AllTranslationAssets.ts | 2 +-
Customizations/JSON/LayerConfig.ts | 1 -
Customizations/JSON/LayoutConfig.ts | 1 -
InitUiElements.ts | 18 ++++++++----
Logic/Actors/HistoryHandling.ts | 19 +++++++++++++
Logic/Actors/StrayClickHandler.ts | 4 +--
Logic/Actors/TitleHandler.ts | 33 ++++++++++++++++++++++
Logic/Web/Hash.ts | 43 ++++++++++++++++++++++-------
Logic/Web/QueryParameters.ts | 12 +++-----
State.ts | 17 ++++--------
UI/FullScreenMessageBoxHandler.ts | 2 +-
UI/Popup/FeatureInfoBox.ts | 3 ++
UI/ShowDataLayer.ts | 2 +-
index.ts | 1 -
14 files changed, 115 insertions(+), 43 deletions(-)
create mode 100644 Logic/Actors/HistoryHandling.ts
create mode 100644 Logic/Actors/TitleHandler.ts
diff --git a/AllTranslationAssets.ts b/AllTranslationAssets.ts
index ac65d3d9a..f4ca2c3e1 100644
--- a/AllTranslationAssets.ts
+++ b/AllTranslationAssets.ts
@@ -39,7 +39,7 @@ export default class AllTranslationAssets {
number: new Translation( {"en":"number","ca":"nombre","es":"número","nl":"getal","fr":"nombre","gl":"número","de":"Zahl"} ),
osmLinkTooltip: new Translation( {"en":"See this object on OpenStreetMap for history and more editing options","ca":"Mira aquest objecte a OpenStreetMap per veure historial i altres opcions d'edició","es":"Mira este objeto en OpenStreetMap para ver historial y otras opciones de edición","nl":"Bekijk dit object op OpenStreetMap waar geschiedenis en meer aanpasopties zijn","fr":"Voir l'historique de cet objet sur OpenStreetMap et plus d'options d'édition","gl":"Ollar este obxecto no OpenStreetMap para ollar o historial e outras opcións de edición","de":"Dieses Objekt auf OpenStreetMap anschauen für die Geschichte und weitere Bearbeitungsmöglichkeiten"} ),
add: { addNew: new Translation( {"en":"Add a new {category} here","ca":"Afegir {category} aquí","es":"Añadir {category} aquí","nl":"Voeg hier een {category} toe","fr":"Ajouter un/une {category} ici","gl":"Engadir {category} aquí","de":"Hier eine neue {category} hinzufügen"} ),
- title: new Translation( {"en":"Add a point?","ca":"Vols afegir un punt?","es":"Quieres añadir un punto?","nl":"Punt toevoegen?","fr":"Pas de données","gl":"Queres engadir un punto?","de":"Punkt hinzufügen?"} ),
+ title: new Translation( {"en":"Add a new point?","ca":"Vols afegir un punt?","es":"Quieres añadir un punto?","nl":"Nieuw punt toevoegen?","fr":"Pas de données","gl":"Queres engadir un punto?","de":"Punkt hinzufügen?"} ),
intro: new Translation( {"en":"You clicked somewhere where no data is known yet. ","ca":"Has marcat un lloc on no coneixem les dades. ","es":"Has marcado un lugar del que no conocemos los datos. ","nl":"Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen ","fr":"Vous avez cliqué sur un endroit où il n'y a pas encore de données. ","gl":"Marcaches un lugar onde non coñecemos os datos. ","de":"Sie haben irgendwo geklickt, wo noch keine Daten bekannt sind. "} ),
pleaseLogin: new Translation( {"en":"Please log in to add a new point","ca":"Entra per afegir un nou punt","es":"Entra para añadir un nuevo punto","nl":"Gelieve je aan te melden om een punt to te voegen","fr":"Vous devez vous connecter pour ajouter un point","gl":"Inicia a sesión para engadir un novo punto","de":"Bitte loggen Sie sich ein, um einen neuen Punkt hinzuzufügen"} ),
zoomInFurther: new Translation( {"en":"Zoom in further to add a point.","ca":"Apropa per afegir un punt.","es":"Acerca para añadir un punto.","nl":"Gelieve verder in te zoomen om een punt toe te voegen.","fr":"Rapprochez vous pour ajouter un point.","gl":"Achégate para engadir un punto.","de":"Weiter einzoomen, um einen Punkt hinzuzufügen."} ),
diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 53825613d..6dc3f6382 100644
--- a/Customizations/JSON/LayerConfig.ts
+++ b/Customizations/JSON/LayerConfig.ts
@@ -177,7 +177,6 @@ export default class LayerConfig {
this.tagRenderings.push(...addAll.tagRenderings);
this.iconOverlays.push(...addAll.iconOverlays);
for (const icon of addAll.titleIcons) {
- console.log("Adding ",icon, "to", this.id)
this.titleIcons.splice(0,0, icon);
}
return this;
diff --git a/Customizations/JSON/LayoutConfig.ts b/Customizations/JSON/LayoutConfig.ts
index 81be65c8e..a1f58c7d6 100644
--- a/Customizations/JSON/LayoutConfig.ts
+++ b/Customizations/JSON/LayoutConfig.ts
@@ -96,7 +96,6 @@ export default class LayoutConfig {
if (shared === undefined) {
throw "Unkown fixed layer " + name;
}
- console.log("PREMERGE", layer, shared)
// @ts-ignore
layer = Utils.Merge(layer.override, shared);
}
diff --git a/InitUiElements.ts b/InitUiElements.ts
index 6c7811716..62c6e59c6 100644
--- a/InitUiElements.ts
+++ b/InitUiElements.ts
@@ -41,6 +41,7 @@ import FeatureDuplicatorPerLayer from "./Logic/FeatureSource/FeatureDuplicatorPe
import LayerConfig from "./Customizations/JSON/LayerConfig";
import ShowDataLayer from "./UI/ShowDataLayer";
import Hash from "./Logic/Web/Hash";
+import HistoryHandling from "./Logic/Actors/HistoryHandling";
export class InitUiElements {
@@ -133,7 +134,6 @@ export class InitUiElements {
if (feature === undefined) {
State.state.fullScreenMessage.setData(undefined);
- Hash.hash.setData(undefined);
}
if (feature?.properties === undefined) {
return;
@@ -159,12 +159,18 @@ export class InitUiElements {
layer
);
- State.state.fullScreenMessage.setData(featureBox);
+ State.state.fullScreenMessage.setData({
+ content: featureBox,
+ hashText: feature.properties.id.replace("/", "_"),
+ titleText: featureBox.title
+ });
break;
}
}
);
+ new HistoryHandling(Hash.hash, State.state.fullScreenMessage);
+
InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => {
new UserBadge().AttachTo('userbadge');
});
@@ -279,20 +285,20 @@ export class InitUiElements {
})
State.state.selectedElement.addCallback(selected => {
- if(selected !== undefined){
+ if (selected !== undefined) {
checkbox.isEnabled.setData(false);
}
})
const fullOptions2 = new FullWelcomePaneWithTabs();
- State.state.fullScreenMessage.setData(fullOptions2)
+ State.state.fullScreenMessage.setData({content: fullOptions2, hashText: "welcome"})
Svg.help_svg()
.SetClass("open-welcome-button")
.SetClass("shadow")
.onClick(() => {
- State.state.fullScreenMessage.setData(fullOptions2)
+ State.state.fullScreenMessage.setData({content: fullOptions2, hashText: "welcome"})
}).AttachTo("help-button-mobile");
@@ -326,7 +332,7 @@ export class InitUiElements {
const fullScreen = new LayerControlPanel();
checkbox.isEnabled.addCallback(isEnabled => {
if (isEnabled) {
- State.state.fullScreenMessage.setData(fullScreen);
+ State.state.fullScreenMessage.setData({content: fullScreen, hashText: "layer-select"});
}
})
State.state.fullScreenMessage.addCallback(latest => {
diff --git a/Logic/Actors/HistoryHandling.ts b/Logic/Actors/HistoryHandling.ts
new file mode 100644
index 000000000..6b317a286
--- /dev/null
+++ b/Logic/Actors/HistoryHandling.ts
@@ -0,0 +1,19 @@
+import {UIEventSource} from "../UIEventSource";
+import {UIElement} from "../../UI/UIElement";
+
+export default class HistoryHandling {
+
+ constructor(hash: UIEventSource, fullscreenMessage: UIEventSource<{ content: UIElement, hashText: string }>) {
+ hash.addCallback(h => {
+ if (h === undefined || h === "") {
+ fullscreenMessage.setData(undefined);
+ }
+ })
+
+ fullscreenMessage.addCallback(fs => {
+ hash.setData(fs?.hashText);
+ })
+
+ }
+
+}
\ No newline at end of file
diff --git a/Logic/Actors/StrayClickHandler.ts b/Logic/Actors/StrayClickHandler.ts
index a7041d6dc..bc185130d 100644
--- a/Logic/Actors/StrayClickHandler.ts
+++ b/Logic/Actors/StrayClickHandler.ts
@@ -17,7 +17,7 @@ export default class StrayClickHandler {
selectedElement: UIEventSource,
filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource }[]>,
leafletMap: UIEventSource,
- fullscreenMessage: UIEventSource,
+ fullscreenMessage: UIEventSource<{content: UIElement, hashText: string}>,
uiToShow: (() => UIElement)) {
this._uiToShow = uiToShow;
const self = this;
@@ -51,7 +51,7 @@ export default class StrayClickHandler {
self._lastMarker.bindPopup(popup);
self._lastMarker.on("click", () => {
- fullscreenMessage.setData(self._uiToShow());
+ fullscreenMessage.setData({content: self._uiToShow(), hashText: "new"});
uiElement.Update();
});
});
diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts
new file mode 100644
index 000000000..8093b64e0
--- /dev/null
+++ b/Logic/Actors/TitleHandler.ts
@@ -0,0 +1,33 @@
+import {UIEventSource} from "../UIEventSource";
+import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
+import Translations from "../../UI/i18n/Translations";
+import Locale from "../../UI/i18n/Locale";
+import {UIElement} from "../../UI/UIElement";
+
+export default class TitleHandler{
+ constructor(layoutToUse: UIEventSource, fullScreenMessage: UIEventSource<{ content: UIElement, hashText: string, titleText?: UIElement }>) {
+
+
+ layoutToUse.map((layoutToUse) => {
+ return Translations.WT(layoutToUse?.title)?.txt ?? "MapComplete"
+ }, [Locale.language]
+ ).addCallbackAndRun((title) => {
+ document.title = title
+ });
+
+ fullScreenMessage.addCallbackAndRun(selected => {
+ const title = Translations.WT(layoutToUse.data?.title)?.txt ?? "MapComplete"
+ if(selected?.titleText?.data === undefined){
+ document.title = title
+ }else{
+ selected.titleText.Update();
+ var d = document.createElement('div');
+ d.innerHTML = selected.titleText.InnerRender();
+ const poi = (d.textContent || d.innerText)
+ document.title = title + " | " + poi;
+ }
+ })
+
+
+ }
+}
\ No newline at end of file
diff --git a/Logic/Web/Hash.ts b/Logic/Web/Hash.ts
index 4ef26916b..07372f2ff 100644
--- a/Logic/Web/Hash.ts
+++ b/Logic/Web/Hash.ts
@@ -1,29 +1,52 @@
import {UIEventSource} from "../UIEventSource";
-import Constants from "../../Models/Constants";
import {Utils} from "../../Utils";
export default class Hash {
-
- public static hash : UIEventSource = Hash.Get();
-
- private static Get() : UIEventSource{
- if(Utils.runningFromConsole){
+
+ public static hash: UIEventSource = Hash.Get();
+
+ /**
+ * Gets the current string, including the pound sign
+ * @constructor
+ */
+ public static Current(): string {
+ if (Hash.hash.data === undefined || Hash.hash.data === "") {
+ return ""
+ } else {
+ return "#" + Hash.hash.data;
+ }
+ }
+
+ private static Get(): UIEventSource {
+ if (Utils.runningFromConsole) {
return new UIEventSource(undefined);
}
const hash = new UIEventSource(window.location.hash.substr(1));
hash.addCallback(h => {
- if(h === undefined || h === ""){
+ if (h === "undefined") {
+ console.warn("Got a literal 'undefined' as hash, ignoring")
+ h = undefined;
+ }
+
+ if (h === undefined || h === "") {
window.location.hash = "";
return;
}
+
h = h.replace(/\//g, "_");
window.location.hash = "#" + h;
});
+
+
window.onhashchange = () => {
- hash.setData(window.location.hash.substr(1))
+ let newValue = window.location.hash.substr(1);
+ if (newValue === "") {
+ newValue = undefined;
+ }
+ hash.setData(newValue)
}
-
+
return hash;
}
-
+
}
\ No newline at end of file
diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts
index 7c3458325..406afa214 100644
--- a/Logic/Web/QueryParameters.ts
+++ b/Logic/Web/QueryParameters.ts
@@ -43,27 +43,23 @@ export class QueryParameters {
private static Serialize() {
const parts = []
for (const key of QueryParameters.order) {
- if (QueryParameters.knownSources[key] === undefined || QueryParameters.knownSources[key].data === undefined) {
+ if (QueryParameters.knownSources[key]?.data === undefined) {
continue;
}
- if (QueryParameters.knownSources[key].data === undefined) {
- continue;
- }
-
if (QueryParameters.knownSources[key].data === "undefined") {
continue;
}
-
- if (QueryParameters.knownSources[key].data == QueryParameters.defaults[key]) {
+ if (QueryParameters.knownSources[key].data === QueryParameters.defaults[key]) {
continue;
}
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data))
}
// Don't pollute the history every time a parameter changes
- history.replaceState(null, "", "?" + parts.join("&") + "#" + Hash.hash.data);
+
+ history.replaceState(null, "", "?" + parts.join("&") + Hash.Current());
}
diff --git a/State.ts b/State.ts
index 0f3e43d12..2be700e45 100644
--- a/State.ts
+++ b/State.ts
@@ -4,7 +4,6 @@ import {ElementStorage} from "./Logic/ElementStorage";
import {Changes} from "./Logic/Osm/Changes";
import {OsmConnection} from "./Logic/Osm/OsmConnection";
import Locale from "./UI/i18n/Locale";
-import Translations from "./UI/i18n/Translations";
import {UIEventSource} from "./Logic/UIEventSource";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {QueryParameters} from "./Logic/Web/QueryParameters";
@@ -18,6 +17,7 @@ import Constants from "./Models/Constants";
import UpdateFromOverpass from "./Logic/Actors/UpdateFromOverpass";
import LayerConfig from "./Customizations/JSON/LayerConfig";
+import TitleHandler from "./Logic/Actors/TitleHandler";
/**
* Contains the global state: a bunch of UI-event sources
@@ -75,7 +75,7 @@ export default class State {
/**
This message is shown full screen on mobile devices
*/
- public readonly fullScreenMessage = new UIEventSource(undefined)
+ public readonly fullScreenMessage = new UIEventSource<{ content: UIElement, hashText: string, titleText?: UIElement }>(undefined)
/**
The latest element that was selected - used to generate the right UI at the right place
@@ -112,9 +112,9 @@ export default class State {
public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
- public layerControlIsOpened: UIEventSource =
+ public layerControlIsOpened: UIEventSource =
QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Whether or not the layer control is shown")
- .map((str) => str !== "false", [], b => "" + b)
+ .map((str) => str !== "false", [], b => "" + b)
public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`).map(
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
@@ -240,13 +240,8 @@ export default class State {
}
}).ping()
- this.layoutToUse.map((layoutToUse) => {
- return Translations.WT(layoutToUse?.title)?.txt ?? "MapComplete"
- }, [Locale.language]
- ).addCallbackAndRun((title) => {
- document.title = title
- });
-
+ new TitleHandler(this.layoutToUse, this.fullScreenMessage);
+
this.allElements = new ElementStorage();
this.changes = new Changes();
diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts
index 0a67765a3..9fa2493f8 100644
--- a/UI/FullScreenMessageBoxHandler.ts
+++ b/UI/FullScreenMessageBoxHandler.ts
@@ -19,7 +19,7 @@ export default class FullScreenMessageBox extends UIElement {
if (State.state.fullScreenMessage.data === undefined) {
return "";
}
- this._content = State.state.fullScreenMessage.data;
+ this._content = State.state.fullScreenMessage.data.content;
return new Combine([this._content]).SetClass("fullscreenmessage-content").Render();
}
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index 9ad6f9dfc..4818e1563 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -12,6 +12,8 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen";
export default class FeatureInfoBox extends UIElement {
private _component: UIElement;
+ public title: UIEventSource ;
+
constructor(
tags: UIEventSource,
layerConfig: LayerConfig
@@ -24,6 +26,7 @@ export default class FeatureInfoBox extends UIElement {
const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
.SetClass("featureinfobox-title");
+ this.title = title;
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
.SetClass("featureinfobox-icons");
diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts
index 0c9c39b44..1b03a562d 100644
--- a/UI/ShowDataLayer.ts
+++ b/UI/ShowDataLayer.ts
@@ -154,7 +154,7 @@ export default class ShowDataLayer {
this._onSelectedTrigger[feature.properties.id.replace("/","_")] = this._onSelectedTrigger[id];
if (feature.properties.id.replace(/\//g, "_") === Hash.hash.data) {
// This element is in the URL, so this is a share link
- // We already open it
+ // We open the relevant popup straight away
uiElement.Activate();
popup.setContent(uiElement.Render());
diff --git a/index.ts b/index.ts
index 0efc118bc..eb291a977 100644
--- a/index.ts
+++ b/index.ts
@@ -6,7 +6,6 @@ import {UIEventSource} from "./Logic/UIEventSource";
import * as $ from "jquery";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
import {Utils} from "./Utils";
-import {Overpass} from "./Logic/Osm/Overpass";
let defaultLayout = "bookcases"
// --------------------- Special actions based on the parameters -----------------
From bd6f325fc17df1c8aa0bcf3da92eac223259e685 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Fri, 8 Jan 2021 18:02:55 +0100
Subject: [PATCH 20/25] version bump
---
Models/Constants.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Models/Constants.ts b/Models/Constants.ts
index dcd75e0ee..662900774 100644
--- a/Models/Constants.ts
+++ b/Models/Constants.ts
@@ -1,7 +1,7 @@
import { Utils } from "../Utils";
export default class Constants {
- public static vNumber = "0.4.4";
+ public static vNumber = "0.4.5";
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {
From 641b59bca534f677b08a029c367210192c71eff5 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sat, 9 Jan 2021 00:03:21 +0100
Subject: [PATCH 21/25] Add ornament
---
UI/Base/Ornament.ts | 20 ++++++++++++++------
UI/Base/ScrollableFullScreen.ts | 13 +++++++------
css/fullscreenmessagebox.css | 9 ++++++---
3 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/UI/Base/Ornament.ts b/UI/Base/Ornament.ts
index 2a4ac175b..2cbf75974 100644
--- a/UI/Base/Ornament.ts
+++ b/UI/Base/Ornament.ts
@@ -1,18 +1,25 @@
import {UIElement} from "../UIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Svg from "../../Svg";
+import State from "../../State";
export default class Ornament extends UIElement {
private static readonly ornamentsCount = Ornament.countOrnaments();
- private readonly _index = new UIEventSource(0)
+ private readonly _index = new UIEventSource("0")
- constructor(index = new UIEventSource(0)) {
- super(index);
+ constructor(index = undefined) {
+ super();
+ index = index ?? State.state.osmConnection.GetPreference("ornament");
+ this.ListenTo(index);
this._index = index;
this.SetClass("ornament")
const self = this;
this.onClick(() => {
- self._index.setData((self._index.data + 1) % (Ornament.ornamentsCount + 1));
+ let c = Number(index.data);
+ if(isNaN(c)){
+ c = 0;
+ }
+ self._index.setData(""+ ((c + 1) % (Ornament.ornamentsCount + 1)));
})
}
@@ -28,10 +35,11 @@ export default class Ornament extends UIElement {
}
InnerRender(): string {
- if(this._index.data == 0){
+ if(this._index.data == "0"){
return ""
}
- return Svg.All[`Ornament-Horiz-${this._index.data - 1}.svg`]
+ console.log(this._index.data)
+ return Svg.All[`Ornament-Horiz-${Number(this._index.data) - 1}.svg`]
}
}
\ No newline at end of file
diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts
index 6c27d1210..8fd910d8c 100644
--- a/UI/Base/ScrollableFullScreen.ts
+++ b/UI/Base/ScrollableFullScreen.ts
@@ -7,9 +7,9 @@ import Ornament from "./Ornament";
/**
* Wraps some contents into a panel that scrolls the content _under_ the title
*/
-export default class ScrollableFullScreen extends UIElement{
+export default class ScrollableFullScreen extends UIElement {
private _component: Combine;
-
+
constructor(title: UIElement, content: UIElement) {
super();
@@ -19,16 +19,17 @@ export default class ScrollableFullScreen extends UIElement{
}).SetClass("only-on-mobile")
.SetClass("featureinfobox-back-to-the-map")
title.SetClass("featureinfobox-title")
+ const ornament = new Combine([new Ornament().SetStyle("height:5em;")]).SetClass("only-on-mobile")
+
this._component = new Combine([
new Combine([returnToTheMap, title]).SetClass("featureinfobox-titlebar"),
- new Combine([content]).SetClass("featureinfobox-content"),
- new Combine([ new Ornament().SetStyle("height:5em;")]).SetClass("only-on-mobile")
+ new Combine(["",content,"", ornament]).SetClass("featureinfobox-content"),
// We add an ornament which takes around 5em. This is in order to make sure the Web UI doesn't hide
])
this.SetClass("featureinfobox");
-
+
}
-
+
InnerRender(): string {
return this._component.Render();
}
diff --git a/css/fullscreenmessagebox.css b/css/fullscreenmessagebox.css
index e6d558bdd..2c681b919 100644
--- a/css/fullscreenmessagebox.css
+++ b/css/fullscreenmessagebox.css
@@ -15,12 +15,15 @@
.fullscreenmessage-content .featureinfobox-content {
padding: 1em;
top: var(--variable-title-height);
- /* 2em extra: padding from the title */
max-height: calc(100vh - var(--variable-title-height)) !important;
- display: block;
+ min-height: calc(100vh - var(--variable-title-height)) !important;
position: absolute;
overflow-y: auto;
box-sizing: border-box;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between
}
.fullscreenmessage-content .featureinfobox-titlebar {
@@ -32,7 +35,7 @@
padding: 0.5em;
width: 100%;
box-sizing: border-box;
-
+
}
.fullscreenmessage-content .featureinfobox-tail {
From a2bec35a43f812b10889f9836ecc8b8a46a40de8 Mon Sep 17 00:00:00 2001
From: pietervdvn
Date: Sat, 9 Jan 2021 00:29:12 +0100
Subject: [PATCH 22/25] Fix wiki links on mobile
---
index.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/index.ts b/index.ts
index eb291a977..a62a0b616 100644
--- a/index.ts
+++ b/index.ts
@@ -76,8 +76,8 @@ if (layoutFromBase64.startsWith("wiki:")) {
data = data.substr(start,
data.indexOf("