From 874f92d70fc54285e42be69d9350b3b943029fc3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 15 Mar 2025 00:45:00 +0100 Subject: [PATCH 01/15] Fix: tests --- src/Logic/Tags/TagUtils.ts | 7 ++++--- src/Models/ThemeConfig/TagRenderingConfig.ts | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Logic/Tags/TagUtils.ts b/src/Logic/Tags/TagUtils.ts index aa92c24bd..2f5506b96 100644 --- a/src/Logic/Tags/TagUtils.ts +++ b/src/Logic/Tags/TagUtils.ts @@ -353,6 +353,7 @@ export class TagUtils { /** * Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set. + * The result must be interpreted as 'and' * * @see MatchesMultiAnswer to do the reverse * @@ -369,10 +370,10 @@ export class TagUtils { * and: ["x=b", "y=2"] * } * ]}) - * TagUtils.FlattenMultiAnswer([tag]) // => TagUtils.Tag({and:["x=a;b", "y=0;1;2;3"] }) + * TagUtils.FlattenMultiAnswer([tag]) // => [new Tag("x","a;b"),new Tag("y","0;1;2;3")] * - * TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => new And([new Tag("x","y"), new Tag("a","b")]) - * TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => new And([new Tag("x","")]) + * TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => [new Tag("x","y"), new Tag("a","b")] + * TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => [new Tag("x","")] */ static FlattenMultiAnswer(tagsFilters: UploadableTag[]): UploadableTag[] { if (tagsFilters === undefined) { diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 8751e7738..3d083299f 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -686,7 +686,8 @@ export default class TagRenderingConfig { } /** - * Given a value for the freeform key and an overview of the selected mappings, construct the correct tagsFilter to apply + * Given a value for the freeform key and an overview of the selected mappings, construct the correct tagsFilter to apply. + * Result should be interpreted as "and" * * const config = new TagRenderingConfig({"id":"bookcase-booktypes","render":{"en":"This place mostly serves {books}" }, * "question":{"en":"What kind of books can be found in this public bookcase?"}, @@ -696,17 +697,17 @@ export default class TagRenderingConfig { * "mappings":[{"if":"books=children","then":"Mostly children books"}, * {"if":"books=adults","then": "Mostly books for adults"}]} * , "testcase") - * config.constructChangeSpecification(undefined, undefined, [false, true, false], {amenity: "public_bookcase"}) // => new And([new Tag("books","adults")]) + * config.constructChangeSpecification(undefined, undefined, [false, true, false], {amenity: "public_bookcase"}) // => [new Tag("books","adults")] * * const config = new TagRenderingConfig({"id":"capacity", "render": "Fits {capcity} books",freeform: {"key":"capacity",type:"pnat"} }) * config.constructChangeSpecification("", undefined, undefined, {}) // => undefined - * config.constructChangeSpecification("5", undefined, undefined, {}).optimize() // => new Tag("capacity", "5") + * config.constructChangeSpecification("5", undefined, undefined, {}).optimize() // => [new Tag("capacity", "5")] * - * // Should pick a mapping, even if freeform is used + * // Should pick a mapping, even if freeform is usedconstructChange * const config = new TagRenderingConfig({"id": "shop-types", render: "Shop type is {shop}", freeform: {key: "shop", addExtraTags:["fixme=freeform shop type used"]}, mappings:[{if: "shop=second_hand", then: "Second hand shop"}]}) - * config.constructChangeSpecification("freeform", 1, undefined, {}).asHumanString(false, false, {}) // => "shop=freeform & fixme=freeform shop type used" - * config.constructChangeSpecification("freeform", undefined, undefined, {}).asHumanString(false, false, {}) // => "shop=freeform & fixme=freeform shop type used" - * config.constructChangeSpecification("second_hand", 1, undefined, {}).asHumanString(false, false, {}) // => "shop=second_hand" + * config.constructChangeSpecification("freeform", 1, undefined, {}).asHumanString(false, false, {}) // => [new Tag("shop","freeform",new Tag("fixme","freeform shop type used")] + * config.constructChangeSpecification("freeform", undefined, undefined, {}) // => [new Tag("shop","freeform), new Tag("fixme","freeform shop type used")] + * config.constructChangeSpecification("second_hand", 1, undefined, {}) // => [new Tag("shop","second_hand")] * * * const config = new TagRenderingConfig({id: "oh", render: "{opening_hours}", question: {"en":"When open?"}, freeform: {key: "opening_hours"}, @@ -716,11 +717,11 @@ export default class TagRenderingConfig { * }, * "hideInAnswer": true}] } * const tags = config.constructChangeSpecification("Tu-Fr 05:30-09:30", undefined, undefined, { }} - * tags // =>new And([ new Tag("opening_hours", "Tu-Fr 05:30-09:30")]) + * tags // => [ new Tag("opening_hours", "Tu-Fr 05:30-09:30")] * * const config = new TagRenderingConfig({"id": "charge", render: "One tube costs {charge}", freeform: {key: "charge", postfixDistinguished: "bicycle_tube"]}, }) * const tags = config.constructChangeSpecification("€5", undefined, undefined, {vending: "books;bicycle_tubes" charge: "€42/book"}) - * tags // =>new And([ new Tag("charge", "€5/bicycle_tube; €42/book")]) + * tags // => [ new Tag("charge", "€5/bicycle_tube; €42/book")] * * * @param freeformValue The freeform value which will be applied as 'freeform.key'. Ignored if 'freeform.key' is not set From ac19702189c36df50f20f7063387ac506b3dbbf9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 15 Mar 2025 01:27:12 +0100 Subject: [PATCH 02/15] Themes(wayside_shrine): add level, do not count 'artwork' --- CHANGELOG.md | 3 ++- assets/layers/wayside_shrine/wayside_shrine.json | 3 ++- assets/themes/wayside_shrines/wayside_shrines.json | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3588ba422..235b0a712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ All notable changes to this project will be documented in this file. See [standa * **onwheels:** move building magic back to onwheels theme, decrease minzoom for entrances ([98e8adf](https://github.com/pietervdvn/mapcomplete/commits/98e8adf41b3e296dcdf26ac9c6df21c1f5259f46)) * **wayside_shrine,artwork:** add artwork as adjacent concept to shrines ([266e91e](https://github.com/pietervdvn/mapcomplete/commits/266e91e45db85d27d5271267a1db2175e2588c1d)) -* **wayside_shrine:** add preset to snap to a wal ([e8951e2](https://github.com/pietervdvn/mapcomplete/commits/e8951e2b80ff2e4a9818452bc0c0f03f058e7cf2)) +* **wayside_shrine:** add preset to snap to a + wall ([e8951e2](https://github.com/pietervdvn/mapcomplete/commits/e8951e2b80ff2e4a9818452bc0c0f03f058e7cf2)) * **wayside_shrines:** integrate wayside shrines ([1f7b156](https://github.com/pietervdvn/mapcomplete/commits/1f7b156a2c18200ece3c3ec9aa3d86f814d2590b)) * **wayside_shrines:** small language fixes ([aaf46cd](https://github.com/pietervdvn/mapcomplete/commits/aaf46cd7b01a9f11720d5e47ac6ffcde15b29add)) diff --git a/assets/layers/wayside_shrine/wayside_shrine.json b/assets/layers/wayside_shrine/wayside_shrine.json index ebe563e70..4bbbcc29f 100644 --- a/assets/layers/wayside_shrine/wayside_shrine.json +++ b/assets/layers/wayside_shrine/wayside_shrine.json @@ -379,7 +379,8 @@ "religion!=" ] } - } + }, + "level" ], "deletion": { "softDeletionTags": { diff --git a/assets/themes/wayside_shrines/wayside_shrines.json b/assets/themes/wayside_shrines/wayside_shrines.json index 4392d787d..6894fd1ed 100644 --- a/assets/themes/wayside_shrines/wayside_shrines.json +++ b/assets/themes/wayside_shrines/wayside_shrines.json @@ -14,6 +14,7 @@ "artwork" ], "override": { + "isCounted": false, "minzoom": 18 } } From fb4fbe2be30968d52add8fac0599d49f57554a33 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 15 Mar 2025 01:34:08 +0100 Subject: [PATCH 03/15] Chore: rm obsolete log --- src/UI/Map/MapLibreAdaptor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/UI/Map/MapLibreAdaptor.ts b/src/UI/Map/MapLibreAdaptor.ts index 9973beaaf..643d1ea32 100644 --- a/src/UI/Map/MapLibreAdaptor.ts +++ b/src/UI/Map/MapLibreAdaptor.ts @@ -611,7 +611,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { if (!map) { return } - console.log("Bounds are", bbox?.asGeometry()) if (bbox) { if (GeoOperations.surfaceAreaInSqMeters(bbox.asGeojsonCached()) > 1) { map?.setMaxBounds(bbox.toLngLat()) From 40c6a582c76baacf007fa6a7675b1564c4e31f46 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 18:49:48 +0100 Subject: [PATCH 04/15] Themes(toilets): add supervision, hand_drying questions --- assets/layers/toilet/toilet.json | 141 ++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 30 deletions(-) diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index aeced6e97..8da1b40be 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -954,45 +954,35 @@ "id": "toilet-changing_table:location" }, { - "labels": [ - "relevant-questions" - ], - "id": "toilet-handwashing", + "id": "toilet-supervised", "question": { - "en": "Do these toilets have a sink to wash your hands?", - "nl": "Hebben deze toiletten een lavabo om de handen te wassen?", - "de": "Gibt es hier ein Handwaschbecken?", - "es": "¿Estos baños tienen un lavabo para lavarse las manos?", - "fr": "Ces toilettes ont-ils un lavabo pour se laver les mains ?", - "da": "Har disse toiletter en håndvask til at vaske hænder?", - "ca": "Aquests lavabos tenen una pica per a rentar-se les mans?", - "cs": "Mají tyto toalety umyvadlo na mytí rukou?" + "en": "Is this toilets supervised by a person?", + "nl": "Is er toezicht op deze toilet?" + }, + "questionHint": { + "en": "This is typically a person tasked with keeping the toilets clean and collecting the fee", + "nl": "Deze persoon houdt typisch de toiletten proper en int het toiletgeld" }, "mappings": [ { - "if": "toilets:handwashing=yes", + "if": "supervised=yes", "then": { - "en": "These toilets have a sink to wash your hands", - "nl": "Deze toiletten hebben een lavabo waar men de handen kan wassen", - "de": "Die Toilette hat ein Handwaschbecken", - "es": "Estos baños tienen un lavabo para lavarse las manos", - "fr": "Ces toilettes ont un lavabo pour se laver les mains", - "da": "Dette toilet har en vask til at vaske dine hænder", - "ca": "Aquests lavabos tenen una pica per a rentar-se les mans", - "cs": "Tyto toalety mají umyvadlo na mytí rukou" + "en": "There is a person supervising these toilets during (most of) the opening hours", + "nl": "Er is een persoon die toezicht houdt op deze toiletten" } }, { - "if": "toilets:handwashing=no", + "if": "supervised=interval", "then": { - "en": "These toilets don't have a sink to wash your hands", - "nl": "Deze toiletten hebben geen lavabo waar men de handen kan wassen", - "de": "Die Toilette hat kein Handwaschbecken", - "es": "Estos baños no tienen un lavabo para lavarse las manos", - "fr": "Ces toilettes n'ont pas de lavabo pour se laver les mains", - "da": "Disse toiletter har ikke en vask til at vaske dine hænder", - "ca": "Aquests lavabos no tenen una pica per a rentar-se les mans", - "cs": "Tyto toalety nemají umyvadlo na mytí rukou" + "en": "There is a person supervising these toilets, but they are present only during certain times of the opening hours", + "nl": "Er is een persoon die toezicht houdt op deze toiletten, maar deze is slechts op enkele vaste momenten aanwezig" + } + }, + { + "if": "supervised=no", + "then": { + "en": "These toilets are not supervised", + "nl": "Er is geen persoon die toezicht houdt" } } ] @@ -1047,6 +1037,97 @@ ] } }, + { + "labels": [ + "relevant-questions" + ], + "id": "toilet-handwashing", + "question": { + "en": "Do these toilets have a sink to wash your hands?", + "nl": "Hebben deze toiletten een lavabo om de handen te wassen?", + "de": "Gibt es hier ein Handwaschbecken?", + "es": "¿Estos baños tienen un lavabo para lavarse las manos?", + "fr": "Ces toilettes ont-ils un lavabo pour se laver les mains ?", + "da": "Har disse toiletter en håndvask til at vaske hænder?", + "ca": "Aquests lavabos tenen una pica per a rentar-se les mans?", + "cs": "Mají tyto toalety umyvadlo na mytí rukou?" + }, + "mappings": [ + { + "if": "toilets:handwashing=yes", + "then": { + "en": "These toilets have a sink to wash your hands", + "nl": "Deze toiletten hebben een lavabo waar men de handen kan wassen", + "de": "Die Toilette hat ein Handwaschbecken", + "es": "Estos baños tienen un lavabo para lavarse las manos", + "fr": "Ces toilettes ont un lavabo pour se laver les mains", + "da": "Dette toilet har en vask til at vaske dine hænder", + "ca": "Aquests lavabos tenen una pica per a rentar-se les mans", + "cs": "Tyto toalety mají umyvadlo na mytí rukou" + } + }, + { + "if": "toilets:handwashing=no", + "then": { + "en": "These toilets don't have a sink to wash your hands", + "nl": "Deze toiletten hebben geen lavabo waar men de handen kan wassen", + "de": "Die Toilette hat kein Handwaschbecken", + "es": "Estos baños no tienen un lavabo para lavarse las manos", + "fr": "Ces toilettes n'ont pas de lavabo pour se laver les mains", + "da": "Disse toiletter har ikke en vask til at vaske dine hænder", + "ca": "Aquests lavabos no tenen una pica per a rentar-se les mans", + "cs": "Tyto toalety nemají umyvadlo na mytí rukou" + } + } + ] + }, + + { + "id": "toilet-drying", + "question": { + "en": "Do these toilets have a device to dry your hands?", + "nl": "Hebben deze toiletten een apparaat om je handen te drogen?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "toilets:hands_drying=electric_hand_dryer", + "then": { + "en": "Electric hand dryers are available for drying hands.", + "nl": "Elektrische handdrogers zijn beschikbaar om handen te drogen." + } + }, + { + "if": "toilets:hands_drying=paper_towel", + "then": { + "en": "Paper towels are available for drying hands.", + "nl": "Papieren wegwerphanddoeken zijn beschikbaar om handen te drogen." + } + }, + { + "if": "toilets:hands_drying=towel_cabinet", + "then": { + "en": "A towel roll cabinet is available for drying hands", + "nl": "Een apparaat met een handdoekrol is beschikbaar om handen te drogen" + } + }, + { + "if": "toilets:hands_drying=towel", + "then": { + "en": "A fabric towel available to dry your hands.", + "nl": "Een stoffen handdoek is beschikbaar om handen te drogen." + } + }, + { + "if": "toilets:hands_drying=no", + "then": { + "en": "There are no hand drying facilities available.", + "nl": "Er zijn geen handdroogvoorzieningen beschikbaar." + } + } + ], + "condition": "toilets:handwashing=yes" + }, "description" ], "filter": [ From 1dd78563efe0d4d8513f621884e8d42fbaa18513 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 22:48:12 +0100 Subject: [PATCH 05/15] Themes(toilets): add condition to supervision question: only ask if a public toilet --- assets/layers/toilet/toilet.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 8da1b40be..ff4ff196b 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -985,7 +985,17 @@ "nl": "Er is geen persoon die toezicht houdt" } } - ] + ], + "condition": { + "and": [ + { + "or": [ + "access=yes", + "access=" + ] + } + ] + } }, { "id": "toilet-has-paper", @@ -1081,7 +1091,6 @@ } ] }, - { "id": "toilet-drying", "question": { From 1933b8a7be28d78bddb83c13619766338c85afcf Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 22:48:54 +0100 Subject: [PATCH 06/15] Themes(entrance): add question about reference numbers --- assets/layers/entrance/entrance.json | 56 ++++++++++++++++++++++------ langs/layers/ca.json | 2 +- langs/layers/cs.json | 2 +- langs/layers/cy.json | 2 +- langs/layers/da.json | 2 +- langs/layers/de.json | 2 +- langs/layers/en.json | 46 ++++++++++++++++++++++- langs/layers/es.json | 2 +- langs/layers/fr.json | 2 +- langs/layers/nl.json | 46 ++++++++++++++++++++++- langs/layers/pa_PK.json | 2 +- langs/layers/pl.json | 2 +- 12 files changed, 143 insertions(+), 23 deletions(-) diff --git a/assets/layers/entrance/entrance.json b/assets/layers/entrance/entrance.json index 50148fc9c..e044044ff 100644 --- a/assets/layers/entrance/entrance.json +++ b/assets/layers/entrance/entrance.json @@ -33,17 +33,17 @@ "minzoom": 14, "title": { "render": { - "en": "Entrance", - "nl": "Ingang", - "ca": "Entrada", - "da": "Indgang", - "de": "Eingang", - "es": "Entrada", - "fr": "Entrée", - "pa_PK": "دروازہ", - "pl": "Wejście", - "cs": "Vchod", - "cy": "Mynedfa" + "en": "Entrance {ref}", + "nl": "Ingang {ref}", + "ca": "Entrada {ref}", + "da": "Indgang {ref}", + "de": "Eingang {ref}", + "es": "Entrada {ref}", + "fr": "Entrée {ref}", + "pa_PK": "دروازہ{ref}", + "pl": "Wejście {ref}", + "cs": "Vchod {ref}", + "cy": "Mynedfa {ref}" } }, "pointRendering": [ @@ -68,7 +68,16 @@ ] } } - ] + ], + "label": { + "mappings": [ + { + "if": "ref~*", + "then": "{ref}" + } + ] + }, + "labelCssClasses": "rounded bg-white px-2" } ], "lineRendering": [], @@ -578,6 +587,29 @@ } } ] + }, + { + "id": "ref", + "question": { + "en": "Does this door have a reference number?", + "nl": "Heeft deze toegang een referentienummer?" + }, + "render": { + "en": "This door has {ref} as reference number", + "nl": "Deze deur heeft {ref} als referentienummer" + }, + "mappings": [ + { + "if": "ref=", + "then": { + "en": "No reference number", + "nl": "Geen referentienummer" + } + } + ], + "freeform": { + "key": "ref" + } } ], "filter": [ diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 64a94bc08..3fdf28f25 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -5245,7 +5245,7 @@ } }, "title": { - "render": "Entrada" + "render": "Entrada {ref}" } }, "etymology": { diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 4d19e10c0..5ad1e02b9 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -5049,7 +5049,7 @@ } }, "title": { - "render": "Vchod" + "render": "Vchod {ref}" } }, "etymology": { diff --git a/langs/layers/cy.json b/langs/layers/cy.json index aed6d66f8..5aaca51be 100644 --- a/langs/layers/cy.json +++ b/langs/layers/cy.json @@ -356,7 +356,7 @@ "entrance": { "name": "Mynedfa", "title": { - "render": "Mynedfa" + "render": "Mynedfa {ref}" } }, "extinguisher": { diff --git a/langs/layers/da.json b/langs/layers/da.json index c784113f9..3418120da 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -1564,7 +1564,7 @@ } }, "title": { - "render": "Indgang" + "render": "Indgang {ref}" } }, "etymology": { diff --git a/langs/layers/de.json b/langs/layers/de.json index b122b8aed..497d741b4 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -5204,7 +5204,7 @@ } }, "title": { - "render": "Eingang" + "render": "Eingang {ref}" } }, "etymology": { diff --git a/langs/layers/en.json b/langs/layers/en.json index f68d3443f..e1356aa1f 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -5321,13 +5321,22 @@ "question": "What is the height of this kerb?", "render": "The kerb height of this door is {kerb:height}" }, + "ref": { + "mappings": { + "0": { + "then": "No reference number" + } + }, + "question": "Does this door have a reference number?", + "render": "This door has {ref} as reference number" + }, "width": { "question": "What is the width of this door/entrance?", "render": "This door has a width of {canonical(width)}" } }, "title": { - "render": "Entrance" + "render": "Entrance {ref}" } }, "etymology": { @@ -12133,6 +12142,26 @@ "question": "How much does one have to pay for these toilets?", "render": "The fee is {charge}" }, + "toilet-drying": { + "mappings": { + "0": { + "then": "Electric hand dryers are available for drying hands." + }, + "1": { + "then": "Paper towels are available for drying hands." + }, + "2": { + "then": "A towel roll cabinet is available for drying hands" + }, + "3": { + "then": "A fabric towel available to dry your hands." + }, + "4": { + "then": "There are no hand drying facilities available." + } + }, + "question": "Do these toilets have a device to dry your hands?" + }, "toilet-handwashing": { "mappings": { "0": { @@ -12162,6 +12191,21 @@ } } }, + "toilet-supervised": { + "mappings": { + "0": { + "then": "There is a person supervising these toilets during (most of) the opening hours" + }, + "1": { + "then": "There is a person supervising these toilets, but they are present only during certain times of the opening hours" + }, + "2": { + "then": "These toilets are not supervised" + } + }, + "question": "Is this toilets supervised by a person?", + "questionHint": "This is typically a person tasked with keeping the toilets clean and collecting the fee" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/layers/es.json b/langs/layers/es.json index 6ece3aa66..59387bb15 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -4903,7 +4903,7 @@ } }, "title": { - "render": "Entrada" + "render": "Entrada {ref}" } }, "etymology": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index fa7aace69..5b2cfcac2 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -3467,7 +3467,7 @@ } }, "title": { - "render": "Entrée" + "render": "Entrée {ref}" } }, "etymology": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index adb678192..a58a6d128 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4990,13 +4990,22 @@ "question": "Hoe hoog is de drempel?", "render": "De drempel bij deze deur is {kerb:height}" }, + "ref": { + "mappings": { + "0": { + "then": "Geen referentienummer" + } + }, + "question": "Heeft deze toegang een referentienummer?", + "render": "Deze deur heeft {ref} als referentienummer" + }, "width": { "question": "Wat is de breedte van deze deur/toegang?", "render": "Deze deur heeft een breedte van {canonical(width)}" } }, "title": { - "render": "Ingang" + "render": "Ingang {ref}" } }, "etymology": { @@ -9757,6 +9766,26 @@ "question": "Hoeveel moet men betalen om deze toiletten te gebruiken?", "render": "De toiletten gebruiken kost {charge}" }, + "toilet-drying": { + "mappings": { + "0": { + "then": "Elektrische handdrogers zijn beschikbaar om handen te drogen." + }, + "1": { + "then": "Papieren wegwerphanddoeken zijn beschikbaar om handen te drogen." + }, + "2": { + "then": "Een apparaat met een handdoekrol is beschikbaar om handen te drogen" + }, + "3": { + "then": "Een stoffen handdoek is beschikbaar om handen te drogen." + }, + "4": { + "then": "Er zijn geen handdroogvoorzieningen beschikbaar." + } + }, + "question": "Hebben deze toiletten een apparaat om je handen te drogen?" + }, "toilet-handwashing": { "mappings": { "0": { @@ -9786,6 +9815,21 @@ } } }, + "toilet-supervised": { + "mappings": { + "0": { + "then": "Er is een persoon die toezicht houdt op deze toiletten" + }, + "1": { + "then": "Er is een persoon die toezicht houdt op deze toiletten, maar deze is slechts op enkele vaste momenten aanwezig" + }, + "2": { + "then": "Er is geen persoon die toezicht houdt" + } + }, + "question": "Is er toezicht op deze toilet?", + "questionHint": "Deze persoon houdt typisch de toiletten proper en int het toiletgeld" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/layers/pa_PK.json b/langs/layers/pa_PK.json index 5e7360bba..a870525b6 100644 --- a/langs/layers/pa_PK.json +++ b/langs/layers/pa_PK.json @@ -151,7 +151,7 @@ "entrance": { "name": "دروازہ", "title": { - "render": "دروازہ" + "render": "دروازہ{ref}" } }, "food": { diff --git a/langs/layers/pl.json b/langs/layers/pl.json index c5d216106..fc45953e5 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -1881,7 +1881,7 @@ } }, "title": { - "render": "Wejście" + "render": "Wejście {ref}" } }, "etymology": { From ac5565d54646f14a444833bfa93b0f8607421d2b Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 22:49:47 +0100 Subject: [PATCH 07/15] Chore: ignore 'bing.json' --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6c69ad958..14f62e468 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ dist-full/ public/assets/icons/*.webp uploaded_images.json /app/dist/ +src/assets/bing.json From b3009367a38c12bc97950d85e0b716c4e28545d8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 23:47:12 +0100 Subject: [PATCH 08/15] Scripts: deploy hosted now actually stops when the tests fail --- .forgejo/workflows/deploy_hosted.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/deploy_hosted.yml b/.forgejo/workflows/deploy_hosted.yml index 77698c8a7..6cf54b381 100644 --- a/.forgejo/workflows/deploy_hosted.yml +++ b/.forgejo/workflows/deploy_hosted.yml @@ -16,7 +16,7 @@ jobs: node-version: "20" cache: "npm" cache-dependency-path: package-lock.json - + - name: install deps run: npm ci shell: bash @@ -43,8 +43,7 @@ jobs: export NODE_OPTIONS="--max-old-space-size=8192" npm run clean:tests npm run generate:doctests 2>&1 | grep -v "No doctests found in" - vitest --run test - npm run clean:tests + vitest --run test && npm run clean:tests shell: bash From c0f7de25fd1baeb0899c49dc8ebbbd8b6c0dc3b8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 16 Mar 2025 23:47:46 +0100 Subject: [PATCH 09/15] Fix tests --- src/Models/ThemeConfig/TagRenderingConfig.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 3d083299f..313cd1bd0 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -701,12 +701,12 @@ export default class TagRenderingConfig { * * const config = new TagRenderingConfig({"id":"capacity", "render": "Fits {capcity} books",freeform: {"key":"capacity",type:"pnat"} }) * config.constructChangeSpecification("", undefined, undefined, {}) // => undefined - * config.constructChangeSpecification("5", undefined, undefined, {}).optimize() // => [new Tag("capacity", "5")] + * config.constructChangeSpecification("5", undefined, undefined, {}) // => [new Tag("capacity", "5")] * * // Should pick a mapping, even if freeform is usedconstructChange * const config = new TagRenderingConfig({"id": "shop-types", render: "Shop type is {shop}", freeform: {key: "shop", addExtraTags:["fixme=freeform shop type used"]}, mappings:[{if: "shop=second_hand", then: "Second hand shop"}]}) - * config.constructChangeSpecification("freeform", 1, undefined, {}).asHumanString(false, false, {}) // => [new Tag("shop","freeform",new Tag("fixme","freeform shop type used")] - * config.constructChangeSpecification("freeform", undefined, undefined, {}) // => [new Tag("shop","freeform), new Tag("fixme","freeform shop type used")] + * config.constructChangeSpecification("freeform", 1, undefined, {}) // => [new Tag("shop","freeform"),new Tag("fixme","freeform shop type used")] + * config.constructChangeSpecification("freeform", undefined, undefined, {}) // => [new Tag("shop","freeform"), new Tag("fixme","freeform shop type used")] * config.constructChangeSpecification("second_hand", 1, undefined, {}) // => [new Tag("shop","second_hand")] * * From c3d905b26ab2825ed5e6f3849c694414ba68d98d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 01:16:30 +0100 Subject: [PATCH 10/15] Scripts: deploy hosted now actually now uses the '/tmp/' directory --- .forgejo/workflows/deploy_hosted.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/deploy_hosted.yml b/.forgejo/workflows/deploy_hosted.yml index 6cf54b381..6cf6e2d9d 100644 --- a/.forgejo/workflows/deploy_hosted.yml +++ b/.forgejo/workflows/deploy_hosted.yml @@ -52,11 +52,11 @@ jobs: - name: Zipping dist file run: | - mv dist ${{ github.ref_name }} - zip ${{ github.ref_name }}.zip -r ${{ github.ref_name }}/* + mv dist /tmp/${{ github.ref_name }} + zip /tmp/${{ github.ref_name }}.zip -r /tmp/${{ github.ref_name }}/* - name: uploading file - run: scp ${{ github.ref_name }}.zip hetzner:/root/staging/ + run: scp /tmp/${{ github.ref_name }}.zip hetzner:/root/staging/ - name: unzipping remote file run: ssh hetzner "cd /root/staging && rm -rf ${{ github.ref_name }} && unzip ${{ github.ref_name }}.zip && rm -rf /root/public/${{ github.ref_name }} && mv /root/staging/${{ github.ref_name }}/ /root/public/ && rm ${{ github.ref_name }}.zip" From ecd8f5e1da5b929ddeba0a3ed2651f8b4dbc0cc1 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 01:17:02 +0100 Subject: [PATCH 11/15] Feature(grb): add popup feature to validate e.g. a user profile --- assets/themes/grb/grb.json | 44 ++++++++ langs/themes/en.json | 24 +++++ langs/themes/nl.json | 24 +++++ .../ThemeConfig/Conversion/PrepareTheme.ts | 101 +++++++++--------- .../ThemeConfig/Json/ThemeConfigJson.ts | 22 ++++ src/Models/ThemeConfig/ThemeConfig.ts | 31 +++++- src/Models/ThemeConfig/WithContextLoader.ts | 2 +- src/UI/Base/LoginButton.svelte | 14 ++- src/UI/Base/Popup.svelte | 4 +- .../SettingsVisualisations.ts | 21 +++- src/UI/ThemeViewGUI.svelte | 24 ++++- 11 files changed, 247 insertions(+), 64 deletions(-) diff --git a/assets/themes/grb/grb.json b/assets/themes/grb/grb.json index 58409d303..5508a22ed 100644 --- a/assets/themes/grb/grb.json +++ b/assets/themes/grb/grb.json @@ -19,6 +19,50 @@ "shortDescription": { "nl": "Grb import helper tool" }, + "popup": [ + { + "id": "wikilink-needed", + "condition": "_description!~.*https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import.*", + "dismissable": false, + "title": { + "render": { + "en": "Profile mention obligated", + "nl": "Link op profiel verplicht" + } + }, + "body": [ + { + "render": { + "special": { + "type": "link", + "href": "https://www.openstreetmap.org/profile/edit", + "text": { + "en": "Edit your user profile", + "nl": "Pas je profiel aan" + } + }, + "after": { + "en": "to include the link https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import", + "nl": " en voeg deze link toe: https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import" + } + } + }, + { + "id": "reload_profile", + "render": { + "special": { + "type": "login_button", + "force": "yes", + "message": { + "en": "Reload your profile", + "nl": "Herlaad je profiel" + } + } + } + } + ] + } + ], "icon": "./assets/themes/grb/logo.svg", "startZoom": 9, "startLat": 51.0249, diff --git a/langs/themes/en.json b/langs/themes/en.json index 8a91f3192..8744e0448 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -623,6 +623,30 @@ } } } + }, + "popup": { + "0": { + "body": { + "0": { + "render": { + "special": { + "after": "to include the link https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import", + "text": "Edit your user profile" + } + } + }, + "1": { + "render": { + "special": { + "msg": "Reload your profile" + } + } + } + }, + "title": { + "render": "Profile mention obligated" + } + } } }, "guideposts": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 49c9e8b22..56088c853 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -669,6 +669,30 @@ } } }, + "popup": { + "0": { + "body": { + "0": { + "render": { + "special": { + "after": " en voeg deze link toe: https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import", + "text": "Pas je profiel aan" + } + } + }, + "1": { + "render": { + "special": { + "msg": "Herlaad je profiel" + } + } + } + }, + "title": { + "render": "Link op profiel verplicht" + } + } + }, "shortDescription": "Grb import helper tool", "title": "GRB import helper" }, diff --git a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts index 4ad260ce1..3e28b85df 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -1,16 +1,6 @@ -import { - Concat, - Conversion, - DesugaringContext, - DesugaringStep, - Each, - Fuse, - On, - Pass, - SetDefault, -} from "./Conversion" +import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion" import { ThemeConfigJson } from "../Json/ThemeConfigJson" -import { PrepareLayer } from "./PrepareLayer" +import { PrepareLayer, RewriteSpecial } from "./PrepareLayer" import { LayerConfigJson } from "../Json/LayerConfigJson" import { Utils } from "../../../Utils" import Constants from "../../Constants" @@ -40,7 +30,7 @@ class SubstituteLayer extends Conversion [ lname, - Utils.levenshteinDistance(name, lname), + Utils.levenshteinDistance(name, lname) ]) withDistance.sort((a, b) => a[1] - b[1]) const ids = withDistance.map((n) => n[0]) @@ -130,9 +120,9 @@ class SubstituteLayer extends Conversion 0) { context.err( "This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " + - unused.join(", ") + - "\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore" + unused.join(", ") + + "\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore" ) } found.tagRenderings = filtered @@ -205,10 +195,10 @@ export class AddDefaultLayers extends DesugaringStep { if (alreadyLoaded.has(v.id)) { context.warn( "Layout " + - context + - " already has a layer with name " + - v.id + - "; skipping inclusion of this builtin layer" + context + + " already has a layer with name " + + v.id + + "; skipping inclusion of this builtin layer" ) continue } @@ -352,10 +342,10 @@ class AddDependencyLayersToTheme extends DesugaringStep { .enters("layer dependency") .err( "Layer " + - dependency.neededLayer + - " is loaded because " + - dependency.reason + - "; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`" + dependency.neededLayer + + " is loaded because " + + dependency.reason + + "; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`" ) } } @@ -380,12 +370,12 @@ class AddDependencyLayersToTheme extends DesugaringStep { if (dep === undefined) { const message = [ "Loading a dependency failed: layer " + - unmetDependency.neededLayer + - " is not found, neither as layer of " + - themeId + - " nor as builtin layer.", + unmetDependency.neededLayer + + " is not found, neither as layer of " + + themeId + + " nor as builtin layer.", reason, - "Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","), + "Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(",") ] throw message.join("\n\t") } @@ -395,7 +385,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { dep.description = reason dependenciesToAdd.unshift({ config: dep, - reason, + reason }) loadedLayerIds.add(dep.id) unmetDependencies = unmetDependencies.filter( @@ -440,7 +430,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { return { ...theme, - layers: layers, + layers: layers } } } @@ -510,10 +500,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep context.warn( "The theme " + - json.id + - " has an inline layer: " + - layer["id"] + - ". This is discouraged." + json.id + + " has an inline layer: " + + layer["id"] + + ". This is discouraged." ) } return json @@ -555,12 +545,12 @@ class PostvalidateTheme extends DesugaringStep { if (minZoomAll < layer.minzoom) { context.err( "There are multiple layers based on " + - basedOn + - ". The layer with id " + - layer.id + - " has a minzoom of " + - layer.minzoom + - ", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer." + basedOn + + ". The layer with id " + + layer.id + + " has a minzoom of " + + layer.minzoom + + ", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer." ) } } @@ -586,11 +576,11 @@ class PostvalidateTheme extends DesugaringStep { .enters("layers", config.id, "filter", "sameAs") .err( "The layer " + - config.id + - " follows the filter state of layer " + - sameAs + - ", but no layer with this name was found.\n\tDid you perhaps mean one of: " + - closeLayers.slice(0, 3).join(", ") + config.id + + " follows the filter state of layer " + + sameAs + + ", but no layer with this name was found.\n\tDid you perhaps mean one of: " + + closeLayers.slice(0, 3).join(", ") ) } } @@ -618,6 +608,13 @@ export class PrepareTheme extends Fuse { new SetDefault("socialImage", "assets/SocialImage.png", true), // We expand all tagrenderings first... new On("layers", new Each(new PrepareLayer(state))), + new On("popup", new Each( + new Fuse("Prepare popups", + new On("body", new Each(new RewriteSpecial())), + new On("title", new RewriteSpecial()) + ) + )), + // Then we apply the override all. We must first expand everything in case that we override something in an expanded tag // Note that it'll cheat with tagRenderings+ new ApplyOverrideAll(), diff --git a/src/Models/ThemeConfig/Json/ThemeConfigJson.ts b/src/Models/ThemeConfig/Json/ThemeConfigJson.ts index cbdeef07b..654a6b746 100644 --- a/src/Models/ThemeConfig/Json/ThemeConfigJson.ts +++ b/src/Models/ThemeConfig/Json/ThemeConfigJson.ts @@ -3,6 +3,8 @@ import ExtraLinkConfigJson from "./ExtraLinkConfigJson" import { RasterLayerProperties } from "../../RasterLayerProperties" import { Translatable } from "./Translatable" +import { TagConfigJson } from "./TagConfigJson" +import { TagRenderingConfigJson } from "./TagRenderingConfigJson" /** * Defines the entire theme. @@ -468,4 +470,24 @@ export interface ThemeConfigJson { * group: hidden */ _usedImages?: string[] + + /** + * If set, an _additional_ popup will be shown under the theme introduction page. + * + * The embedded tagRenderingConfigs will be run against the settings-state of the contributor. + * If multiple popups are set, the first popup of the list will be rendered on top (and thus be seen first). + */ + popup?: { + /** + * ifset: the user can dismiss this message + */ + dismissible?: boolean + condition?: TagConfigJson + title: TagRenderingConfigJson, + body: TagRenderingConfigJson[], + /** + * id of the popup, mostly to keep the translations in check + */ + id: string, + }[] } diff --git a/src/Models/ThemeConfig/ThemeConfig.ts b/src/Models/ThemeConfig/ThemeConfig.ts index de1851fc6..ad677a380 100644 --- a/src/Models/ThemeConfig/ThemeConfig.ts +++ b/src/Models/ThemeConfig/ThemeConfig.ts @@ -9,6 +9,10 @@ import LanguageUtils from "../../Utils/LanguageUtils" import { RasterLayerProperties } from "../RasterLayerProperties" import { Translatable } from "./Json/Translatable" +import { TagsFilter } from "../../Logic/Tags/TagsFilter" +import TagRenderingConfig from "./TagRenderingConfig" +import { TagUtils } from "../../Logic/Tags/TagUtils" +import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" /** * Minimal information about a theme @@ -93,6 +97,14 @@ export default class ThemeConfig implements ThemeInformation { public readonly source: ThemeConfigJson public readonly enableCache: boolean + public readonly popups: Readonly<{ + id: string, + dismissible?: boolean, + condition: TagsFilter, + title: TagRenderingConfig, + body: TagRenderingConfig[] + }>[] + constructor( json: ThemeConfigJson, official = true, @@ -193,11 +205,26 @@ export default class ThemeConfig implements ThemeInformation { icon: "./assets/svg/pop-out.svg", href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}", newTab: true, - requirements: ["iframe", "no-welcome-message"], + requirements: ["iframe", "no-welcome-message"] }, context + ".extraLink" ) + this.popups = (json.popup ?? []).map((p, i) => { + const ctx = context + ".popup." + i + if (!p.id) { + throw (ctx + ": an id is required") + } + const body: TagRenderingConfigJson[] = Array.isArray(p.body) ? p.body : [p.body] + return { + id: p.id, + dismissible: p.dismissible ?? false, + condition: TagUtils.Tag(p.condition), + title: new TagRenderingConfig(p.title, ctx + ".title"), + body: body.map((body, i) => new TagRenderingConfig(body, ctx + ".body." + i)) + } + }) + this.hideFromOverview = json.hideFromOverview ?? false this.lockLocation = <[[number, number], [number, number]]>json.lockLocation ?? undefined this.enableUserBadge = json.enableUserBadge ?? true @@ -351,7 +378,7 @@ export default class ThemeConfig implements ThemeInformation { // The 'favourite'-layer contains pretty much all images as it bundles all layers, so we exclude it const jsonNoFavourites = { ...json, - layers: json.layers.filter((l) => l["id"] !== "favourite"), + layers: json.layers.filter((l) => l["id"] !== "favourite") } const usedImages = jsonNoFavourites._usedImages usedImages.sort() diff --git a/src/Models/ThemeConfig/WithContextLoader.ts b/src/Models/ThemeConfig/WithContextLoader.ts index 0fee9d6db..46d16e281 100644 --- a/src/Models/ThemeConfig/WithContextLoader.ts +++ b/src/Models/ThemeConfig/WithContextLoader.ts @@ -12,7 +12,7 @@ export default class WithContextLoader { this._context = context } - /** Given a key, gets the corresponding property from the json (or the default if not found + /** 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 * */ diff --git a/src/UI/Base/LoginButton.svelte b/src/UI/Base/LoginButton.svelte index 21d332726..011732003 100644 --- a/src/UI/Base/LoginButton.svelte +++ b/src/UI/Base/LoginButton.svelte @@ -6,18 +6,26 @@ export let osmConnection: OsmConnection export let clss: string | undefined = undefined - + /** + * Show the button, even though we are logged in + */ + export let forceShow: boolean = false + export let msg: String = undefined if (osmConnection === undefined) { console.error("No osmConnection passed into loginButton") } let isLoggedIn = osmConnection.isLoggedIn -{#if !$isLoggedIn} +{#if !$isLoggedIn || forceShow} {/if} diff --git a/src/UI/Base/Popup.svelte b/src/UI/Base/Popup.svelte index 0670c5e7b..96df2ac07 100644 --- a/src/UI/Base/Popup.svelte +++ b/src/UI/Base/Popup.svelte @@ -42,8 +42,8 @@ shown.set(false)} - outsideclose + on:close={() =>shown.set(false)} + outsideclose={dismissable} size="xl" {dismissable} {defaultClass} diff --git a/src/UI/SpecialVisualisations/SettingsVisualisations.ts b/src/UI/SpecialVisualisations/SettingsVisualisations.ts index 8d1242039..b7ff0890c 100644 --- a/src/UI/SpecialVisualisations/SettingsVisualisations.ts +++ b/src/UI/SpecialVisualisations/SettingsVisualisations.ts @@ -111,12 +111,27 @@ export class SettingsVisualisations { }, { funcName: "login_button", - args: [], + args: [{ + name: "force", + doc: "Always show this button, even if logged in" + }, { + name: "message", + doc: "Message to display on the button" + }], docs: "Show a login button", needsUrls: [], group: "settings", - constr(state: SpecialVisualizationState): SvelteUIElement { - return new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }) + constr(state: SpecialVisualizationState, _, args): SvelteUIElement { + const force = args[0].toLowerCase() + let msg = args[1] + if (msg === "") { + msg = undefined + } + return new SvelteUIElement(LoginButton, { + osmConnection: state.osmConnection, + msg, + forceShow: force === "yes" || force === "true" + }) }, }, diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index a7d54765f..bff3dac80 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -49,6 +49,8 @@ import Loading from "./Base/Loading.svelte" import { WithSearchState } from "../Models/ThemeViewState/WithSearchState" import TitleHandler from "../Logic/Actors/TitleHandler" + import Popup from "./Base/Popup.svelte" + import TagRenderingAnswer from "./Popup/TagRendering/TagRenderingAnswer.svelte" export let state: WithSearchState new TitleHandler(state.selectedElement, state) @@ -76,6 +78,7 @@ let mapproperties: MapProperties = state.mapProperties let searchOpened = state.searchState.showSearchDrawer + let metatags = state.userRelatedState.preferencesAsTags Orientation.singleton.startMeasurements() let slideDuration = 150 // ms @@ -148,7 +151,7 @@ const bottomRight = mlmap.unproject([rect.right, rect.bottom]) const bbox = new BBox([ [topLeft.lng, topLeft.lat], - [bottomRight.lng, bottomRight.lat], + [bottomRight.lng, bottomRight.lat] ]) state.visualFeedbackViewportBounds.setData(bbox) } @@ -500,5 +503,24 @@ {/if} {/if} + {#each theme.popups as popup} + {#if popup.condition.matchesProperties($metatags)} + + +
+ {#each popup.body as body} + + {/each} + {popup.id} +
+
+ {/if} + {/each} From 75a0eeed83990b727abd04ef78126001c3675e69 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 02:04:40 +0100 Subject: [PATCH 12/15] Scripts: fixes to deploy-hosted --- .forgejo/workflows/deploy_hosted.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/deploy_hosted.yml b/.forgejo/workflows/deploy_hosted.yml index 6cf6e2d9d..88b6f5c5c 100644 --- a/.forgejo/workflows/deploy_hosted.yml +++ b/.forgejo/workflows/deploy_hosted.yml @@ -52,8 +52,10 @@ jobs: - name: Zipping dist file run: | - mv dist /tmp/${{ github.ref_name }} - zip /tmp/${{ github.ref_name }}.zip -r /tmp/${{ github.ref_name }}/* + mv -f dist /tmp/${{ github.ref_name }} + cd /tmp + zip ${{ github.ref_name }}.zip -r ${{ github.ref_name }}/* + cd - - name: uploading file run: scp /tmp/${{ github.ref_name }}.zip hetzner:/root/staging/ From be68837ffbc6875843f9d3fd0d3e77d89df1a2d2 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 02:05:45 +0100 Subject: [PATCH 13/15] Feature: add user_block endpoint --- scripts/generateLayerOverview.ts | 10 +++------- src/Logic/Osm/OsmConnection.ts | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index d3f4dd4f9..cf4673643 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -9,16 +9,12 @@ import { DoesImageExist, PrevalidateTheme, ValidateLayer, - ValidateThemeEnsemble, + ValidateThemeEnsemble } from "../src/Models/ThemeConfig/Conversion/Validation" import { Translation } from "../src/UI/i18n/Translation" import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer" import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme" -import { - Conversion, - DesugaringContext, - DesugaringStep, -} from "../src/Models/ThemeConfig/Conversion/Conversion" +import { Conversion, DesugaringContext, DesugaringStep } from "../src/Models/ThemeConfig/Conversion/Conversion" import { Utils } from "../src/Utils" import Script from "./Script" import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" @@ -501,7 +497,7 @@ class LayerOverviewUtils extends Script { priviliged.delete(key) }) - // These two get a free pass + // These get a free pass priviliged.delete("summary") priviliged.delete("last_click") priviliged.delete("search") diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts index 0230aa4ee..c8dea75dd 100644 --- a/src/Logic/Osm/OsmConnection.ts +++ b/src/Logic/Osm/OsmConnection.ts @@ -65,6 +65,16 @@ interface OsmUserInfo { } } +interface UserBlock { + "id": number, + "created_at": string, + "updated_at": string, + "ends_at": string, + "needs_view": boolean, + "user": { "uid": number, "user": string }, + "creator": { "uid": number, "user": string } +} + export default interface UserDetails { name: string uid: number @@ -77,7 +87,8 @@ export default interface UserDetails { account_created: string tracesCount: number description?: string - languages: string[] + languages: string[], + active_blocks: number } export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable" @@ -309,6 +320,7 @@ export class OsmConnection { account_created: user.account_created, tracesCount: user.traces?.count ?? 0, unreadMessages: user.messages.received?.unread ?? 0, + active_blocks: user.blocks.received.active ?? 0 } this.userDetails.set(userdetails) this.loadingStatus.setData("logged-in") @@ -559,6 +571,14 @@ export class OsmConnection { }) } + /** + * Have you been banned by the DWG? + */ + public async getUserBlocks(): Promise { + const raw = await this.interact("/user/blocks/active.json") + return JSON.parse(raw)["user_blocks"] + } + /** * To be called by land.html */ From c559b4ae1ee089c47db3a6be07ed4e04a084c31c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 02:15:06 +0100 Subject: [PATCH 14/15] Chore: resize some images to fix warnings --- assets/layers/ice_cream/ice_cream.svg | 101 +++--- assets/layers/item_with_image/camera.svg | 356 +++++++++++++++++--- assets/themes/advertising/wall_painting.svg | 76 +++-- 3 files changed, 408 insertions(+), 125 deletions(-) diff --git a/assets/layers/ice_cream/ice_cream.svg b/assets/layers/ice_cream/ice_cream.svg index f40ca6c48..5cd349812 100644 --- a/assets/layers/ice_cream/ice_cream.svg +++ b/assets/layers/ice_cream/ice_cream.svg @@ -1,40 +1,61 @@ - - - - - - - - image/svg+xml - - - - - - - - - + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/assets/layers/item_with_image/camera.svg b/assets/layers/item_with_image/camera.svg index a29853276..5dae59d81 100644 --- a/assets/layers/item_with_image/camera.svg +++ b/assets/layers/item_with_image/camera.svg @@ -1,64 +1,316 @@ - - Adwaita Icon Template - - - - + + + Adwaita Icon Template + + + + - - - - - - - + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - - + + + + Adwaita Icon Template + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/advertising/wall_painting.svg b/assets/themes/advertising/wall_painting.svg index fbf462c64..b1b1908a4 100644 --- a/assets/themes/advertising/wall_painting.svg +++ b/assets/themes/advertising/wall_painting.svg @@ -2,12 +2,12 @@ + inkscape:current-layer="layer1" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1"> + id="guide1027" + inkscape:locked="false" /> + id="guide1029" + inkscape:locked="false" /> + id="guide1031" + inkscape:locked="false" /> + id="guide1033" + inkscape:locked="false" /> + id="guide1035" + inkscape:locked="false" /> + id="guide1037" + inkscape:locked="false" /> + id="guide1039" + inkscape:locked="false" /> + id="guide1041" + inkscape:locked="false" /> @@ -86,26 +96,25 @@ + style="display:inline"> + rx="12.591385" /> + mask="url(#mask1309)" + transform="matrix(12.591377,0,0,12.591377,0.30278402,0.86556738)"> From e34ecd2f62c1c333596be7a321f9a2e141e8a825 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Mar 2025 02:37:31 +0100 Subject: [PATCH 15/15] Chore: deploy script now cleans up files --- .forgejo/workflows/deploy_hosted.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/deploy_hosted.yml b/.forgejo/workflows/deploy_hosted.yml index 88b6f5c5c..e11635f0f 100644 --- a/.forgejo/workflows/deploy_hosted.yml +++ b/.forgejo/workflows/deploy_hosted.yml @@ -52,7 +52,7 @@ jobs: - name: Zipping dist file run: | - mv -f dist /tmp/${{ github.ref_name }} + mv dist /tmp/${{ github.ref_name }} cd /tmp zip ${{ github.ref_name }}.zip -r ${{ github.ref_name }}/* cd - @@ -60,6 +60,9 @@ jobs: - name: uploading file run: scp /tmp/${{ github.ref_name }}.zip hetzner:/root/staging/ + - name: cleanup files + run: rm /tmp/${{ github.ref_name }}.zip && rm -rf /tmp/${{ github.ref_name }}/ + - name: unzipping remote file run: ssh hetzner "cd /root/staging && rm -rf ${{ github.ref_name }} && unzip ${{ github.ref_name }}.zip && rm -rf /root/public/${{ github.ref_name }} && mv /root/staging/${{ github.ref_name }}/ /root/public/ && rm ${{ github.ref_name }}.zip"