diff --git a/.github/workflows/deploy_prod.yml b/.github/workflows/deploy_prod.yml index 1334f196c0..faf288f2d8 100644 --- a/.github/workflows/deploy_prod.yml +++ b/.github/workflows/deploy_prod.yml @@ -26,7 +26,7 @@ jobs: shell: bash - name: create dependencies - run: npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:service-worker; npm run generate:editor-layer-index + run: npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:service-worker; npm run download:editor-layer-index shell: bash - name: sync translations diff --git a/assets/layers/atm/atm.json b/assets/layers/atm/atm.json index c9f3bb7534..36cd15328e 100644 --- a/assets/layers/atm/atm.json +++ b/assets/layers/atm/atm.json @@ -613,7 +613,6 @@ } ], "filter": [ - "open_now", { "id": "speech_output", "options": [ diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 757c3ae9c4..d9a6196052 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -893,7 +893,6 @@ "description" ], "filter": [ - "open_now", { "id": "sells_second-hand", "options": [ diff --git a/assets/layers/elongated_coin/elongated_coin.json b/assets/layers/elongated_coin/elongated_coin.json index d76da9ad76..e56b47ddfd 100644 --- a/assets/layers/elongated_coin/elongated_coin.json +++ b/assets/layers/elongated_coin/elongated_coin.json @@ -435,7 +435,6 @@ "check_date" ], "filter": [ - "open_now", "accepts_debit_cards", "accepts_credit_cards" ], diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json index f522c2eddc..3c0eb2e1d0 100644 --- a/assets/layers/etymology/etymology.json +++ b/assets/layers/etymology/etymology.json @@ -288,7 +288,8 @@ "cs": "Pojmenováno po {name:etymology}" }, "freeform": { - "key": "name:etymology" + "key": "name:etymology", + "type": "text" }, "mappings": [ { diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 1077a5fde6..9719ebcb6a 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -1304,10 +1304,10 @@ } ] }, - "has_organic", - "sugar_free", - "gluten_free", - "lactose_free", + "filters.has_organic", + "filters.sugar_free", + "filters.gluten_free", + "filters.lactose_free", "accepts_cash", "accepts_cards", "dogs" diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index 00dfea69fd..38dfa9b3a7 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -632,7 +632,8 @@ "de": "Hunde sind nur draußen erlaubt" } } - ] + ], + "filter": ["filters.dogs"] }, { "id": "description", @@ -756,7 +757,8 @@ }, "hideInAnswer": true } - ] + ], + "filter": ["filters.open_now"] }, { "id": "opening_hours_24_7", @@ -1040,7 +1042,8 @@ "cs": "Platba QR kódem je zde možná" } } - ] + ], + "filter": ["filters.accepts_cash","filters.accepts_cards"] }, { "id": "payment-options-split", @@ -2120,7 +2123,8 @@ "pl": "To miejsce oferuje przewodowy dostęp do Internetu" } } - ] + ], + "filter": ["filters.has_internet"] }, { "id": "internet-fee", @@ -2451,7 +2455,8 @@ "cs": "Tento obchod nemá žádnou nabídku bez cukru" } } - ] + ], + "filter": ["filters.sugar_free"] }, { "id": "lactose_free", @@ -2496,7 +2501,9 @@ "cs": "Žádná nabídka bez laktózy" } } - ] + ], + "filter": ["filters.lactose_free"] + }, { "id": "gluten_free", @@ -2541,7 +2548,9 @@ "cs": "Tento obchod nemá bezlepkovou nabídku" } } - ] + ], + "filter": ["filters.gluten_free"] + }, { "id": "vegan", diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index a3983508c6..a794639e55 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -663,9 +663,6 @@ } ] }, - "accepts_cash", - "accepts_cards", - "has_organic", { "id": "second_hand", "options": [ @@ -686,9 +683,7 @@ } ] }, - "sugar_free", - "gluten_free", - "lactose_free" + "filters.has_organic" ], "deletion": { "softDeletionTags": { diff --git a/langs/en.json b/langs/en.json index 15fb70b067..18eaa8f49d 100644 --- a/langs/en.json +++ b/langs/en.json @@ -245,7 +245,7 @@ "licenseInfo": "

Copyright notice

The provided data is available under ODbL. Reusing it is gratis for any purpose, but Please read the full copyright notice for details.", "noDataLoaded": "No data is loaded yet. Download will be available soon", "pdf": { - "current_view_generic": "Export a PDF off the current view for {paper_size} in {orientation} orientation" + "current_view_generic": "Export a PDF of the current view for {paper_size} in {orientation} orientation" }, "title": "Download", "toMuch": "There are to many features to download them all", diff --git a/langs/nl.json b/langs/nl.json index 883c5f165e..2be0a59cb7 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -45,6 +45,9 @@ "useSomethingElse": "Gebruik een ander OpenStreetMap-bewerkprogramma om dit object te verwijderen", "whyDelete": "Waarom moet dit object van de kaart verwijderd worden?" }, + "external": { + "error": "Kon geen gestructureerde informatie uit de website ophalen" + }, "favourite": { "loginNeeded": "

Log in

Je moet je aanmelden met OpenStreetMap om een persoonlijk thema te gebruiken", "panelIntro": "

Jouw persoonlijke thema

Activeer je favorite lagen van alle andere themas", @@ -151,6 +154,7 @@ "editId": "Hier bewerken met de OpenStreetMap online editor", "editJosm": "Hier bewerken met JOSM", "followOnMastodon": "Volg MapComplete op Mastodon", + "gotoSourceCode": "Bekijk de broncode", "iconAttribution": { "title": "Iconen en afbeeldingen" }, @@ -163,6 +167,7 @@ "openIssueTracker": "Geef een fout in de software door", "openMapillary": "Open Mapillary op deze locatie", "openOsmcha": "Bekijk de laatste bijdragen gemaakt met {theme}", + "openOsmchaLastWeek": "Bekijk aanpassingen van de voorbije 7 dagen", "themeBy": "Thema gemaakt door {author}", "title": "Copyright en attributie", "translatedBy": "MapComplete werd vertaald door {contributors} en {hiddenCount} meer vertalers" diff --git a/langs/zh_Hant.json b/langs/zh_Hant.json index 01527a2f18..7e81c7bc7d 100644 --- a/langs/zh_Hant.json +++ b/langs/zh_Hant.json @@ -245,7 +245,7 @@ "licenseInfo": "

著作權聲明

提供的資料採用 ODbL 授權釋出。可以用任何目標再利用資料,但是需 請閱讀完整的 著作權聲明。", "noDataLoaded": "還未載入資料,之後能夠下載", "pdf": { - "current_view_generic": "匯出目前檢視為 {paper_size} 的 {orientation} 方向 PDF" + "current_view_generic": "以 {orientation} 方向匯出 {paper_size} 大小的目前檢視 PDF" }, "title": "下載", "toMuch": "有很多圖徵可以下載了", diff --git a/scripts/generateFavouritesLayer.ts b/scripts/generateFavouritesLayer.ts index 41b8fd035a..fa592b0f63 100644 --- a/scripts/generateFavouritesLayer.ts +++ b/scripts/generateFavouritesLayer.ts @@ -236,8 +236,11 @@ export class GenerateFavouritesLayer extends Script { if (seenTitleIcons.has(titleIcon.id)) { continue } + if(titleIcon.id === undefined){ + continue + } seenTitleIcons.add(titleIcon.id) - console.log("Adding ", titleIcon.id) + console.log("Adding title icon", titleIcon.id) titleIcons.push(titleIcon) } } diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index cc496437ab..b0df401c49 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -332,6 +332,7 @@ class LayerOverviewUtils extends Script { return sharedQuestions.tagRenderings } + return this.getSharedTagRenderings( doesImageExist, dict, diff --git a/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts b/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts index 150b7ed9a8..74ffa97457 100644 --- a/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts +++ b/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts @@ -114,7 +114,7 @@ export class SummaryTileSource extends DynamicTileSource { ): Store[]> { const [z, x, y] = Tiles.tile_from_index(tileIndex) let coordinates = Tiles.centerPointOf(z, x, y) - const url = `${cacheserver}/${layersSummed}/${z}/${x}/${y}.json` + const url = `${cacheserver}/summary/${layersSummed}/${z}/${x}/${y}.json` const count = UIEventSource.FromPromiseWithErr(Utils.downloadJson(url)) return count.mapD((count) => { if (count["error"] !== undefined) { diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 7a859a5a6f..2199ff44b0 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -62,8 +62,37 @@ class ExpandFilter extends DesugaringStep { const newFilters: FilterConfigJson[] = [] const filters = <(FilterConfigJson | string)[]>json.filter + + for (let i = 0; i < json.tagRenderings?.length; i++){ + const tagRendering = json.tagRenderings[i] + if(!tagRendering.filter){ + continue + } + for (const filterName of tagRendering.filter ?? []) { + if(typeof filterName !== "string"){ + context.enters("tagRenderings",i,"filter").err("Not a string: "+ filterName) + } + const exists = filters.some(existing => { + const id : string = existing["id"] ?? existing + return filterName === id || (filterName.startsWith("filters.") && filterName.endsWith("."+id)) + }) + if(exists){ + continue + } + if(!filterName){ + context.err("Got undefined as filter expansion in "+tagRendering["id"]) + continue + } + console.log("Adding filter",filterName," due to", tagRendering["id"]) + filters.push(filterName) + } + } + for (let i = 0; i < filters.length; i++) { const filter = filters[i] + if(filter === undefined){ + continue + } if (typeof filter !== "string") { newFilters.push(filter) continue @@ -85,7 +114,7 @@ class ExpandFilter extends DesugaringStep { osmTags: mapping.if, })) options.unshift({ - question: { + question: matchingTr["question"] ?? { en: "All types", }, osmTags: undefined, @@ -113,7 +142,11 @@ class ExpandFilter extends DesugaringStep { const expandedFilter = (<(FilterConfigJson | string)[]>layer.filter).find( (f) => typeof f !== "string" && f.id === expectedId ) - newFilters.push(expandedFilter) + if(expandedFilter === undefined){ + context.err("Did not find filter with name "+filter) + }else{ + newFilters.push(expandedFilter) + } } else { // This is a bootstrapping-run, we can safely ignore this } diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index b309c13777..f8cddc46c8 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -1761,6 +1761,10 @@ export class ValidateFilter extends DesugaringStep { // Calling another filter, we skip return filter } + if(filter === undefined){ + context.err("Trying to validate a filter, but this filter is undefined") + return undefined + } for (const option of filter.options) { for (let i = 0; i < option.fields?.length ?? 0; i++) { const field = option.fields[i] diff --git a/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 5d037b9c8c..645b0eaca3 100644 --- a/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/src/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -222,4 +222,9 @@ export interface TagRenderingConfigJson { * Values are split on ` ` (space) */ classes?: string + + /** + * This tagRendering can introduce this builtin filter + */ + filter?: string[] } diff --git a/src/UI/BigComponents/Filterview.svelte b/src/UI/BigComponents/Filterview.svelte index 0ff7115436..1aa5957a3f 100644 --- a/src/UI/BigComponents/Filterview.svelte +++ b/src/UI/BigComponents/Filterview.svelte @@ -43,16 +43,12 @@ {#if filteredLayer.layerDef.name}
- +
layer.defaultIcon()?.SetClass("block h-6 w-6 no-image-background")} + construct={() => layer.defaultIcon()} /> - - layer.defaultIcon()?.SetClass("block h-6 w-6 no-image-background opacity-50")} - /> - +
+ diff --git a/src/UI/InputElement/Validators/StringValidator.ts b/src/UI/InputElement/Validators/StringValidator.ts index 9da065c9a3..6b62e0f590 100644 --- a/src/UI/InputElement/Validators/StringValidator.ts +++ b/src/UI/InputElement/Validators/StringValidator.ts @@ -1,7 +1,24 @@ import { Validator } from "../Validator" +import { Translation } from "../../i18n/Translation" +import Translations from "../../i18n/Translations" export default class StringValidator extends Validator { - constructor() { - super("string", "A simple piece of text") + + constructor(type?: string, doc?: string, inputmode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search", textArea?: boolean) { + super(type ?? "string", + doc ?? "A simple piece of text which is at most 255 characters long", + inputmode, + textArea) + } + + isValid(s: string): boolean { + return s.length <= 255 + } + + getFeedback(s: string, getCountry?: () => string): Translation | undefined { + if (s.length > 255) { + return Translations.t.validation.tooLong.Subs({ count: s.length }) + } + return super.getFeedback(s, getCountry) } } diff --git a/src/UI/InputElement/Validators/TextValidator.ts b/src/UI/InputElement/Validators/TextValidator.ts index d1bc1e1604..a60368ee76 100644 --- a/src/UI/InputElement/Validators/TextValidator.ts +++ b/src/UI/InputElement/Validators/TextValidator.ts @@ -1,6 +1,6 @@ -import { Validator } from "../Validator" +import StringValidator from "./StringValidator" -export default class TextValidator extends Validator { +export default class TextValidator extends StringValidator { constructor() { super( "text",