Favourites: include _all_ tagRenderings

This commit is contained in:
Pieter Vander Vennet 2023-11-30 00:39:55 +01:00
parent eb444ab849
commit 473931891c
20 changed files with 436 additions and 154 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ scratch
assets/editor-layer-index.json
assets/generated/*
src/assets/generated/
assets/layers/favourite/favourite.json
public/*.webmanifest
/*.html
!/index.html

View file

@ -1,4 +1,5 @@
{
"#":"no-translations",
"pointRendering": [
{
"location": [
@ -37,35 +38,9 @@
"render": {
"en": "Favourite location",
"nl": "Favoriete locatie"
},
"mappings": [
{
"if": "name~*",
"then": {
"*": "{name}"
}
}
]
},
"tagRenderings": [
{
"id": "Explanation",
"classes": "thanks",
"icon": {
"class": "large",
"path": "heart"
},
"render": {
"en": "You marked this location as a personal favourite. As such, it is shown on every map you load.",
"nl": "Je hebt deze locatie als persoonlijke favoriet aangeduid en worden op alle MapComplete-kaarten getoond."
}
},
{
"id": "show_images",
"render": {
"*": "{image_carousel()}"
}
},
"all_tags"
]
}

View file

@ -1,6 +1,6 @@
{
"name": "mapcomplete",
"version": "0.35.0",
"version": "0.36.0",
"repository": "https://github.com/pietervdvn/MapComplete",
"description": "A small website to edit OSM easily",
"bugs": "https://github.com/pietervdvn/MapComplete/issues",

View file

@ -2,24 +2,242 @@ import Script from "./Script"
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
import { readFileSync, writeFileSync } from "fs"
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
import { AllKnownLayoutsLazy } from "../src/Customizations/AllKnownLayouts"
import { Utils } from "../src/Utils"
import { AddEditingElements } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"
import {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
} from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { TagConfigJson } from "../src/Models/ThemeConfig/Json/TagConfigJson"
import { TagUtils } from "../src/Logic/Tags/TagUtils"
import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
export class GenerateFavouritesLayer extends Script {
private readonly layers: LayerConfigJson[] = []
class PrepareFavouritesLayerJson extends Script {
constructor() {
super("Prepares the 'favourites'-layer")
const allThemes = new AllKnownLayoutsLazy(false).values()
for (const theme of allThemes) {
if (theme.hideFromOverview) {
continue
}
for (const layer of theme.layers) {
if (!layer.source) {
continue
}
if (layer.source.geojsonSource) {
continue
}
const layerConfig = AllSharedLayers.getSharedLayersConfigs().get(layer.id)
if (!layerConfig) {
continue
}
this.layers.push(layerConfig)
}
}
}
private addTagRenderings(proto: LayerConfigJson) {
const blacklistedIds = new Set([
"images",
"questions",
"mapillary",
"leftover-questions",
"last_edit",
"minimap",
"move-button",
"delete-button",
"all-tags",
...AddEditingElements.addedElements,
])
const generateTagRenderings: (string | QuestionableTagRenderingConfigJson)[] = []
const trPerId = new Map<
string,
{ conditions: TagConfigJson[]; tr: QuestionableTagRenderingConfigJson }
>()
for (const layerConfig of this.layers) {
if (!layerConfig.tagRenderings) {
continue
}
for (const tagRendering of layerConfig.tagRenderings) {
if (typeof tagRendering === "string") {
if (blacklistedIds.has(tagRendering)) {
continue
}
generateTagRenderings.push(tagRendering)
blacklistedIds.add(tagRendering)
continue
}
if (tagRendering["builtin"]) {
continue
}
const id = tagRendering.id
if (blacklistedIds.has(id)) {
continue
}
if (trPerId.has(id)) {
const old = trPerId.get(id).tr
// We need to figure out if this was a 'recycled' tag rendering or just happens to have the same id
function isSame(fieldName: string) {
return old[fieldName]?.["en"] === tagRendering[fieldName]?.["en"]
}
const sameQuestion = isSame("question") && isSame("render")
if (!sameQuestion) {
const newTr = <QuestionableTagRenderingConfigJson>Utils.Clone(tagRendering)
newTr.id = layerConfig.id + "_" + newTr.id
if (blacklistedIds.has(newTr.id)) {
continue
}
newTr.condition = {
and: Utils.NoNull([(newTr.condition, layerConfig.source["osmTags"])]),
}
generateTagRenderings.push(newTr)
blacklistedIds.add(newTr.id)
continue
}
}
if (!trPerId.has(id)) {
const newTr = <QuestionableTagRenderingConfigJson>Utils.Clone(tagRendering)
generateTagRenderings.push(newTr)
trPerId.set(newTr.id, { tr: newTr, conditions: [] })
}
const conditions = trPerId.get(id).conditions
if (tagRendering["condition"]) {
conditions.push({
and: [tagRendering["condition"], layerConfig.source["osmTags"]],
})
} else {
conditions.push(layerConfig.source["osmTags"])
}
}
}
for (const { tr, conditions } of Array.from(trPerId.values())) {
const optimized = TagUtils.optimzeJson({ or: conditions })
if (optimized === true) {
continue
}
if (optimized === false) {
throw "Optimized into 'false', this is weird..."
}
tr.condition = optimized
}
const allTags: QuestionableTagRenderingConfigJson = {
id: "all-tags",
render: { "*": "{all_tags()}" },
metacondition: {
or: [
"__featureSwitchIsDebugging=true",
"mapcomplete-show_tags=full",
"mapcomplete-show_debug=yes",
],
},
}
proto.tagRenderings = [
...generateTagRenderings,
...proto.tagRenderings,
"questions",
allTags,
]
}
private addTitle(proto: LayerConfigJson) {
const mappings: MappingConfigJson[] = []
for (const layer of this.layers) {
const t = layer.title
const tags: TagConfigJson = layer.source["osmTags"]
if (!t) {
continue
}
if (typeof t === "string") {
mappings.push({ if: tags, then: t })
} else if (t["render"] !== undefined || t["mappings"] !== undefined) {
const tr = <TagRenderingConfigJson>t
for (let i = 0; i < (tr.mappings ?? []).length; i++) {
const mapping = tr.mappings[i]
const optimized = TagUtils.optimzeJson({
and: [mapping.if, tags],
})
if (optimized === false) {
console.warn(
"The following tags yielded 'false':",
JSON.stringify(mapping.if),
JSON.stringify(tags)
)
continue
}
if (optimized === true) {
console.error(
"The following tags yielded 'false':",
JSON.stringify(mapping.if),
JSON.stringify(tags)
)
throw "Tags for title optimized to true"
}
if (!mapping.then) {
throw (
"The title has a missing 'then' for mapping " +
i +
" in layer " +
layer.id
)
}
mappings.push({
if: optimized,
then: mapping.then,
})
}
if (tr.render) {
mappings.push({
if: tags,
then: <Translatable>tr.render,
})
}
} else {
mappings.push({ if: tags, then: <Record<string, string>>t })
}
}
if (proto.title["mappings"]) {
mappings.unshift(...proto.title["mappings"])
}
if (proto.title["render"]) {
mappings.push({
if: "id~*",
then: proto.title["render"],
})
}
proto.title = {
mappings,
}
}
async main(args: string[]): Promise<void> {
const allConfigs = AllSharedLayers.getSharedLayersConfigs()
console.log("Generating the favourite layer: stealing _all_ tagRenderings")
const proto = this.readLayer("favourite/favourite.proto.json")
const questions = allConfigs.get("questions")
proto.tagRenderings.push(...questions.tagRenderings)
this.addTagRenderings(proto)
this.addTitle(proto)
writeFileSync("./assets/layers/favourite/favourite.json", JSON.stringify(proto, null, " "))
}
private readLayer(path: string): LayerConfigJson {
try {
return JSON.parse(readFileSync("./assets/layers/" + path, "utf8"))
} catch (e) {
console.error("Could not read ./assets/layers/" + path)
throw e
}
}
}
new PrepareFavouritesLayerJson().run()
new GenerateFavouritesLayer().run()

View file

@ -28,6 +28,7 @@ import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Js
import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
import PointRenderingConfig from "../src/Models/ThemeConfig/PointRenderingConfig"
import { ConversionContext } from "../src/Models/ThemeConfig/Conversion/ConversionContext"
import { GenerateFavouritesLayer } from "./generateFavouritesLayer"
// This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files.
// It spits out an overview of those to be used to load them
@ -381,16 +382,11 @@ class LayerOverviewUtils extends Script {
forceReload
)
writeFileSync(
"./src/assets/generated/known_themes.json",
JSON.stringify({
themes: Array.from(sharedThemes.values()),
})
)
writeFileSync(
"./src/assets/generated/known_layers.json",
JSON.stringify({ layers: Array.from(sharedLayers.values()) })
JSON.stringify({
layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"),
})
)
const mcChangesPath = "./assets/themes/mapcomplete-changes/mapcomplete-changes.json"
@ -428,6 +424,19 @@ class LayerOverviewUtils extends Script {
ConversionContext.construct([], [])
)
for (const [_, theme] of sharedThemes) {
theme.layers = theme.layers.filter(
(l) => Constants.added_by_default.indexOf(l["id"]) < 0
)
}
writeFileSync(
"./src/assets/generated/known_themes.json",
JSON.stringify({
themes: Array.from(sharedThemes.values()),
})
)
const end = new Date()
const millisNeeded = end.getTime() - start.getTime()
if (AllSharedLayers.getSharedLayersConfigs().size == 0) {
@ -791,4 +800,5 @@ class LayerOverviewUtils extends Script {
}
}
new GenerateFavouritesLayer().run()
new LayerOverviewUtils().run()

View file

@ -1,45 +1,54 @@
import known_themes from "../assets/generated/known_themes.json"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import favourite from "../assets/generated/layers/favourite.json"
import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson"
import { AllSharedLayers } from "./AllSharedLayers"
import Constants from "../Models/Constants"
/**
* Somewhat of a dictionary, which lazily parses needed themes
*/
export class AllKnownLayoutsLazy {
private readonly dict: Map<string, { data: LayoutConfig } | { func: () => LayoutConfig }> =
new Map()
constructor() {
private readonly raw: Map<string, LayoutConfigJson> = new Map()
private readonly dict: Map<string, LayoutConfig> = new Map()
constructor(includeFavouriteLayer = true) {
for (const layoutConfigJson of known_themes["themes"]) {
this.dict.set(layoutConfigJson.id, {
func: () => {
const layout = new LayoutConfig(<LayoutConfigJson>layoutConfigJson, true)
for (let i = 0; i < layout.layers.length; i++) {
let layer = layout.layers[i]
if (typeof layer === "string") {
throw "Layer " + layer + " was not expanded in " + layout.id
for (const layerId of Constants.added_by_default) {
if (layerId === "favourite") {
if (includeFavouriteLayer) {
layoutConfigJson.layers.push(favourite)
}
continue
}
return layout
},
})
const defaultLayer = AllSharedLayers.getSharedLayersConfigs().get(layerId)
if (defaultLayer === undefined) {
console.error("Could not find builtin layer", layerId)
continue
}
layoutConfigJson.layers.push(defaultLayer)
}
this.raw.set(layoutConfigJson.id, layoutConfigJson)
}
}
public getConfig(key: string): LayoutConfigJson {
return this.raw.get(key)
}
public get(key: string): LayoutConfig {
const thunk = this.dict.get(key)
if (thunk === undefined) {
return undefined
const cached = this.dict.get(key)
if (cached !== undefined) {
return cached
}
if (thunk["data"]) {
return thunk["data"]
}
const layout = thunk["func"]()
this.dict.set(key, { data: layout })
const layout = new LayoutConfig(this.getConfig(key))
this.dict.set(key, layout)
return layout
}
public keys() {
return this.dict.keys()
return this.raw.keys()
}
public values() {

View file

@ -173,7 +173,6 @@ export default class GeoLocationHandler {
properties[k] = location[k]
}
}
console.debug("Current location object:", location)
properties["_all"] = JSON.stringify(location)
const feature = <Feature>{

View file

@ -16,6 +16,11 @@ export default class FavouritesFeatureSource extends StaticFeatureSource {
private readonly _osmConnection: OsmConnection
private readonly _detectedIds: Store<string[]>
/**
* All favourites, including the ones which are filtered away because they are already displayed
*/
public readonly allFavourites: Store<Feature[]>
constructor(
connection: OsmConnection,
indexedSource: FeaturePropertiesStore,
@ -53,6 +58,7 @@ export default class FavouritesFeatureSource extends StaticFeatureSource {
)
super(featuresWithoutAlreadyPresent)
this.allFavourites = features
this._osmConnection = connection
this._detectedIds = Stores.ListStabilized(
@ -76,6 +82,7 @@ export default class FavouritesFeatureSource extends StaticFeatureSource {
const geometry = <[number, number]>JSON.parse(prefs[key])
const properties = FavouritesFeatureSource.getPropertiesFor(prefs, id)
properties._orig_layer = prefs[FavouritesFeatureSource.prefix + id + "-layer"]
properties._orig_theme = prefs[FavouritesFeatureSource.prefix + id + "-theme"]
properties.id = osmId
properties._favourite = "yes"

View file

@ -2,6 +2,7 @@ import { LayerConfigJson } from "../Json/LayerConfigJson"
import { Utils } from "../../../Utils"
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
import { ConversionContext } from "./ConversionContext"
import { T } from "vitest/dist/types-aac763a5"
export interface DesugaringContext {
tagRenderings: Map<string, QuestionableTagRenderingConfigJson>
@ -81,18 +82,36 @@ export class Pure<TIn, TOut> extends Conversion<TIn, TOut> {
}
}
export class Bypass<T> extends DesugaringStep<T> {
private readonly _applyIf: (t: T) => boolean
private readonly _step: DesugaringStep<T>
constructor(applyIf: (t: T) => boolean, step: DesugaringStep<T>) {
super("Applies the step on the object, if the object satisfies the predicate", [], "Bypass")
this._applyIf = applyIf
this._step = step
}
convert(json: T, context: ConversionContext): T {
if (!this._applyIf(json)) {
return json
}
return this._step.convert(json, context)
}
}
export class Each<X, Y> extends Conversion<X[], Y[]> {
private readonly _step: Conversion<X, Y>
private readonly _msg: string
private readonly _filter: (x: X) => boolean
constructor(step: Conversion<X, Y>, msg?: string) {
constructor(step: Conversion<X, Y>, options?: { msg?: string }) {
super(
"Applies the given step on every element of the list",
[],
"OnEach(" + step.name + ")"
)
this._step = step
this._msg = msg
this._msg = options?.msg
}
convert(values: X[], context: ConversionContext): Y[] {

View file

@ -85,7 +85,7 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
description: trs(t.description, { title: layer.title.render }),
source: {
osmTags: {
and: ["id~*"],
and: ["id~[0-9]+", "comment_url~.*notes/[0-9]*g.json"],
},
geoJson:
"https://api.openstreetmap.org/api/0.6/notes.json?limit=10000&closed=" +

View file

@ -566,6 +566,16 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
}
export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
static addedElements: string[] = [
"minimap",
"just_created",
"split_button",
"move_button",
"delete_button",
"last_edit",
"favourite_state",
"all_tags",
]
private readonly _desugaring: DesugaringContext
constructor(desugaring: DesugaringContext) {
@ -1210,7 +1220,7 @@ class AddFavouriteBadges extends DesugaringStep<LayerConfigJson> {
}
convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
if (json.id === "favourite") {
if (json.source === "special" || json.source === "special:library") {
return json
}
const pr = json.pointRendering?.[0]

View file

@ -1,4 +1,4 @@
import { Conversion, DesugaringStep, Each, Fuse, On, Pipe, Pure } from "./Conversion"
import { Bypass, Conversion, DesugaringStep, Each, Fuse, On } from "./Conversion"
import { LayerConfigJson } from "../Json/LayerConfigJson"
import LayerConfig from "../LayerConfig"
import { Utils } from "../../../Utils"
@ -11,7 +11,6 @@ import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { ExtractImages } from "./FixImages"
import { And } from "../../../Logic/Tags/And"
import Translations from "../../../UI/i18n/Translations"
import Svg from "../../../Svg"
import FilterConfigJson from "../Json/FilterConfigJson"
import DeleteConfig from "../DeleteConfig"
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
@ -276,9 +275,9 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
new On(
"layers",
new Each(
new Pipe(
new ValidateLayer(undefined, isBuiltin, doesImageExist, false, true),
new Pure((x) => x?.raw)
new Bypass(
(layer) => Constants.added_by_default.indexOf(<any>layer.id) < 0,
new ValidateLayerConfig(undefined, isBuiltin, doesImageExist, false, true)
)
)
)
@ -968,7 +967,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
"Various validation on tagRenderingConfigs",
new DetectShadowedMappings(layerConfig),
new DetectConflictingAddExtraTags(),
// new DetectNonErasedKeysInMappings(),
// TODO enable new DetectNonErasedKeysInMappings(),
new DetectMappingsWithImages(doesImageExist),
new On("render", new ValidatePossibleLinks()),
new On("question", new ValidatePossibleLinks()),
@ -1350,6 +1349,29 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
}
export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> {
private readonly validator: ValidateLayer
constructor(
path: string,
isBuiltin: boolean,
doesImageExist: DoesImageExist,
studioValidations: boolean = false,
skipDefaultLayers: boolean = false
) {
super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig")
this.validator = new ValidateLayer(
path,
isBuiltin,
doesImageExist,
studioValidations,
skipDefaultLayers
)
}
convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
return this.validator.convert(json, context).raw
}
}
export class ValidateLayer extends Conversion<
LayerConfigJson,
{ parsed: LayerConfig; raw: LayerConfigJson }

View file

@ -462,6 +462,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
* @private
*/
private selectClosestAtCenter(i: number = 0) {
this.mapProperties.lastKeyNavigation.setData(Date.now() / 1000)
const toSelect = this.closestFeatures.features.data[i]
if (!toSelect) {
return
@ -567,46 +568,6 @@ export default class ThemeViewState implements SpecialVisualizationState {
})
}
private addLastClick(last_click: LastClickFeatureSource) {
// The last_click gets a _very_ special treatment as it interacts with various parts
this.featureProperties.trackFeatureSource(last_click)
this.indexedFeatures.addSource(last_click)
last_click.features.addCallbackAndRunD((features) => {
if (this.selectedLayer.data?.id === "last_click") {
// The last-click location moved, but we have selected the last click of the previous location
// So, we update _after_ clearing the selection to make sure no stray data is sticking around
this.selectedElement.setData(undefined)
this.selectedElement.setData(features[0])
}
})
new ShowDataLayer(this.map, {
features: new FilteringFeatureSource(this.newPointDialog, last_click),
doShowLayer: this.featureSwitches.featureSwitchEnableLogin,
layer: this.newPointDialog.layerDef,
selectedElement: this.selectedElement,
selectedLayer: this.selectedLayer,
metaTags: this.userRelatedState.preferencesAsTags,
onClick: (feature: Feature) => {
if (this.mapProperties.zoom.data < Constants.minZoomLevelToAddNewPoint) {
this.map.data.flyTo({
zoom: Constants.minZoomLevelToAddNewPoint,
center: this.mapProperties.lastClickLocation.data,
})
return
}
// We first clear the selection to make sure no weird state is around
this.selectedLayer.setData(undefined)
this.selectedElement.setData(undefined)
this.selectedElement.setData(feature)
this.selectedLayer.setData(this.newPointDialog.layerDef)
},
})
}
/**
* Add the special layers to the map
*/
@ -663,9 +624,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
}
const rangeFLayer: FilteredLayer = this.layerState.filteredLayers.get("range")
const rangeIsDisplayed = rangeFLayer?.isDisplayed
if (
!QueryParameters.wasInitialized(FilteredLayer.queryParameterKey(rangeFLayer.layerDef))
) {

View file

@ -121,9 +121,9 @@ export default class UploadTraceToOsmUI extends LoginToggle {
]).SetClass("flex p-2 rounded-xl border-2 subtle-border items-center"),
new Toggle(
confirmPanel,
new SubtleButton(new SvelteUIElement(Upload), t.title).onClick(() =>
clicked.setData(true)
),
new SubtleButton(new SvelteUIElement(Upload), t.title)
.onClick(() => clicked.setData(true))
.SetClass("w-full"),
clicked
),
uploadFinished

View file

@ -0,0 +1,10 @@
<script lang="ts">
export let properties: Record<string, string>
</script>
<div>
{JSON.stringify(properties)}
{properties?.id ?? "undefined"}
<a href={properties._backend +"/"+ properties?.id}>OSM</a>
</div>

View file

@ -1,8 +1,19 @@
<script lang="ts">
import { SpecialVisualization } from "../SpecialVisualization";
import type { SpecialVisualizationState } from "../SpecialVisualization";
import FavouriteSummary from "./FavouriteSummary.svelte";
/**
* A panel showing all your favourites
*/
export let state: SpecialVisualizationState
export let state: SpecialVisualizationState;
let favourites = state.favourites.allFavourites;
</script>
<div class="flex flex-col">
You marked {$favourites.length} locations as a favourite location.
This list is only visible to you
{#each $favourites as f}
<FavouriteSummary properties={f.properties} />
{/each}
</div>

View file

@ -31,7 +31,9 @@ export class ExportAsGpxViz implements SpecialVisualization {
t.downloadFeatureAsGpx.SetClass("font-bold text-lg"),
t.downloadGpxHelper.SetClass("subtle"),
]).SetClass("flex flex-col")
).onClick(() => {
)
.SetClass("w-full")
.onClick(() => {
console.log("Exporting as GPX!")
const tags = tagSource.data
const title = layer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "gpx_track"

View file

@ -534,6 +534,9 @@ export default class SpecialVisualizations {
feature: Feature,
layer: LayerConfig
): BaseUIElement {
if (!layer.deletion) {
return undefined
}
return new SvelteUIElement(DeleteWizard, {
tags: tagSource,
deleteConfig: layer.deletion,
@ -873,7 +876,8 @@ export default class SpecialVisualizations {
t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"),
t.downloadGeoJsonHelper.SetClass("subtle"),
]).SetClass("flex flex-col")
).onClick(() => {
)
.onClick(() => {
console.log("Exporting as Geojson")
const tags = tagSource.data
const title =
@ -887,6 +891,7 @@ export default class SpecialVisualizations {
}
)
})
.SetClass("w-full")
},
},
{

View file

@ -15,7 +15,7 @@
import type { MapProperties } from "../Models/MapProperties";
import Geosearch from "./BigComponents/Geosearch.svelte";
import Translations from "./i18n/Translations";
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
import { CogIcon, EyeIcon, HeartIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
import Tr from "./Base/Tr.svelte";
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
import FloatOver from "./Base/FloatOver.svelte";
@ -64,6 +64,8 @@
import Community from "../assets/svg/Community.svelte";
import Download from "../assets/svg/Download.svelte";
import Share from "../assets/svg/Share.svelte";
import FavouriteSummary from "./Favourites/FavouriteSummary.svelte";
import Favourites from "./Favourites/Favourites.svelte";
export let state: ThemeViewState;
let layout = state.layout;
@ -493,22 +495,31 @@
</div>
<div class="flex" slot="title2">
<HeartIcon class="h-6 w-6" />
Your favourites
</div>
<div class="flex flex-col" slot="content2">
<h3>Your favourite locations</h3>
<Favourites {state}/>
</div>
<div class="flex" slot="title3">
<Community class="w-6 h-6"/>
<Tr t={Translations.t.communityIndex.title} />
</div>
<div class="m-2" slot="content2">
<div class="m-2" slot="content3">
<CommunityIndexView location={state.mapProperties.location} />
</div>
<div class="flex" slot="title3">
<div class="flex" slot="title4">
<EyeIcon class="w-6" />
<Tr t={Translations.t.privacy.title} />
</div>
<div class="m-2" slot="content3">
<div class="m-2" slot="content4">
<ToSvelte construct={() => new PrivacyPolicy()} />
</div>
<Tr slot="title4" t={Translations.t.advanced.title} />
<div class="m-2 flex flex-col" slot="content4">
<Tr slot="title5" t={Translations.t.advanced.title} />
<div class="m-2 flex flex-col" slot="content5">
<If condition={featureSwitches.featureSwitchEnableLogin}>
<OpenIdEditor mapProperties={state.mapProperties} />
<OpenJosm {state}/>

View file

@ -179,7 +179,21 @@ describe("PrepareTheme", () => {
id: "layer-example",
name: null,
minzoom: 18,
pointRendering: [{ location: ["point"], label: "xyz" }],
pointRendering: [
{
location: ["point"],
label: "xyz",
iconBadges: [
{
if: "_favourite=yes",
then: {
id: "circlewhiteheartred",
render: "circle:white;heart:red",
},
},
],
},
],
lineRendering: [{ width: 1 }],
titleIcons: [],
})