forked from MapComplete/MapComplete
Feature(grb): add popup feature to validate e.g. a user profile
This commit is contained in:
parent
c3d905b26a
commit
ecd8f5e1da
11 changed files with 247 additions and 64 deletions
|
|
@ -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<string | LayerConfigJson, LayerConfigJs
|
|||
const knownLayers = Array.from(state.sharedLayers.keys())
|
||||
const withDistance: [string, number][] = knownLayers.map((lname) => [
|
||||
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<string | LayerConfigJson, LayerConfigJs
|
|||
usedLabels.add(labels[forbiddenLabel])
|
||||
context.info(
|
||||
"Dropping tagRendering " +
|
||||
tr["id"] +
|
||||
" as it has a forbidden label: " +
|
||||
labels[forbiddenLabel]
|
||||
tr["id"] +
|
||||
" as it has a forbidden label: " +
|
||||
labels[forbiddenLabel]
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
|
@ -150,10 +140,10 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
usedLabels.add(tr["group"])
|
||||
context.info(
|
||||
"Dropping tagRendering " +
|
||||
tr["id"] +
|
||||
" as its group `" +
|
||||
tr["group"] +
|
||||
"` is a forbidden label"
|
||||
tr["id"] +
|
||||
" as its group `" +
|
||||
tr["group"] +
|
||||
"` is a forbidden label"
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
|
@ -164,8 +154,8 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
|||
if (unused.length > 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<ThemeConfigJson> {
|
|||
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<ThemeConfigJson> {
|
|||
.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<ThemeConfigJson> {
|
|||
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<ThemeConfigJson> {
|
|||
dep.description = reason
|
||||
dependenciesToAdd.unshift({
|
||||
config: dep,
|
||||
reason,
|
||||
reason
|
||||
})
|
||||
loadedLayerIds.add(dep.id)
|
||||
unmetDependencies = unmetDependencies.filter(
|
||||
|
|
@ -440,7 +430,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
|||
|
||||
return {
|
||||
...theme,
|
||||
layers: layers,
|
||||
layers: layers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -510,10 +500,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
|
|||
|
||||
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<ThemeConfigJson> {
|
|||
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<ThemeConfigJson> {
|
|||
.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<ThemeConfigJson> {
|
|||
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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue