forked from MapComplete/MapComplete
		
	Performance: trim svg.ts, use Svelte-SVG-componenets where possible (WIP)
This commit is contained in:
		
							parent
							
								
									ac5c60c3f0
								
							
						
					
					
						commit
						c13d80f062
					
				
					 37 changed files with 367 additions and 378 deletions
				
			
		|  | @ -160,7 +160,7 @@ | ||||||
|               "craft=key_cutter" |               "craft=key_cutter" | ||||||
|             ] |             ] | ||||||
|           }, |           }, | ||||||
|           "then": "./assets/layers/id_presets/fas-key.svg" |           "then": "circle:white;./assets/layers/id_presets/fas-key.svg" | ||||||
|         } |         } | ||||||
|       ], |       ], | ||||||
|       "label": { |       "label": { | ||||||
|  |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?> |  | ||||||
| <svg xmlns="http://www.w3.org/2000/svg" width="374px" height="259px" viewBox="0 0 374 259" version="1.1"> |  | ||||||
|   <g id="surface1"/> |  | ||||||
| </svg> |  | ||||||
| Before Width: | Height: | Size: 189 B | 
|  | @ -1,4 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?> |  | ||||||
| <svg xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1"> |  | ||||||
|   <g id="surface1"/> |  | ||||||
| </svg> |  | ||||||
| Before Width: | Height: | Size: 189 B | 
|  | @ -1,11 +1,47 @@ | ||||||
| import * as fs from "fs" | import * as fs from "fs" | ||||||
|  | 
 | ||||||
| function genImages(dryrun = false) { | function genImages(dryrun = false) { | ||||||
|     console.log("Generating images") |     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") |     const dir = fs.readdirSync("./assets/svg") | ||||||
| 
 | 
 | ||||||
|     let module = |     let module = | ||||||
|         'import Img from "./UI/Base/Img";\nimport {FixedUiElement} from "./UI/Base/FixedUiElement";\n\n/* @deprecated */\nexport default class Svg {\n\n\n' |         '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) { |     for (const path of dir) { | ||||||
|         if (path.endsWith("license_info.json")) { |         if (path.endsWith("license_info.json")) { | ||||||
|             continue |             continue | ||||||
|  | @ -21,6 +57,7 @@ function genImages(dryrun = false) { | ||||||
|         let svg: string = fs |         let svg: string = fs | ||||||
|             .readFileSync("./assets/svg/" + path, "utf-8") |             .readFileSync("./assets/svg/" + path, "utf-8") | ||||||
|             .replace(/<\?xml.*?>/, "") |             .replace(/<\?xml.*?>/, "") | ||||||
|  |             .replace(/<!DOCTYPE [^>]*>/, "") | ||||||
|             .replace(/fill: ?none;/g, "fill: none !important;") // This is such a brittle hack...
 |             .replace(/fill: ?none;/g, "fill: none !important;") // This is such a brittle hack...
 | ||||||
|             .replace(/\n/g, " ") |             .replace(/\n/g, " ") | ||||||
|             .replace(/\r/g, "") |             .replace(/\r/g, "") | ||||||
|  | @ -37,18 +74,6 @@ function genImages(dryrun = false) { | ||||||
|         } |         } | ||||||
|         const name = path.substring(0, path.length - 4).replace(/[ -]/g, "_") |         const name = path.substring(0, path.length - 4).replace(/[ -]/g, "_") | ||||||
| 
 | 
 | ||||||
|         if (dryrun) { |  | ||||||
|             svg = "<omitting svg - dryrun>" |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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 nameUC = name.toUpperCase().at(0) + name.substring(1) | ||||||
|         const svelteCode = |         const svelteCode = | ||||||
|             '<script>\nexport let color = "#000000"\n</script>\n' + |             '<script>\nexport let color = "#000000"\n</script>\n' + | ||||||
|  | @ -60,8 +85,23 @@ function genImages(dryrun = false) { | ||||||
|                 .replace(/\\"/g, '"') |                 .replace(/\\"/g, '"') | ||||||
|                 .replace(/(rgb\(0%,0%,0%\)|#000000|#000)/g, "{color}") |                 .replace(/(rgb\(0%,0%,0%\)|#000000|#000)/g, "{color}") | ||||||
|         fs.writeFileSync("./src/assets/svg/" + nameUC + ".svelte", svelteCode, "utf8") |         fs.writeFileSync("./src/assets/svg/" + nameUC + ".svelte", svelteCode, "utf8") | ||||||
|  | 
 | ||||||
|  |         if (blacklist.some((item) => path.toLowerCase().endsWith(item + ".svg"))) { | ||||||
|  |             continue | ||||||
|  |         } | ||||||
|  |         if (dryrun) { | ||||||
|  |             svg = "<omitting svg - dryrun>" | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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" |     module += "}\n" | ||||||
|     fs.writeFileSync("src/Svg.ts", module) |     fs.writeFileSync("src/Svg.ts", module) | ||||||
|     console.log("Done") |     console.log("Done") | ||||||
|  |  | ||||||
|  | @ -92,7 +92,7 @@ export class DoesImageExist extends DesugaringStep<string> { | ||||||
|             return image |             return image | ||||||
|         } |         } | ||||||
|         if (image.match(/[a-z]*/)) { |         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'
 |                 // This is a builtin img, e.g. 'checkmark' or 'crosshair'
 | ||||||
|                 return image |                 return image | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import { VariableUiElement } from "../../UI/Base/VariableUIElement" | ||||||
| import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" | ||||||
| import SvelteUIElement from "../../UI/Base/SvelteUIElement" | import SvelteUIElement from "../../UI/Base/SvelteUIElement" | ||||||
| import DynamicMarker from "../../UI/Map/DynamicMarker.svelte" | import DynamicMarker from "../../UI/Map/DynamicMarker.svelte" | ||||||
|  | import { html } from "svelte/types/compiler/utils/namespaces" | ||||||
| 
 | 
 | ||||||
| export class IconConfig extends WithContextLoader { | export class IconConfig extends WithContextLoader { | ||||||
|     public static readonly defaultIcon = new IconConfig({ icon: "pin", color: "#ff9939" }) |     public static readonly defaultIcon = new IconConfig({ icon: "pin", color: "#ff9939" }) | ||||||
|  | @ -133,71 +134,23 @@ export default class PointRenderingConfig extends WithContextLoader { | ||||||
|             context + ".rotationAlignment" |             context + ".rotationAlignment" | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 |     private static FromHtmlMulti(multiSpec: string, tags: Store<Record<string, string>>) { | ||||||
|     /** |         const icons: IconConfig[] = [] | ||||||
|      * Given a single HTML spec (either a single image path OR "image_path_to_known_svg:fill-colour", returns a fixedUIElement containing that |         for (const subspec of multiSpec.split(";")) { | ||||||
|      * The element will fill 100% and be positioned absolutely with top:0 and left: 0 |             const [icon, color] = subspec.split(":") | ||||||
|      */ |             icons.push(new IconConfig({ icon, color })) | ||||||
|     private static FromHtmlSpec(htmlSpec: string, style: string, isBadge = false): BaseUIElement { |  | ||||||
|         if (htmlSpec === undefined) { |  | ||||||
|             return undefined |  | ||||||
|         } |         } | ||||||
|         const match = htmlSpec.match(/([a-zA-Z0-9_]*):([^;]*)/) |         return new SvelteUIElement(DynamicMarker, { marker: icons, tags }).SetClass( | ||||||
|         if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { |             "w-full h-full block absolute top-0 left-0" | ||||||
|             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(`<img src="${htmlSpec}" style="${style}" />`) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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) |  | ||||||
|         ) |         ) | ||||||
|         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<string, string>): BaseUIElement { |     public GetBaseIcon(tags?: Record<string, string>): 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( |     public RenderIcon( | ||||||
|  | @ -249,9 +202,11 @@ export default class PointRenderingConfig extends WithContextLoader { | ||||||
|             anchorH = -iconH / 2 |             anchorH = -iconH / 2 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const icon = new SvelteUIElement(DynamicMarker, { config: this, tags }).SetClass( |         const icon = new SvelteUIElement(DynamicMarker, { | ||||||
|             "w-full h-full" |             marker: this.marker, | ||||||
|         ) |             rotation: this.rotation, | ||||||
|  |             tags, | ||||||
|  |         }).SetClass("w-full h-full") | ||||||
|         let badges = undefined |         let badges = undefined | ||||||
|         if (options?.includeBadges ?? true) { |         if (options?.includeBadges ?? true) { | ||||||
|             badges = this.GetBadges(tags, options?.metatags) |             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( |         tags.map((tags) => this.iconSize.GetRenderValue(tags).Subs(tags).txt ?? "[40,40]").map( | ||||||
|             (size) => { |             (size) => { | ||||||
|                 const [iconW, iconH] = size.split(",").map((x) => num(x)) |                 const [iconW, iconH] = size.split(",").map((x) => num(x)) | ||||||
|                 console.log("Setting size to", iconW, iconH) |  | ||||||
|                 iconAndBadges.SetStyle(`width: ${iconW}px; height: ${iconH}px`) |                 iconAndBadges.SetStyle(`width: ${iconW}px; height: ${iconH}px`) | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | @ -307,9 +261,9 @@ export default class PointRenderingConfig extends WithContextLoader { | ||||||
|         } |         } | ||||||
|         return new VariableUiElement( |         return new VariableUiElement( | ||||||
|             tags.map( |             tags.map( | ||||||
|                 (tags) => { |                 (tagsData) => { | ||||||
|                     const badgeElements = this.iconBadges.map((badge) => { |                     const badgeElements = this.iconBadges.map((badge) => { | ||||||
|                         if (!badge.if.matchesProperties(tags)) { |                         if (!badge.if.matchesProperties(tagsData)) { | ||||||
|                             // Doesn't match...
 |                             // Doesn't match...
 | ||||||
|                             return undefined |                             return undefined | ||||||
|                         } |                         } | ||||||
|  | @ -324,19 +278,23 @@ export default class PointRenderingConfig extends WithContextLoader { | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         const htmlDefs = Utils.SubstituteKeys( |                         const htmlDefs = Utils.SubstituteKeys( | ||||||
|                             badge.then.GetRenderValue(tags)?.txt, |                             badge.then.GetRenderValue(tagsData)?.txt, | ||||||
|                             tags |                             tagsData | ||||||
|                         ) |                         ) | ||||||
|                         if (htmlDefs.startsWith("<") && htmlDefs.endsWith(">")) { |                         if (htmlDefs.startsWith("<") && htmlDefs.endsWith(">")) { | ||||||
|                             // This is probably an HTML-element
 |                             // This is probably an HTML-element
 | ||||||
|                             return new FixedUiElement(Utils.SubstituteKeys(htmlDefs, tags)) |                             return new FixedUiElement(Utils.SubstituteKeys(htmlDefs, tagsData)) | ||||||
|                                 .SetStyle("width: 1.5rem") |                                 .SetStyle("width: 1.5rem") | ||||||
|                                 .SetClass("block") |                                 .SetClass("block") | ||||||
|                         } |                         } | ||||||
|  | 
 | ||||||
|  |                         if (!htmlDefs) { | ||||||
|  |                             return undefined | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|                         const badgeElement = PointRenderingConfig.FromHtmlMulti( |                         const badgeElement = PointRenderingConfig.FromHtmlMulti( | ||||||
|                             htmlDefs, |                             htmlDefs, | ||||||
|                             "0", |                             tags | ||||||
|                             true |  | ||||||
|                         )?.SetClass("block relative") |                         )?.SetClass("block relative") | ||||||
|                         if (badgeElement === undefined) { |                         if (badgeElement === undefined) { | ||||||
|                             return undefined |                             return undefined | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ import { Paragraph } from "../../UI/Base/Paragraph" | ||||||
| import Svg from "../../Svg" | import Svg from "../../Svg" | ||||||
| import Validators, { ValidatorType } from "../../UI/InputElement/Validators" | import Validators, { ValidatorType } from "../../UI/InputElement/Validators" | ||||||
| import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" | import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" | ||||||
|  | import Constants from "../Constants" | ||||||
| 
 | 
 | ||||||
| export interface Icon {} | export interface Icon {} | ||||||
| 
 | 
 | ||||||
|  | @ -362,7 +363,7 @@ export default class TagRenderingConfig { | ||||||
|                 if (stripped.endsWith(".svg")) { |                 if (stripped.endsWith(".svg")) { | ||||||
|                     stripped = stripped.substring(0, stripped.length - 4) |                     stripped = stripped.substring(0, stripped.length - 4) | ||||||
|                 } |                 } | ||||||
|                 if (Svg.All[stripped + ".svg"] !== undefined) { |                 if (Constants.defaultPinIcons.indexOf(stripped) >= 0) { | ||||||
|                     icon = "./assets/svg/" + mapping.icon |                     icon = "./assets/svg/" + mapping.icon | ||||||
|                     if (!icon.endsWith(".svg")) { |                     if (!icon.endsWith(".svg")) { | ||||||
|                         icon += ".svg" |                         icon += ".svg" | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
|    */ |    */ | ||||||
|   import { Store } from "../../Logic/UIEventSource" |   import { Store } from "../../Logic/UIEventSource" | ||||||
|   import { onDestroy } from "svelte" |   import { onDestroy } from "svelte" | ||||||
|  |   import Hand from "../../assets/svg/Hand.svelte"; | ||||||
| 
 | 
 | ||||||
|   let mainElem: HTMLElement |   let mainElem: HTMLElement | ||||||
|   export let hideSignal: Store<any> |   export let hideSignal: Store<any> | ||||||
|  | @ -34,7 +35,7 @@ | ||||||
| 
 | 
 | ||||||
| <div bind:this={mainElem} class="pointer-events-none absolute bottom-0 right-0 h-full w-full"> | <div bind:this={mainElem} class="pointer-events-none absolute bottom-0 right-0 h-full w-full"> | ||||||
|   <div id="hand-container"> |   <div id="hand-container"> | ||||||
|     <img src="./assets/svg/hand.svg" /> |     <Hand /> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
|   import Tr from "./Tr.svelte" |   import Tr from "./Tr.svelte" | ||||||
|   import { OsmConnection } from "../../Logic/Osm/OsmConnection" |   import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||||
|   import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource" |   import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource" | ||||||
|  |   import Invalid from "../../assets/svg/Invalid.svelte"; | ||||||
| 
 | 
 | ||||||
|   export let state: { |   export let state: { | ||||||
|     osmConnection: OsmConnection |     osmConnection: OsmConnection | ||||||
|  | @ -35,7 +36,7 @@ | ||||||
|     </slot> |     </slot> | ||||||
|   {:else if $loadingStatus === "error"} |   {:else if $loadingStatus === "error"} | ||||||
|     <div class="alert max-w-64 flex items-center"> |     <div class="alert max-w-64 flex items-center"> | ||||||
|       <img src="./assets/svg/invalid.svg" class="m-2 h-8 w-8 shrink-0" /> |       <Invalid class="m-2 h-8 w-8 shrink-0" /> | ||||||
|       <Tr t={offlineModes[$apiState]} /> |       <Tr t={offlineModes[$apiState]} /> | ||||||
|     </div> |     </div> | ||||||
|   {:else if $loadingStatus === "logged-in"} |   {:else if $loadingStatus === "logged-in"} | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								src/UI/Base/LogoutButton.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/UI/Base/LogoutButton.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <script lang="ts"> | ||||||
|  |   import { OsmConnection } from "../../Logic/Osm/OsmConnection"; | ||||||
|  |   import Logout from "../../assets/svg/Logout.svelte"; | ||||||
|  |   import Translations from "../i18n/Translations"; | ||||||
|  |   import Tr from "./Tr.svelte"; | ||||||
|  | 
 | ||||||
|  |   export let osmConnection: OsmConnection | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <button on:click={() => { | ||||||
|  |                         state.osmConnection.LogOut() | ||||||
|  |                     }}> | ||||||
|  |   <Logout class="w-6 h-6"/> | ||||||
|  |   <Tr t={Translations.t.general.logout}/> | ||||||
|  | </button> | ||||||
							
								
								
									
										48
									
								
								src/UI/Base/OpenJosm.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/UI/Base/OpenJosm.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | <script lang="ts"> | ||||||
|  |   import { UIEventSource } from "../../Logic/UIEventSource"; | ||||||
|  |   import Translations from "../i18n/Translations"; | ||||||
|  |   import Tr from "./Tr.svelte"; | ||||||
|  |   import Josm_logo from "../../assets/svg/Josm_logo.svelte"; | ||||||
|  |   import Constants from "../../Models/Constants"; | ||||||
|  |   import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||||
|  | 
 | ||||||
|  |   export let state : SpecialVisualizationState | ||||||
|  |   const t = Translations.t.general.attribution; | ||||||
|  |   const josmState = new UIEventSource<"OK" | string>(undefined); | ||||||
|  |   // Reset after 15s | ||||||
|  |   josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined)); | ||||||
|  | 
 | ||||||
|  |   const showButton = state.osmConnection.userDetails.map( | ||||||
|  |     (ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   function openJosm() { | ||||||
|  |     const bbox = state.mapProperties. 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")); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | {#if $showButton} | ||||||
|  |   {#if $josmState === undefined} | ||||||
|  |     <!-- empty --> | ||||||
|  |   {:else if state === "OK"} | ||||||
|  |     <Tr cls="thanks" t={t.josmOpened} /> | ||||||
|  |   {:else} | ||||||
|  |     <Tr cls="alert" t={t.josmNotOpened} /> | ||||||
|  |   {/if} | ||||||
|  | 
 | ||||||
|  |   <button class="flex items-center" on:click={openJosm}> | ||||||
|  |     <Josm_logo class="h-12 w-12 p-2 pr-4" /> | ||||||
|  |     <Tr t={t.editJosm} /> | ||||||
|  |   </button> | ||||||
|  | {/if} | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import ToSvelte from "./ToSvelte.svelte" |   import ToSvelte from "./ToSvelte.svelte" | ||||||
|   import Svg from "../../Svg" |   import Svg from "../../Svg" | ||||||
|  |   import Share from "../../assets/svg/Share.svelte"; | ||||||
| 
 | 
 | ||||||
|   export let generateShareData: () => { |   export let generateShareData: () => { | ||||||
|     text: string |     text: string | ||||||
|  | @ -25,6 +26,6 @@ | ||||||
| 
 | 
 | ||||||
| <button on:click={share} class="secondary m-0 h-8 w-8 p-0"> | <button on:click={share} class="secondary m-0 h-8 w-8 p-0"> | ||||||
|   <slot name="content"> |   <slot name="content"> | ||||||
|     <ToSvelte construct={Svg.share_svg().SetClass("w-7 h-7 p-1")} /> |     <Share class="w-7 h-7 p-1"/> | ||||||
|   </slot> |   </slot> | ||||||
| </button> | </button> | ||||||
|  |  | ||||||
|  | @ -23,10 +23,10 @@ export default class SvelteUIElement< | ||||||
|     private readonly _events: Events |     private readonly _events: Events | ||||||
|     private readonly _slots: Slots |     private readonly _slots: Slots | ||||||
| 
 | 
 | ||||||
|     constructor(svelteElement, props: Props, events?: Events, slots?: Slots) { |     constructor(svelteElement, props?: Props, events?: Events, slots?: Slots) { | ||||||
|         super() |         super() | ||||||
|         this._svelteComponent = svelteElement |         this._svelteComponent = svelteElement | ||||||
|         this._props = props |         this._props = props ?? <Props>{} | ||||||
|         this._events = events |         this._events = events | ||||||
|         this._slots = slots |         this._slots = slots | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import Locale from "../i18n/Locale" |   import Locale from "../i18n/Locale" | ||||||
|   import LinkToWeblate from "./LinkToWeblate" |   import LinkToWeblate from "./LinkToWeblate" | ||||||
|  |   import Translate from "../../assets/svg/Translate.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Shows a small icon which will open up weblate; a contributor can translate the item for 'context' there |    * Shows a small icon which will open up weblate; a contributor can translate the item for 'context' there | ||||||
|  | @ -19,7 +20,7 @@ | ||||||
|       target="_blank" |       target="_blank" | ||||||
|       class="weblate-link mx-1" |       class="weblate-link mx-1" | ||||||
|     > |     > | ||||||
|       <img src="./assets/svg/translate.svg" class="font-gray" /> |       <Translate class="font-gray" /> | ||||||
|     </a> |     </a> | ||||||
|   {:else if $linkToWeblate} |   {:else if $linkToWeblate} | ||||||
|     <a |     <a | ||||||
|  | @ -27,7 +28,7 @@ | ||||||
|       class="weblate-link hidden-on-mobile mx-1" |       class="weblate-link hidden-on-mobile mx-1" | ||||||
|       target="_blank" |       target="_blank" | ||||||
|     > |     > | ||||||
|       <img src="./assets/svg/translate.svg" class="font-gray inline-block" /> |       <Translate class="font-gray inline-block" /> | ||||||
|     </a> |     </a> | ||||||
|   {/if} |   {/if} | ||||||
| {/if} | {/if} | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
|   import { Store } from "../../Logic/UIEventSource" |   import { Store } from "../../Logic/UIEventSource" | ||||||
|   import Tr from "../Base/Tr.svelte" |   import Tr from "../Base/Tr.svelte" | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import ToSvelte from "../Base/ToSvelte.svelte" | ||||||
|  |   import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"; | ||||||
| 
 | 
 | ||||||
|   /* |   /* | ||||||
|     A subtleButton which opens mapillary in a new tab at the current location |     A subtleButton which opens mapillary in a new tab at the current location | ||||||
|  | @ -21,7 +22,7 @@ | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <a class="button flex items-center" href={mapillaryLink} target="_blank"> | <a class="button flex items-center" href={mapillaryLink} target="_blank"> | ||||||
|   <ToSvelte construct={() => Svg.mapillary_black_svg().SetClass("w-12 h-12 m-2 mr-4 shrink-0")} /> |   <Mapillary_black class="w-12 h-12 m-2 mr-4 shrink-0"/> | ||||||
|   <div class="flex flex-col"> |   <div class="flex flex-col"> | ||||||
|     <Tr t={Translations.t.general.attribution.openMapillary} /> |     <Tr t={Translations.t.general.attribution.openMapillary} /> | ||||||
|     <Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} /> |     <Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} /> | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ | ||||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" |   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||||
|   import { Utils } from "../../Utils" |   import { Utils } from "../../Utils" | ||||||
|   import { createEventDispatcher } from "svelte" |   import { createEventDispatcher } from "svelte" | ||||||
|  |   import Move_arrows from "../../assets/svg/Move_arrows.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * An advanced location input, which has support to: |    * An advanced location input, which has support to: | ||||||
|  | @ -125,6 +126,6 @@ | ||||||
|   maxDistanceInMeters="50" |   maxDistanceInMeters="50" | ||||||
| > | > | ||||||
|   <slot name="image" slot="image"> |   <slot name="image" slot="image"> | ||||||
|     <img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" /> |     <Move_arrows  class="h-full max-h-24" /> | ||||||
|   </slot> |   </slot> | ||||||
| </LocationInput> | </LocationInput> | ||||||
|  |  | ||||||
|  | @ -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<BBox>, iconStyle?: string) { |  | ||||||
|         const t = Translations.t.general.attribution |  | ||||||
| 
 |  | ||||||
|         const josmState = new UIEventSource<string>(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]) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
|   import If from "../Base/If.svelte" |   import If from "../Base/If.svelte" | ||||||
|   import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini" |   import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini" | ||||||
|   import type { Readable } from "svelte/store" |   import type { Readable } from "svelte/store" | ||||||
|  |   import Add from "../../assets/svg/Add.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * The theme introduction panel |    * The theme introduction panel | ||||||
|  | @ -156,7 +157,7 @@ | ||||||
|   <div class="links-as-button links-w-full m-2 flex flex-col gap-y-1"> |   <div class="links-as-button links-w-full m-2 flex flex-col gap-y-1"> | ||||||
|     <!-- bottom buttons, a bit hidden away: switch layout --> |     <!-- bottom buttons, a bit hidden away: switch layout --> | ||||||
|     <a class="flex" href={Utils.HomepageLink()}> |     <a class="flex" href={Utils.HomepageLink()}> | ||||||
|       <img class="h-6 w-6" src="./assets/svg/add.svg" /> |       <Add  class="h-6 w-6"/> | ||||||
|       <Tr t={Translations.t.general.backToIndex} /> |       <Tr t={Translations.t.general.backToIndex} /> | ||||||
|     </a> |     </a> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||||
| import { Translation } from "../i18n/Translation" | import { Translation } from "../i18n/Translation" | ||||||
| import { LoginToggle } from "../Popup/LoginButton" | import { LoginToggle } from "../Popup/LoginButton" | ||||||
|  | import SvelteUIElement from "../Base/SvelteUIElement" | ||||||
|  | import Upload from "../../assets/svg/Upload.svelte" | ||||||
| 
 | 
 | ||||||
| export default class UploadTraceToOsmUI extends LoginToggle { | export default class UploadTraceToOsmUI extends LoginToggle { | ||||||
|     constructor( |     constructor( | ||||||
|  | @ -83,7 +85,7 @@ export default class UploadTraceToOsmUI extends LoginToggle { | ||||||
|                         clicked.setData(false) |                         clicked.setData(false) | ||||||
|                     }) |                     }) | ||||||
|                     .SetClass(""), |                     .SetClass(""), | ||||||
|                 new SubtleButton(Svg.upload_svg(), t.confirm).OnClickWithLoading( |                 new SubtleButton(new SvelteUIElement(Upload, {}), t.confirm).OnClickWithLoading( | ||||||
|                     t.uploading, |                     t.uploading, | ||||||
|                     async () => { |                     async () => { | ||||||
|                         const titleStr = UploadTraceToOsmUI.createDefault( |                         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"), |                     ]).SetClass("flex p-2 rounded-xl border-2 subtle-border items-center"), | ||||||
|                     new Toggle( |                     new Toggle( | ||||||
|                         confirmPanel, |                         confirmPanel, | ||||||
|                         new SubtleButton(Svg.upload_svg(), t.title).onClick(() => |                         new SubtleButton(new SvelteUIElement(Upload), t.title).onClick(() => | ||||||
|                             clicked.setData(true) |                             clicked.setData(true) | ||||||
|                         ), |                         ), | ||||||
|                         clicked |                         clicked | ||||||
|  |  | ||||||
|  | @ -3,16 +3,15 @@ | ||||||
|    * Shows an 'upload'-button which will start the upload for this feature |    * Shows an 'upload'-button which will start the upload for this feature | ||||||
|    */ |    */ | ||||||
| 
 | 
 | ||||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" |   import type { SpecialVisualizationState } from "../SpecialVisualization"; | ||||||
|   import { ImmutableStore, Store } from "../../Logic/UIEventSource" |   import { ImmutableStore, Store } from "../../Logic/UIEventSource"; | ||||||
|   import type { OsmTags } from "../../Models/OsmFeature" |   import type { OsmTags } from "../../Models/OsmFeature"; | ||||||
|   import LoginToggle from "../Base/LoginToggle.svelte" |   import LoginToggle from "../Base/LoginToggle.svelte"; | ||||||
|   import Translations from "../i18n/Translations" |   import Translations from "../i18n/Translations"; | ||||||
|   import Tr from "../Base/Tr.svelte" |   import Tr from "../Base/Tr.svelte"; | ||||||
|   import UploadingImageCounter from "./UploadingImageCounter.svelte" |   import UploadingImageCounter from "./UploadingImageCounter.svelte"; | ||||||
|   import FileSelector from "../Base/FileSelector.svelte" |   import FileSelector from "../Base/FileSelector.svelte"; | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import Camera_plus from "../../assets/svg/Camera_plus.svelte"; | ||||||
|   import Svg from "../../Svg" |  | ||||||
| 
 | 
 | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState | ||||||
| 
 | 
 | ||||||
|  | @ -58,7 +57,7 @@ | ||||||
|         {#if image !== undefined} |         {#if image !== undefined} | ||||||
|           <img src={image} /> |           <img src={image} /> | ||||||
|         {:else} |         {:else} | ||||||
|           <ToSvelte construct={Svg.camera_plus_svg().SetClass("block w-12 h-12 p-1 text-4xl ")} /> |           <Camera_plus class="block w-12 h-12 p-1 text-4xl"/> | ||||||
|         {/if} |         {/if} | ||||||
|         {#if labelText} |         {#if labelText} | ||||||
|           {labelText} |           {labelText} | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
|   import * as turf from "@turf/turf" |   import * as turf from "@turf/turf" | ||||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" |   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" | ||||||
|   import { createEventDispatcher, onDestroy } from "svelte" |   import { createEventDispatcher, onDestroy } from "svelte" | ||||||
|  |   import Move_arrows from "../../../assets/svg/Move_arrows.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * A visualisation to pick a location on a map background |    * 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" |     class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center p-8 opacity-50" | ||||||
|   > |   > | ||||||
|     <slot name="image"> |     <slot name="image"> | ||||||
|       <img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" /> |       <Move_arrows class="h-full max-h-24"/> | ||||||
|     </slot> |     </slot> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,21 +1,21 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import PointRenderingConfig, { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig" |   import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig"; | ||||||
|   import { Store } from "../../Logic/UIEventSource" |   import { ImmutableStore, Store } from "../../Logic/UIEventSource"; | ||||||
|   import DynamicIcon from "./DynamicIcon.svelte" |   import DynamicIcon from "./DynamicIcon.svelte"; | ||||||
|  |   import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Renders a 'marker', which consists of multiple 'icons' |    * Renders a 'marker', which consists of multiple 'icons' | ||||||
|    */ |    */ | ||||||
|   export let config: PointRenderingConfig |   export let marker: IconConfig[] = config?.marker; | ||||||
|   let icons: IconConfig[] = config.marker |   export let rotation: TagRenderingConfig | ||||||
|   export let tags: Store<Record<string, string>> |   export let tags: Store<Record<string, string>>; | ||||||
|   let rotation = tags.map(tags => config.rotation.GetRenderValue(tags).Subs(tags).txt) |   let _rotation = rotation ? tags.map(tags => rotation.GetRenderValue(tags).Subs(tags).txt) : new ImmutableStore(0); | ||||||
| 
 |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| {#if config !== undefined} | {#if marker && marker} | ||||||
|   <div class="relative h-full w-full" style={`transform: rotate(${$rotation})`}> |   <div class="relative h-full w-full" style={`transform: rotate(${$_rotation})`}> | ||||||
|     {#each icons as icon} |     {#each marker as icon} | ||||||
|       <DynamicIcon {icon} {tags} /> |       <DynamicIcon {icon} {tags} /> | ||||||
|     {/each} |     {/each} | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
|   import { createEventDispatcher } from "svelte" |   import { createEventDispatcher } from "svelte" | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import ToSvelte from "../Base/ToSvelte.svelte" | ||||||
|   import Svg from "../../Svg" |   import Svg from "../../Svg" | ||||||
|  |   import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * The main entry point for the plantnet wizard |    * The main entry point for the plantnet wizard | ||||||
|  | @ -142,9 +143,7 @@ | ||||||
|     </BackButton> |     </BackButton> | ||||||
|   {/if} |   {/if} | ||||||
|   <div class="low-interaction flex self-end rounded-xl p-2"> |   <div class="low-interaction flex self-end rounded-xl p-2"> | ||||||
|     <ToSvelte |     <Plantnet_logo class="w-8 h-8 p-1 mr-1 bg-white rounded-full"/> | ||||||
|       construct={Svg.plantnet_logo_svg().SetClass("w-8 h-8 p-1 mr-1 bg-white rounded-full")} |  | ||||||
|     /> |  | ||||||
|     <Tr t={t.poweredByPlantnet} /> |     <Tr t={t.poweredByPlantnet} /> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -3,109 +3,110 @@ | ||||||
|    * This component ties together all the steps that are needed to create a new point. |    * This component ties together all the steps that are needed to create a new point. | ||||||
|    * There are many subcomponents which help with that |    * There are many subcomponents which help with that | ||||||
|    */ |    */ | ||||||
|   import type { SpecialVisualizationState } from "../../SpecialVisualization" |   import type { SpecialVisualizationState } from "../../SpecialVisualization"; | ||||||
|   import PresetList from "./PresetList.svelte" |   import PresetList from "./PresetList.svelte"; | ||||||
|   import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig" |   import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"; | ||||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" |   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; | ||||||
|   import Tr from "../../Base/Tr.svelte" |   import Tr from "../../Base/Tr.svelte"; | ||||||
|   import SubtleButton from "../../Base/SubtleButton.svelte" |   import SubtleButton from "../../Base/SubtleButton.svelte"; | ||||||
|   import FromHtml from "../../Base/FromHtml.svelte" |   import Translations from "../../i18n/Translations.js"; | ||||||
|   import Translations from "../../i18n/Translations.js" |   import TagHint from "../TagHint.svelte"; | ||||||
|   import TagHint from "../TagHint.svelte" |   import { And } from "../../../Logic/Tags/And.js"; | ||||||
|   import { And } from "../../../Logic/Tags/And.js" |   import LoginToggle from "../../Base/LoginToggle.svelte"; | ||||||
|   import LoginToggle from "../../Base/LoginToggle.svelte" |   import Constants from "../../../Models/Constants.js"; | ||||||
|   import Constants from "../../../Models/Constants.js" |   import FilteredLayer from "../../../Models/FilteredLayer"; | ||||||
|   import FilteredLayer from "../../../Models/FilteredLayer" |   import { Store, UIEventSource } from "../../../Logic/UIEventSource"; | ||||||
|   import { Store, UIEventSource } from "../../../Logic/UIEventSource" |   import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||||
|   import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid" |   import LoginButton from "../../Base/LoginButton.svelte"; | ||||||
|   import LoginButton from "../../Base/LoginButton.svelte" |   import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte"; | ||||||
|   import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte" |   import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction"; | ||||||
|   import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction" |   import { OsmWay } from "../../../Logic/Osm/OsmObject"; | ||||||
|   import { OsmWay } from "../../../Logic/Osm/OsmObject" |   import { Tag } from "../../../Logic/Tags/Tag"; | ||||||
|   import { Tag } from "../../../Logic/Tags/Tag" |   import type { WayId } from "../../../Models/OsmFeature"; | ||||||
|   import type { WayId } from "../../../Models/OsmFeature" |   import Loading from "../../Base/Loading.svelte"; | ||||||
|   import Loading from "../../Base/Loading.svelte" |   import type { GlobalFilter } from "../../../Models/GlobalFilter"; | ||||||
|   import type { GlobalFilter } from "../../../Models/GlobalFilter" |   import { onDestroy } from "svelte"; | ||||||
|   import { onDestroy } from "svelte" |   import NextButton from "../../Base/NextButton.svelte"; | ||||||
|   import NextButton from "../../Base/NextButton.svelte" |   import BackButton from "../../Base/BackButton.svelte"; | ||||||
|   import BackButton from "../../Base/BackButton.svelte" |   import ToSvelte from "../../Base/ToSvelte.svelte"; | ||||||
|   import ToSvelte from "../../Base/ToSvelte.svelte" |   import Svg from "../../../Svg"; | ||||||
|   import Svg from "../../../Svg" |   import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"; | ||||||
|   import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte" |   import { twJoin } from "tailwind-merge"; | ||||||
|   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 coordinate: { lon: number; lat: number }; | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState; | ||||||
| 
 | 
 | ||||||
|   let selectedPreset: { |   let selectedPreset: { | ||||||
|     preset: PresetConfig |     preset: PresetConfig | ||||||
|     layer: LayerConfig |     layer: LayerConfig | ||||||
|     icon: string |     icon: string | ||||||
|     tags: Record<string, string> |     tags: Record<string, string> | ||||||
|   } = undefined |   } = undefined; | ||||||
|   let checkedOfGlobalFilters: number = 0 |   let checkedOfGlobalFilters: number = 0; | ||||||
|   let confirmedCategory = false |   let confirmedCategory = false; | ||||||
|   $: if (selectedPreset === undefined) { |   $: if (selectedPreset === undefined) { | ||||||
|     confirmedCategory = false |     confirmedCategory = false; | ||||||
|     creating = false |     creating = false; | ||||||
|     checkedOfGlobalFilters = 0 |     checkedOfGlobalFilters = 0; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   let flayer: FilteredLayer = undefined |   let flayer: FilteredLayer = undefined; | ||||||
|   let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined |   let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined; | ||||||
|   let layerHasFilters: Store<boolean> | undefined = undefined |   let layerHasFilters: Store<boolean> | undefined = undefined; | ||||||
|   let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters |   let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters; | ||||||
|   let _globalFilter: GlobalFilter[] = [] |   let _globalFilter: GlobalFilter[] = []; | ||||||
|   onDestroy( |   onDestroy( | ||||||
|     globalFilter.addCallbackAndRun((globalFilter) => { |     globalFilter.addCallbackAndRun((globalFilter) => { | ||||||
|       console.log("Global filters are", globalFilter) |       console.log("Global filters are", globalFilter); | ||||||
|       _globalFilter = globalFilter ?? [] |       _globalFilter = globalFilter ?? []; | ||||||
|     }) |     }) | ||||||
|   ) |   ); | ||||||
|   $: { |   $: { | ||||||
|     flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id) |     flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id); | ||||||
|     layerIsDisplayed = flayer?.isDisplayed |     layerIsDisplayed = flayer?.isDisplayed; | ||||||
|     layerHasFilters = flayer?.hasFilter |     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 |   const isLoading = state.dataIsLoading; | ||||||
|   let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined) |   let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined); | ||||||
|   let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined) |   let snappedToObject: UIEventSource<string> = new UIEventSource<string>(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 |   // 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. |    * Call when the user should restart the flow by clicking on the map, e.g. because they disabled filters. | ||||||
|    * Will delete the lastclick-location |    * Will delete the lastclick-location | ||||||
|    */ |    */ | ||||||
|   function abort() { |   function abort() { | ||||||
|     state.selectedElement.setData(undefined) |     state.selectedElement.setData(undefined); | ||||||
|     // When aborted, we force the contributors to place the pin _again_ |     // 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 |     // This is because there might be a nearby object that was disabled; this forces them to re-evaluate the map | ||||||
|     state.lastClickObject.features.setData([]) |     state.lastClickObject.features.setData([]); | ||||||
|     preciseInputIsTapped = false |     preciseInputIsTapped = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async function confirm() { |   async function confirm() { | ||||||
|     creating = true |     creating = true; | ||||||
|     const location: { lon: number; lat: number } = preciseCoordinate.data |     const location: { lon: number; lat: number } = preciseCoordinate.data; | ||||||
|     const snapTo: WayId | undefined = <WayId>snappedToObject.data |     const snapTo: WayId | undefined = <WayId>snappedToObject.data; | ||||||
|     const tags: Tag[] = selectedPreset.preset.tags.concat( |     const tags: Tag[] = selectedPreset.preset.tags.concat( | ||||||
|       ..._globalFilter.map((f) => f?.onNewPoint?.tags ?? []) |       ..._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) { |     if (snapTo !== undefined && snapTo !== null) { | ||||||
|       const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0) |       const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0); | ||||||
|       if (downloaded !== "deleted") { |       if (downloaded !== "deleted") { | ||||||
|         snapToWay = downloaded |         snapToWay = downloaded; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -113,44 +114,44 @@ | ||||||
|       theme: state.layout?.id ?? "unkown", |       theme: state.layout?.id ?? "unkown", | ||||||
|       changeType: "create", |       changeType: "create", | ||||||
|       snapOnto: snapToWay, |       snapOnto: snapToWay, | ||||||
|       reusePointWithinMeters: 1, |       reusePointWithinMeters: 1 | ||||||
|     }) |     }); | ||||||
|     await state.changes.applyAction(newElementAction) |     await state.changes.applyAction(newElementAction); | ||||||
|     state.newFeatures.features.ping() |     state.newFeatures.features.ping(); | ||||||
|     // The 'changes' should have created a new point, which added this into the 'featureProperties' |     // The 'changes' should have created a new point, which added this into the 'featureProperties' | ||||||
|     const newId = newElementAction.newElementId |     const newId = newElementAction.newElementId; | ||||||
|     console.log("Applied pending changes, fetching store for", newId) |     console.log("Applied pending changes, fetching store for", newId); | ||||||
|     const tagsStore = state.featureProperties.getStore(newId) |     const tagsStore = state.featureProperties.getStore(newId); | ||||||
|     if (!tagsStore) { |     if (!tagsStore) { | ||||||
|       console.error("Bug: no tagsStore found for", newId) |       console.error("Bug: no tagsStore found for", newId); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|       // Set some metainfo |       // Set some metainfo | ||||||
|       const properties = tagsStore.data |       const properties = tagsStore.data; | ||||||
|       if (snapTo) { |       if (snapTo) { | ||||||
|         // metatags (starting with underscore) are not uploaded, so we can safely mark this |         // metatags (starting with underscore) are not uploaded, so we can safely mark this | ||||||
|         delete properties["_referencing_ways"] |         delete properties["_referencing_ways"]; | ||||||
|         properties["_referencing_ways"] = `["${snapTo}"]` |         properties["_referencing_ways"] = `["${snapTo}"]`; | ||||||
|       } |       } | ||||||
|       properties["_backend"] = state.osmConnection.Backend() |       properties["_backend"] = state.osmConnection.Backend(); | ||||||
|       properties["_last_edit:timestamp"] = new Date().toISOString() |       properties["_last_edit:timestamp"] = new Date().toISOString(); | ||||||
|       const userdetails = state.osmConnection.userDetails.data |       const userdetails = state.osmConnection.userDetails.data; | ||||||
|       properties["_last_edit:contributor"] = userdetails.name |       properties["_last_edit:contributor"] = userdetails.name; | ||||||
|       properties["_last_edit:uid"] = "" + userdetails.uid |       properties["_last_edit:uid"] = "" + userdetails.uid; | ||||||
|       tagsStore.ping() |       tagsStore.ping(); | ||||||
|     } |     } | ||||||
|     const feature = state.indexedFeatures.featuresById.data.get(newId) |     const feature = state.indexedFeatures.featuresById.data.get(newId); | ||||||
|     console.log("Selecting feature", feature, "and opening their popup") |     console.log("Selecting feature", feature, "and opening their popup"); | ||||||
|     abort() |     abort(); | ||||||
|     state.selectedLayer.setData(selectedPreset.layer) |     state.selectedLayer.setData(selectedPreset.layer); | ||||||
|     state.selectedElement.setData(feature) |     state.selectedElement.setData(feature); | ||||||
|     tagsStore.ping() |     tagsStore.ping(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function confirmSync() { |   function confirmSync() { | ||||||
|     confirm() |     confirm() | ||||||
|       .then((_) => console.debug("New point successfully handled")) |       .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)); | ||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -285,7 +286,7 @@ | ||||||
|         <NextButton on:click={() => (confirmedCategory = true)} clss="primary w-full"> |         <NextButton on:click={() => (confirmedCategory = true)} clss="primary w-full"> | ||||||
|           <div slot="image" class="relative"> |           <div slot="image" class="relative"> | ||||||
|             <ToSvelte construct={selectedPreset.icon} /> |             <ToSvelte construct={selectedPreset.icon} /> | ||||||
|             <img class="absolute bottom-0 right-0 h-4 w-4" src="./assets/svg/confirm.svg" /> |             <Confirm class="absolute bottom-0 right-0 h-4 w-4" /> | ||||||
|           </div> |           </div> | ||||||
|           <div class="w-full"> |           <div class="w-full"> | ||||||
|             <Tr t={selectedPreset.text} /> |             <Tr t={selectedPreset.text} /> | ||||||
|  | @ -299,11 +300,7 @@ | ||||||
|           checkedOfGlobalFilters = checkedOfGlobalFilters + 1 |           checkedOfGlobalFilters = checkedOfGlobalFilters + 1 | ||||||
|         }} |         }} | ||||||
|       > |       > | ||||||
|         <img |         <Confirm slot="image" class="h-12 w-12" /> | ||||||
|           slot="image" |  | ||||||
|           src={_globalFilter[checkedOfGlobalFilters].onNewPoint?.icon ?? "./assets/svg/confirm.svg"} |  | ||||||
|           class="h-12 w-12" |  | ||||||
|         /> |  | ||||||
|         <Tr |         <Tr | ||||||
|           slot="message" |           slot="message" | ||||||
|           t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.confirmAddNew.Subs({ |           t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.confirmAddNew.Subs({ | ||||||
|  | @ -317,7 +314,7 @@ | ||||||
|           abort() |           abort() | ||||||
|         }} |         }} | ||||||
|       > |       > | ||||||
|         <img slot="image" src="./assets/svg/close.svg" class="h-8 w-8" /> |         <Close slot="image" class="h-8 w-8" /> | ||||||
|         <Tr slot="message" t={Translations.t.general.cancel} /> |         <Tr slot="message" t={Translations.t.general.cancel} /> | ||||||
|       </SubtleButton> |       </SubtleButton> | ||||||
|     {:else if !creating} |     {:else if !creating} | ||||||
|  |  | ||||||
|  | @ -15,6 +15,8 @@ | ||||||
|   import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte" |   import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte" | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import ToSvelte from "../Base/ToSvelte.svelte" | ||||||
|   import Svg from "../../Svg" |   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 coordinate: UIEventSource<{ lon: number; lat: number }> | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState | ||||||
|  | @ -97,7 +99,7 @@ | ||||||
|           <Tr t={Translations.t.notes.noteLayerHasFilters} /> |           <Tr t={Translations.t.notes.noteLayerHasFilters} /> | ||||||
|         </div> |         </div> | ||||||
|         <SubtleButton on:click={() => notelayer.disableAllFilters()}> |         <SubtleButton on:click={() => notelayer.disableAllFilters()}> | ||||||
|           <img slot="image" src="./assets/svg/filter.svg" class="mr-4 h-8 w-8" /> |           <Layers class="mr-4 h-8 w-8"/> | ||||||
|           <Tr slot="message" t={Translations.t.notes.disableAllNoteFilters} /> |           <Tr slot="message" t={Translations.t.notes.disableAllNoteFilters} /> | ||||||
|         </SubtleButton> |         </SubtleButton> | ||||||
|       </div> |       </div> | ||||||
|  | @ -126,7 +128,7 @@ | ||||||
| 
 | 
 | ||||||
|           {#if $comment?.length >= 3} |           {#if $comment?.length >= 3} | ||||||
|             <SubtleButton on:click={uploadNote}> |             <SubtleButton on:click={uploadNote}> | ||||||
|               <img slot="image" src="./assets/svg/addSmall.svg" class="mr-4 h-8 w-8" /> |               <AddSmall slot="image" class="mr-4 h-8 w-8" /> | ||||||
|               <Tr slot="message" t={Translations.t.notes.createNote} /> |               <Tr slot="message" t={Translations.t.notes.createNote} /> | ||||||
|             </SubtleButton> |             </SubtleButton> | ||||||
|           {:else} |           {:else} | ||||||
|  | @ -143,7 +145,7 @@ | ||||||
|         <Tr t={Translations.t.notes.noteLayerNotEnabled} /> |         <Tr t={Translations.t.notes.noteLayerNotEnabled} /> | ||||||
|       </div> |       </div> | ||||||
|       <SubtleButton on:click={enableNoteLayer}> |       <SubtleButton on:click={enableNoteLayer}> | ||||||
|         <img slot="image" src="./assets/svg/layers.svg" class="mr-4 h-8 w-8" /> |         <Layers slot="image" class="mr-4 h-8 w-8" /> | ||||||
|         <Tr slot="message" t={Translations.t.notes.noteLayerDoEnable} /> |         <Tr slot="message" t={Translations.t.notes.noteLayerDoEnable} /> | ||||||
|       </SubtleButton> |       </SubtleButton> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import ToSvelte from "../Base/ToSvelte.svelte" | ||||||
|   import { XCircleIcon } from "@babeard/svelte-heroicons/solid" |   import { XCircleIcon } from "@babeard/svelte-heroicons/solid" | ||||||
|   import exp from "constants" |   import exp from "constants" | ||||||
|  |   import Camera_plus from "../../assets/svg/Camera_plus.svelte"; | ||||||
| 
 | 
 | ||||||
|   export let tags: Store<OsmTags> |   export let tags: Store<OsmTags> | ||||||
|   export let state: SpecialVisualizationState |   export let state: SpecialVisualizationState | ||||||
|  | @ -42,7 +43,7 @@ | ||||||
|       expanded = true |       expanded = true | ||||||
|     }} |     }} | ||||||
|   > |   > | ||||||
|     <ToSvelte construct={Svg.camera_plus_svg().SetClass("block w-8 h-8 p-1 mr-2 ")} /> |     <Camera_plus class="block w-8 h-8 p-1 mr-2"/> | ||||||
|     <Tr t={t.seeNearby} /> |     <Tr t={t.seeNearby} /> | ||||||
|   </button> |   </button> | ||||||
| {/if} | {/if} | ||||||
|  |  | ||||||
|  | @ -23,6 +23,8 @@ | ||||||
|   import UserRelatedState from "../../../Logic/State/UserRelatedState" |   import UserRelatedState from "../../../Logic/State/UserRelatedState" | ||||||
|   import { twJoin } from "tailwind-merge" |   import { twJoin } from "tailwind-merge" | ||||||
|   import { TagUtils } from "../../../Logic/Tags/TagUtils" |   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 config: TagRenderingConfig | ||||||
|   export let tags: UIEventSource<Record<string, string>> |   export let tags: UIEventSource<Record<string, string>> | ||||||
|  | @ -217,7 +219,7 @@ | ||||||
| 
 | 
 | ||||||
|     {#if config.mappings?.length >= 8} |     {#if config.mappings?.length >= 8} | ||||||
|       <div class="sticky flex w-full"> |       <div class="sticky flex w-full"> | ||||||
|         <img src="./assets/svg/search.svg" class="h-6 w-6" /> |         <Search class="h-6 w-6"/> | ||||||
|         <input type="text" bind:value={$searchTerm} class="w-full" /> |         <input type="text" bind:value={$searchTerm} class="w-full" /> | ||||||
|       </div> |       </div> | ||||||
|     {/if} |     {/if} | ||||||
|  | @ -324,7 +326,7 @@ | ||||||
|     <LoginToggle {state}> |     <LoginToggle {state}> | ||||||
|       <Loading slot="loading" /> |       <Loading slot="loading" /> | ||||||
|       <SubtleButton slot="not-logged-in" on:click={() => state?.osmConnection?.AttemptLogin()}> |       <SubtleButton slot="not-logged-in" on:click={() => state?.osmConnection?.AttemptLogin()}> | ||||||
|         <img slot="image" src="./assets/svg/login.svg" class="h-8 w-8" /> |         <Login slot="image" class="h-8 w-8" /> | ||||||
|         <Tr t={Translations.t.general.loginToStart} slot="message" /> |         <Tr t={Translations.t.general.loginToStart} slot="message" /> | ||||||
|       </SubtleButton> |       </SubtleButton> | ||||||
|       {#if $feedback !== undefined} |       {#if $feedback !== undefined} | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" |   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||||
|   import ToSvelte from "../Base/ToSvelte.svelte" |   import ToSvelte from "../Base/ToSvelte.svelte" | ||||||
|   import Svg from "../../Svg" |   import Svg from "../../Svg" | ||||||
|  |   import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * An element showing all reviews |    * An element showing all reviews | ||||||
|  | @ -40,7 +41,7 @@ | ||||||
|     <Tr t={Translations.t.reviews.no_reviews_yet} /> |     <Tr t={Translations.t.reviews.no_reviews_yet} /> | ||||||
|   {/if} |   {/if} | ||||||
|   <div class="flex justify-end"> |   <div class="flex justify-end"> | ||||||
|     <ToSvelte construct={Svg.mangrove_logo_svg().SetClass("w-12 h-12 shrink-0 p-1 ")} /> |     <Mangrove_logo class="w-12 h-12 shrink-0 p-1"/> | ||||||
|     <Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} /> |     <Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} /> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -58,7 +58,6 @@ import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz | ||||||
| import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" | import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" | ||||||
| import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" | import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" | ||||||
| import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" | import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" | ||||||
| import { OpenJosm } from "./BigComponents/OpenJosm" |  | ||||||
| import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | ||||||
| import FediverseValidator from "./InputElement/Validators/FediverseValidator" | import FediverseValidator from "./InputElement/Validators/FediverseValidator" | ||||||
| import SendEmail from "./Popup/SendEmail.svelte" | import SendEmail from "./Popup/SendEmail.svelte" | ||||||
|  | @ -78,6 +77,8 @@ import { TagUtils } from "../Logic/Tags/TagUtils" | ||||||
| import Giggity from "./BigComponents/Giggity.svelte" | import Giggity from "./BigComponents/Giggity.svelte" | ||||||
| import ThemeViewState from "../Models/ThemeViewState" | import ThemeViewState from "../Models/ThemeViewState" | ||||||
| import LanguagePicker from "./InputElement/LanguagePicker.svelte" | import LanguagePicker from "./InputElement/LanguagePicker.svelte" | ||||||
|  | import LogoutButton from "./Base/LogoutButton.svelte" | ||||||
|  | import OpenJosm from "./Base/OpenJosm.svelte" | ||||||
| 
 | 
 | ||||||
| class NearbyImageVis implements SpecialVisualization { | class NearbyImageVis implements SpecialVisualization { | ||||||
|     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 |     // 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], |                 needsUrls: [Constants.osmAuthConfig.url], | ||||||
|                 docs: "Shows a button where the user can log out", |                 docs: "Shows a button where the user can log out", | ||||||
|                 constr(state: SpecialVisualizationState): BaseUIElement { |                 constr(state: SpecialVisualizationState): BaseUIElement { | ||||||
|                     return new SubtleButton(Svg.logout_svg(), Translations.t.general.logout, { |                     return new SvelteUIElement(LogoutButton, { osmConnection: state.osmConnection }) | ||||||
|                         imgSize: "w-6 h-6", |  | ||||||
|                     }).onClick(() => { |  | ||||||
|                         state.osmConnection.LogOut() |  | ||||||
|                     }) |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|             new HistogramViz(), |             new HistogramViz(), | ||||||
|  | @ -903,10 +900,10 @@ export default class SpecialVisualizations { | ||||||
|                 funcName: "open_in_josm", |                 funcName: "open_in_josm", | ||||||
|                 docs: "Opens the current view in the JOSM-editor", |                 docs: "Opens the current view in the JOSM-editor", | ||||||
|                 args: [], |                 args: [], | ||||||
|                 needsUrls: OpenJosm.needsUrls, |                 needsUrls: ["http://127.0.0.1:8111/load_and_zoom"], | ||||||
| 
 | 
 | ||||||
|                 constr: (state) => { |                 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) { |                     if (maproulette_id_key === "" || maproulette_id_key === undefined) { | ||||||
|                         maproulette_id_key = "mr_taskId" |                         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 failed = new UIEventSource(false) | ||||||
| 
 | 
 | ||||||
|                     const closeButton = new SubtleButton(image, message).OnClickWithLoading( |                     const closeButton = new SubtleButton(image, message).OnClickWithLoading( | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
|   import { Utils } from "../Utils"; |   import { Utils } from "../Utils"; | ||||||
|   import Translations from "./i18n/Translations"; |   import Translations from "./i18n/Translations"; | ||||||
|   import Tr from "./Base/Tr.svelte"; |   import Tr from "./Base/Tr.svelte"; | ||||||
|  |   import Add from "../assets/svg/Add.svelte"; | ||||||
| 
 | 
 | ||||||
|   export let studioUrl = |   export let studioUrl = | ||||||
|     window.location.hostname === "127.0.0.2" |     window.location.hostname === "127.0.0.2" | ||||||
|  | @ -197,7 +198,7 @@ | ||||||
|             Show the introduction again |             Show the introduction again | ||||||
|           </button> |           </button> | ||||||
|           <a class="flex button" href={Utils.HomepageLink()}> |           <a class="flex button" href={Utils.HomepageLink()}> | ||||||
|             <img class="h-6 w-6" src="./assets/svg/add.svg" /> |             <Add class="h-6 w-6" /> | ||||||
|             <Tr t={Translations.t.general.backToIndex} /> |             <Tr t={Translations.t.general.backToIndex} /> | ||||||
|           </a> |           </a> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  | @ -43,18 +43,25 @@ | ||||||
|   import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"; |   import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"; | ||||||
|   import IfHidden from "./Base/IfHidden.svelte"; |   import IfHidden from "./Base/IfHidden.svelte"; | ||||||
|   import { onDestroy } from "svelte"; |   import { onDestroy } from "svelte"; | ||||||
|   import { OpenJosm } from "./BigComponents/OpenJosm"; |  | ||||||
|   import MapillaryLink from "./BigComponents/MapillaryLink.svelte"; |   import MapillaryLink from "./BigComponents/MapillaryLink.svelte"; | ||||||
|   import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"; |   import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"; | ||||||
|   import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"; |   import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"; | ||||||
|   import StateIndicator from "./BigComponents/StateIndicator.svelte"; |   import StateIndicator from "./BigComponents/StateIndicator.svelte"; | ||||||
|   import Locale from "./i18n/Locale"; |  | ||||||
|   import ShareScreen from "./BigComponents/ShareScreen.svelte"; |   import ShareScreen from "./BigComponents/ShareScreen.svelte"; | ||||||
|   import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"; |   import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"; | ||||||
|   import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"; |   import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"; | ||||||
|   import Cross from "../assets/svg/Cross.svelte"; |   import Cross from "../assets/svg/Cross.svelte"; | ||||||
|   import Summary from "./BigComponents/Summary.svelte"; |   import Summary from "./BigComponents/Summary.svelte"; | ||||||
|   import LanguagePicker from "./InputElement/LanguagePicker.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; |   export let state: ThemeViewState; | ||||||
|   let layout = state.layout; |   let layout = state.layout; | ||||||
|  | @ -205,7 +212,7 @@ | ||||||
|         <!-- bottom left elements --> |         <!-- bottom left elements --> | ||||||
|         <If condition={state.featureSwitches.featureSwitchFilter}> |         <If condition={state.featureSwitches.featureSwitchFilter}> | ||||||
|           <MapControlButton on:click={() => state.guistate.openFilterView()}> |           <MapControlButton on:click={() => state.guistate.openFilterView()}> | ||||||
|             <ToSvelte construct={Svg.filter_svg().SetClass("h-6 w-6")} /> |             <Filter class="h-6 w-6"/> | ||||||
|           </MapControlButton> |           </MapControlButton> | ||||||
|         </If> |         </If> | ||||||
|         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> |         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> | ||||||
|  | @ -244,10 +251,10 @@ | ||||||
|         </div> |         </div> | ||||||
|       </If> |       </If> | ||||||
|       <MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)}> |       <MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)}> | ||||||
|         <ToSvelte construct={Svg.plus_svg().SetClass("w-8 h-8")} /> |         <Plus class="w-8 h-8" /> | ||||||
|       </MapControlButton> |       </MapControlButton> | ||||||
|       <MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)}> |       <MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)}> | ||||||
|         <ToSvelte construct={Svg.min_svg().SetClass("w-8 h-8")} /> |         <Min class="w-8 h-8"/> | ||||||
|       </MapControlButton> |       </MapControlButton> | ||||||
|       <If condition={featureSwitches.featureSwitchGeolocation}> |       <If condition={featureSwitches.featureSwitchGeolocation}> | ||||||
|         <MapControlButton> |         <MapControlButton> | ||||||
|  | @ -340,7 +347,7 @@ | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div class="flex" slot="title1"> |       <div class="flex" slot="title1"> | ||||||
|         <ToSvelte construct={Svg.filter_svg().SetClass("w-4 h-4")} /> |         <Filter class="w-4 h-4"/> | ||||||
|         <Tr t={Translations.t.general.menu.filter} /> |         <Tr t={Translations.t.general.menu.filter} /> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|  | @ -431,27 +438,27 @@ | ||||||
|         <Tr t={Translations.t.general.aboutMapComplete.intro} /> |         <Tr t={Translations.t.general.aboutMapComplete.intro} /> | ||||||
| 
 | 
 | ||||||
|         <a class="flex" href={Utils.HomepageLink()}> |         <a class="flex" href={Utils.HomepageLink()}> | ||||||
|           <img class="h-6 w-6" src="./assets/svg/add.svg" /> |           <Add class="h-6 w-6"/> | ||||||
|           <Tr t={Translations.t.general.backToIndex} /> |           <Tr t={Translations.t.general.backToIndex} /> | ||||||
|         </a> |         </a> | ||||||
| 
 | 
 | ||||||
|         <a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank"> |         <a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank"> | ||||||
|           <img class="h-6 w-6" src="./assets/svg/bug.svg" /> |           <Bug class="h-6 w-6"/> | ||||||
|           <Tr t={Translations.t.general.attribution.openIssueTracker} /> |           <Tr t={Translations.t.general.attribution.openIssueTracker} /> | ||||||
|         </a> |         </a> | ||||||
| 
 | 
 | ||||||
|         <a class="flex" href="https://en.osm.town/@MapComplete" target="_blank"> |         <a class="flex" href="https://en.osm.town/@MapComplete" target="_blank"> | ||||||
|           <img class="h-6 w-6" src="./assets/svg/mastodon.svg" /> |           <Mastodon class="w-6 h-6" /> | ||||||
|           <Tr t={Translations.t.general.attribution.followOnMastodon} /> |           <Tr t={Translations.t.general.attribution.followOnMastodon} /> | ||||||
|         </a> |         </a> | ||||||
| 
 | 
 | ||||||
|         <a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank"> |         <a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank"> | ||||||
|           <img class="h-6 w-6" src="./assets/svg/liberapay.svg" /> |           <Liberapay class="h-6 w-6" /> | ||||||
|           <Tr t={Translations.t.general.attribution.donate} /> |           <Tr t={Translations.t.general.attribution.donate} /> | ||||||
|         </a> |         </a> | ||||||
| 
 | 
 | ||||||
|         <a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank"> |         <a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank"> | ||||||
|           <img class="h-6 w-6" src="./assets/svg/statistics.svg" /> |           <Statistics class="h-6 w-6" /> | ||||||
|           <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: "MapComplete" })} /> |           <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: "MapComplete" })} /> | ||||||
|         </a> |         </a> | ||||||
|         {Constants.vNumber} |         {Constants.vNumber} | ||||||
|  | @ -503,10 +510,7 @@ | ||||||
|       <div class="m-2 flex flex-col" slot="content4"> |       <div class="m-2 flex flex-col" slot="content4"> | ||||||
|         <If condition={featureSwitches.featureSwitchEnableLogin}> |         <If condition={featureSwitches.featureSwitchEnableLogin}> | ||||||
|           <OpenIdEditor mapProperties={state.mapProperties} /> |           <OpenIdEditor mapProperties={state.mapProperties} /> | ||||||
|           <ToSvelte |           <OpenJosm {state}/> | ||||||
|             construct={() => |  | ||||||
|               new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")} |  | ||||||
|           /> |  | ||||||
|           <MapillaryLink mapProperties={state.mapProperties} /> |           <MapillaryLink mapProperties={state.mapProperties} /> | ||||||
|         </If> |         </If> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
|   import WikidataPreviewBox from "./WikidataPreviewBox" |   import WikidataPreviewBox from "./WikidataPreviewBox" | ||||||
|   import Tr from "../Base/Tr.svelte" |   import Tr from "../Base/Tr.svelte" | ||||||
|   import Translations from "../i18n/Translations" |   import Translations from "../i18n/Translations" | ||||||
|  |   import Wikipedia from "../../assets/svg/Wikipedia.svelte"; | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Shows a wikipedia-article + wikidata preview for the given item |    * Shows a wikipedia-article + wikidata preview for the given item | ||||||
|  | @ -18,7 +19,7 @@ | ||||||
| 
 | 
 | ||||||
| {#if $wikipediaDetails.articleUrl} | {#if $wikipediaDetails.articleUrl} | ||||||
|   <a class="flex" href={$wikipediaDetails.articleUrl} rel="noreferrer" target="_blank"> |   <a class="flex" href={$wikipediaDetails.articleUrl} rel="noreferrer" target="_blank"> | ||||||
|     <img class="h-6 w-6" src="./assets/svg/wikipedia.svg" /> |     <Wikipedia class="h-6 w-6"/> | ||||||
|     <Tr t={Translations.t.general.wikipedia.fromWikipedia} /> |     <Tr t={Translations.t.general.wikipedia.fromWikipedia} /> | ||||||
|   </a> |   </a> | ||||||
| {/if} | {/if} | ||||||
|  |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| <script> |  | ||||||
| export let color = "#000000" |  | ||||||
| </script> |  | ||||||
|  <svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown xmlns="http://www.w3.org/2000/svg" width="374px" height="259px" viewBox="0 0 374 259" version="1.1">   <g id="surface1"/> </svg> |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| <script> |  | ||||||
| export let color = "#000000" |  | ||||||
| </script> |  | ||||||
|  <svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1">   <g id="surface1"/> </svg> |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
|  <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"374px\" height=\"374px\" viewBox=\"0 0 374 374\" version=\"1.1\">   <g id=\"surface1\">     <path style=\"fill: none !important;stroke-width:107.38591;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;\" d=\"M 674.316761 62.954545 C 661.244886 65.427807 645.489205 78.502674 631.082102 98.743316 C 611.320739 126.71123 594.430114 160.307487 554.52017 251.283422 C 521.406534 326.684492 508.134375 353.42246 490.856534 378.903743 C 470.721307 408.582888 456.781534 414.505348 407.044318 414.59893 C 378.123295 414.59893 352.353409 412.5 281.532955 404.47861 C 251.930966 401.042781 217.949432 397.513369 192.753693 395.227273 C 174.527841 393.596257 117.153125 393.596257 106.364489 395.227273 C 72.102557 400.574866 56.253409 411.831551 55.011648 431.684492 C 54.344034 443.903743 59.124148 456.97861 70.380114 473.877005 C 89.193466 501.938503 119.449716 532.299465 196.091761 600.160428 C 287.26108 680.828877 315.127273 712.23262 319.22642 738.770053 C 322.764773 761.096257 310.160227 801.377005 275.991761 877.941176 C 241.249148 955.828877 234.946875 969.852941 229.21875 983.890374 C 205.157955 1042.219251 195.624432 1079.157754 198.869034 1101.871658 C 201.819886 1123.061497 212.701989 1133.663102 232.944034 1134.893048 C 262.626136 1136.898396 306.248011 1118.770053 395.120739 1067.700535 C 466.608807 1026.564171 479.600568 1019.21123 495.436364 1010.802139 C 538.484091 987.90107 563.97358 978.542781 583.641477 978.262032 C 595.191193 978.168449 599.009943 979.21123 613.230114 986.283422 C 638.519318 998.970588 668.027841 1022.553476 737.326136 1085.548128 C 833.34233 1172.981283 871.436364 1200.668449 904.55 1207.352941 C 914.297159 1209.358289 921.066761 1208.596257 928.891193 1204.772727 C 938.731818 1200 944.166193 1191.885027 948.372159 1175.748663 C 950.375 1168.02139 950.561932 1165.160428 950.561932 1145.681818 C 950.561932 1133.756684 950.094602 1120.200535 949.413636 1115.13369 C 944.553409 1078.957219 939.2125 1050.802139 925.272727 987.713904 C 903.415057 889.010695 898.634943 861.042781 898.634943 830.494652 C 898.634943 815.026738 900.170455 805.681818 903.882386 797.473262 C 913.522727 776.377005 947.984943 750.695187 1025.588352 706.590909 C 1088.691193 670.802139 1104.914205 661.44385 1121.524432 651.417112 C 1192.064489 608.850267 1223.188636 578.783422 1223.188636 553.395722 C 1223.188636 538.596257 1214.015625 527.339572 1193.973864 517.312834 C 1165.333239 502.994652 1122.579261 494.786096 1025.588352 484.665775 C 925.179261 474.358289 898.26108 470.534759 869.340057 463.181818 C 832.394318 453.636364 820.270455 443.997326 807.478977 414.024064 C 796.316477 387.687166 787.34375 353.983957 770.159375 272.566845 C 757.274432 211.096257 749.91733 179.692513 742.57358 154.491979 C 731.598011 116.497326 719.66108 90.721925 706.295455 75.828877 C 697.322727 65.815508 685.198864 60.949198 674.316761 62.954545 Z M 674.316761 62.954545 \" transform=\"matrix(0.292553,0,0,0.292188,0.0585106,0)\"/>     <path style=\" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;\" d=\"M 197.332031 18.394531 C 193.507812 19.117188 188.898438 22.9375 184.683594 28.851562 C 178.902344 37.023438 173.960938 46.839844 162.285156 73.421875 C 152.597656 95.453125 148.714844 103.265625 143.660156 110.710938 C 137.769531 119.382812 133.691406 121.113281 119.140625 121.140625 C 110.679688 121.140625 103.140625 120.527344 82.421875 118.183594 C 73.761719 117.179688 63.820312 116.148438 56.449219 115.480469 C 51.117188 115.003906 34.332031 115.003906 31.175781 115.480469 C 21.152344 117.042969 16.515625 120.332031 16.152344 126.132812 C 15.957031 129.703125 17.355469 133.523438 20.648438 138.460938 C 26.152344 146.660156 35.003906 155.53125 57.425781 175.359375 C 84.097656 198.929688 92.25 208.105469 93.449219 215.859375 C 94.484375 222.382812 90.796875 234.152344 80.800781 256.523438 C 70.636719 279.28125 68.792969 283.378906 67.117188 287.480469 C 60.078125 304.523438 57.289062 315.316406 58.238281 321.953125 C 59.101562 328.144531 62.285156 331.242188 68.207031 331.601562 C 76.890625 332.1875 89.652344 326.890625 115.652344 311.96875 C 136.566406 299.949219 140.367188 297.800781 145 295.34375 C 157.996094 288.640625 172.394531 285.695312 170.804688 285.835938 Z M 197.332031 18.394531 \"/>   </g> </svg> |  | ||||||
| Before Width: | Height: | Size: 4.3 KiB | 
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -4,14 +4,16 @@ import { describe, it } from "vitest" | ||||||
| import { parse as parse_html } from "node-html-parser" | import { parse as parse_html } from "node-html-parser" | ||||||
| import { readFileSync } from "fs" | import { readFileSync } from "fs" | ||||||
| import ScriptUtils from "../scripts/ScriptUtils" | 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 forbidden: a GREP-regex. This means that '.' is a wildcard and should be escaped to match a literal dot | ||||||
|  * @param reason |  * @param reason | ||||||
|  * @private |  * @private | ||||||
|  */ |  */ | ||||||
| function detectInCode(forbidden: string, reason: string): Promise<void> { | function detectInCodeUnwrapped(forbidden: string, reason: string): Promise<void> { | ||||||
|     return new Promise<void>((done) => { |     return new Promise<void>((done) => { | ||||||
|         const excludedDirs = [ |         const excludedDirs = [ | ||||||
|             ".git", |             ".git", | ||||||
|  | @ -24,37 +26,35 @@ function detectInCode(forbidden: string, reason: string): Promise<void> { | ||||||
|             ".idea/", |             ".idea/", | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|         exec( |         const command = | ||||||
|             'grep -n "' + |             'grep -n "' + | ||||||
|                 forbidden + |             forbidden + | ||||||
|                 '" -r . ' + |             '" -r . ' + | ||||||
|                 excludedDirs.map((d) => "--exclude-dir=" + d).join(" "), |             excludedDirs.map((d) => "--exclude-dir=" + d).join(" ") | ||||||
|             (error, stdout, stderr) => { |         console.log(command) | ||||||
|                 if (error?.message?.startsWith("Command failed: grep")) { |         exec(command, (error, stdout, stderr) => { | ||||||
|                     console.warn("Command failed!", error) |             if (error?.message?.startsWith("Command failed: grep")) { | ||||||
|                     return |                 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 |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         ) |             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<void>): (done: () => void) => void { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function itAsync(name: string, promise: Promise<void>) { |  | ||||||
|     it(name, wrap(promise)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function validateScriptIntegrityOf(path: string) { | function validateScriptIntegrityOf(path: string) { | ||||||
|     const htmlContents = readFileSync(path, "utf8") |     const htmlContents = readFileSync(path, "utf8") | ||||||
|     const doc = parse_html(htmlContents) |     const doc = parse_html(htmlContents) | ||||||
|  | @ -95,7 +91,7 @@ function validateScriptIntegrityOf(path: string) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| describe("Code quality", () => { | describe("Code quality", () => { | ||||||
|     itAsync( |     it( | ||||||
|         "should not contain reverse", |         "should not contain reverse", | ||||||
|         detectInCode( |         detectInCode( | ||||||
|             "reverse()", |             "reverse()", | ||||||
|  | @ -103,12 +99,12 @@ describe("Code quality", () => { | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     itAsync( |     it( | ||||||
|         "should not contain 'constructor.name'", |         "should not contain 'constructor.name'", | ||||||
|         detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") |         detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     itAsync( |     it( | ||||||
|         "should not contain 'innerText'", |         "should not contain 'innerText'", | ||||||
|         detectInCode( |         detectInCode( | ||||||
|             "innerText", |             "innerText", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue