From 105120060d219fcc5f78d5ee422837a7c2309b09 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 15 Apr 2023 02:28:24 +0200 Subject: [PATCH] Refactoring: add metatagging, add 'last edited by' element, add 'metacondition' --- Logic/DetermineLayout.ts | 6 +- .../Actors/FeaturePropertiesStore.ts | 6 +- Logic/MetaTagging.ts | 3 +- Logic/SimpleMetaTagger.ts | 12 ++-- Models/ThemeConfig/Conversion/PrepareLayer.ts | 30 ++++++++- Models/ThemeConfig/TagRenderingConfig.ts | 18 +++--- Models/ThemeViewState.ts | 6 ++ UI/BigComponents/SelectedElementView.svelte | 7 ++- UI/Map/MapLibreAdaptor.ts | 1 + UI/Popup/AddNewPoint/AddNewPoint.svelte | 6 +- UI/Popup/AllTagsPanel.svelte | 61 ++++++++++++------- UI/Popup/FeatureInfoBox.ts | 56 ----------------- UI/Popup/TagRendering/Questionbox.svelte | 6 +- .../TagRendering/TagRenderingQuestion.svelte | 1 - UI/SpecialVisualization.ts | 1 + UI/SpecialVisualizations.ts | 19 +++--- UI/i18n/Translation.ts | 9 ++- Utils.ts | 11 +++- assets/layers/bench/license_info.json | 2 +- assets/layers/last_click/last_click.json | 11 +++- assets/tagRenderings/questions.json | 20 +++++- langs/layers/ca.json | 5 +- langs/layers/de.json | 6 +- langs/layers/en.json | 9 +-- langs/layers/es.json | 2 +- langs/layers/it.json | 6 +- langs/layers/nl.json | 10 +-- langs/shared-questions/en.json | 7 +++ langs/shared-questions/nl.json | 7 +++ scripts/generateLayerOverview.ts | 10 ++- .../OSM/Actions/ReplaceGeometryAction.spec.ts | 5 +- 31 files changed, 217 insertions(+), 142 deletions(-) diff --git a/Logic/DetermineLayout.ts b/Logic/DetermineLayout.ts index 020467758d..d2dc46175c 100644 --- a/Logic/DetermineLayout.ts +++ b/Logic/DetermineLayout.ts @@ -62,7 +62,11 @@ export default class DetermineLayout { layoutId, "The layout to load into MapComplete" ).data - return AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase()) + const layout = AllKnownLayouts.allKnownLayouts.get(layoutId?.toLowerCase()) + if (layout === undefined) { + throw "No layout with name " + layoutId + " exists" + } + return layout } public static LoadLayoutFromHash(userLayoutParam: UIEventSource): LayoutConfig | null { diff --git a/Logic/FeatureSource/Actors/FeaturePropertiesStore.ts b/Logic/FeatureSource/Actors/FeaturePropertiesStore.ts index b78cc79d6e..1708852d97 100644 --- a/Logic/FeatureSource/Actors/FeaturePropertiesStore.ts +++ b/Logic/FeatureSource/Actors/FeaturePropertiesStore.ts @@ -6,7 +6,7 @@ import { UIEventSource } from "../../UIEventSource" */ export default class FeaturePropertiesStore { private readonly _source: FeatureSource & IndexedFeatureSource - private readonly _elements = new Map>() + private readonly _elements = new Map>>() constructor(source: FeatureSource & IndexedFeatureSource) { this._source = source @@ -83,7 +83,9 @@ export default class FeaturePropertiesStore { return changeMade } - addAlias(oldId: string, newId: string): void { + // noinspection JSUnusedGlobalSymbols + public addAlias(oldId: string, newId: string): void { + console.log("FeaturePropertiesStore: adding alias for", oldId, newId) if (newId === undefined) { // We removed the node/way/relation with type 'type' and id 'oldId' on openstreetmap! const element = this._elements.get(oldId) diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 2344b32efc..ce1d143fa5 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -94,8 +94,9 @@ export default class MetaTagging { let definedTags = new Set(Object.getOwnPropertyNames(feature.properties)) for (const metatag of metatagsToApply) { try { - if (!metatag.keys.some((key) => feature.properties[key] === undefined)) { + if (!metatag.keys.some((key) => !(key in feature.properties))) { // All keys are already defined, we probably already ran this one + // Note that we use 'key in properties', not 'properties[key] === undefined'. The latter will cause evaluation of lazy properties continue } diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 1fe54e32a7..d1e28704ce 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -96,16 +96,11 @@ export class ReferencingWaysMetaTagger extends SimpleMetaTagger { return false } - console.trace("Downloading referencing ways for", feature.properties.id) - OsmObject.DownloadReferencingWays(id).then((referencingWays) => { - const currentTagsSource = state.allElements?.getEventSourceById(id) ?? [] + Utils.AddLazyPropertyAsync(feature.properties, "_referencing_ways", async () => { + const referencingWays = await OsmObject.DownloadReferencingWays(id) const wayIds = referencingWays.map((w) => "way/" + w.id) wayIds.sort() - const wayIdsStr = wayIds.join(";") - if (wayIdsStr !== "" && currentTagsSource.data["_referencing_ways"] !== wayIdsStr) { - currentTagsSource.data["_referencing_ways"] = wayIdsStr - currentTagsSource.ping() - } + return wayIds.join(";") }) return true @@ -221,6 +216,7 @@ class RewriteMetaInfoTags extends SimpleMetaTagger { return movedSomething } } + export default class SimpleMetaTaggers { /** * A simple metatagger which rewrites various metatags as needed diff --git a/Models/ThemeConfig/Conversion/PrepareLayer.ts b/Models/ThemeConfig/Conversion/PrepareLayer.ts index 680dc166d5..370464d19c 100644 --- a/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -575,12 +575,14 @@ export class AddQuestionBox extends DesugaringStep { } export class AddEditingElements extends DesugaringStep { - constructor() { + private readonly _desugaring: DesugaringContext + constructor(desugaring: DesugaringContext) { super( "Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements", [], "AddEditingElements" ) + this._desugaring = desugaring } convert( @@ -609,6 +611,30 @@ export class AddEditingElements extends DesugaringStep { }) } + if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "all_tags")) { + const trc: TagRenderingConfigJson = { + id: "all-tags", + render: { "*": "{all_tags()}" }, + metacondition: { + or: [ + "__featureSwitchIsTesting=true", + "__featureSwitchIsDebugging=true", + "mapcomplete-show_debug=yes", + ], + }, + } + json.tagRenderings.push(trc) + } + + if ( + json.source !== "special" && + json.source !== "special:library" && + json.tagRenderings && + !json.tagRenderings.some((tr) => tr["id"] === "last_edit") + ) { + json.tagRenderings.push(this._desugaring.tagRenderings.get("last_edit")) + } + return { result: json } } } @@ -1145,7 +1171,7 @@ export class PrepareLayer extends Fuse { new On("tagRenderings", new Each(new DetectInline())), new AddQuestionBox(), new AddMiniMap(state), - new AddEditingElements(), + new AddEditingElements(state), new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>( "mapRendering", diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 7105d021f2..1c68e0eaad 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -47,6 +47,10 @@ export default class TagRenderingConfig { public readonly question?: TypedTranslation public readonly questionhint?: TypedTranslation public readonly condition?: TagsFilter + /** + * Evaluated against the current 'usersettings'-state + */ + public readonly metacondition?: TagsFilter public readonly description?: Translation public readonly configuration_warnings: string[] = [] @@ -70,14 +74,6 @@ export default class TagRenderingConfig { if (json === undefined) { throw "Initing a TagRenderingConfig with undefined in " + context } - if (json === "questions") { - // Very special value - this.render = null - this.question = null - this.condition = null - this.id = "questions" - return - } if (typeof json === "number") { json = "" + json @@ -114,11 +110,15 @@ export default class TagRenderingConfig { } this.labels = json.labels ?? [] - this.render = Translations.T(json.render, translationKey + ".render") + this.render = Translations.T(json.render, translationKey + ".render") this.question = Translations.T(json.question, translationKey + ".question") this.questionhint = Translations.T(json.questionHint, translationKey + ".questionHint") this.description = Translations.T(json.description, translationKey + ".description") this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`) + this.metacondition = TagUtils.Tag( + json.metacondition ?? { and: [] }, + `${context}.metacondition` + ) if (json.freeform) { if ( json.freeform.addExtraTags !== undefined && diff --git a/Models/ThemeViewState.ts b/Models/ThemeViewState.ts index 16217e6d30..82323baf57 100644 --- a/Models/ThemeViewState.ts +++ b/Models/ThemeViewState.ts @@ -205,6 +205,12 @@ export default class ThemeViewState implements SpecialVisualizationState { */ private miscSetup() { this.userRelatedState.markLayoutAsVisited(this.layout) + + this.selectedElement.addCallbackAndRunD(() => { + // As soon as we have a selected element, we clear it + // This is to work around maplibre, which'll _first_ register the click on the map and only _then_ on the feature + this.lastClickObject.features.setData([]) + }) } private initHotkeys() { diff --git a/UI/BigComponents/SelectedElementView.svelte b/UI/BigComponents/SelectedElementView.svelte index 493af1d355..5899e05762 100644 --- a/UI/BigComponents/SelectedElementView.svelte +++ b/UI/BigComponents/SelectedElementView.svelte @@ -18,6 +18,11 @@ onDestroy(tags.addCallbackAndRun(tags => { _tags = tags; })); + + let _metatags: Record + onDestroy(state.userRelatedState.preferencesAsTags .addCallbackAndRun(tags => { + _metatags = tags; + }));
@@ -40,7 +45,7 @@
{#each layer.tagRenderings as config (config.id)} - {#if config.condition === undefined || config.condition.matchesProperties(_tags)} + {#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties(_metatags))} {#if config.IsKnown(_tags)} {/if} diff --git a/UI/Map/MapLibreAdaptor.ts b/UI/Map/MapLibreAdaptor.ts index e4e0d03d7d..b05d3c0bef 100644 --- a/UI/Map/MapLibreAdaptor.ts +++ b/UI/Map/MapLibreAdaptor.ts @@ -91,6 +91,7 @@ export class MapLibreAdaptor implements MapProperties { // Workaround, 'ShowPointLayer' sets this flag return } + console.log(e) const lon = e.lngLat.lng const lat = e.lngLat.lat lastClickLocation.setData({ lon, lat }) diff --git a/UI/Popup/AddNewPoint/AddNewPoint.svelte b/UI/Popup/AddNewPoint/AddNewPoint.svelte index 75b35a0d73..26b3975918 100644 --- a/UI/Popup/AddNewPoint/AddNewPoint.svelte +++ b/UI/Popup/AddNewPoint/AddNewPoint.svelte @@ -96,14 +96,15 @@ } }); state.newFeatures.features.ping(); + const tagsStore = state.featureProperties.getStore(newId); { // Set some metainfo - const tagsStore = state.featureProperties.getStore(newId); const properties = tagsStore.data; if (snapTo) { // metatags (starting with underscore) are not uploaded, so we can safely mark this properties["_referencing_ways"] = `["${snapTo}"]`; } + properties["_backend"] = state.osmConnection.Backend() properties["_last_edit:timestamp"] = new Date().toISOString(); const userdetails = state.osmConnection.userDetails.data; properties["_last_edit:contributor"] = userdetails.name; @@ -112,8 +113,9 @@ } const feature = state.indexedFeatures.featuresById.data.get(newId); abort(); - state.selectedElement.setData(feature); state.selectedLayer.setData(selectedPreset.layer); + state.selectedElement.setData(feature); + tagsStore.ping() } diff --git a/UI/Popup/AllTagsPanel.svelte b/UI/Popup/AllTagsPanel.svelte index b2beb2cef0..e2e918bed7 100644 --- a/UI/Popup/AllTagsPanel.svelte +++ b/UI/Popup/AllTagsPanel.svelte @@ -1,46 +1,63 @@
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 9a56c1cecb..cc0dcdbfd5 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -99,63 +99,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { }) ), ] - allRenderings.push( - new Toggle( - new Lazy(() => - FeatureInfoBox.createEditElements(questionBoxes, layerConfig, tags, state) - ), - undefined, - state.featureSwitchUserbadge - ) - ) return new Combine(allRenderings).SetClass("block") } - - /** - * All the edit elements, together (note that the question boxes are passed though) - * @param questionBoxes - * @param layerConfig - * @param tags - * @param state - * @private - */ - private static createEditElements( - questionBoxes: Map, - layerConfig: LayerConfig, - tags: UIEventSource, - state: FeaturePipelineState - ): BaseUIElement { - let editElements: BaseUIElement[] = [] - questionBoxes.forEach((questionBox) => { - editElements.push(questionBox) - }) - - editElements.push( - new VariableUiElement( - state.osmConnection.userDetails - .map((ud) => ud.csCount) - .map( - (csCount) => { - if ( - csCount <= Constants.userJourney.historyLinkVisible && - state.featureSwitchIsDebugging.data == false && - state.featureSwitchIsTesting.data === false - ) { - return undefined - } - - return new TagRenderingAnswer( - tags, - SharedTagRenderings.SharedTagRendering.get("last_edit"), - state - ) - }, - [state.featureSwitchIsDebugging, state.featureSwitchIsTesting] - ) - ) - ) - - return new Combine(editElements).SetClass("flex flex-col") - } } diff --git a/UI/Popup/TagRendering/Questionbox.svelte b/UI/Popup/TagRendering/Questionbox.svelte index e8f4dddecd..8bfc71a019 100644 --- a/UI/Popup/TagRendering/Questionbox.svelte +++ b/UI/Popup/TagRendering/Questionbox.svelte @@ -41,7 +41,10 @@ return true; } - const baseQuestions = (layer.tagRenderings ?? [])?.filter(tr => allowed(tr.labels) && tr.question !== undefined); + let baseQuestions = [] + $: { + baseQuestions = (layer.tagRenderings ?? [])?.filter(tr => allowed(tr.labels) && tr.question !== undefined); + } let skippedQuestions = new UIEventSource>(new Set()); let questionsToAsk = tags.map(tags => { @@ -80,6 +83,7 @@ skipped++; } } + $: console.log("Current questionbox state:", {answered, skipped, questionsToAsk, layer, selectedElement, tags}) {#if _questionsToAsk.length === 0} diff --git a/UI/Popup/TagRendering/TagRenderingQuestion.svelte b/UI/Popup/TagRendering/TagRenderingQuestion.svelte index 032627a2df..38e681ae50 100644 --- a/UI/Popup/TagRendering/TagRenderingQuestion.svelte +++ b/UI/Popup/TagRendering/TagRenderingQuestion.svelte @@ -32,7 +32,6 @@ checkedMappings = [...config.mappings.map(_ => false), false /*One element extra in case a freeform value is added*/]; } } - $: console.log("Checked mappings:", checkedMappings) let selectedTags: TagsFilter = undefined; function mappingIsHidden(mapping: Mapping): boolean { diff --git a/UI/SpecialVisualization.ts b/UI/SpecialVisualization.ts index a43e382feb..dd0d6db17b 100644 --- a/UI/SpecialVisualization.ts +++ b/UI/SpecialVisualization.ts @@ -63,6 +63,7 @@ export interface SpecialVisualizationState { readonly userRelatedState: { readonly mangroveIdentity: MangroveIdentity readonly showAllQuestionsAtOnce: UIEventSource + readonly preferencesAsTags: Store> } readonly lastClickObject: WritableFeatureSource } diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 652f988182..9e1acdf160 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -1265,21 +1265,24 @@ export default class SpecialVisualizations { doc: "The URL to link to", required: true, }, + { + name: "class", + doc: "CSS-classes to add to the element", + }, ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[] ): BaseUIElement { - const [text, href] = args + const [text, href, classnames] = args return new VariableUiElement( - tagSource.map( - (tags) => - new Link( - Utils.SubstituteKeys(text, tags), - Utils.SubstituteKeys(href, tags), - true - ) + tagSource.map((tags) => + new Link( + Utils.SubstituteKeys(text, tags), + Utils.SubstituteKeys(href, tags), + true + ).SetClass(classnames) ) ) }, diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 67c9c84387..72f00dd007 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -29,7 +29,14 @@ export class Translation extends BaseUIElement { } count++ if (typeof translations[translationsKey] != "string") { - console.error("Non-string object in translation: ", translations[translationsKey]) + console.error( + "Non-string object at", + context, + "in translation: ", + translations[translationsKey], + "\n current translations are: ", + translations + ) throw ( "Error in an object depicting a translation: a non-string object was found. (" + context + diff --git a/Utils.ts b/Utils.ts index 260a01495d..4086627e35 100644 --- a/Utils.ts +++ b/Utils.ts @@ -307,13 +307,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * @param init * @constructor */ - public static AddLazyProperty(object: any, name: string, init: () => any) { + public static AddLazyProperty( + object: any, + name: string, + init: () => any, + whenDone?: () => void + ) { Object.defineProperty(object, name, { enumerable: false, configurable: true, get: () => { delete object[name] object[name] = init() + if (whenDone) { + whenDone() + } return object[name] }, }) @@ -332,6 +340,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be enumerable: false, configurable: true, get: () => { + console.trace("Property", name, "got requested") init().then((r) => { delete object[name] object[name] = r diff --git a/assets/layers/bench/license_info.json b/assets/layers/bench/license_info.json index fab2a775e2..63bb2ba0f2 100644 --- a/assets/layers/bench/license_info.json +++ b/assets/layers/bench/license_info.json @@ -19,4 +19,4 @@ "https://commons.wikimedia.org/wiki/File:ISO_7010_P018.svg" ] } -] +] \ No newline at end of file diff --git a/assets/layers/last_click/last_click.json b/assets/layers/last_click/last_click.json index 6d138a098e..ef84ed86ab 100644 --- a/assets/layers/last_click/last_click.json +++ b/assets/layers/last_click/last_click.json @@ -55,7 +55,16 @@ "*": "{open_note()}" } }, - "all_tags" + { + "metacondition": { + "or": [ + "__featureSwitchDebugging=true" + ] + }, + "render": { + "*": "{all_tags()}" + } + } ], "mapRendering": [ { diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index cbc84fc913..986c1327e3 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -1365,10 +1365,26 @@ ] }, "last_edit": { - "#": "Gives some metainfo about the last edit and who did edit it - rendering only", + "description": "Gives some metainfo about the last edit and who did edit it - rendering only", "condition": "_last_edit:contributor~*", + "metacondition": { + "or": [ + "__featureSwitchIsTesting=true", + "__featureSwitchIsDebugging=true", + "mapcomplete-show_debug=yes", + "_csCount>=10" + ] + }, "render": { - "*": "" + "special": { + "type": "link", + "href": "{_backend}/changeset/{_last_edit:changeset}", + "text": { + "en": "Last edited on {_last_edit:timestamp} by {_last_edit:contributor}", + "nl": "Laatst gewijzigd op {_last_edit:timestamp} door {_last_edit:contributor}" + }, + "class": "subtle font-small" + } } }, "all_tags": { diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 76e4864b2a..b61bfa8821 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -3560,6 +3560,9 @@ "19": { "then": "Aquí es poden reciclar sabates" }, + "20": { + "then": "Aquí es poden reciclar petits aparells elèctrics" + }, "21": { "then": "Aquí es poden reciclar petits aparells elèctrics" }, @@ -4554,4 +4557,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index fb968e0c74..416e7e10fd 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -6927,13 +6927,13 @@ "16": { "question": "Recycling von Kunststoffen" }, - "18": { + "17": { "question": "Recycling von Metallschrott" }, - "19": { + "18": { "question": "Recycling von Elektrokleingeräten" }, - "20": { + "19": { "question": "Recycling von Restabfällen" }, "20": { diff --git a/langs/layers/en.json b/langs/layers/en.json index e56da389ff..d00a20d2f1 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -6946,15 +6946,12 @@ "question": "Recycling of plastic" }, "17": { - "question": "Recycling of printer cartridges" - }, - "18": { "question": "Recycling of scrap metal" }, - "19": { + "18": { "question": "Recycling of small electrical appliances" }, - "20": { + "19": { "question": "Recycling of residual waste" }, "20": { @@ -9182,4 +9179,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/es.json b/langs/layers/es.json index 314f0fceb3..b3b31c2da6 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -3448,7 +3448,7 @@ "16": { "question": "Reciclaje de plástico" }, - "18": { + "17": { "question": "Reciclaje de chatarra" }, "18": { diff --git a/langs/layers/it.json b/langs/layers/it.json index 4187552586..b2e9cff656 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -1799,13 +1799,13 @@ "16": { "question": "Riciclo di plastica" }, - "18": { + "17": { "question": "Riciclo di rottami metallici" }, - "19": { + "18": { "question": "Riciclo di piccoli elettrodomestici" }, - "20": { + "19": { "question": "Riciclo di secco" }, "20": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 74468ce28a..7479891b77 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -6512,14 +6512,14 @@ "question": "Recycling van plastic" }, "17": { - "question": "Recycling van printer cartridges" - }, - "18": { "question": "Recycling van oud metaal" }, - "19": { + "18": { "question": "Recycling van kleine elektrische apparaten" }, + "19": { + "question": "Recycling van restafval" + }, "20": { "question": "Recycling van restafval" } @@ -8652,4 +8652,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index dadb187b95..2ceea52424 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -131,6 +131,13 @@ "question": "What is the network name for the wireless internet access?", "render": "The network name is {internet_access:ssid}" }, + "last_edit": { + "render": { + "special": { + "text": "Last edited on {_last_edit:timestamp} by {_last_edit:contributor}" + } + } + }, "level": { "mappings": { "0": { diff --git a/langs/shared-questions/nl.json b/langs/shared-questions/nl.json index ac5434b2d7..a1a66199ad 100644 --- a/langs/shared-questions/nl.json +++ b/langs/shared-questions/nl.json @@ -131,6 +131,13 @@ "question": "Wat is de netwerknaam voor de draadloze internettoegang?", "render": "De netwerknaam is {internet_access:ssid}" }, + "last_edit": { + "render": { + "special": { + "text": "Laatst gewijzigd op {_last_edit:timestamp} door {_last_edit:contributor} " + } + } + }, "level": { "mappings": { "0": { diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index de201c47f6..eeef143e82 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -16,7 +16,7 @@ import { Translation } from "../UI/i18n/Translation" import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" import questions from "../assets/tagRenderings/questions.json" import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingConfigJson" -import { PrepareLayer } from "../Models/ThemeConfig/Conversion/PrepareLayer" +import { PrepareLayer, RewriteSpecial } from "../Models/ThemeConfig/Conversion/PrepareLayer" import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme" import { DesugaringContext } from "../Models/ThemeConfig/Conversion/Conversion" import { Utils } from "../Utils" @@ -156,6 +156,7 @@ class LayerOverviewUtils extends Script { getSharedTagRenderings(doesImageExist: DoesImageExist): Map { const dict = new Map() + const prep = new RewriteSpecial() const validator = new ValidateTagRenderings(undefined, doesImageExist) for (const key in questions) { if (key === "id") { @@ -163,7 +164,12 @@ class LayerOverviewUtils extends Script { } questions[key].id = key questions[key]["source"] = "shared-questions" - const config = questions[key] + const config = prep.convertStrict( + questions[key], + "questions.json:" + key + ) + delete config.description + delete config["#"] validator.convertStrict( config, "generate-layer-overview:tagRenderings/questions.json:" + key diff --git a/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts b/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts index 2c9998e8da..fc4b89cd96 100644 --- a/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts +++ b/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts @@ -883,7 +883,10 @@ describe("ReplaceGeometryAction", () => { const data = await Utils.downloadJson(url) const fullNodeDatabase = undefined // TODO new FullNodeDatabaseSource(undefined) // TODO fullNodeDatabase.handleOsmJson(data, 0) - const changes = new Changes() + const changes = new Changes({ + dryRun: new ImmutableStore(true), + osmConnection: new OsmConnection() + }) const osmConnection = new OsmConnection({ dryRun: new ImmutableStore(true), })