From 02203dd05b1a0c651a5abb6387a20cb6906ebc20 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 17 Jul 2023 01:07:01 +0200 Subject: [PATCH] Feature: add button to download mangrove identity --- Logic/State/UserRelatedState.ts | 53 ++++++++----- UI/SubstitutedTranslation.ts | 1 - Utils.ts | 82 +++++++++++--------- assets/layers/usersettings/usersettings.json | 5 ++ package.json | 2 +- 5 files changed, 87 insertions(+), 56 deletions(-) diff --git a/Logic/State/UserRelatedState.ts b/Logic/State/UserRelatedState.ts index 7d0d959f9..2f6840b4b 100644 --- a/Logic/State/UserRelatedState.ts +++ b/Logic/State/UserRelatedState.ts @@ -24,6 +24,7 @@ export default class UserRelatedState { public static readonly usersettingsConfig = UserRelatedState.initUserRelatedState() public static readonly availableUserSettingsIds: string[] = UserRelatedState.usersettingsConfig?.tagRenderings?.map((tr) => tr.id) ?? [] + public static readonly SHOW_TAGS_VALUES = ["always", "yes", "full"] as const /** The user credentials */ @@ -34,7 +35,6 @@ export default class UserRelatedState { public readonly mangroveIdentity: MangroveIdentity public readonly installedUserThemes: Store public readonly showAllQuestionsAtOnce: UIEventSource - public static readonly SHOW_TAGS_VALUES = ["always", "yes", "full"] as const public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> public readonly homeLocation: FeatureSource public readonly language: UIEventSource @@ -111,13 +111,13 @@ export default class UserRelatedState { public GetUnofficialTheme(id: string): | { - id: string - icon: string - title: any - shortDescription: any - definition?: any - isOfficial: boolean - } + id: string + icon: string + title: any + shortDescription: any + definition?: any + isOfficial: boolean + } | undefined { console.log("GETTING UNOFFICIAL THEME") const pref = this.osmConnection.GetLongPreference("unofficial-theme-" + id) @@ -142,8 +142,8 @@ export default class UserRelatedState { } catch (e) { console.warn( "Removing theme " + - id + - " as it could not be parsed from the preferences; the content is:", + id + + " as it could not be parsed from the preferences; the content is:", str ) pref.setData(null) @@ -178,6 +178,7 @@ export default class UserRelatedState { ) } } + private InitInstalledUserThemes(): Store { const prefix = "mapcomplete-unofficial-theme-" const postfix = "-combined-length" @@ -247,9 +248,23 @@ export default class UserRelatedState { const osmConnection = this.osmConnection osmConnection.preferencesHandler.preferences.addCallback((newPrefs) => { for (const k in newPrefs) { - amendedPrefs.data[k] = newPrefs[k] + const v = newPrefs[k] + if (k.endsWith("-combined-length")) { + const l = Number(v) + const key = k.substring(0, k.length - "length".length) + let combined = "" + for (let i = 0; i < l; i++) { + combined += newPrefs[key + i] + } + amendedPrefs.data[key.substring(0, key.length - "-combined-".length)] = combined + + } else { + amendedPrefs.data[k] = newPrefs[k] + } } + amendedPrefs.ping() + console.log("Amended prefs are:", amendedPrefs.data) }) const usersettingsConfig = UserRelatedState.usersettingsConfig const translationMode = osmConnection.GetPreference("translation-mode") @@ -272,13 +287,13 @@ export default class UserRelatedState { const zenLinks: { link: string; id: string }[] = Utils.NoNull([ hasMissingTheme ? { - id: "theme:" + layout.id, - link: LinkToWeblate.hrefToWeblateZen( - language, - "themes", - layout.id - ), - } + id: "theme:" + layout.id, + link: LinkToWeblate.hrefToWeblateZen( + language, + "themes", + layout.id + ), + } : undefined, ...missingLayers.map((id) => ({ id: "layer:" + id, @@ -350,7 +365,7 @@ export default class UserRelatedState { // Language is managed seperately continue } - this.osmConnection.GetPreference(key, undefined, { prefix: "" }).setData(tags[key]) + this.osmConnection.GetPreference(key, undefined, {prefix: ""}).setData(tags[key]) } }) diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 03377fce4..a6721532a 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -2,7 +2,6 @@ import { UIEventSource } from "../Logic/UIEventSource" import { Translation } from "./i18n/Translation" import Locale from "./i18n/Locale" import { FixedUiElement } from "./Base/FixedUiElement" -// import SpecialVisualizations from "./SpecialVisualizations" import { Utils } from "../Utils" import { VariableUiElement } from "./Base/VariableUIElement" import Combine from "./Base/Combine" diff --git a/Utils.ts b/Utils.ts index e6a91d20f..3ff3c59bb 100644 --- a/Utils.ts +++ b/Utils.ts @@ -1,5 +1,5 @@ import colors from "./assets/colors.json" -import { HTMLElement } from "node-html-parser" +import {HTMLElement} from "node-html-parser" export class Utils { /** @@ -437,6 +437,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be /** * Given a piece of text, will replace any key occuring in 'tags' by the corresponding value + * + * Utils.SubstituteKeys("abc{def}ghi", {def: 'XYZ'}) // => "abcXYZghi" + * Utils.SubstituteKeys("abc{def}{def}ghi", {def: 'XYZ'}) // => "abcXYZXYZghi" + * Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi" + * * @param txt * @param tags * @param useLang @@ -450,12 +455,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (txt === undefined) { return undefined } - const regex = /.*{([^}]*)}.*/ + const regex = /(.*?){([^}]*)}(.*)/ let match = txt.match(regex) + if(!match){ + return txt + } + let result = "" while (match) { - const key = match[1] + const [_, normal, key, leftover] = match let v = tags === undefined ? undefined : tags[key] if (v !== undefined && v !== null) { if (v["toISOString"] != undefined) { @@ -490,11 +499,14 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be // v === undefined v = "" } - txt = txt.replace("{" + key + "}", v) - match = txt.match(regex) - } - return txt + result += normal + v + match = leftover.match(regex) + if(!match){ + result += leftover + } + } + return result } public static LoadCustomCss(location: string) { @@ -687,10 +699,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (Array.isArray(leaf)) { for (let i = 0; i < (leaf).length; i++) { const l = (leaf)[i] - collectedList.push({ leaf: l, path: [...travelledPath, "" + i] }) + collectedList.push({leaf: l, path: [...travelledPath, "" + i]}) } } else { - collectedList.push({ leaf, path: travelledPath }) + collectedList.push({leaf, path: travelledPath}) } return collectedList } @@ -768,7 +780,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be }) } - const cp = { ...json } + const cp = {...json} for (const key in json) { cp[key] = Utils.WalkJson(json[key], f, isLeaf, [...path, key]) } @@ -898,11 +910,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const xhr = new XMLHttpRequest() xhr.onload = () => { if (xhr.status == 200) { - resolve({ content: xhr.response }) + resolve({content: xhr.response}) } else if (xhr.status === 302) { - resolve({ redirect: xhr.getResponseHeader("location") }) + resolve({redirect: xhr.getResponseHeader("location")}) } else if (xhr.status === 509 || xhr.status === 429) { - resolve({ error: "rate limited", url, statuscode: xhr.status }) + resolve({error: "rate limited", url, statuscode: xhr.status}) } else { resolve({ error: "other error: " + xhr.statusText, @@ -972,10 +984,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } const promise = /*NO AWAIT as we work with the promise directly */ Utils.downloadJsonAdvanced( - url, - headers - ) - Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() }) + url, + headers + ) + Utils._download_cache.set(url, {promise, timestamp: new Date().getTime()}) return await promise } @@ -994,11 +1006,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const injected = Utils.injectedDownloads[url] if (injected !== undefined) { console.log("Using injected resource for test for URL", url) - return new Promise((resolve, _) => resolve({ content: injected })) + return new Promise((resolve, _) => resolve({content: injected})) } const result = await Utils.downloadAdvanced( url, - Utils.Merge({ accept: "application/json" }, headers ?? {}) + Utils.Merge({accept: "application/json"}, headers ?? {}) ) if (result["error"] !== undefined) { return <{ error: string; url: string; statuscode?: number }>result @@ -1006,12 +1018,12 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const data = result["content"] try { if (typeof data === "string") { - return { content: JSON.parse(data) } + return {content: JSON.parse(data)} } - return { content: data } + return {content: data} } catch (e) { console.error("Could not parse ", data, "due to", e, "\n", e.stack) - return { error: "malformed", url } + return {error: "malformed", url} } } @@ -1035,7 +1047,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const element = document.createElement("a") let file if (typeof contents === "string") { - file = new Blob([contents], { type: options?.mimetype ?? "text/plain" }) + file = new Blob([contents], {type: options?.mimetype ?? "text/plain"}) } else { file = contents } @@ -1306,7 +1318,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (match == undefined) { return undefined } - return { r: Number(match[1]), g: Number(match[2]), b: Number(match[3]) } + return {r: Number(match[1]), g: Number(match[2]), b: Number(match[3])} } if (!hex.startsWith("#")) { @@ -1366,7 +1378,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (inView) { return } - element.scrollIntoView({ behavior: "smooth", block: "nearest" }) + element.scrollIntoView({behavior: "smooth", block: "nearest"}) } public static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement { @@ -1443,13 +1455,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return false } - private static colorDiff( - c0: { r: number; g: number; b: number }, - c1: { r: number; g: number; b: number } - ) { - return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b) - } - /** * * Utils.splitIntoSubstitutionParts("abc") // => [{message: "abc"}] @@ -1465,15 +1470,22 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be const postParts = prepart.split("}") if (postParts.length === 1) { // This was a normal part - spec.push({ message: postParts[0] }) + spec.push({message: postParts[0]}) } else { const [subs, message] = postParts - spec.push({ subs }) + spec.push({subs}) if (message !== "") { - spec.push({ message }) + spec.push({message}) } } } return spec } + + private static colorDiff( + c0: { r: number; g: number; b: number }, + c1: { r: number; g: number; b: number } + ) { + return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b) + } } diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index 0d604510d..11432bde6 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -221,6 +221,11 @@ } ] }, + {"id": "mangrove-keys", + "render": { + "en": "Download the private key for your Mangrove Account

Anyone possessing this file can make reviews with your identity

" + } + }, { "id": "translations-title", "label": [ diff --git a/package.json b/package.json index fa5cb3e5c..d5fe388f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.30.9", + "version": "0.31.0", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues",