diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 8cc53cc9a..e78f1361e 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -160,7 +160,7 @@ "craft=key_cutter" ] }, - "then": "./assets/layers/id_presets/fas-key.svg" + "then": "circle:white;./assets/layers/id_presets/fas-key.svg" } ], "label": { diff --git a/assets/svg/no_checkmark.svg b/assets/svg/no_checkmark.svg deleted file mode 100644 index 7c6392c8d..000000000 --- a/assets/svg/no_checkmark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/assets/svg/none.svg b/assets/svg/none.svg deleted file mode 100644 index 5adcc79f0..000000000 --- a/assets/svg/none.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/scripts/generateIncludedImages.ts b/scripts/generateIncludedImages.ts index a8d19237d..93a163ef3 100644 --- a/scripts/generateIncludedImages.ts +++ b/scripts/generateIncludedImages.ts @@ -1,11 +1,47 @@ import * as fs from "fs" + function genImages(dryrun = false) { console.log("Generating images") + // These images are not referenced via 'Svg.ts' anymore and can be ignored + const blacklist: string[] = [ + "SocialImageForeground", + "wikipedia", + "Upload", + "pin", + "mapillary_black", + "plantnet_logo", + "mastodon", + "move-arrows", + "mapcomplete_logo", + "logo", + "logout", + "hand", + "help", + "home", + "reload", + "min", + "plus", + "not_found", + "osm_logo_us", + "party", + "filter", + "filter_disable", + "floppy", + "eye", + "gear", + "gender_bi", + "compass", + "blocked", + "brick_wall", + "brick_wall_raw", + "brick_wall_round", + "bug", + "back", + ].map((s) => s.toLowerCase()) const dir = fs.readdirSync("./assets/svg") let module = 'import Img from "./UI/Base/Img";\nimport {FixedUiElement} from "./UI/Base/FixedUiElement";\n\n/* @deprecated */\nexport default class Svg {\n\n\n' - const allNames: string[] = [] for (const path of dir) { if (path.endsWith("license_info.json")) { continue @@ -21,6 +57,7 @@ function genImages(dryrun = false) { let svg: string = fs .readFileSync("./assets/svg/" + path, "utf-8") .replace(/<\?xml.*?>/, "") + .replace(/]*>/, "") .replace(/fill: ?none;/g, "fill: none !important;") // This is such a brittle hack... .replace(/\n/g, " ") .replace(/\r/g, "") @@ -37,18 +74,6 @@ function genImages(dryrun = false) { } const name = path.substring(0, path.length - 4).replace(/[ -]/g, "_") - if (dryrun) { - svg = "" - } - - let rawName = name - - module += ` public static ${name} = "${svg}"\n` - module += ` public static ${name}_svg() { return new Img(Svg.${rawName}, true);}\n` - if (!dryrun) { - allNames.push(`"${path}": Svg.${name}`) - } - const nameUC = name.toUpperCase().at(0) + name.substring(1) const svelteCode = '\n' + @@ -60,8 +85,23 @@ function genImages(dryrun = false) { .replace(/\\"/g, '"') .replace(/(rgb\(0%,0%,0%\)|#000000|#000)/g, "{color}") fs.writeFileSync("./src/assets/svg/" + nameUC + ".svelte", svelteCode, "utf8") + + if (blacklist.some((item) => path.toLowerCase().endsWith(item + ".svg"))) { + continue + } + if (dryrun) { + svg = "" + } + + let rawName = name + + module += ` public static ${name} = "${svg}"\n` + if (!dryrun) { + module += ` public static ${name}_svg() { return new Img(Svg.${rawName}, true);}\n` + } else { + module += ` public static ${name}_svg() { return new Img("", true);}\n` + } } - module += `public static All = {${allNames.join(",")}};` module += "}\n" fs.writeFileSync("src/Svg.ts", module) console.log("Done") diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index a8ac52374..ddf224576 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -92,7 +92,7 @@ export class DoesImageExist extends DesugaringStep { return image } if (image.match(/[a-z]*/)) { - if (Svg.All[image + ".svg"] !== undefined) { + if (Constants.defaultPinIcons.indexOf(image) >= 0) { // This is a builtin img, e.g. 'checkmark' or 'crosshair' return image } diff --git a/src/Models/ThemeConfig/PointRenderingConfig.ts b/src/Models/ThemeConfig/PointRenderingConfig.ts index e36cc9823..b73a00868 100644 --- a/src/Models/ThemeConfig/PointRenderingConfig.ts +++ b/src/Models/ThemeConfig/PointRenderingConfig.ts @@ -14,6 +14,7 @@ import { VariableUiElement } from "../../UI/Base/VariableUIElement" import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" import SvelteUIElement from "../../UI/Base/SvelteUIElement" import DynamicMarker from "../../UI/Map/DynamicMarker.svelte" +import { html } from "svelte/types/compiler/utils/namespaces" export class IconConfig extends WithContextLoader { public static readonly defaultIcon = new IconConfig({ icon: "pin", color: "#ff9939" }) @@ -133,71 +134,23 @@ export default class PointRenderingConfig extends WithContextLoader { context + ".rotationAlignment" ) } - - /** - * Given a single HTML spec (either a single image path OR "image_path_to_known_svg:fill-colour", returns a fixedUIElement containing that - * The element will fill 100% and be positioned absolutely with top:0 and left: 0 - */ - private static FromHtmlSpec(htmlSpec: string, style: string, isBadge = false): BaseUIElement { - if (htmlSpec === undefined) { - return undefined + private static FromHtmlMulti(multiSpec: string, tags: Store>) { + const icons: IconConfig[] = [] + for (const subspec of multiSpec.split(";")) { + const [icon, color] = subspec.split(":") + icons.push(new IconConfig({ icon, color })) } - const match = htmlSpec.match(/([a-zA-Z0-9_]*):([^;]*)/) - if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { - const svg = Svg.All[match[1] + ".svg"] as string - const targetColor = match[2] - const img = new Img( - svg.replace(/(rgb\(0%,0%,0%\)|#000000|#000)/g, targetColor), - true - ).SetStyle(style) - if (isBadge) { - img.SetClass("badge") - } - return img - } else if (Svg.All[htmlSpec + ".svg"] !== undefined) { - const svg = Svg.All[htmlSpec + ".svg"] as string - const img = new Img(svg, true).SetStyle(style) - if (isBadge) { - img.SetClass("badge") - } - return img - } else { - return new FixedUiElement(``) - } - } - - private static FromHtmlMulti( - multiSpec: string, - rotation: string, - isBadge: boolean, - defaultElement: BaseUIElement = undefined, - options?: { - noFullWidth?: boolean - } - ) { - if (multiSpec === undefined) { - return defaultElement - } - const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0` - - const htmlDefs = multiSpec.trim()?.split(";") ?? [] - const elements = Utils.NoEmpty(htmlDefs).map((def) => - PointRenderingConfig.FromHtmlSpec(def, style, isBadge) + return new SvelteUIElement(DynamicMarker, { marker: icons, tags }).SetClass( + "w-full h-full block absolute top-0 left-0" ) - if (elements.length === 0) { - return defaultElement - } else { - const combine = new Combine(elements).SetClass("relative block") - if (options?.noFullWidth) { - return combine - } - combine.SetClass("w-full h-full") - return combine - } } public GetBaseIcon(tags?: Record): BaseUIElement { - return new SvelteUIElement(DynamicMarker, { config: this, tags: new ImmutableStore(tags) }) + return new SvelteUIElement(DynamicMarker, { + marker: this.marker, + rotation: this.rotation, + tags: new ImmutableStore(tags), + }) } public RenderIcon( @@ -249,9 +202,11 @@ export default class PointRenderingConfig extends WithContextLoader { anchorH = -iconH / 2 } - const icon = new SvelteUIElement(DynamicMarker, { config: this, tags }).SetClass( - "w-full h-full" - ) + const icon = new SvelteUIElement(DynamicMarker, { + marker: this.marker, + rotation: this.rotation, + tags, + }).SetClass("w-full h-full") let badges = undefined if (options?.includeBadges ?? true) { badges = this.GetBadges(tags, options?.metatags) @@ -264,7 +219,6 @@ export default class PointRenderingConfig extends WithContextLoader { tags.map((tags) => this.iconSize.GetRenderValue(tags).Subs(tags).txt ?? "[40,40]").map( (size) => { const [iconW, iconH] = size.split(",").map((x) => num(x)) - console.log("Setting size to", iconW, iconH) iconAndBadges.SetStyle(`width: ${iconW}px; height: ${iconH}px`) } ) @@ -307,9 +261,9 @@ export default class PointRenderingConfig extends WithContextLoader { } return new VariableUiElement( tags.map( - (tags) => { + (tagsData) => { const badgeElements = this.iconBadges.map((badge) => { - if (!badge.if.matchesProperties(tags)) { + if (!badge.if.matchesProperties(tagsData)) { // Doesn't match... return undefined } @@ -324,19 +278,23 @@ export default class PointRenderingConfig extends WithContextLoader { } const htmlDefs = Utils.SubstituteKeys( - badge.then.GetRenderValue(tags)?.txt, - tags + badge.then.GetRenderValue(tagsData)?.txt, + tagsData ) if (htmlDefs.startsWith("<") && htmlDefs.endsWith(">")) { // This is probably an HTML-element - return new FixedUiElement(Utils.SubstituteKeys(htmlDefs, tags)) + return new FixedUiElement(Utils.SubstituteKeys(htmlDefs, tagsData)) .SetStyle("width: 1.5rem") .SetClass("block") } + + if (!htmlDefs) { + return undefined + } + const badgeElement = PointRenderingConfig.FromHtmlMulti( htmlDefs, - "0", - true + tags )?.SetClass("block relative") if (badgeElement === undefined) { return undefined diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index d6d3d15a4..424141bd4 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -19,6 +19,7 @@ import { Paragraph } from "../../UI/Base/Paragraph" import Svg from "../../Svg" import Validators, { ValidatorType } from "../../UI/InputElement/Validators" import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" +import Constants from "../Constants" export interface Icon {} @@ -362,7 +363,7 @@ export default class TagRenderingConfig { if (stripped.endsWith(".svg")) { stripped = stripped.substring(0, stripped.length - 4) } - if (Svg.All[stripped + ".svg"] !== undefined) { + if (Constants.defaultPinIcons.indexOf(stripped) >= 0) { icon = "./assets/svg/" + mapping.icon if (!icon.endsWith(".svg")) { icon += ".svg" diff --git a/src/UI/Base/DragInvitation.svelte b/src/UI/Base/DragInvitation.svelte index c296f0ada..b2eb6c20d 100644 --- a/src/UI/Base/DragInvitation.svelte +++ b/src/UI/Base/DragInvitation.svelte @@ -5,6 +5,7 @@ */ import { Store } from "../../Logic/UIEventSource" import { onDestroy } from "svelte" + import Hand from "../../assets/svg/Hand.svelte"; let mainElem: HTMLElement export let hideSignal: Store @@ -34,7 +35,7 @@
- +
diff --git a/src/UI/Base/LoginToggle.svelte b/src/UI/Base/LoginToggle.svelte index f0dd14003..a3ac0787d 100644 --- a/src/UI/Base/LoginToggle.svelte +++ b/src/UI/Base/LoginToggle.svelte @@ -6,6 +6,7 @@ import Tr from "./Tr.svelte" import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource" + import Invalid from "../../assets/svg/Invalid.svelte"; export let state: { osmConnection: OsmConnection @@ -35,7 +36,7 @@ {:else if $loadingStatus === "error"}
- +
{:else if $loadingStatus === "logged-in"} diff --git a/src/UI/Base/LogoutButton.svelte b/src/UI/Base/LogoutButton.svelte new file mode 100644 index 000000000..020c411e1 --- /dev/null +++ b/src/UI/Base/LogoutButton.svelte @@ -0,0 +1,15 @@ + + + diff --git a/src/UI/Base/OpenJosm.svelte b/src/UI/Base/OpenJosm.svelte new file mode 100644 index 000000000..e6920368f --- /dev/null +++ b/src/UI/Base/OpenJosm.svelte @@ -0,0 +1,48 @@ + +{#if $showButton} + {#if $josmState === undefined} + + {:else if state === "OK"} + + {:else} + + {/if} + + +{/if} diff --git a/src/UI/Base/ShareButton.svelte b/src/UI/Base/ShareButton.svelte index 731cdeb83..48be0f1c9 100644 --- a/src/UI/Base/ShareButton.svelte +++ b/src/UI/Base/ShareButton.svelte @@ -1,6 +1,7 @@ - Svg.mapillary_black_svg().SetClass("w-12 h-12 m-2 mr-4 shrink-0")} /> +
diff --git a/src/UI/BigComponents/NewPointLocationInput.svelte b/src/UI/BigComponents/NewPointLocationInput.svelte index 5ca19ba11..f69ca6b93 100644 --- a/src/UI/BigComponents/NewPointLocationInput.svelte +++ b/src/UI/BigComponents/NewPointLocationInput.svelte @@ -16,6 +16,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import { Utils } from "../../Utils" import { createEventDispatcher } from "svelte" + import Move_arrows from "../../assets/svg/Move_arrows.svelte"; /** * An advanced location input, which has support to: @@ -125,6 +126,6 @@ maxDistanceInMeters="50" > - + diff --git a/src/UI/BigComponents/OpenJosm.ts b/src/UI/BigComponents/OpenJosm.ts deleted file mode 100644 index 3eafbd2ac..000000000 --- a/src/UI/BigComponents/OpenJosm.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Combine from "../Base/Combine" -import { OsmConnection } from "../../Logic/Osm/OsmConnection" -import { Store, UIEventSource } from "../../Logic/UIEventSource" -import { BBox } from "../../Logic/BBox" -import Translations from "../i18n/Translations" -import { VariableUiElement } from "../Base/VariableUIElement" -import Toggle from "../Input/Toggle" -import { SubtleButton } from "../Base/SubtleButton" -import Svg from "../../Svg" -import { Utils } from "../../Utils" -import Constants from "../../Models/Constants" - -export class OpenJosm extends Combine { - public static readonly needsUrls = ["http://127.0.0.1:8111/load_and_zoom"] - constructor(osmConnection: OsmConnection, bounds: Store, iconStyle?: string) { - const t = Translations.t.general.attribution - - const josmState = new UIEventSource(undefined) - // Reset after 15s - josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined)) - - const stateIndication = new VariableUiElement( - josmState.map((state) => { - if (state === undefined) { - return undefined - } - state = state.toUpperCase() - if (state === "OK") { - return t.josmOpened.SetClass("thanks") - } - return t.josmNotOpened.SetClass("alert") - }) - ) - - const toggle = new Toggle( - new SubtleButton(Svg.josm_logo_svg().SetStyle(iconStyle), t.editJosm) - .onClick(() => { - const bbox = bounds.data - if (bbox === undefined) { - return - } - const top = bbox.getNorth() - const bottom = bbox.getSouth() - const right = bbox.getEast() - const left = bbox.getWest() - const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` - Utils.download(josmLink) - .then((answer) => josmState.setData(answer.replace(/\n/g, "").trim())) - .catch(() => josmState.setData("ERROR")) - }) - .SetClass("w-full"), - undefined, - osmConnection.userDetails.map( - (ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible - ) - ) - - super([stateIndication, toggle]) - } -} diff --git a/src/UI/BigComponents/ThemeIntroPanel.svelte b/src/UI/BigComponents/ThemeIntroPanel.svelte index a250489b8..e0dde8cd1 100644 --- a/src/UI/BigComponents/ThemeIntroPanel.svelte +++ b/src/UI/BigComponents/ThemeIntroPanel.svelte @@ -15,6 +15,7 @@ import If from "../Base/If.svelte" import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini" import type { Readable } from "svelte/store" + import Add from "../../assets/svg/Add.svelte"; /** * The theme introduction panel @@ -156,7 +157,7 @@ diff --git a/src/UI/BigComponents/UploadTraceToOsmUI.ts b/src/UI/BigComponents/UploadTraceToOsmUI.ts index a73f7ffef..5344da228 100644 --- a/src/UI/BigComponents/UploadTraceToOsmUI.ts +++ b/src/UI/BigComponents/UploadTraceToOsmUI.ts @@ -12,6 +12,8 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection" import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import { Translation } from "../i18n/Translation" import { LoginToggle } from "../Popup/LoginButton" +import SvelteUIElement from "../Base/SvelteUIElement" +import Upload from "../../assets/svg/Upload.svelte" export default class UploadTraceToOsmUI extends LoginToggle { constructor( @@ -83,7 +85,7 @@ export default class UploadTraceToOsmUI extends LoginToggle { clicked.setData(false) }) .SetClass(""), - new SubtleButton(Svg.upload_svg(), t.confirm).OnClickWithLoading( + new SubtleButton(new SvelteUIElement(Upload, {}), t.confirm).OnClickWithLoading( t.uploading, async () => { const titleStr = UploadTraceToOsmUI.createDefault( @@ -119,7 +121,7 @@ export default class UploadTraceToOsmUI extends LoginToggle { ]).SetClass("flex p-2 rounded-xl border-2 subtle-border items-center"), new Toggle( confirmPanel, - new SubtleButton(Svg.upload_svg(), t.title).onClick(() => + new SubtleButton(new SvelteUIElement(Upload), t.title).onClick(() => clicked.setData(true) ), clicked diff --git a/src/UI/Image/UploadImage.svelte b/src/UI/Image/UploadImage.svelte index d71875c16..fda98ae4e 100644 --- a/src/UI/Image/UploadImage.svelte +++ b/src/UI/Image/UploadImage.svelte @@ -3,16 +3,15 @@ * Shows an 'upload'-button which will start the upload for this feature */ - import type { SpecialVisualizationState } from "../SpecialVisualization" - import { ImmutableStore, Store } from "../../Logic/UIEventSource" - import type { OsmTags } from "../../Models/OsmFeature" - import LoginToggle from "../Base/LoginToggle.svelte" - import Translations from "../i18n/Translations" - import Tr from "../Base/Tr.svelte" - import UploadingImageCounter from "./UploadingImageCounter.svelte" - import FileSelector from "../Base/FileSelector.svelte" - import ToSvelte from "../Base/ToSvelte.svelte" - import Svg from "../../Svg" + import type { SpecialVisualizationState } from "../SpecialVisualization"; + import { ImmutableStore, Store } from "../../Logic/UIEventSource"; + import type { OsmTags } from "../../Models/OsmFeature"; + import LoginToggle from "../Base/LoginToggle.svelte"; + import Translations from "../i18n/Translations"; + import Tr from "../Base/Tr.svelte"; + import UploadingImageCounter from "./UploadingImageCounter.svelte"; + import FileSelector from "../Base/FileSelector.svelte"; + import Camera_plus from "../../assets/svg/Camera_plus.svelte"; export let state: SpecialVisualizationState @@ -58,7 +57,7 @@ {#if image !== undefined} {:else} - + {/if} {#if labelText} {labelText} diff --git a/src/UI/InputElement/Helpers/LocationInput.svelte b/src/UI/InputElement/Helpers/LocationInput.svelte index 1f7d4a8be..306ff2b7b 100644 --- a/src/UI/InputElement/Helpers/LocationInput.svelte +++ b/src/UI/InputElement/Helpers/LocationInput.svelte @@ -12,6 +12,7 @@ import * as turf from "@turf/turf" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import { createEventDispatcher, onDestroy } from "svelte" + import Move_arrows from "../../../assets/svg/Move_arrows.svelte"; /** * A visualisation to pick a location on a map background @@ -90,7 +91,7 @@ class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center p-8 opacity-50" > - +
diff --git a/src/UI/Map/DynamicMarker.svelte b/src/UI/Map/DynamicMarker.svelte index fbf3d1776..ce22eef75 100644 --- a/src/UI/Map/DynamicMarker.svelte +++ b/src/UI/Map/DynamicMarker.svelte @@ -1,21 +1,21 @@ -{#if config !== undefined} -
- {#each icons as icon} +{#if marker && marker} +
+ {#each marker as icon} {/each}
diff --git a/src/UI/PlantNet/PlantNet.svelte b/src/UI/PlantNet/PlantNet.svelte index c8b3896fb..2d01a3362 100644 --- a/src/UI/PlantNet/PlantNet.svelte +++ b/src/UI/PlantNet/PlantNet.svelte @@ -12,6 +12,7 @@ import { createEventDispatcher } from "svelte" import ToSvelte from "../Base/ToSvelte.svelte" import Svg from "../../Svg" + import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte"; /** * The main entry point for the plantnet wizard @@ -142,9 +143,7 @@ {/if}
- +
diff --git a/src/UI/Popup/AddNewPoint/AddNewPoint.svelte b/src/UI/Popup/AddNewPoint/AddNewPoint.svelte index bb94de893..782bfdbd4 100644 --- a/src/UI/Popup/AddNewPoint/AddNewPoint.svelte +++ b/src/UI/Popup/AddNewPoint/AddNewPoint.svelte @@ -3,109 +3,110 @@ * This component ties together all the steps that are needed to create a new point. * There are many subcomponents which help with that */ - import type { SpecialVisualizationState } from "../../SpecialVisualization" - import PresetList from "./PresetList.svelte" - import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig" - import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" - import Tr from "../../Base/Tr.svelte" - import SubtleButton from "../../Base/SubtleButton.svelte" - import FromHtml from "../../Base/FromHtml.svelte" - import Translations from "../../i18n/Translations.js" - import TagHint from "../TagHint.svelte" - import { And } from "../../../Logic/Tags/And.js" - import LoginToggle from "../../Base/LoginToggle.svelte" - import Constants from "../../../Models/Constants.js" - import FilteredLayer from "../../../Models/FilteredLayer" - import { Store, UIEventSource } from "../../../Logic/UIEventSource" - import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid" - import LoginButton from "../../Base/LoginButton.svelte" - import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte" - import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction" - import { OsmWay } from "../../../Logic/Osm/OsmObject" - import { Tag } from "../../../Logic/Tags/Tag" - import type { WayId } from "../../../Models/OsmFeature" - import Loading from "../../Base/Loading.svelte" - import type { GlobalFilter } from "../../../Models/GlobalFilter" - import { onDestroy } from "svelte" - import NextButton from "../../Base/NextButton.svelte" - import BackButton from "../../Base/BackButton.svelte" - import ToSvelte from "../../Base/ToSvelte.svelte" - import Svg from "../../../Svg" - import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte" - import { twJoin } from "tailwind-merge" + import type { SpecialVisualizationState } from "../../SpecialVisualization"; + import PresetList from "./PresetList.svelte"; + import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"; + import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; + import Tr from "../../Base/Tr.svelte"; + import SubtleButton from "../../Base/SubtleButton.svelte"; + import Translations from "../../i18n/Translations.js"; + import TagHint from "../TagHint.svelte"; + import { And } from "../../../Logic/Tags/And.js"; + import LoginToggle from "../../Base/LoginToggle.svelte"; + import Constants from "../../../Models/Constants.js"; + import FilteredLayer from "../../../Models/FilteredLayer"; + import { Store, UIEventSource } from "../../../Logic/UIEventSource"; + import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid"; + import LoginButton from "../../Base/LoginButton.svelte"; + import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte"; + import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction"; + import { OsmWay } from "../../../Logic/Osm/OsmObject"; + import { Tag } from "../../../Logic/Tags/Tag"; + import type { WayId } from "../../../Models/OsmFeature"; + import Loading from "../../Base/Loading.svelte"; + import type { GlobalFilter } from "../../../Models/GlobalFilter"; + import { onDestroy } from "svelte"; + import NextButton from "../../Base/NextButton.svelte"; + import BackButton from "../../Base/BackButton.svelte"; + import ToSvelte from "../../Base/ToSvelte.svelte"; + import Svg from "../../../Svg"; + import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"; + import { twJoin } from "tailwind-merge"; + import Confirm from "../../../assets/svg/Confirm.svelte"; + import Close from "../../../assets/svg/Close.svelte"; - export let coordinate: { lon: number; lat: number } - export let state: SpecialVisualizationState + export let coordinate: { lon: number; lat: number }; + export let state: SpecialVisualizationState; let selectedPreset: { preset: PresetConfig layer: LayerConfig icon: string tags: Record - } = undefined - let checkedOfGlobalFilters: number = 0 - let confirmedCategory = false + } = undefined; + let checkedOfGlobalFilters: number = 0; + let confirmedCategory = false; $: if (selectedPreset === undefined) { - confirmedCategory = false - creating = false - checkedOfGlobalFilters = 0 + confirmedCategory = false; + creating = false; + checkedOfGlobalFilters = 0; } - let flayer: FilteredLayer = undefined - let layerIsDisplayed: UIEventSource | undefined = undefined - let layerHasFilters: Store | undefined = undefined - let globalFilter: UIEventSource = state.layerState.globalFilters - let _globalFilter: GlobalFilter[] = [] + let flayer: FilteredLayer = undefined; + let layerIsDisplayed: UIEventSource | undefined = undefined; + let layerHasFilters: Store | undefined = undefined; + let globalFilter: UIEventSource = state.layerState.globalFilters; + let _globalFilter: GlobalFilter[] = []; onDestroy( globalFilter.addCallbackAndRun((globalFilter) => { - console.log("Global filters are", globalFilter) - _globalFilter = globalFilter ?? [] + console.log("Global filters are", globalFilter); + _globalFilter = globalFilter ?? []; }) - ) + ); $: { - flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id) - layerIsDisplayed = flayer?.isDisplayed - layerHasFilters = flayer?.hasFilter + flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id); + layerIsDisplayed = flayer?.isDisplayed; + layerHasFilters = flayer?.hasFilter; } - const t = Translations.t.general.add + const t = Translations.t.general.add; - const zoom = state.mapProperties.zoom + const zoom = state.mapProperties.zoom; - const isLoading = state.dataIsLoading - let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined) - let snappedToObject: UIEventSource = new UIEventSource(undefined) + const isLoading = state.dataIsLoading; + let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined); + let snappedToObject: UIEventSource = new UIEventSource(undefined); // Small helper variable: if the map is tapped, we should let the 'Next'-button grab some attention as users have to click _that_ to continue, not the map - let preciseInputIsTapped = false + let preciseInputIsTapped = false; - let creating = false + let creating = false; /** * Call when the user should restart the flow by clicking on the map, e.g. because they disabled filters. * Will delete the lastclick-location */ function abort() { - state.selectedElement.setData(undefined) + state.selectedElement.setData(undefined); // When aborted, we force the contributors to place the pin _again_ // This is because there might be a nearby object that was disabled; this forces them to re-evaluate the map - state.lastClickObject.features.setData([]) - preciseInputIsTapped = false + state.lastClickObject.features.setData([]); + preciseInputIsTapped = false; } async function confirm() { - creating = true - const location: { lon: number; lat: number } = preciseCoordinate.data - const snapTo: WayId | undefined = snappedToObject.data + creating = true; + const location: { lon: number; lat: number } = preciseCoordinate.data; + const snapTo: WayId | undefined = snappedToObject.data; const tags: Tag[] = selectedPreset.preset.tags.concat( ..._globalFilter.map((f) => f?.onNewPoint?.tags ?? []) - ) - console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags) + ); + console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags); - let snapToWay: undefined | OsmWay = undefined + let snapToWay: undefined | OsmWay = undefined; if (snapTo !== undefined && snapTo !== null) { - const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0) + const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0); if (downloaded !== "deleted") { - snapToWay = downloaded + snapToWay = downloaded; } } @@ -113,44 +114,44 @@ theme: state.layout?.id ?? "unkown", changeType: "create", snapOnto: snapToWay, - reusePointWithinMeters: 1, - }) - await state.changes.applyAction(newElementAction) - state.newFeatures.features.ping() + reusePointWithinMeters: 1 + }); + await state.changes.applyAction(newElementAction); + state.newFeatures.features.ping(); // The 'changes' should have created a new point, which added this into the 'featureProperties' - const newId = newElementAction.newElementId - console.log("Applied pending changes, fetching store for", newId) - const tagsStore = state.featureProperties.getStore(newId) + const newId = newElementAction.newElementId; + console.log("Applied pending changes, fetching store for", newId); + const tagsStore = state.featureProperties.getStore(newId); if (!tagsStore) { - console.error("Bug: no tagsStore found for", newId) + console.error("Bug: no tagsStore found for", newId); } { // Set some metainfo - const properties = tagsStore.data + const properties = tagsStore.data; if (snapTo) { // metatags (starting with underscore) are not uploaded, so we can safely mark this - delete properties["_referencing_ways"] - properties["_referencing_ways"] = `["${snapTo}"]` + delete properties["_referencing_ways"]; + properties["_referencing_ways"] = `["${snapTo}"]`; } - properties["_backend"] = state.osmConnection.Backend() - properties["_last_edit:timestamp"] = new Date().toISOString() - const userdetails = state.osmConnection.userDetails.data - properties["_last_edit:contributor"] = userdetails.name - properties["_last_edit:uid"] = "" + userdetails.uid - tagsStore.ping() + properties["_backend"] = state.osmConnection.Backend(); + properties["_last_edit:timestamp"] = new Date().toISOString(); + const userdetails = state.osmConnection.userDetails.data; + properties["_last_edit:contributor"] = userdetails.name; + properties["_last_edit:uid"] = "" + userdetails.uid; + tagsStore.ping(); } - const feature = state.indexedFeatures.featuresById.data.get(newId) - console.log("Selecting feature", feature, "and opening their popup") - abort() - state.selectedLayer.setData(selectedPreset.layer) - state.selectedElement.setData(feature) - tagsStore.ping() + const feature = state.indexedFeatures.featuresById.data.get(newId); + console.log("Selecting feature", feature, "and opening their popup"); + abort(); + state.selectedLayer.setData(selectedPreset.layer); + state.selectedElement.setData(feature); + tagsStore.ping(); } function confirmSync() { confirm() .then((_) => console.debug("New point successfully handled")) - .catch((e) => console.error("Handling the new point went wrong due to", e)) + .catch((e) => console.error("Handling the new point went wrong due to", e)); } @@ -285,7 +286,7 @@ (confirmedCategory = true)} clss="primary w-full">
- +
@@ -299,11 +300,7 @@ checkedOfGlobalFilters = checkedOfGlobalFilters + 1 }} > - + - + {:else if !creating} diff --git a/src/UI/Popup/CreateNewNote.svelte b/src/UI/Popup/CreateNewNote.svelte index 6cd1f5153..9cc70d560 100644 --- a/src/UI/Popup/CreateNewNote.svelte +++ b/src/UI/Popup/CreateNewNote.svelte @@ -15,6 +15,8 @@ import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte" import ToSvelte from "../Base/ToSvelte.svelte" import Svg from "../../Svg" + import Layers from "../../assets/svg/Layers.svelte"; + import AddSmall from "../../assets/svg/AddSmall.svelte"; export let coordinate: UIEventSource<{ lon: number; lat: number }> export let state: SpecialVisualizationState @@ -97,7 +99,7 @@
notelayer.disableAllFilters()}> - + @@ -126,7 +128,7 @@ {#if $comment?.length >= 3} - + {:else} @@ -143,7 +145,7 @@ - + diff --git a/src/UI/Popup/NearbyImagesCollapsed.svelte b/src/UI/Popup/NearbyImagesCollapsed.svelte index 0a315dcb3..c78917a77 100644 --- a/src/UI/Popup/NearbyImagesCollapsed.svelte +++ b/src/UI/Popup/NearbyImagesCollapsed.svelte @@ -11,6 +11,7 @@ import ToSvelte from "../Base/ToSvelte.svelte" import { XCircleIcon } from "@babeard/svelte-heroicons/solid" import exp from "constants" + import Camera_plus from "../../assets/svg/Camera_plus.svelte"; export let tags: Store export let state: SpecialVisualizationState @@ -42,7 +43,7 @@ expanded = true }} > - + {/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte index 9db0c8ccd..94f28218f 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte @@ -23,6 +23,8 @@ import UserRelatedState from "../../../Logic/State/UserRelatedState" import { twJoin } from "tailwind-merge" import { TagUtils } from "../../../Logic/Tags/TagUtils" + import Search from "../../../assets/svg/Search.svelte"; + import Login from "../../../assets/svg/Login.svelte"; export let config: TagRenderingConfig export let tags: UIEventSource> @@ -217,7 +219,7 @@ {#if config.mappings?.length >= 8}
- +
{/if} @@ -324,7 +326,7 @@ state?.osmConnection?.AttemptLogin()}> - + {#if $feedback !== undefined} diff --git a/src/UI/Reviews/AllReviews.svelte b/src/UI/Reviews/AllReviews.svelte index 75d5ec63c..fc4b635d0 100644 --- a/src/UI/Reviews/AllReviews.svelte +++ b/src/UI/Reviews/AllReviews.svelte @@ -12,6 +12,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import ToSvelte from "../Base/ToSvelte.svelte" import Svg from "../../Svg" + import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte"; /** * An element showing all reviews @@ -40,7 +41,7 @@ {/if}
- +
diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 2c65d6228..aea5f3906 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -58,7 +58,6 @@ import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" -import { OpenJosm } from "./BigComponents/OpenJosm" import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" import FediverseValidator from "./InputElement/Validators/FediverseValidator" import SendEmail from "./Popup/SendEmail.svelte" @@ -78,6 +77,8 @@ import { TagUtils } from "../Logic/Tags/TagUtils" import Giggity from "./BigComponents/Giggity.svelte" import ThemeViewState from "../Models/ThemeViewState" import LanguagePicker from "./InputElement/LanguagePicker.svelte" +import LogoutButton from "./Base/LogoutButton.svelte" +import OpenJosm from "./Base/OpenJosm.svelte" class NearbyImageVis implements SpecialVisualization { // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests @@ -465,11 +466,7 @@ export default class SpecialVisualizations { needsUrls: [Constants.osmAuthConfig.url], docs: "Shows a button where the user can log out", constr(state: SpecialVisualizationState): BaseUIElement { - return new SubtleButton(Svg.logout_svg(), Translations.t.general.logout, { - imgSize: "w-6 h-6", - }).onClick(() => { - state.osmConnection.LogOut() - }) + return new SvelteUIElement(LogoutButton, { osmConnection: state.osmConnection }) }, }, new HistogramViz(), @@ -903,10 +900,10 @@ export default class SpecialVisualizations { funcName: "open_in_josm", docs: "Opens the current view in the JOSM-editor", args: [], - needsUrls: OpenJosm.needsUrls, + needsUrls: ["http://127.0.0.1:8111/load_and_zoom"], constr: (state) => { - return new OpenJosm(state.osmConnection, state.mapProperties.bounds) + return new SvelteUIElement(OpenJosm, { state }) }, }, { @@ -1099,12 +1096,6 @@ export default class SpecialVisualizations { if (maproulette_id_key === "" || maproulette_id_key === undefined) { maproulette_id_key = "mr_taskId" } - if (Svg.All[image] !== undefined || Svg.All[image + ".svg"] !== undefined) { - if (image.endsWith(".svg")) { - image = image.substring(0, image.length - 4) - } - image = Svg[image + "_svg"]() - } const failed = new UIEventSource(false) const closeButton = new SubtleButton(image, message).OnClickWithLoading( diff --git a/src/UI/StudioGUI.svelte b/src/UI/StudioGUI.svelte index bd2d6cf14..cfdfda1ae 100644 --- a/src/UI/StudioGUI.svelte +++ b/src/UI/StudioGUI.svelte @@ -29,6 +29,7 @@ import { Utils } from "../Utils"; import Translations from "./i18n/Translations"; import Tr from "./Base/Tr.svelte"; + import Add from "../assets/svg/Add.svelte"; export let studioUrl = window.location.hostname === "127.0.0.2" @@ -197,7 +198,7 @@ Show the introduction again - + diff --git a/src/UI/ThemeViewGUI.svelte b/src/UI/ThemeViewGUI.svelte index 1bf9f0cb8..04356ee27 100644 --- a/src/UI/ThemeViewGUI.svelte +++ b/src/UI/ThemeViewGUI.svelte @@ -43,18 +43,25 @@ import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"; import IfHidden from "./Base/IfHidden.svelte"; import { onDestroy } from "svelte"; - import { OpenJosm } from "./BigComponents/OpenJosm"; import MapillaryLink from "./BigComponents/MapillaryLink.svelte"; import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"; import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"; import StateIndicator from "./BigComponents/StateIndicator.svelte"; - import Locale from "./i18n/Locale"; import ShareScreen from "./BigComponents/ShareScreen.svelte"; import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"; import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"; import Cross from "../assets/svg/Cross.svelte"; import Summary from "./BigComponents/Summary.svelte"; import LanguagePicker from "./InputElement/LanguagePicker.svelte"; + import Mastodon from "../assets/svg/Mastodon.svelte"; + import Bug from "../assets/svg/Bug.svelte"; + import Liberapay from "../assets/svg/Liberapay.svelte"; + import OpenJosm from "./Base/OpenJosm.svelte"; + import Min from "../assets/svg/Min.svelte"; + import Plus from "../assets/svg/Plus.svelte"; + import Filter from "../assets/svg/Filter.svelte"; + import Add from "../assets/svg/Add.svelte"; + import Statistics from "../assets/svg/Statistics.svelte"; export let state: ThemeViewState; let layout = state.layout; @@ -205,7 +212,7 @@ state.guistate.openFilterView()}> - + @@ -244,10 +251,10 @@ mapproperties.zoom.update((z) => z + 1)}> - + mapproperties.zoom.update((z) => z - 1)}> - + @@ -340,7 +347,7 @@
- +
@@ -431,27 +438,27 @@ - + - + - + - + - + {Constants.vNumber} @@ -503,10 +510,7 @@
- - new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")} - /> + diff --git a/src/UI/Wikipedia/WikipediaArticle.svelte b/src/UI/Wikipedia/WikipediaArticle.svelte index 9ffba16e4..ab37314ef 100644 --- a/src/UI/Wikipedia/WikipediaArticle.svelte +++ b/src/UI/Wikipedia/WikipediaArticle.svelte @@ -9,6 +9,7 @@ import WikidataPreviewBox from "./WikidataPreviewBox" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" + import Wikipedia from "../../assets/svg/Wikipedia.svelte"; /** * Shows a wikipedia-article + wikidata preview for the given item @@ -18,7 +19,7 @@ {#if $wikipediaDetails.articleUrl} - + {/if} diff --git a/src/assets/svg/No_checkmark.svelte b/src/assets/svg/No_checkmark.svelte deleted file mode 100644 index a0721c478..000000000 --- a/src/assets/svg/No_checkmark.svelte +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/src/assets/svg/None.svelte b/src/assets/svg/None.svelte deleted file mode 100644 index d9222c9c8..000000000 --- a/src/assets/svg/None.svelte +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/src/assets/svg/Star_outline_half.svelte b/src/assets/svg/Star_outline_half.svelte deleted file mode 100644 index 1c209b5e5..000000000 --- a/src/assets/svg/Star_outline_half.svelte +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/svg/Wikipedia.svelte b/src/assets/svg/Wikipedia.svelte index fa9d45da1..6164f2f83 100644 --- a/src/assets/svg/Wikipedia.svelte +++ b/src/assets/svg/Wikipedia.svelte @@ -1,4 +1,4 @@ - Wikipedia logo version 2 \ No newline at end of file + Wikipedia logo version 2 \ No newline at end of file diff --git a/test/CodeQuality.spec.ts b/test/CodeQuality.spec.ts index dcf5d27eb..513fcaafb 100644 --- a/test/CodeQuality.spec.ts +++ b/test/CodeQuality.spec.ts @@ -4,14 +4,16 @@ import { describe, it } from "vitest" import { parse as parse_html } from "node-html-parser" import { readFileSync } from "fs" import ScriptUtils from "../scripts/ScriptUtils" - +function detectInCode(forbidden: string, reason: string) { + return wrap(detectInCodeUnwrapped(forbidden, reason)) +} /** * * @param forbidden: a GREP-regex. This means that '.' is a wildcard and should be escaped to match a literal dot * @param reason * @private */ -function detectInCode(forbidden: string, reason: string): Promise { +function detectInCodeUnwrapped(forbidden: string, reason: string): Promise { return new Promise((done) => { const excludedDirs = [ ".git", @@ -24,37 +26,35 @@ function detectInCode(forbidden: string, reason: string): Promise { ".idea/", ] - exec( + const command = 'grep -n "' + - forbidden + - '" -r . ' + - excludedDirs.map((d) => "--exclude-dir=" + d).join(" "), - (error, stdout, stderr) => { - if (error?.message?.startsWith("Command failed: grep")) { - console.warn("Command failed!", error) - return - } - if (error !== null) { - throw error - } - if (stderr !== "") { - throw stderr - } - - const found = stdout - .split("\n") - .filter((s) => s !== "") - .filter((s) => !s.startsWith("./test/")) - if (found.length > 0) { - const msg = `Found a '${forbidden}' at \n ${found.join( - "\n " - )}.\n ${reason}` - console.error(msg) - console.error(found.length, "issues found") - throw msg - } + forbidden + + '" -r . ' + + excludedDirs.map((d) => "--exclude-dir=" + d).join(" ") + console.log(command) + exec(command, (error, stdout, stderr) => { + if (error?.message?.startsWith("Command failed: grep")) { + console.warn("Command failed!", error) + throw error } - ) + if (error !== null) { + throw error + } + if (stderr !== "") { + throw stderr + } + + const found = stdout + .split("\n") + .filter((s) => s !== "") + .filter((s) => !s.startsWith("./test/")) + if (found.length > 0) { + const msg = `Found a '${forbidden}' at \n ${found.join("\n ")}.\n ${reason}` + console.error(msg) + console.error(found.length, "issues found") + throw msg + } + }) }) } @@ -64,10 +64,6 @@ function wrap(promise: Promise): (done: () => void) => void { } } -function itAsync(name: string, promise: Promise) { - it(name, wrap(promise)) -} - function validateScriptIntegrityOf(path: string) { const htmlContents = readFileSync(path, "utf8") const doc = parse_html(htmlContents) @@ -95,7 +91,7 @@ function validateScriptIntegrityOf(path: string) { } describe("Code quality", () => { - itAsync( + it( "should not contain reverse", detectInCode( "reverse()", @@ -103,12 +99,12 @@ describe("Code quality", () => { ) ) - itAsync( + it( "should not contain 'constructor.name'", detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") ) - itAsync( + it( "should not contain 'innerText'", detectInCode( "innerText",