Refactoring: fix #2190, refactor noteCommentElement into svelte
This commit is contained in:
parent
565f1041fc
commit
1a3bb7bb27
3 changed files with 232 additions and 243 deletions
103
src/UI/Popup/Notes/NoteCommentElement.svelte
Normal file
103
src/UI/Popup/Notes/NoteCommentElement.svelte
Normal file
|
@ -0,0 +1,103 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import Note from "../../../assets/svg/Note.svelte"
|
||||
import Resolved from "../../../assets/svg/Resolved.svelte"
|
||||
import Speech_bubble from "../../../assets/svg/Speech_bubble.svelte"
|
||||
import { ImmutableStore, Stores } from "../../../Logic/UIEventSource"
|
||||
import { Utils } from "../../../Utils"
|
||||
import Img from "../../Base/Img"
|
||||
import { SlideShow } from "../../Image/SlideShow"
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
|
||||
export let comment: {
|
||||
date: string
|
||||
uid: number
|
||||
user: string
|
||||
user_url: string
|
||||
action: "closed" | "opened" | "reopened" | "commented"
|
||||
text: string
|
||||
html: string
|
||||
highlighted: boolean
|
||||
}
|
||||
export let state: SpecialVisualizationState = undefined
|
||||
|
||||
const t = Translations.t.notes
|
||||
|
||||
// Info about the user who made the comment
|
||||
let userinfo = Stores.FromPromise(
|
||||
Utils.downloadJsonCached<{ user: { img: { href: string } } }>(
|
||||
"https://api.openstreetmap.org/api/0.6/user/" + comment.uid,
|
||||
24 * 60 * 60 * 1000,
|
||||
),
|
||||
)
|
||||
|
||||
const htmlElement = document.createElement("div")
|
||||
htmlElement.innerHTML = Utils.purify(comment.html)
|
||||
let images: string[] = Array.from(htmlElement.getElementsByTagName("a"))
|
||||
.map((link) => link.href)
|
||||
.filter((link) => {
|
||||
link = link.toLowerCase()
|
||||
const lastDotIndex = link.lastIndexOf(".")
|
||||
const extension = link.substring(lastDotIndex + 1, link.length)
|
||||
return Utils.imageExtensions.has(extension)
|
||||
})
|
||||
.filter((link) => !link.startsWith("https://wiki.openstreetmap.org/wiki/File:"))
|
||||
|
||||
|
||||
let imgStore = new ImmutableStore(
|
||||
images.map((i) =>
|
||||
new Img(i).SetClass("w-full block cursor-pointer")
|
||||
.onClick(() =>
|
||||
state?.previewedImage?.setData(
|
||||
<any>{
|
||||
url_hd: i,
|
||||
url: i,
|
||||
}),
|
||||
)))
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col py-2 my-2 border-gray-500 border-b" class:border-interactive={comment.highlighted}>
|
||||
|
||||
<div class="flex">
|
||||
|
||||
<!-- Action icon, e.g. 'created', 'commented', 'closed' -->
|
||||
{#if comment.action === "opened" || comment.action === "reopened"}
|
||||
<Note class="shrink-0 mr-4 w-6" />
|
||||
{:else if comment.action === "closed"}
|
||||
<Resolved class="shrink-0 mr-4 w-6" />
|
||||
{:else}
|
||||
<Speech_bubble class="shrink-0 mr-4 w-6" />
|
||||
{/if}
|
||||
<div class="flex flex-col gap-y-2">
|
||||
{@html comment.html}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if images.length > 0}
|
||||
<ToSvelte
|
||||
construct={() => new SlideShow(imgStore) .SetClass("mb-1").SetStyle("min-width: 50px; background: grey;")} />
|
||||
{/if}
|
||||
|
||||
|
||||
<div class="flex justify-end items-center subtle pt-4 pb-2">
|
||||
<!-- commenter info -->
|
||||
|
||||
{#if $userinfo?.user?.img?.href}
|
||||
<img alt="avatar" aria-hidden="true" src={$userinfo?.user?.img?.href} class="rounded-full w-8 h-8 mr-4" />
|
||||
{/if}
|
||||
|
||||
<span class="mr-2">
|
||||
{#if comment.user === undefined}
|
||||
<Tr t={t.anonymous} />
|
||||
{:else}
|
||||
<a href={comment.user_url} target="_blank">{comment.user}</a>
|
||||
{/if}
|
||||
{comment.date}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,117 +1,7 @@
|
|||
import Combine from "../../Base/Combine"
|
||||
import BaseUIElement from "../../BaseUIElement"
|
||||
import Link from "../../Base/Link"
|
||||
import { FixedUiElement } from "../../Base/FixedUiElement"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import { Utils } from "../../../Utils"
|
||||
import Img from "../../Base/Img"
|
||||
import { SlideShow } from "../../Image/SlideShow"
|
||||
import { Store, Stores, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import { VariableUiElement } from "../../Base/VariableUIElement"
|
||||
import { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import SvelteUIElement from "../../Base/SvelteUIElement"
|
||||
import Note from "../../../assets/svg/Note.svelte"
|
||||
import Resolved from "../../../assets/svg/Resolved.svelte"
|
||||
import Speech_bubble from "../../../assets/svg/Speech_bubble.svelte"
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
|
||||
export default class NoteCommentElement extends Combine {
|
||||
constructor(
|
||||
comment: {
|
||||
date: string
|
||||
uid: number
|
||||
user: string
|
||||
user_url: string
|
||||
action: "closed" | "opened" | "reopened" | "commented"
|
||||
text: string
|
||||
html: string
|
||||
highlighted: boolean
|
||||
},
|
||||
state?: SpecialVisualizationState,
|
||||
index?: number,
|
||||
totalNumberOfComments?: number
|
||||
) {
|
||||
const t = Translations.t.notes
|
||||
export default class NoteCommentElement {
|
||||
|
||||
let actionIcon: BaseUIElement
|
||||
if (comment.action === "opened" || comment.action === "reopened") {
|
||||
actionIcon = new SvelteUIElement(Note)
|
||||
} else if (comment.action === "closed") {
|
||||
actionIcon = new SvelteUIElement(Resolved)
|
||||
} else {
|
||||
actionIcon = new SvelteUIElement(Speech_bubble)
|
||||
}
|
||||
|
||||
let user: BaseUIElement
|
||||
if (comment.user === undefined) {
|
||||
user = t.anonymous
|
||||
} else {
|
||||
user = new Link(comment.user, comment.user_url ?? "", true)
|
||||
}
|
||||
|
||||
const userinfo = Stores.FromPromise(
|
||||
Utils.downloadJsonCached<{ user: { img: { href: string } } }>(
|
||||
"https://api.openstreetmap.org/api/0.6/user/" + comment.uid,
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
)
|
||||
const userImg = new VariableUiElement(
|
||||
userinfo.map((userinfo) => {
|
||||
const href = userinfo?.user?.img?.href
|
||||
if (href !== undefined) {
|
||||
return new Img(href).SetClass("rounded-full w-8 h-8 mr-4")
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
)
|
||||
|
||||
const htmlElement = document.createElement("div")
|
||||
htmlElement.innerHTML = Utils.purify(comment.html)
|
||||
const images = Array.from(htmlElement.getElementsByTagName("a"))
|
||||
.map((link) => link.href)
|
||||
.filter((link) => {
|
||||
link = link.toLowerCase()
|
||||
const lastDotIndex = link.lastIndexOf(".")
|
||||
const extension = link.substring(lastDotIndex + 1, link.length)
|
||||
return Utils.imageExtensions.has(extension)
|
||||
})
|
||||
.filter((link) => !link.startsWith("https://wiki.openstreetmap.org/wiki/File:"))
|
||||
let imagesEl: BaseUIElement = undefined
|
||||
if (images.length > 0) {
|
||||
const imageEls = images.map((i) =>
|
||||
new Img(i)
|
||||
.SetClass("w-full block cursor-pointer")
|
||||
.onClick(() =>
|
||||
state?.previewedImage?.setData(<any>{
|
||||
url_hd: i,
|
||||
url: i,
|
||||
})
|
||||
)
|
||||
.SetStyle("min-width: 50px; background: grey;")
|
||||
)
|
||||
imagesEl = new SlideShow(new UIEventSource<BaseUIElement[]>(imageEls)).SetClass("mb-1")
|
||||
}
|
||||
|
||||
super([
|
||||
new Combine([
|
||||
actionIcon.SetClass("mr-4 w-6").SetStyle("flex-shrink: 0"),
|
||||
new FixedUiElement(comment.html).SetClass("flex flex-col").SetStyle("margin: 0"),
|
||||
]).SetClass("flex"),
|
||||
imagesEl,
|
||||
new Combine([userImg, user.SetClass("mr-2"), comment.date]).SetClass(
|
||||
"flex justify-end items-center subtle"
|
||||
),
|
||||
])
|
||||
this.SetClass("flex flex-col pb-2 mb-2 border-gray-500 border-b")
|
||||
if (comment.highlighted) {
|
||||
this.SetClass("focus")
|
||||
if (index + 2 === totalNumberOfComments) {
|
||||
console.log("Scrolling into view")
|
||||
requestAnimationFrame(() => {
|
||||
this.ScrollIntoView()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the comment to the _visualisation_ of the given note; doesn't _actually_ upload
|
||||
|
|
|
@ -2,11 +2,7 @@ import Combine from "./Base/Combine"
|
|||
import { FixedUiElement } from "./Base/FixedUiElement"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import Title from "./Base/Title"
|
||||
import {
|
||||
RenderingSpecification,
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
} from "./SpecialVisualization"
|
||||
import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization"
|
||||
import { HistogramViz } from "./Popup/HistogramViz"
|
||||
import MinimapViz from "./Popup/MinimapViz.svelte"
|
||||
import { ShareLinkViz } from "./Popup/ShareLinkViz"
|
||||
|
@ -27,7 +23,6 @@ import { Translation } from "./i18n/Translation"
|
|||
import Translations from "./i18n/Translations"
|
||||
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
|
||||
import { SubtleButton } from "./Base/SubtleButton"
|
||||
import NoteCommentElement from "./Popup/Notes/NoteCommentElement"
|
||||
import List from "./Base/List"
|
||||
import StatisticsPanel from "./BigComponents/StatisticsPanel"
|
||||
import AutoApplyButton from "./Popup/AutoApplyButton"
|
||||
|
@ -101,6 +96,7 @@ import QrCode from "./Popup/QrCode.svelte"
|
|||
import ClearCaches from "./Popup/ClearCaches.svelte"
|
||||
import GroupedView from "./Popup/GroupedView.svelte"
|
||||
import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||
import NoteCommentElement from "./Popup/Notes/NoteCommentElement.svelte"
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -126,7 +122,7 @@ class NearbyImageVis implements SpecialVisualization {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): SvelteUIElement {
|
||||
const isOpen = args[0] === "open"
|
||||
const readonly = args[1] === "readonly"
|
||||
|
@ -193,7 +189,7 @@ class StealViz implements SpecialVisualization {
|
|||
selectedElement: otherFeature,
|
||||
state,
|
||||
layer,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
if (elements.length === 1) {
|
||||
|
@ -201,8 +197,8 @@ class StealViz implements SpecialVisualization {
|
|||
}
|
||||
return new Combine(elements).SetClass("flex flex-col")
|
||||
},
|
||||
[state.indexedFeatures.featuresById]
|
||||
)
|
||||
[state.indexedFeatures.featuresById],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -254,11 +250,11 @@ class CloseNoteViz implements SpecialVisualization {
|
|||
public constr(
|
||||
state: SpecialVisualizationState,
|
||||
tags: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
): SvelteUIElement {
|
||||
const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs(
|
||||
this.args,
|
||||
args
|
||||
args,
|
||||
)
|
||||
|
||||
return new SvelteUIElement(CloseNoteButton, {
|
||||
|
@ -299,7 +295,7 @@ export class QuestionViz implements SpecialVisualization {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): SvelteUIElement {
|
||||
const labels = args[0]
|
||||
?.split(";")
|
||||
|
@ -331,7 +327,7 @@ export default class SpecialVisualizations {
|
|||
for (const specialVisualization of SpecialVisualizations.specialVisualizations) {
|
||||
SpecialVisualizations.specialVisualisationsDict.set(
|
||||
specialVisualization.funcName,
|
||||
specialVisualization
|
||||
specialVisualization,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -358,7 +354,7 @@ export default class SpecialVisualizations {
|
|||
defaultArg = "_empty string_"
|
||||
}
|
||||
return [arg.name, defaultArg, arg.doc]
|
||||
})
|
||||
}),
|
||||
)
|
||||
: undefined,
|
||||
"#### Example usage of " + viz.funcName,
|
||||
|
@ -368,18 +364,18 @@ export default class SpecialVisualizations {
|
|||
|
||||
public static constructSpecification(
|
||||
template: string,
|
||||
extraMappings: SpecialVisualization[] = []
|
||||
extraMappings: SpecialVisualization[] = [],
|
||||
): RenderingSpecification[] {
|
||||
return SpecialVisualisationUtils.constructSpecification(
|
||||
template,
|
||||
SpecialVisualizations.specialVisualisationsDict,
|
||||
extraMappings
|
||||
extraMappings,
|
||||
)
|
||||
}
|
||||
|
||||
public static HelpMessage(): string {
|
||||
const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) =>
|
||||
SpecialVisualizations.DocumentationFor(viz)
|
||||
SpecialVisualizations.DocumentationFor(viz),
|
||||
)
|
||||
|
||||
const firstPart = new Combine([
|
||||
|
@ -412,10 +408,10 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
},
|
||||
null,
|
||||
" "
|
||||
)
|
||||
" ",
|
||||
),
|
||||
).SetClass("code"),
|
||||
'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)',
|
||||
"In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)",
|
||||
])
|
||||
.SetClass("flex flex-col")
|
||||
.AsMarkdown()
|
||||
|
@ -453,10 +449,10 @@ export default class SpecialVisualizations {
|
|||
assignTo: state.userRelatedState.language,
|
||||
availableLanguages: languages,
|
||||
preferredLanguages: state.osmConnection.userDetails.map(
|
||||
(ud) => ud.languages
|
||||
(ud) => ud.languages,
|
||||
),
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -495,7 +491,7 @@ export default class SpecialVisualizations {
|
|||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature
|
||||
feature: Feature,
|
||||
): SvelteUIElement {
|
||||
return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource })
|
||||
},
|
||||
|
@ -507,7 +503,7 @@ export default class SpecialVisualizations {
|
|||
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
): BaseUIElement {
|
||||
return new VariableUiElement(
|
||||
tagSource
|
||||
|
@ -517,7 +513,7 @@ export default class SpecialVisualizations {
|
|||
return new SvelteUIElement(SplitRoadWizard, { id, state })
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -531,7 +527,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
if (feature.geometry.type !== "Point") {
|
||||
return undefined
|
||||
|
@ -554,7 +550,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
if (!layer.deletion) {
|
||||
return undefined
|
||||
|
@ -582,7 +578,7 @@ export default class SpecialVisualizations {
|
|||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature
|
||||
feature: Feature,
|
||||
): BaseUIElement {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
return new SvelteUIElement(CreateNewNote, {
|
||||
|
@ -645,7 +641,7 @@ export default class SpecialVisualizations {
|
|||
.map((tags) => tags[args[0]])
|
||||
.map((wikidata) => {
|
||||
wikidata = Utils.NoEmpty(
|
||||
wikidata?.split(";")?.map((wd) => wd.trim()) ?? []
|
||||
wikidata?.split(";")?.map((wd) => wd.trim()) ?? [],
|
||||
)[0]
|
||||
const entry = Wikidata.LoadWikidataEntry(wikidata)
|
||||
return new VariableUiElement(
|
||||
|
@ -655,9 +651,9 @@ export default class SpecialVisualizations {
|
|||
}
|
||||
const response = <WikidataResponse>e["success"]
|
||||
return Translation.fromMap(response.labels)
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
new MapillaryLinkVis(),
|
||||
|
@ -671,7 +667,7 @@ export default class SpecialVisualizations {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
_,
|
||||
__,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
) => new SvelteUIElement(AllTagsPanel, { tags, layer }),
|
||||
},
|
||||
{
|
||||
|
@ -693,7 +689,7 @@ export default class SpecialVisualizations {
|
|||
return new ImageCarousel(
|
||||
AllImageProviders.LoadImagesFor(tags, imagePrefixes),
|
||||
tags,
|
||||
state
|
||||
state,
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -750,7 +746,7 @@ export default class SpecialVisualizations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state.featureSwitchIsTesting,
|
||||
)
|
||||
return new SvelteUIElement(StarsBarIcon, {
|
||||
score: reviews.average,
|
||||
|
@ -784,7 +780,7 @@ export default class SpecialVisualizations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state.featureSwitchIsTesting,
|
||||
)
|
||||
return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer })
|
||||
},
|
||||
|
@ -815,7 +811,7 @@ export default class SpecialVisualizations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state.featureSwitchIsTesting,
|
||||
)
|
||||
return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer })
|
||||
},
|
||||
|
@ -841,7 +837,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new Combine([
|
||||
SpecialVisualizations.specialVisualisationsDict["create_review"].constr(
|
||||
|
@ -849,14 +845,14 @@ export default class SpecialVisualizations {
|
|||
tagSource,
|
||||
args,
|
||||
feature,
|
||||
layer
|
||||
layer,
|
||||
),
|
||||
SpecialVisualizations.specialVisualisationsDict["list_reviews"].constr(
|
||||
state,
|
||||
tagSource,
|
||||
args,
|
||||
feature,
|
||||
layer
|
||||
layer,
|
||||
),
|
||||
])
|
||||
},
|
||||
|
@ -874,7 +870,7 @@ export default class SpecialVisualizations {
|
|||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
_: UIEventSource<Record<string, string>>,
|
||||
argument: string[]
|
||||
argument: string[],
|
||||
): BaseUIElement {
|
||||
const [text] = argument
|
||||
return new SvelteUIElement(ImportReviewIdentity, { state, text })
|
||||
|
@ -931,7 +927,7 @@ export default class SpecialVisualizations {
|
|||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tags: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
): SvelteUIElement {
|
||||
const keyToUse = args[0]
|
||||
const prefix = args[1]
|
||||
|
@ -968,17 +964,17 @@ export default class SpecialVisualizations {
|
|||
return undefined
|
||||
}
|
||||
const allUnits: Unit[] = [].concat(
|
||||
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? [])
|
||||
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []),
|
||||
)
|
||||
const unit = allUnits.filter((unit) =>
|
||||
unit.isApplicableToKey(key)
|
||||
unit.isApplicableToKey(key),
|
||||
)[0]
|
||||
if (unit === undefined) {
|
||||
return value
|
||||
}
|
||||
const getCountry = () => tagSource.data._country
|
||||
return unit.asHumanLongValue(value, getCountry)
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -995,7 +991,7 @@ export default class SpecialVisualizations {
|
|||
new Combine([
|
||||
t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"),
|
||||
t.downloadGeoJsonHelper.SetClass("subtle"),
|
||||
]).SetClass("flex flex-col")
|
||||
]).SetClass("flex flex-col"),
|
||||
)
|
||||
.onClick(() => {
|
||||
console.log("Exporting as Geojson")
|
||||
|
@ -1008,7 +1004,7 @@ export default class SpecialVisualizations {
|
|||
title + "_mapcomplete_export.geojson",
|
||||
{
|
||||
mimetype: "application/vnd.geo+json",
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.SetClass("w-full")
|
||||
|
@ -1044,7 +1040,7 @@ export default class SpecialVisualizations {
|
|||
constr: (state) => {
|
||||
return new SubtleButton(
|
||||
new SvelteUIElement(Trash).SetClass("h-6"),
|
||||
Translations.t.general.removeLocationHistory
|
||||
Translations.t.general.removeLocationHistory,
|
||||
).onClick(() => {
|
||||
state.historicalUserLocations.features.setData([])
|
||||
state.selectedElement.setData(undefined)
|
||||
|
@ -1081,11 +1077,11 @@ export default class SpecialVisualizations {
|
|||
comments
|
||||
.filter((c) => c.text !== "")
|
||||
.map(
|
||||
(c, i) =>
|
||||
new NoteCommentElement(c, state, i, comments.length)
|
||||
)
|
||||
(comment) =>
|
||||
new SvelteUIElement(NoteCommentElement, { comment, state }),
|
||||
),
|
||||
).SetClass("flex flex-col")
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -1118,7 +1114,7 @@ export default class SpecialVisualizations {
|
|||
tagsSource: UIEventSource<Record<string, string>>,
|
||||
_: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
) =>
|
||||
new VariableUiElement(
|
||||
tagsSource.map((tags) => {
|
||||
|
@ -1138,7 +1134,7 @@ export default class SpecialVisualizations {
|
|||
})
|
||||
.SetClass("px-1")
|
||||
.setSpan()
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -1154,8 +1150,8 @@ export default class SpecialVisualizations {
|
|||
const challenge = Stores.FromPromise(
|
||||
Utils.downloadJsonCached<MaprouletteTask>(
|
||||
`${Maproulette.defaultEndpoint}/challenge/${parentId}`,
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
24 * 60 * 60 * 1000,
|
||||
),
|
||||
)
|
||||
|
||||
return new VariableUiElement(
|
||||
|
@ -1180,7 +1176,7 @@ export default class SpecialVisualizations {
|
|||
} else {
|
||||
return [title, new List(listItems)]
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.",
|
||||
|
@ -1194,15 +1190,15 @@ export default class SpecialVisualizations {
|
|||
"\n" +
|
||||
"```json\n" +
|
||||
"{\n" +
|
||||
' "id": "mark_duplicate",\n' +
|
||||
' "render": {\n' +
|
||||
' "special": {\n' +
|
||||
' "type": "maproulette_set_status",\n' +
|
||||
' "message": {\n' +
|
||||
' "en": "Mark as not found or false positive"\n' +
|
||||
" \"id\": \"mark_duplicate\",\n" +
|
||||
" \"render\": {\n" +
|
||||
" \"special\": {\n" +
|
||||
" \"type\": \"maproulette_set_status\",\n" +
|
||||
" \"message\": {\n" +
|
||||
" \"en\": \"Mark as not found or false positive\"\n" +
|
||||
" },\n" +
|
||||
' "status": "2",\n' +
|
||||
' "image": "close"\n' +
|
||||
" \"status\": \"2\",\n" +
|
||||
" \"image\": \"close\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
|
@ -1278,7 +1274,7 @@ export default class SpecialVisualizations {
|
|||
(l) =>
|
||||
l.name !== null &&
|
||||
l.title &&
|
||||
state.perLayer.get(l.id) !== undefined
|
||||
state.perLayer.get(l.id) !== undefined,
|
||||
)
|
||||
.map(
|
||||
(l) => {
|
||||
|
@ -1288,8 +1284,8 @@ export default class SpecialVisualizations {
|
|||
const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox)
|
||||
return new StatisticsPanel(fsBboxed)
|
||||
},
|
||||
[state.mapProperties.bounds]
|
||||
)
|
||||
[state.mapProperties.bounds],
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1359,7 +1355,7 @@ export default class SpecialVisualizations {
|
|||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
): SvelteUIElement {
|
||||
let [text, href, classnames, download, ariaLabel, icon] = args
|
||||
if (download === "") {
|
||||
|
@ -1397,7 +1393,7 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
},
|
||||
null,
|
||||
" "
|
||||
" ",
|
||||
) +
|
||||
"\n```",
|
||||
args: [
|
||||
|
@ -1421,7 +1417,7 @@ export default class SpecialVisualizations {
|
|||
featureTags: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
) {
|
||||
const [key, tr, classesRaw] = args
|
||||
let classes = classesRaw ?? ""
|
||||
|
@ -1439,7 +1435,7 @@ export default class SpecialVisualizations {
|
|||
"Could not create a special visualization for multi(",
|
||||
args.join(", ") + ")",
|
||||
"no properties found for object",
|
||||
feature.properties.id
|
||||
feature.properties.id,
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
|
@ -1455,7 +1451,7 @@ export default class SpecialVisualizations {
|
|||
elements.push(subsTr)
|
||||
}
|
||||
return elements
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1475,7 +1471,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new VariableUiElement(
|
||||
tagSource.map((tags) => {
|
||||
|
@ -1487,7 +1483,7 @@ export default class SpecialVisualizations {
|
|||
console.error("Cannot create a translation for", v, "due to", e)
|
||||
return JSON.stringify(v)
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1507,7 +1503,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const key = argument[0]
|
||||
const validator = new FediverseValidator()
|
||||
|
@ -1517,7 +1513,7 @@ export default class SpecialVisualizations {
|
|||
.map((fediAccount) => {
|
||||
fediAccount = validator.reformat(fediAccount)
|
||||
const [_, username, host] = fediAccount.match(
|
||||
FediverseValidator.usernameAtServer
|
||||
FediverseValidator.usernameAtServer,
|
||||
)
|
||||
|
||||
const normalLink = new SvelteUIElement(Link, {
|
||||
|
@ -1532,7 +1528,7 @@ export default class SpecialVisualizations {
|
|||
]
|
||||
console.log(
|
||||
"LoggedinContributorMastodon",
|
||||
loggedInContributorMastodon
|
||||
loggedInContributorMastodon,
|
||||
)
|
||||
if (!loggedInContributorMastodon) {
|
||||
return normalLink
|
||||
|
@ -1548,7 +1544,7 @@ export default class SpecialVisualizations {
|
|||
newTab: true,
|
||||
}).SetClass("button"),
|
||||
])
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1568,7 +1564,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new FixedUiElement("{" + args[0] + "}")
|
||||
},
|
||||
|
@ -1589,7 +1585,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const key = argument[0] ?? "value"
|
||||
return new VariableUiElement(
|
||||
|
@ -1609,10 +1605,10 @@ export default class SpecialVisualizations {
|
|||
"Could not parse this tag: " +
|
||||
JSON.stringify(value) +
|
||||
" due to " +
|
||||
e
|
||||
e,
|
||||
).SetClass("alert")
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1633,7 +1629,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const giggityUrl = argument[0]
|
||||
return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl })
|
||||
|
@ -1649,12 +1645,12 @@ export default class SpecialVisualizations {
|
|||
_: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const tags = (<ThemeViewState>(
|
||||
state
|
||||
)).geolocation.currentUserLocation.features.map(
|
||||
(features) => features[0]?.properties
|
||||
(features) => features[0]?.properties,
|
||||
)
|
||||
return new Combine([
|
||||
new SvelteUIElement(OrientationDebugPanel, {}),
|
||||
|
@ -1676,7 +1672,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new SvelteUIElement(MarkAsFavourite, {
|
||||
tags: tagSource,
|
||||
|
@ -1696,7 +1692,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new SvelteUIElement(MarkAsFavouriteMini, {
|
||||
tags: tagSource,
|
||||
|
@ -1716,7 +1712,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new SvelteUIElement(DirectionIndicator, { state, feature })
|
||||
},
|
||||
|
@ -1729,7 +1725,7 @@ export default class SpecialVisualizations {
|
|||
state: SpecialVisualizationState,
|
||||
tags: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature
|
||||
feature: Feature,
|
||||
): SvelteUIElement {
|
||||
return new SvelteUIElement(QrCode, { state, tags, feature })
|
||||
},
|
||||
|
@ -1748,7 +1744,7 @@ export default class SpecialVisualizations {
|
|||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[]
|
||||
args: string[],
|
||||
): BaseUIElement {
|
||||
const key = args[0] === "" ? "_direction:centerpoint" : args[0]
|
||||
return new VariableUiElement(
|
||||
|
@ -1759,11 +1755,11 @@ export default class SpecialVisualizations {
|
|||
})
|
||||
.mapD((value) => {
|
||||
const dir = GeoOperations.bearingToHuman(
|
||||
GeoOperations.parseBearing(value)
|
||||
GeoOperations.parseBearing(value),
|
||||
)
|
||||
console.log("Human dir", dir)
|
||||
return Translations.t.general.visualFeedback.directionsAbsolute[dir]
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1793,7 +1789,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const url = args[0]
|
||||
const readonly = args[3] === "yes"
|
||||
|
@ -1819,12 +1815,12 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
args: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new Toggle(
|
||||
undefined,
|
||||
new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }),
|
||||
state.osmConnection.isLoggedIn
|
||||
state.osmConnection.isLoggedIn,
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1862,7 +1858,7 @@ export default class SpecialVisualizations {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const key = argument[0] ?? "website"
|
||||
const useProxy = argument[1] !== "no"
|
||||
|
@ -1889,11 +1885,11 @@ export default class SpecialVisualizations {
|
|||
const features =
|
||||
await LinkedDataLoader.fetchVeloparkEntry(
|
||||
url,
|
||||
loadAll
|
||||
loadAll,
|
||||
)
|
||||
const feature =
|
||||
features.find(
|
||||
(f) => f.properties["ref:velopark"] === url
|
||||
(f) => f.properties["ref:velopark"] === url,
|
||||
) ?? features[0]
|
||||
const properties = feature.properties
|
||||
properties["ref:velopark"] = url
|
||||
|
@ -1903,7 +1899,7 @@ export default class SpecialVisualizations {
|
|||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
})()
|
||||
})(),
|
||||
)
|
||||
}
|
||||
return Stores.FromPromiseWithErr(
|
||||
|
@ -1912,27 +1908,27 @@ export default class SpecialVisualizations {
|
|||
return await LinkedDataLoader.fetchJsonLd(
|
||||
url,
|
||||
{ country },
|
||||
useProxy ? "proxy" : "fetch-lod"
|
||||
useProxy ? "proxy" : "fetch-lod",
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(
|
||||
"Could not get with proxy/download LOD, attempting to download directly. Error for ",
|
||||
url,
|
||||
"is",
|
||||
e
|
||||
e,
|
||||
)
|
||||
return await LinkedDataLoader.fetchJsonLd(
|
||||
url,
|
||||
{ country },
|
||||
"fetch-raw"
|
||||
"fetch-raw",
|
||||
)
|
||||
}
|
||||
})()
|
||||
})(),
|
||||
)
|
||||
})
|
||||
|
||||
externalData.addCallbackAndRunD((lod) =>
|
||||
console.log("linked_data_from_website received the following data:", lod)
|
||||
console.log("linked_data_from_website received the following data:", lod),
|
||||
)
|
||||
|
||||
return new Toggle(
|
||||
|
@ -1947,7 +1943,7 @@ export default class SpecialVisualizations {
|
|||
collapsed: isClosed,
|
||||
}),
|
||||
undefined,
|
||||
url.map((url) => !!url)
|
||||
url.map((url) => !!url),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
@ -1967,7 +1963,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const text = argument[0]
|
||||
const cssClasses = argument[1]
|
||||
|
@ -1989,7 +1985,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
const translation = tagSource.map((tags) => {
|
||||
const layer = state.layout.getMatchingLayer(tags)
|
||||
|
@ -2007,7 +2003,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): BaseUIElement {
|
||||
return new SvelteUIElement(PendingChangesIndicator, { state, compact: false })
|
||||
},
|
||||
|
@ -2027,7 +2023,7 @@ export default class SpecialVisualizations {
|
|||
tagSource: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
feature: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): SvelteUIElement {
|
||||
return new SvelteUIElement<any, any, any>(ClearCaches, {
|
||||
msg: argument[0] ?? "Clear local caches",
|
||||
|
@ -2052,7 +2048,7 @@ export default class SpecialVisualizations {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
selectedElement: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): SvelteUIElement {
|
||||
const [header, labelsStr] = argument
|
||||
const labels = labelsStr.split(";").map((x) => x.trim())
|
||||
|
@ -2075,7 +2071,7 @@ export default class SpecialVisualizations {
|
|||
tags: UIEventSource<Record<string, string>>,
|
||||
argument: string[],
|
||||
selectedElement: Feature,
|
||||
layer: LayerConfig
|
||||
layer: LayerConfig,
|
||||
): SvelteUIElement {
|
||||
const t = Translations.t.preset_type
|
||||
const question: QuestionableTagRenderingConfigJson = {
|
||||
|
@ -2101,22 +2097,22 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
},
|
||||
{
|
||||
funcName:"clear_all",
|
||||
funcName: "clear_all",
|
||||
docs: "Clears all user preferences",
|
||||
needsUrls: [],
|
||||
args: [
|
||||
{
|
||||
name: "text",
|
||||
doc: "Text to show on the button"
|
||||
}
|
||||
doc: "Text to show on the button",
|
||||
},
|
||||
],
|
||||
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
|
||||
const text = argument[0]
|
||||
return new SubtleButton(undefined, text).onClick(() => {
|
||||
state.osmConnection.preferencesHandler.ClearPreferences()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
||||
|
@ -2131,7 +2127,7 @@ export default class SpecialVisualizations {
|
|||
"Invalid special visualisation found: funcName is undefined or doesn't match " +
|
||||
regex +
|
||||
invalid.map((sp) => sp.i).join(", ") +
|
||||
'. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL'
|
||||
". Did you perhaps type \n funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n funcName = \"funcName\" // value definition uses EQUAL"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue