forked from MapComplete/MapComplete
chore: automated housekeeping...
This commit is contained in:
parent
8109c13b38
commit
297bb1c498
185 changed files with 2826 additions and 5874 deletions
|
@ -14,7 +14,6 @@ import {
|
|||
} from "geojson"
|
||||
import { Tiles } from "../Models/TileRange"
|
||||
import { Utils } from "../Utils"
|
||||
|
||||
;("use strict")
|
||||
|
||||
export class GeoOperations {
|
||||
|
|
|
@ -9,13 +9,15 @@ import Constants from "../../Models/Constants"
|
|||
import Locale from "../../UI/i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
|
||||
export class ThemeSearchIndex {
|
||||
|
||||
private readonly themeIndex: Fuse<MinimalThemeInformation>
|
||||
private readonly layerIndex: Fuse<{ id: string, description }>
|
||||
private readonly layerIndex: Fuse<{ id: string; description }>
|
||||
|
||||
constructor(language: string, themesToSearch?: MinimalThemeInformation[], layersToIgnore: string[] = []) {
|
||||
constructor(
|
||||
language: string,
|
||||
themesToSearch?: MinimalThemeInformation[],
|
||||
layersToIgnore: string[] = []
|
||||
) {
|
||||
const themes = Utils.NoNull(themesToSearch ?? ThemeSearch.officialThemes?.themes)
|
||||
if (!themes) {
|
||||
throw "No themes loaded. Did generate:layeroverview fail?"
|
||||
|
@ -27,14 +29,17 @@ export class ThemeSearchIndex {
|
|||
{ name: "id", weight: 2 },
|
||||
"title." + language,
|
||||
"keywords." + language,
|
||||
"shortDescription." + language
|
||||
]
|
||||
"shortDescription." + language,
|
||||
],
|
||||
}
|
||||
|
||||
this.themeIndex = new Fuse(themes.filter(th => th?.id !== "personal"), fuseOptions)
|
||||
this.themeIndex = new Fuse(
|
||||
themes.filter((th) => th?.id !== "personal"),
|
||||
fuseOptions
|
||||
)
|
||||
|
||||
const toIgnore = new Set(layersToIgnore)
|
||||
const layersAsList: { id: string, description: Record<string, string[]> }[] = []
|
||||
const layersAsList: { id: string; description: Record<string, string[]> }[] = []
|
||||
for (const id in ThemeSearch.officialThemes.layers) {
|
||||
if (Constants.isPriviliged(id)) {
|
||||
continue
|
||||
|
@ -50,7 +55,7 @@ export class ThemeSearchIndex {
|
|||
minMatchCharLength: 3,
|
||||
ignoreLocation: true,
|
||||
threshold: 0.02,
|
||||
keys: ["id", "description." + language]
|
||||
keys: ["id", "description." + language],
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -61,7 +66,7 @@ export class ThemeSearchIndex {
|
|||
if (limit) {
|
||||
result = result.slice(0, limit)
|
||||
}
|
||||
return result.map(e => ThemeSearch.officialThemesById.get(e[0]))
|
||||
return result.map((e) => ThemeSearch.officialThemesById.get(e[0]))
|
||||
}
|
||||
|
||||
public searchWithScores(text: string): Map<string, number> {
|
||||
|
@ -76,20 +81,22 @@ export class ThemeSearchIndex {
|
|||
for (const layer of layerResults) {
|
||||
const matchingThemes = ThemeSearch.layersToThemes.get(layer.item.id)
|
||||
const score = layer.score
|
||||
matchingThemes?.forEach(th => {
|
||||
matchingThemes?.forEach((th) => {
|
||||
const previous = result.get(th.id) ?? 10000
|
||||
result.set(th.id, Math.min(previous, score * 5))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a search index containing all public and visited themes, but ignoring the layers loaded by the current theme
|
||||
*/
|
||||
public static fromState(state: { osmConnection: OsmConnection; theme: ThemeConfig }): Store<ThemeSearchIndex> {
|
||||
public static fromState(state: {
|
||||
osmConnection: OsmConnection
|
||||
theme: ThemeConfig
|
||||
}): Store<ThemeSearchIndex> {
|
||||
const layersToIgnore = state.theme.layers.filter((l) => l.isNormal()).map((l) => l.id)
|
||||
const knownHidden: Store<string[]> = UserRelatedState.initDiscoveredHiddenThemes(
|
||||
state.osmConnection
|
||||
|
@ -97,8 +104,11 @@ export class ThemeSearchIndex {
|
|||
const otherThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(
|
||||
(th) => th.id !== state.theme.id
|
||||
)
|
||||
return Locale.language.map(language => {
|
||||
const themes = otherThemes.concat(...knownHidden.data.map(id => ThemeSearch.officialThemesById.get(id)))
|
||||
return Locale.language.map(
|
||||
(language) => {
|
||||
const themes = otherThemes.concat(
|
||||
...knownHidden.data.map((id) => ThemeSearch.officialThemesById.get(id))
|
||||
)
|
||||
return new ThemeSearchIndex(language, themes, layersToIgnore)
|
||||
},
|
||||
[knownHidden]
|
||||
|
@ -116,9 +126,8 @@ export default class ThemeSearch {
|
|||
MinimalThemeInformation
|
||||
>()
|
||||
|
||||
|
||||
/*
|
||||
* For every layer id, states which themes use the layer
|
||||
* For every layer id, states which themes use the layer
|
||||
*/
|
||||
public static readonly layersToThemes: Map<string, MinimalThemeInformation[]> = new Map()
|
||||
static {
|
||||
|
@ -170,6 +179,4 @@ export default class ThemeSearch {
|
|||
|
||||
return `${linkPrefix}`
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -68,7 +68,10 @@ export default class SearchState {
|
|||
)
|
||||
|
||||
const themeSearch = ThemeSearchIndex.fromState(state)
|
||||
this.themeSuggestions = this.searchTerm.mapD((query) => themeSearch.data.search(query, 3), [themeSearch])
|
||||
this.themeSuggestions = this.searchTerm.mapD(
|
||||
(query) => themeSearch.data.search(query, 3),
|
||||
[themeSearch]
|
||||
)
|
||||
|
||||
const layerSearch = new LayerSearch(state.theme)
|
||||
this.layerSuggestions = this.searchTerm.mapD((query) => layerSearch.search(query, 5))
|
||||
|
|
|
@ -1,14 +1,42 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,10 @@ export default class SubstitutingTag extends TagsFilter {
|
|||
|
||||
asChange(properties: Readonly<Record<string, string>>): { k: string; v: string }[] {
|
||||
if (this._invert) {
|
||||
throw "An inverted substituting tag can not be used to create a change. The offending tag is " + this.asHumanString()
|
||||
throw (
|
||||
"An inverted substituting tag can not be used to create a change. The offending tag is " +
|
||||
this.asHumanString()
|
||||
)
|
||||
}
|
||||
const v = SubstitutingTag.substituteString(this._value, properties)
|
||||
if (v.match(/{.*}/) !== null) {
|
||||
|
|
|
@ -70,7 +70,7 @@ export class AndroidPolyfill {
|
|||
AndroidPolyfill.backfillGeolocation(AndroidPolyfill.databridgePlugin)
|
||||
}
|
||||
|
||||
public static async openLoginPage(){
|
||||
public static async openLoginPage() {
|
||||
await DatabridgePluginSingleton.request<{ oauth_token: string }>({ key: "open:login" })
|
||||
}
|
||||
public static async requestLoginCodes() {
|
||||
|
|
|
@ -117,12 +117,12 @@ export class MangroveIdentity {
|
|||
return []
|
||||
}
|
||||
const allReviews = await MangroveReviews.getReviews({
|
||||
kid: pem
|
||||
kid: pem,
|
||||
})
|
||||
this.allReviewsById.setData(
|
||||
allReviews.reviews.map((r) => ({
|
||||
...r,
|
||||
...r.payload
|
||||
...r.payload,
|
||||
}))
|
||||
)
|
||||
})
|
||||
|
@ -283,7 +283,9 @@ export default class FeatureReviews {
|
|||
return cached
|
||||
}
|
||||
const themeIsSensitive = state.theme?.enableMorePrivacy
|
||||
const settings = state.osmConnection.getPreference<"always" | "yes" | "ask" | "hidden">("reviews-allowed")
|
||||
const settings = state.osmConnection.getPreference<"always" | "yes" | "ask" | "hidden">(
|
||||
"reviews-allowed"
|
||||
)
|
||||
const loadingAllowed = new UIEventSource(false)
|
||||
settings.addCallbackAndRun((s) => {
|
||||
console.log("Reviews allowed is", s)
|
||||
|
@ -329,7 +331,7 @@ export default class FeatureReviews {
|
|||
}
|
||||
const r: Review = {
|
||||
sub: this.subjectUri.data,
|
||||
...review
|
||||
...review,
|
||||
}
|
||||
const keypair: CryptoKeyPair = await this._identity.getKeypair()
|
||||
const jwt = await MangroveReviews.signReview(keypair, r)
|
||||
|
@ -344,7 +346,7 @@ export default class FeatureReviews {
|
|||
...r,
|
||||
kid,
|
||||
signature: jwt,
|
||||
madeByLoggedInUser: new ImmutableStore(true)
|
||||
madeByLoggedInUser: new ImmutableStore(true),
|
||||
}
|
||||
this._reviews.data.push(reviewWithKid)
|
||||
this._reviews.ping()
|
||||
|
@ -402,7 +404,7 @@ export default class FeatureReviews {
|
|||
signature: reviewData.signature,
|
||||
madeByLoggedInUser: this._identity.getKeyId().map((user_key_id) => {
|
||||
return reviewData.kid === user_key_id
|
||||
})
|
||||
}),
|
||||
})
|
||||
hasNew = true
|
||||
}
|
||||
|
@ -428,8 +430,8 @@ export default class FeatureReviews {
|
|||
} else if (this._uncertainty > 1000) {
|
||||
console.error(
|
||||
"Not fetching reviews. Only got a point and a very big uncertainty range (" +
|
||||
this._uncertainty +
|
||||
"), so you'd probably only get garbage. Specify a name"
|
||||
this._uncertainty +
|
||||
"), so you'd probably only get garbage. Specify a name"
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import { TagUtils } from "../../Logic/Tags/TagUtils"
|
|||
import { And } from "../../Logic/Tags/And"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import {
|
||||
MappingConfigJson,
|
||||
QuestionableTagRenderingConfigJson,
|
||||
} from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
|
||||
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
|
||||
import { RegexTag } from "../../Logic/Tags/RegexTag"
|
||||
|
@ -812,8 +815,14 @@ export default class TagRenderingConfig {
|
|||
if (and.and.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
console.log(">>> New properties", TagUtils.asProperties(and, currentProperties), this.invalidValues)
|
||||
if (this.invalidValues?.matchesProperties(TagUtils.asProperties(and, currentProperties))) {
|
||||
console.log(
|
||||
">>> New properties",
|
||||
TagUtils.asProperties(and, currentProperties),
|
||||
this.invalidValues
|
||||
)
|
||||
if (
|
||||
this.invalidValues?.matchesProperties(TagUtils.asProperties(and, currentProperties))
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
return and
|
||||
|
|
|
@ -225,13 +225,17 @@ export class UserMapFeatureswitchState extends WithUserRelatedState {
|
|||
this.geolocationControl.handleClick()
|
||||
})
|
||||
|
||||
Hotkeys.RegisterHotkey({ nomod: "H" }, Translations.t.hotkeyDocumentation.homeLocation, () => {
|
||||
const home = this.userRelatedState.osmConnection.userDetails.data?.home
|
||||
if (!home) {
|
||||
console.log("No home location set")
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "H" },
|
||||
Translations.t.hotkeyDocumentation.homeLocation,
|
||||
() => {
|
||||
const home = this.userRelatedState.osmConnection.userDetails.data?.home
|
||||
if (!home) {
|
||||
console.log("No home location set")
|
||||
}
|
||||
this.mapProperties.location.set(home)
|
||||
}
|
||||
this.mapProperties.location.set(home)
|
||||
})
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{
|
||||
|
|
|
@ -41,21 +41,24 @@
|
|||
"oauth_token",
|
||||
undefined,
|
||||
"Used to complete the login"
|
||||
)
|
||||
),
|
||||
})
|
||||
const state = new UserRelatedState(osmConnection)
|
||||
const guistate = new MenuState(undefined)
|
||||
const menuDrawerState = {
|
||||
guistate, osmConnection,
|
||||
guistate,
|
||||
osmConnection,
|
||||
userRelatedState: state,
|
||||
featureSwitches: { featureSwitchEnableLogin: new UIEventSource(true) }
|
||||
featureSwitches: { featureSwitchEnableLogin: new UIEventSource(true) },
|
||||
}
|
||||
|
||||
const t = Translations.t.index
|
||||
const tu = Translations.t.general
|
||||
const tr = Translations.t.general.morescreen
|
||||
|
||||
const recentThemes = state.recentlyVisitedThemes.value.mapD(themes => themes.map(thId => ThemeSearch.officialThemesById.get(thId)))
|
||||
const recentThemes = state.recentlyVisitedThemes.value.mapD((themes) =>
|
||||
themes.map((thId) => ThemeSearch.officialThemesById.get(thId))
|
||||
)
|
||||
|
||||
let userLanguages = osmConnection.userDetails.map((ud) => ud?.languages ?? [])
|
||||
let search: UIEventSource<string | undefined> = new UIEventSource<string>("")
|
||||
|
@ -81,10 +84,12 @@
|
|||
).mapD((stableIds) => Utils.NoNullInplace(stableIds.map((id) => state.getUnofficialTheme(id))))
|
||||
|
||||
function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
|
||||
const searchIndex = Locale.language.map(language => {
|
||||
return new ThemeSearchIndex(language, themes.data)
|
||||
}, [themes])
|
||||
|
||||
const searchIndex = Locale.language.map(
|
||||
(language) => {
|
||||
return new ThemeSearchIndex(language, themes.data)
|
||||
},
|
||||
[themes]
|
||||
)
|
||||
|
||||
return searchStable.map(
|
||||
(searchTerm) => {
|
||||
|
@ -98,7 +103,6 @@
|
|||
const index = searchIndex.data
|
||||
|
||||
return index.search(searchTerm)
|
||||
|
||||
},
|
||||
[searchIndex]
|
||||
)
|
||||
|
@ -113,7 +117,7 @@
|
|||
let customSearched: Store<MinimalThemeInformation[]> = filtered(customThemes)
|
||||
|
||||
let searchIsFocussed = new UIEventSource(false)
|
||||
document.addEventListener("keydown", function(event) {
|
||||
document.addEventListener("keydown", function (event) {
|
||||
if (event.ctrlKey && event.code === "KeyF") {
|
||||
searchIsFocussed.set(true)
|
||||
event.preventDefault()
|
||||
|
@ -150,7 +154,7 @@
|
|||
</script>
|
||||
|
||||
<main>
|
||||
<div class="absolute h-screen w-screen bg-white top-0 left-0" style="z-index: -1;"></div>
|
||||
<div class="absolute left-0 top-0 h-screen w-screen bg-white" style="z-index: -1;" />
|
||||
|
||||
<div class="h-full overflow-hidden">
|
||||
<DrawerLeft shown={guistate.pageStates.menu}>
|
||||
|
@ -162,7 +166,7 @@
|
|||
|
||||
<div class="m-4 flex flex-col">
|
||||
<div class="w-ful flex justify-between">
|
||||
<button on:click={() => guistate.pageStates.menu.set(true)} class="rounded-full m-0 p-2">
|
||||
<button on:click={() => guistate.pageStates.menu.set(true)} class="m-0 rounded-full p-2">
|
||||
<MenuIcon class="h-6 w-6 cursor-pointer" />
|
||||
</button>
|
||||
|
||||
|
@ -179,7 +183,7 @@
|
|||
<Logo alt="MapComplete Logo" class="h-12 w-12 sm:h-24 sm:w-24" />
|
||||
</div>
|
||||
|
||||
<div class="link-underline flex flex-col w-full">
|
||||
<div class="link-underline flex w-full flex-col">
|
||||
<h1 class="m-0 font-extrabold tracking-tight md:text-6xl">
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
|
@ -259,15 +263,12 @@
|
|||
{/if}
|
||||
</LoginToggle>
|
||||
|
||||
|
||||
|
||||
<div class="subtle mb-16 self-end">
|
||||
v{Constants.vNumber}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute top-0 w-0 h-0" style="margin-left: -10em">
|
||||
<div class="absolute top-0 h-0 w-0" style="margin-left: -10em">
|
||||
<MenuDrawer onlyLink={false} state={menuDrawerState} />
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
if (fullscreen) {
|
||||
defaultClass = shared
|
||||
}
|
||||
let dialogClass = "fixed top-0 start-0 end-0 h-modal inset-0 w-full p-4 flex class-marker-dialog " + zIndex
|
||||
let dialogClass =
|
||||
"fixed top-0 start-0 end-0 h-modal inset-0 w-full p-4 flex class-marker-dialog " + zIndex
|
||||
if (fullscreen) {
|
||||
dialogClass += " h-full-child"
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
|
||||
<button on:click={() => clear()}>
|
||||
<Trash class="w-6 h-6" />
|
||||
<Trash class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.removeLocationHistory} />
|
||||
</button>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import { ImmutableStore } from "../../Logic/UIEventSource"
|
||||
|
||||
export let state: { theme?: ThemeConfig, mapProperties?: MapProperties }
|
||||
export let state: { theme?: ThemeConfig; mapProperties?: MapProperties }
|
||||
|
||||
const t = Translations.t.general.attribution
|
||||
const layoutToUse = state.theme
|
||||
|
@ -27,27 +27,28 @@
|
|||
maintainer = t.themeBy.Subs({ author: layoutToUse.credits })
|
||||
}
|
||||
|
||||
const bgMapAttribution = state.mapProperties?.rasterLayer?.mapD((layer) => {
|
||||
const props = layer.properties
|
||||
const attrUrl = props.attribution?.url
|
||||
const attrText = props.attribution?.text
|
||||
const bgMapAttribution =
|
||||
state.mapProperties?.rasterLayer?.mapD((layer) => {
|
||||
const props = layer.properties
|
||||
const attrUrl = props.attribution?.url
|
||||
const attrText = props.attribution?.text
|
||||
|
||||
let bgAttr: BaseUIElement | string = undefined
|
||||
if (attrText && attrUrl) {
|
||||
bgAttr = "<a href='" + attrUrl + "' target='_blank' rel='noopener'>" + attrText + "</a>"
|
||||
} else if (attrUrl) {
|
||||
bgAttr = attrUrl
|
||||
} else {
|
||||
bgAttr = attrText
|
||||
}
|
||||
if (bgAttr) {
|
||||
return Translations.t.general.attribution.attributionBackgroundLayerWithCopyright.Subs({
|
||||
name: props.name,
|
||||
copyright: bgAttr,
|
||||
})
|
||||
}
|
||||
return Translations.t.general.attribution.attributionBackgroundLayer.Subs(props)
|
||||
}) ?? new ImmutableStore(undefined)
|
||||
let bgAttr: BaseUIElement | string = undefined
|
||||
if (attrText && attrUrl) {
|
||||
bgAttr = "<a href='" + attrUrl + "' target='_blank' rel='noopener'>" + attrText + "</a>"
|
||||
} else if (attrUrl) {
|
||||
bgAttr = attrUrl
|
||||
} else {
|
||||
bgAttr = attrText
|
||||
}
|
||||
if (bgAttr) {
|
||||
return Translations.t.general.attribution.attributionBackgroundLayerWithCopyright.Subs({
|
||||
name: props.name,
|
||||
copyright: bgAttr,
|
||||
})
|
||||
}
|
||||
return Translations.t.general.attribution.attributionBackgroundLayer.Subs(props)
|
||||
}) ?? new ImmutableStore(undefined)
|
||||
|
||||
function calculateDataContributions(contributions: Map<string, number>): Translation {
|
||||
if (contributions === undefined) {
|
||||
|
@ -83,9 +84,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
const datacontributions = (state.mapProperties ? new ContributorCount(<any>state).Contributors : new ImmutableStore([])).map((counts) =>
|
||||
calculateDataContributions(counts)
|
||||
)
|
||||
const datacontributions = (
|
||||
state.mapProperties ? new ContributorCount(<any>state).Contributors : new ImmutableStore([])
|
||||
).map((counts) => calculateDataContributions(counts))
|
||||
|
||||
function codeContributors(
|
||||
contributors,
|
||||
|
|
|
@ -64,11 +64,11 @@
|
|||
|
||||
export let state: {
|
||||
favourites: FavouritesFeatureSource
|
||||
guistate: MenuState,
|
||||
osmConnection: OsmConnection,
|
||||
theme?: ThemeConfig,
|
||||
featureSwitches: Partial<FeatureSwitchState>,
|
||||
mapProperties?: MapProperties,
|
||||
guistate: MenuState
|
||||
osmConnection: OsmConnection
|
||||
theme?: ThemeConfig
|
||||
featureSwitches: Partial<FeatureSwitchState>
|
||||
mapProperties?: MapProperties
|
||||
userRelatedState?: UserRelatedState
|
||||
}
|
||||
let hotkeys = Hotkeys._docs
|
||||
|
@ -240,15 +240,15 @@
|
|||
<a
|
||||
class="flex"
|
||||
href={"https://source.mapcomplete.org/MapComplete/MapComplete/src/branch/develop/Docs/Themes/" +
|
||||
theme.id +
|
||||
".md"}
|
||||
theme.id +
|
||||
".md"}
|
||||
target="_blank"
|
||||
>
|
||||
<DocumentMagnifyingGlass class="h-6 w-6" />
|
||||
<Tr
|
||||
t={Translations.t.general.attribution.openThemeDocumentation.Subs({
|
||||
name: theme.title,
|
||||
})}
|
||||
name: theme.title,
|
||||
})}
|
||||
/>
|
||||
</a>
|
||||
|
||||
|
@ -256,10 +256,13 @@
|
|||
<QueueList class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: theme.title })} />
|
||||
</a>
|
||||
<a class="flex" href={`./statistics.html?filter-mapcomplete-changes-theme-search={"search"%3A"${theme.id}"}`}
|
||||
target="_blank">
|
||||
<a
|
||||
class="flex"
|
||||
href={`./statistics.html?filter-mapcomplete-changes-theme-search={"search"%3A"${theme.id}"}`}
|
||||
target="_blank"
|
||||
>
|
||||
<ChartBar class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.openStatistics.Subs({theme: theme.title})} />
|
||||
<Tr t={Translations.t.general.attribution.openStatistics.Subs({ theme: theme.title })} />
|
||||
</a>
|
||||
{/if}
|
||||
</SidebarUnit>
|
||||
|
@ -305,7 +308,6 @@
|
|||
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
||||
</a>
|
||||
|
||||
|
||||
<a class="flex" href="mailto:info@mapcomplete.org">
|
||||
<EnvelopeOpen class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.emailCreators} />
|
||||
|
@ -321,7 +323,6 @@
|
|||
<Tr t={Translations.t.general.attribution.donate} />
|
||||
</a>
|
||||
|
||||
|
||||
<a
|
||||
class="flex"
|
||||
href="https://source.mapcomplete.org/MapComplete/MapComplete/issues"
|
||||
|
@ -340,7 +341,6 @@
|
|||
<Tr t={Translations.t.translations.activateButton} />
|
||||
</a>
|
||||
|
||||
|
||||
<a
|
||||
class="flex"
|
||||
href={window.location.protocol + "//" + window.location.host + "/inspector.html"}
|
||||
|
@ -350,10 +350,9 @@
|
|||
</a>
|
||||
|
||||
{#if !state.theme}
|
||||
<a class="flex" href={`./statistics.html`}
|
||||
target="_blank">
|
||||
<a class="flex" href={`./statistics.html`} target="_blank">
|
||||
<ChartBar class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.openStatistics.Subs({theme: "MapComplete"})} />
|
||||
<Tr t={Translations.t.general.attribution.openStatistics.Subs({ theme: "MapComplete" })} />
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
|
@ -389,11 +388,10 @@
|
|||
<Tr t={Translations.t.privacy.title} />
|
||||
</svelte:fragment>
|
||||
<PrivacyPolicy {state} />
|
||||
<a href="./privacy.html" class="button w-fit float-right" target="_blank">
|
||||
<ArrowTopRightOnSquare class="w-8 h-8" />
|
||||
<a href="./privacy.html" class="button float-right w-fit" target="_blank">
|
||||
<ArrowTopRightOnSquare class="h-8 w-8" />
|
||||
</a>
|
||||
</Page>
|
||||
|
||||
</SidebarUnit>
|
||||
|
||||
<div class="subtle self-end">
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
const errors = changes?.errors
|
||||
const pending = changes?.pendingChanges
|
||||
</script>
|
||||
|
||||
{#if changes}
|
||||
<div
|
||||
class="pointer-events-auto flex flex-col"
|
||||
|
|
|
@ -13,12 +13,14 @@
|
|||
const editThemeHistory = usersettings.tagRenderings.find((tr) => tr.id === "sync-visited-themes")
|
||||
const editReviews = usersettings.tagRenderings.find((tr) => tr.id === "mangrove-reviews-allowed")
|
||||
|
||||
const editLocationHistory = usersettings.tagRenderings.find((tr) => tr.id === "sync-visited-locations")
|
||||
const editLocationHistory = usersettings.tagRenderings.find(
|
||||
(tr) => tr.id === "sync-visited-locations"
|
||||
)
|
||||
|
||||
const selectedElement: Feature = {
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] }
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}
|
||||
const isLoggedIn = state.osmConnection.isLoggedIn
|
||||
</script>
|
||||
|
@ -93,7 +95,6 @@
|
|||
/>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
<TagRenderingEditable
|
||||
config={editThemeHistory}
|
||||
{selectedElement}
|
||||
|
@ -102,8 +103,7 @@
|
|||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{:else }
|
||||
{:else}
|
||||
<button class="as-link" on:click={() => state.osmConnection.AttemptLogin()}>
|
||||
<Tr t={t.browsingHistoryNotLoggedIn} />
|
||||
</button>
|
||||
|
@ -120,7 +120,6 @@
|
|||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
|
||||
|
||||
<h3>
|
||||
<Tr t={t.whileYoureHere} />
|
||||
</h3>
|
||||
|
|
|
@ -12,7 +12,6 @@ export default class StatisticsForLayerPanel extends VariableUiElement {
|
|||
super(
|
||||
elementsInview.features.stabilized(1000).map(
|
||||
(features) => {
|
||||
|
||||
const els: BaseUIElement[] = []
|
||||
const featuresForLayer = features
|
||||
if (featuresForLayer.length === 0) {
|
||||
|
|
|
@ -276,13 +276,8 @@ export default class TagRenderingChart extends Combine {
|
|||
}
|
||||
|
||||
super([
|
||||
new Title(
|
||||
options?.includeTitle ? tagRendering.question ?? tagRendering.id : undefined
|
||||
),
|
||||
new Combine([
|
||||
|
||||
chart
|
||||
]).SetClass("flex flex-col justify-center h-full")
|
||||
new Title(options?.includeTitle ? tagRendering.question ?? tagRendering.id : undefined),
|
||||
new Combine([chart]).SetClass("flex flex-col justify-center h-full"),
|
||||
])
|
||||
|
||||
this.SetClass("block")
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
<section class="w-full">
|
||||
<slot name="title" />
|
||||
<div
|
||||
class={onlyIcons ? "flex gap-x-2 flex-wrap items-center justify-center" : ("theme-list my-2 gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3")}>
|
||||
class={onlyIcons
|
||||
? "flex flex-wrap items-center justify-center gap-x-2"
|
||||
: "theme-list my-2 gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3"}
|
||||
>
|
||||
{#each Utils.NoNull(themes) as theme (theme.id)}
|
||||
<ThemeButton {theme} {state} iconOnly={onlyIcons}>
|
||||
{#if $search && hasSelection && themes?.[0] === theme}
|
||||
|
|
|
@ -4,7 +4,10 @@ import Translations from "../../i18n/Translations"
|
|||
|
||||
export default class RegexValidator extends StringValidator {
|
||||
constructor() {
|
||||
super("regex", "Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme.")
|
||||
super(
|
||||
"regex",
|
||||
"Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
)
|
||||
}
|
||||
|
||||
getFeedback(s: string): Translation | undefined {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -101,7 +101,11 @@ class SingleBackgroundHandler {
|
|||
private async enable() {
|
||||
let ttl = 15
|
||||
await this.awaitStyleIsLoaded()
|
||||
while (this._background.data.properties.id === this._targetLayer.properties.id && !this.tryEnable() && ttl > 0) {
|
||||
while (
|
||||
this._background.data.properties.id === this._targetLayer.properties.id &&
|
||||
!this.tryEnable() &&
|
||||
ttl > 0
|
||||
) {
|
||||
ttl--
|
||||
await Utils.waitFor(250)
|
||||
}
|
||||
|
@ -147,7 +151,6 @@ class SingleBackgroundHandler {
|
|||
console.log("UPDATING")
|
||||
this._languageSupport.update()
|
||||
})
|
||||
|
||||
} else {
|
||||
map.addLayer(
|
||||
{
|
||||
|
@ -155,8 +158,8 @@ class SingleBackgroundHandler {
|
|||
type: "raster",
|
||||
source: background.id,
|
||||
paint: {
|
||||
"raster-opacity": 0
|
||||
}
|
||||
"raster-opacity": 0,
|
||||
},
|
||||
},
|
||||
addLayerBeforeId
|
||||
)
|
||||
|
@ -213,7 +216,12 @@ export default class RasterLayerHandler {
|
|||
background.addCallbackAndRunD((l) => {
|
||||
const key = l.properties.id
|
||||
if (!this._singleLayerHandlers[key]) {
|
||||
this._singleLayerHandlers[key] = new SingleBackgroundHandler(map, l, background, this._languageSupport)
|
||||
this._singleLayerHandlers[key] = new SingleBackgroundHandler(
|
||||
map,
|
||||
l,
|
||||
background,
|
||||
this._languageSupport
|
||||
)
|
||||
}
|
||||
})
|
||||
this._languageSupport = new ProtomapsLanguageSupport(map)
|
||||
|
@ -225,7 +233,7 @@ export default class RasterLayerHandler {
|
|||
if (layer.type === "vector") {
|
||||
return {
|
||||
type: "vector",
|
||||
url: layer.url
|
||||
url: layer.url,
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
@ -237,7 +245,7 @@ export default class RasterLayerHandler {
|
|||
minzoom: layer["min_zoom"] ?? 1,
|
||||
maxzoom: layer["max_zoom"] ?? 25,
|
||||
// Bit of a hack, but seems to work
|
||||
scheme: layer.url.includes("{-y}") ? "tms" : "xyz"
|
||||
scheme: layer.url.includes("{-y}") ? "tms" : "xyz",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +259,7 @@ export default class RasterLayerHandler {
|
|||
"{width}": "" + size,
|
||||
"{height}": "" + size,
|
||||
"{zoom}": "{z}",
|
||||
"{-y}": "{y}"
|
||||
"{-y}": "{y}",
|
||||
}
|
||||
|
||||
for (const key in toReplace) {
|
||||
|
|
|
@ -172,12 +172,12 @@
|
|||
<Tr t={Translations.t.general.add.zoomInFurther} />
|
||||
</div>
|
||||
{:else if $isLoading}
|
||||
<div class="w-full h-full p-2 flex items-center justify-center">
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
<div class="flex h-full w-full items-center justify-center p-2">
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
</div>
|
||||
{:else if selectedPreset === undefined}
|
||||
<!-- First, select the correct preset -->
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
const review: Omit<Review, "sub"> = {
|
||||
rating: confirmedScore,
|
||||
opinion: opinion.data,
|
||||
metadata: { nickname, is_affiliated: isAffiliated.data }
|
||||
metadata: { nickname, is_affiliated: isAffiliated.data },
|
||||
}
|
||||
try {
|
||||
await reviews.createReview(review)
|
||||
|
@ -73,7 +73,6 @@
|
|||
</script>
|
||||
|
||||
<ReviewPrivacyShield hiddenIfNotAllowed {reviews} guistate={state.guistate}>
|
||||
|
||||
{#if uploadFailed}
|
||||
<div class="alert flex">
|
||||
<ExclamationTriangle class="h-6 w-6" />
|
||||
|
@ -92,21 +91,27 @@
|
|||
{#if question}
|
||||
{question}
|
||||
{:else}
|
||||
<SpecialTranslation {feature} {layer} {state} t={Translations.t.reviews.question} {tags} />
|
||||
<SpecialTranslation
|
||||
{feature}
|
||||
{layer}
|
||||
{state}
|
||||
t={Translations.t.reviews.question}
|
||||
{tags}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<StarsBar
|
||||
on:click={(e) => {
|
||||
confirmedScore = e.detail.score
|
||||
score = confirmedScore
|
||||
console.log("Confirmed score is:", confirmedScore)
|
||||
}}
|
||||
confirmedScore = e.detail.score
|
||||
score = confirmedScore
|
||||
console.log("Confirmed score is:", confirmedScore)
|
||||
}}
|
||||
on:hover={(e) => {
|
||||
score = e.detail.score
|
||||
}}
|
||||
score = e.detail.score
|
||||
}}
|
||||
on:mouseout={() => {
|
||||
score = null
|
||||
}}
|
||||
score = null
|
||||
}}
|
||||
score={score ?? confirmedScore ?? 0}
|
||||
starSize="w-8 h-8"
|
||||
/>
|
||||
|
@ -127,9 +132,9 @@
|
|||
<ExclamationTriangle class="h-12 w-12" />
|
||||
<Tr
|
||||
t={t.too_long.Subs({
|
||||
max: FeatureReviews.REVIEW_OPINION_MAX_LENGTH,
|
||||
amount: $opinion?.length ?? 0,
|
||||
})}
|
||||
max: FeatureReviews.REVIEW_OPINION_MAX_LENGTH,
|
||||
amount: $opinion?.length ?? 0,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
<script lang="ts">/**
|
||||
* Due to privacy, we cannot load reviews unless allowed in the privacy policy
|
||||
*/
|
||||
import FeatureReviews from "../../Logic/Web/MangroveReviews"
|
||||
import { MenuState } from "../../Models/MenuState"
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Due to privacy, we cannot load reviews unless allowed in the privacy policy
|
||||
*/
|
||||
import FeatureReviews from "../../Logic/Web/MangroveReviews"
|
||||
import { MenuState } from "../../Models/MenuState"
|
||||
|
||||
export let guistate: MenuState
|
||||
export let reviews: FeatureReviews
|
||||
export let hiddenIfNotAllowed: boolean = false
|
||||
let allowed = reviews.loadingAllowed
|
||||
export let guistate: MenuState
|
||||
export let reviews: FeatureReviews
|
||||
export let hiddenIfNotAllowed: boolean = false
|
||||
let allowed = reviews.loadingAllowed
|
||||
</script>
|
||||
|
||||
{#if $allowed}
|
||||
<slot />
|
||||
{:else if !hiddenIfNotAllowed && $allowed !== null }
|
||||
<div class="low-interaction flex flex-col rounded mx-1">
|
||||
|
||||
{:else if !hiddenIfNotAllowed && $allowed !== null}
|
||||
<div class="low-interaction mx-1 flex flex-col rounded">
|
||||
Reviews are disabled due to your privacy settings.
|
||||
<button on:click={() => reviews.loadingAllowed.set(true)} class="primary">
|
||||
Load reviews once
|
||||
</button>
|
||||
<button class="as-link self-end" on:click={() => guistate.openUsersettings("mangrove-reviews-allowed")}>
|
||||
<button
|
||||
class="as-link self-end"
|
||||
on:click={() => guistate.openUsersettings("mangrove-reviews-allowed")}
|
||||
>
|
||||
Edit your privacy settings
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ export class SettingsVisualisations {
|
|||
docs: "A component to set the language of the user interface",
|
||||
constr(state: SpecialVisualizationState): SvelteUIElement {
|
||||
const availableLanguages = Locale.showLinkToWeblate.map((showTranslations) =>
|
||||
showTranslations ? LanguageUtils.usedLanguagesSorted : (state?.theme?.language ?? LanguageUtils.usedLanguagesSorted)
|
||||
showTranslations
|
||||
? LanguageUtils.usedLanguagesSorted
|
||||
: state?.theme?.language ?? LanguageUtils.usedLanguagesSorted
|
||||
)
|
||||
return new SvelteUIElement(LanguagePicker, {
|
||||
assignTo: state.userRelatedState.language,
|
||||
|
|
|
@ -29,7 +29,7 @@ class QuestionViz implements SpecialVisualizationSvelte {
|
|||
},
|
||||
{
|
||||
name: "blacklisted-labels",
|
||||
doc: "One or more ';'-separated labels of questions which should _not_ be included. Default: 'hidden'"
|
||||
doc: "One or more ';'-separated labels of questions which should _not_ be included. Default: 'hidden'",
|
||||
},
|
||||
]
|
||||
svelteBased = true
|
||||
|
@ -46,7 +46,7 @@ class QuestionViz implements SpecialVisualizationSvelte {
|
|||
?.split(";")
|
||||
?.map((s) => s.trim())
|
||||
?.filter((s) => s !== "")
|
||||
const blacklist = (args[1])
|
||||
const blacklist = args[1]
|
||||
?.split(";")
|
||||
?.map((s) => s.trim())
|
||||
?.filter((s) => s !== "")
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { FixedUiElement } from "./Base/FixedUiElement"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import { default as FeatureTitle } from "./Popup/Title.svelte"
|
||||
import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization"
|
||||
import {
|
||||
RenderingSpecification,
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
} from "./SpecialVisualization"
|
||||
import { HistogramViz } from "./Popup/HistogramViz"
|
||||
import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
|
||||
import { MultiApplyViz } from "./Popup/MultiApplyViz"
|
||||
|
@ -36,11 +40,8 @@ import { UISpecialVisualisations } from "./SpecialVisualisations/UISpecialVisual
|
|||
import { SettingsVisualisations } from "./SpecialVisualisations/SettingsVisualisations"
|
||||
import { ReviewSpecialVisualisations } from "./SpecialVisualisations/ReviewSpecialVisualisations"
|
||||
import { DataImportSpecialVisualisations } from "./SpecialVisualisations/DataImportSpecialVisualisations"
|
||||
import TagrenderingManipulationSpecialVisualisations
|
||||
from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
|
||||
import {
|
||||
WebAndCommunicationSpecialVisualisations
|
||||
} from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
|
||||
import TagrenderingManipulationSpecialVisualisations from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
|
||||
import { WebAndCommunicationSpecialVisualisations } from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
|
||||
import ClearGPSHistory from "./BigComponents/ClearGPSHistory.svelte"
|
||||
import AllFeaturesStatistics from "./Statistics/AllFeaturesStatistics.svelte"
|
||||
|
||||
|
@ -405,7 +406,7 @@ export default class SpecialVisualizations {
|
|||
docs: "Show general statistics about the elements currently in view. Intended to use on the `current_view`-layer",
|
||||
args: [],
|
||||
|
||||
constr: (state) => new SvelteUIElement(AllFeaturesStatistics, { state })
|
||||
constr: (state) => new SvelteUIElement(AllFeaturesStatistics, { state }),
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
* An element showing s
|
||||
*/
|
||||
export let state: ThemeViewState
|
||||
let layers = state.theme.layers.filter(l => l.isNormal())
|
||||
|
||||
let layers = state.theme.layers.filter((l) => l.isNormal())
|
||||
</script>
|
||||
|
||||
<Accordion>
|
||||
|
||||
{#each layers as layer (layer.id)}
|
||||
<LayerStatistics {state} {layer} />
|
||||
{/each}
|
||||
|
|
|
@ -18,12 +18,16 @@
|
|||
let elements: Feature[] = state.perLayer.get(layer.id).GetFeaturesWithin($bbox)
|
||||
$: elements = state.perLayer.get(layer.id).GetFeaturesWithin($bbox)
|
||||
|
||||
let trs = layer.tagRenderings.filter(tr => tr.question)
|
||||
let trs = layer.tagRenderings.filter((tr) => tr.question)
|
||||
</script>
|
||||
|
||||
<AccordionItem paddingDefault="p-2" inactiveClass="text-black" defaultClass="w-full flex-grow justify-start">
|
||||
<AccordionItem
|
||||
paddingDefault="p-2"
|
||||
inactiveClass="text-black"
|
||||
defaultClass="w-full flex-grow justify-start"
|
||||
>
|
||||
<div slot="header" class="flex items-center gap-x-2">
|
||||
<div class="w-8 h-8 inline-block">
|
||||
<div class="inline-block h-8 w-8">
|
||||
<DefaultIcon {layer} />
|
||||
</div>
|
||||
<Tr t={layer.name} />
|
||||
|
@ -35,12 +39,15 @@
|
|||
{:else if elements.length === 0}
|
||||
No features in view
|
||||
{:else}
|
||||
<div class="flex flex-wrap w-full gap-y-4 gap-x-4">
|
||||
|
||||
<div class="flex w-full flex-wrap gap-x-4 gap-y-4">
|
||||
{#each trs as tr}
|
||||
<ToSvelte construct={() => new TagRenderingChart(elements, tr, {
|
||||
chartclasses: "w-full self-center",includeTitle: true
|
||||
}).SetClass(tr.multiAnswer ? "w-128": "w-96") } />
|
||||
<ToSvelte
|
||||
construct={() =>
|
||||
new TagRenderingChart(elements, tr, {
|
||||
chartclasses: "w-full self-center",
|
||||
includeTitle: true,
|
||||
}).SetClass(tr.multiAnswer ? "w-128" : "w-96")}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
import QuestionPreview from "./QuestionPreview.svelte"
|
||||
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"
|
||||
import { EditJsonState } from "./EditLayerState"
|
||||
import type {
|
||||
QuestionableTagRenderingConfigJson
|
||||
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||
import { AccordionItem } from "flowbite-svelte"
|
||||
import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle"
|
||||
|
||||
|
@ -29,7 +27,7 @@
|
|||
|
||||
export let expanded = new UIEventSource(false)
|
||||
|
||||
let errors = state.messagesFor(path).mapD(msgs => msgs.filter(msg => msg.level === "error"))
|
||||
let errors = state.messagesFor(path).mapD((msgs) => msgs.filter((msg) => msg.level === "error"))
|
||||
|
||||
const subparts: ConfigMeta[] = state
|
||||
.getSchemaStartingWith(schema.path)
|
||||
|
@ -92,10 +90,10 @@
|
|||
} catch (e) {
|
||||
console.log(
|
||||
"Warning: could not translate a title for " +
|
||||
`${singular} ${i} with function ` +
|
||||
schema.hints.title +
|
||||
" and value " +
|
||||
JSON.stringify(value)
|
||||
`${singular} ${i} with function ` +
|
||||
schema.hints.title +
|
||||
" and value " +
|
||||
JSON.stringify(value)
|
||||
)
|
||||
}
|
||||
return Translations.T(`${singular} ${i}`)
|
||||
|
@ -152,8 +150,8 @@
|
|||
{:else}
|
||||
<Tr cls="font-bold" t={Translations.T(value?.question ?? value?.render)} />
|
||||
{#if $errors.length > 0}
|
||||
<div class="alert w-fit inline">
|
||||
<ExclamationTriangle class="w-6 h-6 inline" />{$errors.length}
|
||||
<div class="alert inline w-fit">
|
||||
<ExclamationTriangle class="inline h-6 w-6" />{$errors.length}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -76,7 +76,8 @@
|
|||
let selfLayers = layers.mapD(
|
||||
(ls) =>
|
||||
ls.filter(
|
||||
(l) => l.owner === uid.data && l.id.toLowerCase().includes(layerFilterTerm.data.toLowerCase())
|
||||
(l) =>
|
||||
l.owner === uid.data && l.id.toLowerCase().includes(layerFilterTerm.data.toLowerCase())
|
||||
),
|
||||
[uid, layerFilterTerm]
|
||||
)
|
||||
|
@ -93,7 +94,8 @@
|
|||
let officialLayers = layers.mapD(
|
||||
(ls) =>
|
||||
ls.filter(
|
||||
(l) => l.owner === undefined && l.id.toLowerCase().includes(layerFilterTerm.data.toLowerCase())
|
||||
(l) =>
|
||||
l.owner === undefined && l.id.toLowerCase().includes(layerFilterTerm.data.toLowerCase())
|
||||
),
|
||||
[uid, layerFilterTerm]
|
||||
)
|
||||
|
@ -106,7 +108,8 @@
|
|||
let selfThemes = themes.mapD(
|
||||
(ls) =>
|
||||
ls.filter(
|
||||
(l) => l.owner === uid.data && l.id.toLowerCase().includes(themeFilterTerm.data.toLowerCase())
|
||||
(l) =>
|
||||
l.owner === uid.data && l.id.toLowerCase().includes(themeFilterTerm.data.toLowerCase())
|
||||
),
|
||||
[uid, themeFilterTerm]
|
||||
)
|
||||
|
@ -123,7 +126,8 @@
|
|||
let officialThemes = themes.mapD(
|
||||
(ls) =>
|
||||
ls.filter(
|
||||
(l) => l.owner === undefined && l.id.toLowerCase().includes(themeFilterTerm.data.toLowerCase())
|
||||
(l) =>
|
||||
l.owner === undefined && l.id.toLowerCase().includes(themeFilterTerm.data.toLowerCase())
|
||||
),
|
||||
[uid, themeFilterTerm]
|
||||
)
|
||||
|
@ -340,11 +344,7 @@
|
|||
Selecting a layer will create a copy in your account. You will not change the version that
|
||||
is in MapComplete
|
||||
</div>
|
||||
<ChooseLayerToEdit
|
||||
{osmConnection}
|
||||
layerIds={officialLayers}
|
||||
on:layerSelected={editLayer}
|
||||
/>
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={officialLayers} on:layerSelected={editLayer} />
|
||||
</div>
|
||||
{:else if state === "edit_theme"}
|
||||
<div class="m-4 flex flex-col">
|
||||
|
@ -374,11 +374,7 @@
|
|||
<h3>Themes by other contributors</h3>
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={otherThemes} on:layerSelected={editTheme} />
|
||||
<h3>Official themes by MapComplete</h3>
|
||||
<ChooseLayerToEdit
|
||||
{osmConnection}
|
||||
layerIds={officialThemes}
|
||||
on:layerSelected={editTheme}
|
||||
/>
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={officialThemes} on:layerSelected={editTheme} />
|
||||
</div>
|
||||
{:else if state === "loading"}
|
||||
<div class="h-8 w-8">
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 9176,
|
||||
"commits": 9299,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
"commits": 515,
|
||||
"commits": 520,
|
||||
"contributor": "Robin van der Linde"
|
||||
},
|
||||
{
|
||||
|
@ -28,6 +28,10 @@
|
|||
"commits": 33,
|
||||
"contributor": "Christian Neumann"
|
||||
},
|
||||
{
|
||||
"commits": 32,
|
||||
"contributor": "Midgard"
|
||||
},
|
||||
{
|
||||
"commits": 31,
|
||||
"contributor": "Andrews Leruth"
|
||||
|
@ -44,10 +48,6 @@
|
|||
"commits": 29,
|
||||
"contributor": "riQQ"
|
||||
},
|
||||
{
|
||||
"commits": 26,
|
||||
"contributor": "Midgard"
|
||||
},
|
||||
{
|
||||
"commits": 26,
|
||||
"contributor": "Joost"
|
||||
|
@ -60,6 +60,10 @@
|
|||
"commits": 24,
|
||||
"contributor": "Ward"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"contributor": "Osmwithspace"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"contributor": "wjtje"
|
||||
|
@ -97,7 +101,7 @@
|
|||
"contributor": "ToastHawaii"
|
||||
},
|
||||
{
|
||||
"commits": 13,
|
||||
"commits": 14,
|
||||
"contributor": "danieldegroot2"
|
||||
},
|
||||
{
|
||||
|
@ -112,10 +116,6 @@
|
|||
"commits": 12,
|
||||
"contributor": "Bavo Vanderghote"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Osmwithspace"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Flo Edelmann"
|
||||
|
@ -152,6 +152,10 @@
|
|||
"commits": 7,
|
||||
"contributor": "OliNau"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "Languages add-on"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "David Haberthür"
|
||||
|
@ -160,10 +164,6 @@
|
|||
"commits": 5,
|
||||
"contributor": "tiptoptom"
|
||||
},
|
||||
{
|
||||
"commits": 4,
|
||||
"contributor": "Languages add-on"
|
||||
},
|
||||
{
|
||||
"commits": 4,
|
||||
"contributor": "Daniele Santini"
|
||||
|
@ -280,6 +280,10 @@
|
|||
"commits": 2,
|
||||
"contributor": "Stanislas Gueniffey"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Weblate Admin"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Malte"
|
||||
|
|
|
@ -236,9 +236,9 @@
|
|||
"fr"
|
||||
],
|
||||
"GQ": [
|
||||
"pt",
|
||||
"fr",
|
||||
"es"
|
||||
"es",
|
||||
"pt"
|
||||
],
|
||||
"GR": [
|
||||
"el"
|
||||
|
|
|
@ -12468,7 +12468,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -13805,7 +13805,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -15190,7 +15190,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -16579,7 +16579,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -17970,7 +17970,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -19359,7 +19359,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -15115,7 +15115,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -16489,7 +16489,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -17912,7 +17912,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -19337,7 +19337,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -20763,7 +20763,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -22188,7 +22188,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -36926,7 +36926,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -38351,7 +38351,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -39827,7 +39827,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -41303,7 +41303,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -42780,7 +42780,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -44256,7 +44256,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -675,7 +675,7 @@
|
|||
},
|
||||
{
|
||||
"if": "value=regex",
|
||||
"then": "<b>regex</b> Validates a regex"
|
||||
"then": "<b>regex</b> Only used when your input should be a valid regex. This validator is only used as helper for Studio and should not be used in a mapcomplete-theme."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"contributor": "Anonymous"
|
||||
},
|
||||
{
|
||||
"commits": 122,
|
||||
"commits": 128,
|
||||
"contributor": "mcliquid"
|
||||
},
|
||||
{
|
||||
|
@ -25,7 +25,7 @@
|
|||
"contributor": "Allan Nordhøy"
|
||||
},
|
||||
{
|
||||
"commits": 89,
|
||||
"commits": 92,
|
||||
"contributor": "mike140"
|
||||
},
|
||||
{
|
||||
|
@ -45,7 +45,7 @@
|
|||
"contributor": "Jiří Podhorecký"
|
||||
},
|
||||
{
|
||||
"commits": 53,
|
||||
"commits": 57,
|
||||
"contributor": "Supaplex"
|
||||
},
|
||||
{
|
||||
|
@ -108,6 +108,10 @@
|
|||
"commits": 16,
|
||||
"contributor": "macpac"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
"contributor": "Pau Nofuentes"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
"contributor": "Ettore Atalan"
|
||||
|
@ -148,6 +152,10 @@
|
|||
"commits": 12,
|
||||
"contributor": "Piotr Strebski"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Weblate Admin"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Manuel Tassi"
|
||||
|
@ -188,10 +196,6 @@
|
|||
"commits": 10,
|
||||
"contributor": "Irina"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Weblate Admin"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Krzysztof Chorzempa"
|
||||
|
@ -368,6 +372,10 @@
|
|||
"commits": 4,
|
||||
"contributor": "Jan Zabel"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "ceirios"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "Gábor"
|
||||
|
|
|
@ -5,7 +5,7 @@ import { LocalStorageSource } from "./Logic/Web/LocalStorageSource"
|
|||
console.log("Authorizing...")
|
||||
|
||||
if (QueryParameters.wasInitialized("error")) {
|
||||
// error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.
|
||||
// error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.
|
||||
alert("Access was denied")
|
||||
const previousLocation = LocalStorageSource.get("location_before_login")
|
||||
window.location.href = previousLocation.data ?? "./"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue