forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
						commit
						d959b6b40b
					
				
					 290 changed files with 37178 additions and 2200 deletions
				
			
		|  | @ -10,13 +10,14 @@ | |||
|   export let info: { id: string; owner: number }; | ||||
|   export let category: "layers" | "themes"; | ||||
|   export let osmConnection: OsmConnection; | ||||
|   const dispatch = createEventDispatcher<{ layerSelected: string }>(); | ||||
| 
 | ||||
|   let displayName = UIEventSource.FromPromise( | ||||
|     osmConnection.getInformationAboutUser(info.owner) | ||||
|   ).mapD((response) => response.display_name); | ||||
| 
 | ||||
|   let selfId = osmConnection.userDetails.mapD((ud) => ud.uid); | ||||
| 
 | ||||
| 
 | ||||
|   function fetchIconDescription(layerId): any { | ||||
|     if (category === "themes") { | ||||
|       return AllKnownLayouts.allKnownLayouts.get(layerId).icon; | ||||
|  | @ -24,7 +25,6 @@ | |||
|     return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon; | ||||
|   } | ||||
| 
 | ||||
|   const dispatch = createEventDispatcher<{ layerSelected: string }>(); | ||||
| </script> | ||||
| 
 | ||||
| <NextButton clss="small" on:click={() => dispatch("layerSelected", info)}> | ||||
|  | @ -33,12 +33,14 @@ | |||
|   </div> | ||||
|   <b class="px-1">{info.id}</b> | ||||
|   {#if info.owner && info.owner !== $selfId} | ||||
|     {#if displayName} | ||||
|     {#if $displayName} | ||||
|       (made by {$displayName} | ||||
|       {#if window.location.host.startsWith("127.0.0.1")} | ||||
|         - {info.owner} | ||||
|       {/if} | ||||
|       ) | ||||
|     {:else } | ||||
|       ({info.owner}) | ||||
|     {/if} | ||||
|   {/if} | ||||
| </NextButton> | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
|   import { Utils } from "../../Utils" | ||||
|   import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion" | ||||
|   import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte" | ||||
|   import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import { ChevronRightIcon, TrashIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import SchemaBasedInput from "./SchemaBasedInput.svelte" | ||||
|   import FloatOver from "../Base/FloatOver.svelte" | ||||
|   import TagRenderingInput from "./TagRenderingInput.svelte" | ||||
|  | @ -21,6 +21,7 @@ | |||
|   const layerSchema: ConfigMeta[] = <any>layerSchemaRaw | ||||
| 
 | ||||
|   export let state: EditLayerState | ||||
|   export let backToStudio: () => void | ||||
|   let messages = state.messages | ||||
|   let hasErrors = messages.mapD( | ||||
|     (m: ConversionMessage[]) => m.filter((m) => m.level === "error").length | ||||
|  | @ -72,6 +73,10 @@ | |||
|   }) | ||||
| 
 | ||||
|   let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem | ||||
|   function deleteLayer() { | ||||
|       state.delete() | ||||
|       backToStudio() | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <div class="flex h-screen flex-col"> | ||||
|  | @ -113,6 +118,12 @@ | |||
|         </div> | ||||
|         <div class="flex flex-col" slot="content0"> | ||||
|           <Region {state} configs={perRegion["Basic"]} /> | ||||
|           <div class="mt-12"> | ||||
|              | ||||
|           <button on:click={() => deleteLayer()} class="small" > | ||||
|             <TrashIcon class="h-6 w-6"/> Delete this layer | ||||
|           </button> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div slot="title1" class="flex"> | ||||
|  |  | |||
|  | @ -107,6 +107,9 @@ export abstract class EditJsonState<T> { | |||
|         return entry | ||||
|     } | ||||
| 
 | ||||
|     public async delete(){ | ||||
|         await this.server.delete(this.getId().data, this.category) | ||||
|     } | ||||
|     public getStoreFor<T>(path: ReadonlyArray<string | number>): UIEventSource<T | undefined> { | ||||
|         const key = path.join(".") | ||||
| 
 | ||||
|  | @ -294,16 +297,39 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> { | |||
| 
 | ||||
|         this.addMissingTagRenderingIds() | ||||
| 
 | ||||
|         this.configuration.addCallbackAndRunD((layer) => { | ||||
|             if (layer.tagRenderings) { | ||||
| 
 | ||||
|         function cleanArray(data: object, key: string): boolean{ | ||||
|             if(!data){ | ||||
|                 return false | ||||
|             } | ||||
|             if (data[key]) { | ||||
|                 // A bit of cleanup
 | ||||
|                 const lBefore = layer.tagRenderings.length | ||||
|                 const cleaned = Utils.NoNull(layer.tagRenderings) | ||||
|                 const lBefore = data[key].length | ||||
|                 const cleaned = Utils.NoNull(data[key]) | ||||
|                 if (cleaned.length != lBefore) { | ||||
|                     layer.tagRenderings = cleaned | ||||
|                     this.configuration.ping() | ||||
|                     data[key] = cleaned | ||||
|                     return true | ||||
|                 } | ||||
|             } | ||||
|             return false | ||||
|         } | ||||
| 
 | ||||
|         this.configuration.addCallbackAndRunD((layer) => { | ||||
|             let changed = cleanArray(layer, "tagRenderings") || cleanArray(layer, "pointRenderings") | ||||
|             for (const tr of layer.tagRenderings ?? []) { | ||||
|                 if(typeof tr === "string"){ | ||||
|                     continue | ||||
|                 } | ||||
| 
 | ||||
|                 const qtr = (<QuestionableTagRenderingConfigJson> tr) | ||||
|                 if(qtr.freeform && Object.keys(qtr.freeform ).length === 0){ | ||||
|                     delete qtr.freeform | ||||
|                     changed = true | ||||
|                 } | ||||
|             } | ||||
|             if(changed){ | ||||
|                 this.configuration.ping() | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -64,7 +64,16 @@ | |||
|       } | ||||
|     } | ||||
|     newPath.push(...toAdd) | ||||
|     console.log("Fused path ", path.join("."), "+", i,"+", subpartPath.join("."),"into",newPath.join(".")) | ||||
|     console.log( | ||||
|       "Fused path ", | ||||
|       path.join("."), | ||||
|       "+", | ||||
|       i, | ||||
|       "+", | ||||
|       subpartPath.join("."), | ||||
|       "into", | ||||
|       newPath.join(".") | ||||
|     ) | ||||
|     return newPath | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,14 @@ export default class StudioServer { | |||
|             return undefined | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async delete(id: string, category: "layers" | "themes") { | ||||
|         if (id === undefined || id === "") { | ||||
|             return | ||||
|         } | ||||
|         await fetch(this.urlFor(id, category), { | ||||
|             method: "DELETE" | ||||
|         }) | ||||
|     } | ||||
|     async update(id: string, config: string, category: "layers" | "themes") { | ||||
|         if (id === undefined || id === "") { | ||||
|             return | ||||
|  |  | |||
|  | @ -3,104 +3,104 @@ | |||
|    * Little helper class to deal with choosing a builtin tagRendering or defining one yourself. | ||||
|    * Breaks the ideology that everything should be schema based | ||||
|    */ | ||||
|   import EditLayerState from "./EditLayerState"; | ||||
|   import type { ConfigMeta } from "./configMeta"; | ||||
|   import EditLayerState from "./EditLayerState" | ||||
|   import type { ConfigMeta } from "./configMeta" | ||||
|   import type { | ||||
|     MappingConfigJson, | ||||
|     QuestionableTagRenderingConfigJson | ||||
|   } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"; | ||||
|   import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | ||||
|   import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"; | ||||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource"; | ||||
|   import * as questions from "../../assets/generated/layers/questions.json"; | ||||
|   import MappingInput from "./MappingInput.svelte"; | ||||
|   import { TrashIcon } from "@rgossiaux/svelte-heroicons/outline"; | ||||
|   import questionableTagRenderingSchemaRaw from "../../assets/schemas/questionabletagrenderingconfigmeta.json"; | ||||
|   import SchemaBasedField from "./SchemaBasedField.svelte"; | ||||
|   import Region from "./Region.svelte"; | ||||
|   import NextButton from "../Base/NextButton.svelte"; | ||||
|   import { QuestionMarkCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"; | ||||
|   import { onMount } from "svelte"; | ||||
|     QuestionableTagRenderingConfigJson, | ||||
|   } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" | ||||
|   import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | ||||
|   import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte" | ||||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import * as questions from "../../assets/generated/layers/questions.json" | ||||
|   import MappingInput from "./MappingInput.svelte" | ||||
|   import { TrashIcon } from "@rgossiaux/svelte-heroicons/outline" | ||||
|   import questionableTagRenderingSchemaRaw from "../../assets/schemas/questionabletagrenderingconfigmeta.json" | ||||
|   import SchemaBasedField from "./SchemaBasedField.svelte" | ||||
|   import Region from "./Region.svelte" | ||||
|   import NextButton from "../Base/NextButton.svelte" | ||||
|   import { QuestionMarkCircleIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" | ||||
|   import { onMount } from "svelte" | ||||
| 
 | ||||
|   export let state: EditLayerState; | ||||
|   export let schema: ConfigMeta; | ||||
|   export let path: (string | number)[]; | ||||
|   let expertMode = state.expertMode; | ||||
|   const store = state.getStoreFor(path); | ||||
|   let value = store.data; | ||||
|   export let state: EditLayerState | ||||
|   export let schema: ConfigMeta | ||||
|   export let path: (string | number)[] | ||||
|   let expertMode = state.expertMode | ||||
|   const store = state.getStoreFor(path) | ||||
|   let value = store.data | ||||
|   let hasSeenIntro = UIEventSource.asBoolean( | ||||
|     LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false") | ||||
|   ); | ||||
|   ) | ||||
|   onMount(() => { | ||||
|     if (!hasSeenIntro.data) { | ||||
|       state.showIntro.setData("tagrenderings"); | ||||
|       hasSeenIntro.setData(true); | ||||
|       state.showIntro.setData("tagrenderings") | ||||
|       hasSeenIntro.setData(true) | ||||
|     } | ||||
|   }); | ||||
|   }) | ||||
|   /** | ||||
|    * Allows the theme builder to create 'writable' themes. | ||||
|    * Should only be enabled for 'tagrenderings' in the theme, if the source is OSM | ||||
|    */ | ||||
|   let allowQuestions: Store<boolean> = state.configuration.mapD( | ||||
|     (config) => path.at(0) === "tagRenderings" && config.source?.geoJson === undefined | ||||
|   ); | ||||
|   ) | ||||
| 
 | ||||
|   let mappingsBuiltin: MappingConfigJson[] = []; | ||||
|   let perLabel: Record<string, MappingConfigJson> = {}; | ||||
|   let mappingsBuiltin: MappingConfigJson[] = [] | ||||
|   let perLabel: Record<string, MappingConfigJson> = {} | ||||
|   for (const tr of questions.tagRenderings) { | ||||
|     let description = tr["description"] ?? tr["question"] ?? "No description available"; | ||||
|     description = description["en"] ?? description; | ||||
|     let description = tr["description"] ?? tr["question"] ?? "No description available" | ||||
|     description = description["en"] ?? description | ||||
|     if (tr["labels"]) { | ||||
|       const labels: string[] = tr["labels"]; | ||||
|       const labels: string[] = tr["labels"] | ||||
|       for (const label of labels) { | ||||
|         let labelMapping: MappingConfigJson = perLabel[label]; | ||||
|         let labelMapping: MappingConfigJson = perLabel[label] | ||||
| 
 | ||||
|         if (!labelMapping) { | ||||
|           labelMapping = { | ||||
|             if: "value=" + label, | ||||
|             then: { | ||||
|               en: "Builtin collection <b>" + label + "</b>:" | ||||
|             } | ||||
|           }; | ||||
|           perLabel[label] = labelMapping; | ||||
|           mappingsBuiltin.push(labelMapping); | ||||
|               en: "Builtin collection <b>" + label + "</b>:", | ||||
|             }, | ||||
|           } | ||||
|           perLabel[label] = labelMapping | ||||
|           mappingsBuiltin.push(labelMapping) | ||||
|         } | ||||
|         labelMapping.then.en = labelMapping.then.en + "<div>" + description + "</div>"; | ||||
|         labelMapping.then.en = labelMapping.then.en + "<div>" + description + "</div>" | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     mappingsBuiltin.push({ | ||||
|       if: "value=" + tr["id"], | ||||
|       then: { | ||||
|         en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>" | ||||
|       } | ||||
|     }); | ||||
|         en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>", | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson>{ | ||||
|     question: "Which builtin element should be shown?", | ||||
|     mappings: mappingsBuiltin | ||||
|   }); | ||||
|     mappings: mappingsBuiltin, | ||||
|   }) | ||||
| 
 | ||||
|   const tags = new UIEventSource({ value }); | ||||
|   const tags = new UIEventSource({ value }) | ||||
| 
 | ||||
|   tags.addCallbackAndRunD((tgs) => { | ||||
|     store.setData(tgs["value"]); | ||||
|   }); | ||||
|     store.setData(tgs["value"]) | ||||
|   }) | ||||
| 
 | ||||
|   let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"]); | ||||
|   let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"]) | ||||
| 
 | ||||
|   const topLevelItems: Record<string, ConfigMeta> = {}; | ||||
|   const topLevelItems: Record<string, ConfigMeta> = {} | ||||
|   for (const item of questionableTagRenderingSchemaRaw) { | ||||
|     if (item.path.length === 1) { | ||||
|       topLevelItems[item.path[0]] = <ConfigMeta>item; | ||||
|       topLevelItems[item.path[0]] = <ConfigMeta>item | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function initMappings() { | ||||
|     if (mappings.data === undefined) { | ||||
|       mappings.setData([]); | ||||
|       mappings.setData([]) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -113,28 +113,25 @@ | |||
|     "condition", | ||||
|     "metacondition", | ||||
|     "mappings", | ||||
|     "icon" | ||||
|   ]); | ||||
|   const ignored = new Set(["labels", "description", "classes"]); | ||||
|     "icon", | ||||
|   ]) | ||||
|   const ignored = new Set(["labels", "description", "classes"]) | ||||
| 
 | ||||
|   const freeformSchemaAll = <ConfigMeta[]>( | ||||
|     questionableTagRenderingSchemaRaw.filter( | ||||
|       (schema) => | ||||
|         schema.path.length == 2 && | ||||
|         schema.path[0] === "freeform" && | ||||
|         ($allowQuestions) | ||||
|       (schema) => schema.path.length == 2 && schema.path[0] === "freeform" && $allowQuestions | ||||
|     ) | ||||
|   ); | ||||
|   ) | ||||
|   let freeformSchema = $expertMode | ||||
|     ? freeformSchemaAll | ||||
|     : freeformSchemaAll.filter((schema) => schema.hints?.group !== "expert"); | ||||
|     : freeformSchemaAll.filter((schema) => schema.hints?.group !== "expert") | ||||
|   const missing: string[] = questionableTagRenderingSchemaRaw | ||||
|     .filter( | ||||
|       (schema) => | ||||
|         schema.path.length >= 1 && !items.has(schema.path[0]) && !ignored.has(schema.path[0]) | ||||
|     ) | ||||
|     .map((schema) => schema.path.join(".")); | ||||
|   console.log({ state }); | ||||
|     .map((schema) => schema.path.join(".")) | ||||
|   console.log({ state }) | ||||
| </script> | ||||
| 
 | ||||
| {#if typeof $store === "string"} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue