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
|
@ -19,6 +19,50 @@
|
||||||
"shortDescription": {
|
"shortDescription": {
|
||||||
"nl": "Grb import helper tool"
|
"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 <span class='literal-code'>https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import</code>",
|
||||||
|
"nl": " en voeg deze link toe: <span class='literal-code'>https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import</code>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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",
|
"icon": "./assets/themes/grb/logo.svg",
|
||||||
"startZoom": 9,
|
"startZoom": 9,
|
||||||
"startLat": 51.0249,
|
"startLat": 51.0249,
|
||||||
|
|
|
@ -623,6 +623,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"popup": {
|
||||||
|
"0": {
|
||||||
|
"body": {
|
||||||
|
"0": {
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"after": "to include the link <span class='literal-code'>https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import</code>",
|
||||||
|
"text": "Edit your user profile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"msg": "Reload your profile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"render": "Profile mention obligated"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"guideposts": {
|
"guideposts": {
|
||||||
|
|
|
@ -669,6 +669,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"popup": {
|
||||||
|
"0": {
|
||||||
|
"body": {
|
||||||
|
"0": {
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"after": " en voeg deze link toe: <span class='literal-code'>https://wiki.openstreetmap.org/wiki/WikiProject_Belgium/Building_and_address_import</code>",
|
||||||
|
"text": "Pas je profiel aan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"render": {
|
||||||
|
"special": {
|
||||||
|
"msg": "Herlaad je profiel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"render": "Link op profiel verplicht"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"shortDescription": "Grb import helper tool",
|
"shortDescription": "Grb import helper tool",
|
||||||
"title": "GRB import helper"
|
"title": "GRB import helper"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import {
|
import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion"
|
||||||
Concat,
|
|
||||||
Conversion,
|
|
||||||
DesugaringContext,
|
|
||||||
DesugaringStep,
|
|
||||||
Each,
|
|
||||||
Fuse,
|
|
||||||
On,
|
|
||||||
Pass,
|
|
||||||
SetDefault,
|
|
||||||
} from "./Conversion"
|
|
||||||
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
|
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
|
||||||
import { PrepareLayer } from "./PrepareLayer"
|
import { PrepareLayer, RewriteSpecial } from "./PrepareLayer"
|
||||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||||
import { Utils } from "../../../Utils"
|
import { Utils } from "../../../Utils"
|
||||||
import Constants from "../../Constants"
|
import Constants from "../../Constants"
|
||||||
|
@ -40,7 +30,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
||||||
const knownLayers = Array.from(state.sharedLayers.keys())
|
const knownLayers = Array.from(state.sharedLayers.keys())
|
||||||
const withDistance: [string, number][] = knownLayers.map((lname) => [
|
const withDistance: [string, number][] = knownLayers.map((lname) => [
|
||||||
lname,
|
lname,
|
||||||
Utils.levenshteinDistance(name, lname),
|
Utils.levenshteinDistance(name, lname)
|
||||||
])
|
])
|
||||||
withDistance.sort((a, b) => a[1] - b[1])
|
withDistance.sort((a, b) => a[1] - b[1])
|
||||||
const ids = withDistance.map((n) => n[0])
|
const ids = withDistance.map((n) => n[0])
|
||||||
|
@ -130,9 +120,9 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
||||||
usedLabels.add(labels[forbiddenLabel])
|
usedLabels.add(labels[forbiddenLabel])
|
||||||
context.info(
|
context.info(
|
||||||
"Dropping tagRendering " +
|
"Dropping tagRendering " +
|
||||||
tr["id"] +
|
tr["id"] +
|
||||||
" as it has a forbidden label: " +
|
" as it has a forbidden label: " +
|
||||||
labels[forbiddenLabel]
|
labels[forbiddenLabel]
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -150,10 +140,10 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
||||||
usedLabels.add(tr["group"])
|
usedLabels.add(tr["group"])
|
||||||
context.info(
|
context.info(
|
||||||
"Dropping tagRendering " +
|
"Dropping tagRendering " +
|
||||||
tr["id"] +
|
tr["id"] +
|
||||||
" as its group `" +
|
" as its group `" +
|
||||||
tr["group"] +
|
tr["group"] +
|
||||||
"` is a forbidden label"
|
"` is a forbidden label"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -164,8 +154,8 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs
|
||||||
if (unused.length > 0) {
|
if (unused.length > 0) {
|
||||||
context.err(
|
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: " +
|
"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(", ") +
|
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"
|
"\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
|
found.tagRenderings = filtered
|
||||||
|
@ -205,10 +195,10 @@ export class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
|
||||||
if (alreadyLoaded.has(v.id)) {
|
if (alreadyLoaded.has(v.id)) {
|
||||||
context.warn(
|
context.warn(
|
||||||
"Layout " +
|
"Layout " +
|
||||||
context +
|
context +
|
||||||
" already has a layer with name " +
|
" already has a layer with name " +
|
||||||
v.id +
|
v.id +
|
||||||
"; skipping inclusion of this builtin layer"
|
"; skipping inclusion of this builtin layer"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -352,10 +342,10 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
||||||
.enters("layer dependency")
|
.enters("layer dependency")
|
||||||
.err(
|
.err(
|
||||||
"Layer " +
|
"Layer " +
|
||||||
dependency.neededLayer +
|
dependency.neededLayer +
|
||||||
" is loaded because " +
|
" is loaded because " +
|
||||||
dependency.reason +
|
dependency.reason +
|
||||||
"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`"
|
"; 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) {
|
if (dep === undefined) {
|
||||||
const message = [
|
const message = [
|
||||||
"Loading a dependency failed: layer " +
|
"Loading a dependency failed: layer " +
|
||||||
unmetDependency.neededLayer +
|
unmetDependency.neededLayer +
|
||||||
" is not found, neither as layer of " +
|
" is not found, neither as layer of " +
|
||||||
themeId +
|
themeId +
|
||||||
" nor as builtin layer.",
|
" nor as builtin layer.",
|
||||||
reason,
|
reason,
|
||||||
"Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","),
|
"Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(",")
|
||||||
]
|
]
|
||||||
throw message.join("\n\t")
|
throw message.join("\n\t")
|
||||||
}
|
}
|
||||||
|
@ -395,7 +385,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
||||||
dep.description = reason
|
dep.description = reason
|
||||||
dependenciesToAdd.unshift({
|
dependenciesToAdd.unshift({
|
||||||
config: dep,
|
config: dep,
|
||||||
reason,
|
reason
|
||||||
})
|
})
|
||||||
loadedLayerIds.add(dep.id)
|
loadedLayerIds.add(dep.id)
|
||||||
unmetDependencies = unmetDependencies.filter(
|
unmetDependencies = unmetDependencies.filter(
|
||||||
|
@ -440,7 +430,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<ThemeConfigJson> {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...theme,
|
...theme,
|
||||||
layers: layers,
|
layers: layers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,10 +500,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<ThemeConfigJson>
|
||||||
|
|
||||||
context.warn(
|
context.warn(
|
||||||
"The theme " +
|
"The theme " +
|
||||||
json.id +
|
json.id +
|
||||||
" has an inline layer: " +
|
" has an inline layer: " +
|
||||||
layer["id"] +
|
layer["id"] +
|
||||||
". This is discouraged."
|
". This is discouraged."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return json
|
return json
|
||||||
|
@ -555,12 +545,12 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
|
||||||
if (minZoomAll < layer.minzoom) {
|
if (minZoomAll < layer.minzoom) {
|
||||||
context.err(
|
context.err(
|
||||||
"There are multiple layers based on " +
|
"There are multiple layers based on " +
|
||||||
basedOn +
|
basedOn +
|
||||||
". The layer with id " +
|
". The layer with id " +
|
||||||
layer.id +
|
layer.id +
|
||||||
" has a minzoom of " +
|
" has a minzoom of " +
|
||||||
layer.minzoom +
|
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."
|
", 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")
|
.enters("layers", config.id, "filter", "sameAs")
|
||||||
.err(
|
.err(
|
||||||
"The layer " +
|
"The layer " +
|
||||||
config.id +
|
config.id +
|
||||||
" follows the filter state of layer " +
|
" follows the filter state of layer " +
|
||||||
sameAs +
|
sameAs +
|
||||||
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
|
", but no layer with this name was found.\n\tDid you perhaps mean one of: " +
|
||||||
closeLayers.slice(0, 3).join(", ")
|
closeLayers.slice(0, 3).join(", ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,6 +608,13 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
|
||||||
new SetDefault("socialImage", "assets/SocialImage.png", true),
|
new SetDefault("socialImage", "assets/SocialImage.png", true),
|
||||||
// We expand all tagrenderings first...
|
// We expand all tagrenderings first...
|
||||||
new On("layers", new Each(new PrepareLayer(state))),
|
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
|
// 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+
|
// Note that it'll cheat with tagRenderings+
|
||||||
new ApplyOverrideAll(),
|
new ApplyOverrideAll(),
|
||||||
|
|
|
@ -3,6 +3,8 @@ import ExtraLinkConfigJson from "./ExtraLinkConfigJson"
|
||||||
|
|
||||||
import { RasterLayerProperties } from "../../RasterLayerProperties"
|
import { RasterLayerProperties } from "../../RasterLayerProperties"
|
||||||
import { Translatable } from "./Translatable"
|
import { Translatable } from "./Translatable"
|
||||||
|
import { TagConfigJson } from "./TagConfigJson"
|
||||||
|
import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the entire theme.
|
* Defines the entire theme.
|
||||||
|
@ -468,4 +470,24 @@ export interface ThemeConfigJson {
|
||||||
* group: hidden
|
* group: hidden
|
||||||
*/
|
*/
|
||||||
_usedImages?: string[]
|
_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 { RasterLayerProperties } from "../RasterLayerProperties"
|
||||||
import { Translatable } from "./Json/Translatable"
|
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
|
* Minimal information about a theme
|
||||||
|
@ -93,6 +97,14 @@ export default class ThemeConfig implements ThemeInformation {
|
||||||
public readonly source: ThemeConfigJson
|
public readonly source: ThemeConfigJson
|
||||||
public readonly enableCache: boolean
|
public readonly enableCache: boolean
|
||||||
|
|
||||||
|
public readonly popups: Readonly<{
|
||||||
|
id: string,
|
||||||
|
dismissible?: boolean,
|
||||||
|
condition: TagsFilter,
|
||||||
|
title: TagRenderingConfig,
|
||||||
|
body: TagRenderingConfig[]
|
||||||
|
}>[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
json: ThemeConfigJson,
|
json: ThemeConfigJson,
|
||||||
official = true,
|
official = true,
|
||||||
|
@ -193,11 +205,26 @@ export default class ThemeConfig implements ThemeInformation {
|
||||||
icon: "./assets/svg/pop-out.svg",
|
icon: "./assets/svg/pop-out.svg",
|
||||||
href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}",
|
href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}",
|
||||||
newTab: true,
|
newTab: true,
|
||||||
requirements: ["iframe", "no-welcome-message"],
|
requirements: ["iframe", "no-welcome-message"]
|
||||||
},
|
},
|
||||||
context + ".extraLink"
|
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.hideFromOverview = json.hideFromOverview ?? false
|
||||||
this.lockLocation = <[[number, number], [number, number]]>json.lockLocation ?? undefined
|
this.lockLocation = <[[number, number], [number, number]]>json.lockLocation ?? undefined
|
||||||
this.enableUserBadge = json.enableUserBadge ?? true
|
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
|
// The 'favourite'-layer contains pretty much all images as it bundles all layers, so we exclude it
|
||||||
const jsonNoFavourites = {
|
const jsonNoFavourites = {
|
||||||
...json,
|
...json,
|
||||||
layers: json.layers.filter((l) => l["id"] !== "favourite"),
|
layers: json.layers.filter((l) => l["id"] !== "favourite")
|
||||||
}
|
}
|
||||||
const usedImages = jsonNoFavourites._usedImages
|
const usedImages = jsonNoFavourites._usedImages
|
||||||
usedImages.sort()
|
usedImages.sort()
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default class WithContextLoader {
|
||||||
this._context = context
|
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
|
* The found value is interpreted as a tagrendering and fetched/parsed
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -6,18 +6,26 @@
|
||||||
|
|
||||||
export let osmConnection: OsmConnection
|
export let osmConnection: OsmConnection
|
||||||
export let clss: string | undefined = undefined
|
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) {
|
if (osmConnection === undefined) {
|
||||||
console.error("No osmConnection passed into loginButton")
|
console.error("No osmConnection passed into loginButton")
|
||||||
}
|
}
|
||||||
let isLoggedIn = osmConnection.isLoggedIn
|
let isLoggedIn = osmConnection.isLoggedIn
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !$isLoggedIn}
|
{#if !$isLoggedIn || forceShow}
|
||||||
<button class={clss} on:click={() => osmConnection.AttemptLogin()} style="margin-left: 0">
|
<button class={clss} on:click={() => osmConnection.AttemptLogin()} style="margin-left: 0">
|
||||||
<ArrowLeftOnRectangle class="m-1 w-12" />
|
<ArrowLeftOnRectangle class="m-1 w-12" />
|
||||||
<slot>
|
<slot>
|
||||||
<Tr t={Translations.t.general.loginWithOpenStreetMap} />
|
{#if msg}
|
||||||
|
{msg}
|
||||||
|
{:else}
|
||||||
|
<Tr t={Translations.t.general.loginWithOpenStreetMap} />
|
||||||
|
{/if}
|
||||||
</slot>
|
</slot>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -42,8 +42,8 @@
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
open={_shown}
|
open={_shown}
|
||||||
on:close={() => shown.set(false)}
|
on:close={() =>shown.set(false)}
|
||||||
outsideclose
|
outsideclose={dismissable}
|
||||||
size="xl"
|
size="xl"
|
||||||
{dismissable}
|
{dismissable}
|
||||||
{defaultClass}
|
{defaultClass}
|
||||||
|
|
|
@ -111,12 +111,27 @@ export class SettingsVisualisations {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
funcName: "login_button",
|
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",
|
docs: "Show a login button",
|
||||||
needsUrls: [],
|
needsUrls: [],
|
||||||
group: "settings",
|
group: "settings",
|
||||||
constr(state: SpecialVisualizationState): SvelteUIElement {
|
constr(state: SpecialVisualizationState, _, args): SvelteUIElement {
|
||||||
return new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection })
|
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"
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
import Loading from "./Base/Loading.svelte"
|
import Loading from "./Base/Loading.svelte"
|
||||||
import { WithSearchState } from "../Models/ThemeViewState/WithSearchState"
|
import { WithSearchState } from "../Models/ThemeViewState/WithSearchState"
|
||||||
import TitleHandler from "../Logic/Actors/TitleHandler"
|
import TitleHandler from "../Logic/Actors/TitleHandler"
|
||||||
|
import Popup from "./Base/Popup.svelte"
|
||||||
|
import TagRenderingAnswer from "./Popup/TagRendering/TagRenderingAnswer.svelte"
|
||||||
|
|
||||||
export let state: WithSearchState
|
export let state: WithSearchState
|
||||||
new TitleHandler(state.selectedElement, state)
|
new TitleHandler(state.selectedElement, state)
|
||||||
|
@ -76,6 +78,7 @@
|
||||||
let mapproperties: MapProperties = state.mapProperties
|
let mapproperties: MapProperties = state.mapProperties
|
||||||
let searchOpened = state.searchState.showSearchDrawer
|
let searchOpened = state.searchState.showSearchDrawer
|
||||||
|
|
||||||
|
let metatags = state.userRelatedState.preferencesAsTags
|
||||||
Orientation.singleton.startMeasurements()
|
Orientation.singleton.startMeasurements()
|
||||||
|
|
||||||
let slideDuration = 150 // ms
|
let slideDuration = 150 // ms
|
||||||
|
@ -148,7 +151,7 @@
|
||||||
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
|
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
|
||||||
const bbox = new BBox([
|
const bbox = new BBox([
|
||||||
[topLeft.lng, topLeft.lat],
|
[topLeft.lng, topLeft.lat],
|
||||||
[bottomRight.lng, bottomRight.lat],
|
[bottomRight.lng, bottomRight.lat]
|
||||||
])
|
])
|
||||||
state.visualFeedbackViewportBounds.setData(bbox)
|
state.visualFeedbackViewportBounds.setData(bbox)
|
||||||
}
|
}
|
||||||
|
@ -500,5 +503,24 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#each theme.popups as popup}
|
||||||
|
{#if popup.condition.matchesProperties($metatags)}
|
||||||
|
<Popup shown={new UIEventSource(true)} dismissable={popup.dismissible}>
|
||||||
|
<TagRenderingAnswer slot="header" config={popup.title} {state}
|
||||||
|
tags={metatags}
|
||||||
|
layer={undefined}
|
||||||
|
selectedElement={({type: "Feature", properties: $metatags, geometry: {type: "Point", coordinates: [0,0]}})} />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
{#each popup.body as body}
|
||||||
|
<TagRenderingAnswer config={body} {state}
|
||||||
|
tags={metatags}
|
||||||
|
layer={undefined}
|
||||||
|
selectedElement={({type: "Feature", properties: $metatags, geometry: {type: "Point", coordinates: [0,0]}})} />
|
||||||
|
{/each}
|
||||||
|
<span class="subtle">{popup.id}</span>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
<MenuDrawer onlyLink={false} {state} />
|
<MenuDrawer onlyLink={false} {state} />
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue