forked from MapComplete/MapComplete
		
	Studio: more fixes
This commit is contained in:
		
							parent
							
								
									3ceebaba12
								
							
						
					
					
						commit
						80b7a038cf
					
				
					 7 changed files with 129 additions and 77 deletions
				
			
		|  | @ -11,9 +11,9 @@ | |||
| 
 | ||||
| cp config.json config.json.bu && | ||||
| cp ./scripts/hetzner/config.json . && # Copy the config _before_ building, as the config might contain some needed URLs | ||||
| npm run reset:layeroverview | ||||
| npm run test | ||||
| # npm run reset:layeroverview | ||||
| npm run prepare-deploy && | ||||
| npm run test && | ||||
| zip dist.zip -r dist/* && | ||||
| mv config.json.bu config.json && | ||||
| scp ./scripts/hetzner/config/* hetzner:/root/ && | ||||
|  |  | |||
|  | @ -39,6 +39,12 @@ export class ConversionContext { | |||
|     } | ||||
| 
 | ||||
|     static print(msg: ConversionMessage) { | ||||
|         const noString = msg.context.path.filter( | ||||
|             (p) => typeof p !== "string" && typeof p !== "number" | ||||
|         ) | ||||
|         if (noString.length > 0) { | ||||
|             console.warn("Non-string value in path:", ...noString) | ||||
|         } | ||||
|         if (msg.level === "error") { | ||||
|             console.error( | ||||
|                 ConversionContext.red("ERR "), | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <script lang="ts"> | ||||
| 
 | ||||
|   import { LayerStateSender } from "./EditLayerState"; | ||||
|   import EditLayerState, { LayerStateSender } from "./EditLayerState"; | ||||
|   import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json"; | ||||
|   import Region from "./Region.svelte"; | ||||
|   import TabbedGroup from "../Base/TabbedGroup.svelte"; | ||||
|  | @ -8,11 +8,13 @@ | |||
|   import type { ConfigMeta } from "./configMeta"; | ||||
|   import { Utils } from "../../Utils"; | ||||
|   import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"; | ||||
|   import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion"; | ||||
| 
 | ||||
|   const layerSchema: ConfigMeta[] = <any>layerSchemaRaw; | ||||
| 
 | ||||
|   export let state; | ||||
|   export let state: EditLayerState; | ||||
|   const messages = state.messages; | ||||
|   const hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length); | ||||
|   export let initialLayerConfig: Partial<LayerConfigJson> = {}; | ||||
|   state.configuration.setData(initialLayerConfig); | ||||
|   const configuration = state.configuration; | ||||
|  | @ -37,9 +39,18 @@ | |||
|   } | ||||
|   const leftoverRegions: string[] = allNames.filter(r => regionBlacklist.indexOf(r) < 0 && baselayerRegions.indexOf(r) < 0); | ||||
|   const title: Store<string> = state.getStoreFor(["id"]); | ||||
|   const wl = window.location | ||||
|   const baseUrl = wl.protocol+"//"+wl.host+"/theme.html?userlayout=" | ||||
| </script> | ||||
| 
 | ||||
| <h3>Editing layer {$title}</h3> | ||||
| <div class="w-full flex justify-between"> | ||||
|   <h3>Editing layer {$title}</h3> | ||||
|   {#if $hasErrors > 0} | ||||
|     <div class="alert">{$hasErrors} errors detected</div> | ||||
|   {:else} | ||||
|     <a class="primary button" href={baseUrl+state.server.layerUrl(title.data)} target="_blank" rel="noopener">Try it out</a> | ||||
|   {/if} | ||||
| </div> | ||||
| <div class="m4"> | ||||
|   <TabbedGroup tab={new UIEventSource(2)}> | ||||
|     <div slot="title0">General properties</div> | ||||
|  |  | |||
|  | @ -177,7 +177,12 @@ export default class EditLayerState { | |||
|             } | ||||
|             entry = entry[breadcrumb] | ||||
|         } | ||||
|         if (v !== undefined && v !== null && v !== "") { | ||||
|         if ( | ||||
|             v !== undefined && | ||||
|             v !== null && | ||||
|             v !== "" && | ||||
|             !(typeof v === "object" && Object.keys({}).length === 0) | ||||
|         ) { | ||||
|             entry[path.at(-1)] = v | ||||
|         } else if (entry) { | ||||
|             delete entry[path.at(-1)] | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
|     export let state: EditLayerState | ||||
|     export let path: (string | number)[] = [] | ||||
|     export let schema: ConfigMeta | ||||
|     let value = new UIEventSource<string>(undefined) | ||||
|     let value = new UIEventSource<string | any>(undefined) | ||||
|      | ||||
|     const isTranslation = schema.hints.typehint === "translation" || schema.hints.typehint === "rendered" || ConfigMetaUtils.isTranslation(schema) | ||||
|     let type = schema.hints.typehint ?? "string" | ||||
|  | @ -122,7 +122,7 @@ | |||
|                 } | ||||
|                 return Number(v) | ||||
|             } | ||||
|             if (isTranslation) { | ||||
|             if (isTranslation && typeof v === "string") { | ||||
|                 if (v === "") { | ||||
|                     return {} | ||||
|                 } | ||||
|  |  | |||
|  | @ -22,9 +22,7 @@ export default class StudioServer { | |||
| 
 | ||||
|     async fetchLayer(layerId: string): Promise<LayerConfigJson> { | ||||
|         try { | ||||
|             return await Utils.downloadJson( | ||||
|                 this.url + "/layers/" + layerId + "/" + layerId + ".json" | ||||
|             ) | ||||
|             return await Utils.downloadJson(this.layerUrl(layerId)) | ||||
|         } catch (e) { | ||||
|             return undefined | ||||
|         } | ||||
|  | @ -35,7 +33,7 @@ export default class StudioServer { | |||
|         if (id === undefined || id === "") { | ||||
|             return | ||||
|         } | ||||
|         await fetch(`${this.url}/layers/${id}/${id}.json`, { | ||||
|         await fetch(this.layerUrl(id), { | ||||
|             method: "POST", | ||||
|             headers: { | ||||
|                 "Content-Type": "application/json;charset=utf-8", | ||||
|  | @ -43,4 +41,8 @@ export default class StudioServer { | |||
|             body: JSON.stringify(config, null, "  "), | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public layerUrl(id: string) { | ||||
|         return `${this.url}/layers/${id}/${id}.json` | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -15,10 +15,12 @@ | |||
|   import { QueryParameters } from "../Logic/Web/QueryParameters"; | ||||
| 
 | ||||
|   import layerSchemaRaw from "../../src/assets/schemas/layerconfigmeta.json"; | ||||
|   import If from "./Base/If.svelte"; | ||||
| 
 | ||||
|   export let studioUrl = /*"https://studio.mapcomplete.org"; /*/ "http://127.0.0.1:1235"; //*/ | ||||
|   export let studioUrl = /* "https://studio.mapcomplete.org"; /*/ "http://127.0.0.1:1235"; //*/ | ||||
|   const studio = new StudioServer(studioUrl); | ||||
|   let layers = UIEventSource.FromPromise(studio.fetchLayerOverview()); | ||||
|   let layersWithErr = UIEventSource.FromPromiseWithErr(studio.fetchLayerOverview()); | ||||
|   let layers = layersWithErr.mapD(l => l.success); | ||||
|   let state: undefined | "edit_layer" | "new_layer" | "edit_theme" | "new_theme" | "editing_layer" | "loading" = undefined; | ||||
| 
 | ||||
|   let initialLayerConfig: { id: string }; | ||||
|  | @ -61,8 +63,8 @@ | |||
|             }] | ||||
|           } | ||||
|         ], | ||||
|         lineRendering : [{ | ||||
|           width : 1, | ||||
|         lineRendering: [{ | ||||
|           width: 1, | ||||
|           color: "blue" | ||||
|         }] | ||||
|       }; | ||||
|  | @ -82,73 +84,99 @@ | |||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <LoginToggle state={{osmConnection}} ignoreLoading={true}> | ||||
|   <div slot="not-logged-in"> | ||||
|     <NextButton clss="primary"> | ||||
|       Please log in to use MapComplete Studio | ||||
|     </NextButton> | ||||
|   </div> | ||||
|   {#if state === undefined} | ||||
|     <h1>MapComplete Studio</h1> | ||||
|     <div class="w-full flex flex-col"> | ||||
| <If condition={layersWithErr.map(d => d?.error !== undefined)}> | ||||
|   <div> | ||||
|     <div class="alert"> | ||||
|       Something went wrong while contacting the MapComplete Studio Server: {$layersWithErr["error"]} | ||||
|     </div> | ||||
|     The server might be offline. Please: | ||||
|     <ul> | ||||
| 
 | ||||
|       <NextButton on:click={() => state = "edit_layer"}> | ||||
|         Edit an existing layer | ||||
|       </NextButton> | ||||
|       <NextButton on:click={() => state = "new_layer"}> | ||||
|         Create a new layer | ||||
|       </NextButton> | ||||
|       <NextButton on:click={() => state = "edit_theme"}> | ||||
|         Edit a theme | ||||
|       </NextButton> | ||||
|       <NextButton on:click={() => state = "new_theme"}> | ||||
|         Create a new theme | ||||
|       <li> | ||||
|         Try again in a few minutes | ||||
|       </li> | ||||
|       <li> | ||||
|         Contact <a href="https://app.element.io/#/room/#MapComplete:matrix.org">the MapComplete community via the | ||||
|         chat.</a> Someone might be able to help you | ||||
|       </li> | ||||
|       <li> | ||||
|         File <a href="https://github.com/pietervdvn/MapComplete/issues">an issue</a> | ||||
|       </li> | ||||
|       <li> | ||||
|         Contact the devs via <a href="mailto:info@posteo.net">email</a> | ||||
|       </li> | ||||
|     </ul> | ||||
|   </div> | ||||
|   <LoginToggle ignoreLoading={true} slot="else" state={{osmConnection}}> | ||||
|     <div slot="not-logged-in"> | ||||
|       <NextButton clss="primary"> | ||||
|         Please log in to use MapComplete Studio | ||||
|       </NextButton> | ||||
|     </div> | ||||
|   {:else if state === "edit_layer"} | ||||
|     <div class="flex flex-wrap"> | ||||
|       {#each Array.from($layers) as layerId} | ||||
|         <NextButton clss="small" on:click={async () => { | ||||
|     {#if state === undefined} | ||||
|       <h1>MapComplete Studio</h1> | ||||
|       <div class="w-full flex flex-col"> | ||||
| 
 | ||||
|         <NextButton on:click={() => state = "edit_layer"}> | ||||
|           Edit an existing layer | ||||
|         </NextButton> | ||||
|         <NextButton on:click={() => state = "new_layer"}> | ||||
|           Create a new layer | ||||
|         </NextButton> | ||||
|         <!-- | ||||
|         <NextButton on:click={() => state = "edit_theme"}> | ||||
|           Edit a theme | ||||
|         </NextButton> | ||||
|         <NextButton on:click={() => state = "new_theme"}> | ||||
|           Create a new theme | ||||
|         </NextButton> | ||||
|         --> | ||||
|       </div> | ||||
|     {:else if state === "edit_layer"} | ||||
|       <div class="flex flex-wrap"> | ||||
|         {#each Array.from($layers) as layerId} | ||||
|           <NextButton clss="small" on:click={async () => { | ||||
|         state = "loading" | ||||
|         initialLayerConfig = await studio.fetchLayer(layerId) | ||||
|         state = "editing_layer" | ||||
|        }}> | ||||
|           <div class="w-4 h-4 mr-1"> | ||||
|             <Marker icons={fetchIconDescription(layerId)} /> | ||||
|           </div> | ||||
|           {layerId} | ||||
|         </NextButton> | ||||
|       {/each} | ||||
|     </div> | ||||
|   {:else if state === "new_layer"} | ||||
|     <div class="interactive flex m-2 rounded-2xl flex-col p-2"> | ||||
|       <h3>Enter the ID for the new layer</h3> | ||||
|       A good ID is: | ||||
|       <ul> | ||||
|         <li>a noun</li> | ||||
|         <li>singular</li> | ||||
|         <li>describes the object</li> | ||||
|         <li>in English</li> | ||||
|       </ul> | ||||
|       <div class="m-2 p-2 w-full"> | ||||
| 
 | ||||
|         <ValidatedInput type="id" value={newLayerId} feedback={layerIdFeedback} on:submit={() => createNewLayer()} /> | ||||
|             <div class="w-4 h-4 mr-1"> | ||||
|               <Marker icons={fetchIconDescription(layerId)} /> | ||||
|             </div> | ||||
|             {layerId} | ||||
|           </NextButton> | ||||
|         {/each} | ||||
|       </div> | ||||
|       {#if $layerIdFeedback !== undefined} | ||||
|         <div class="alert"> | ||||
|           {$layerIdFeedback} | ||||
|     {:else if state === "new_layer"} | ||||
|       <div class="interactive flex m-2 rounded-2xl flex-col p-2"> | ||||
|         <h3>Enter the ID for the new layer</h3> | ||||
|         A good ID is: | ||||
|         <ul> | ||||
|           <li>a noun</li> | ||||
|           <li>singular</li> | ||||
|           <li>describes the object</li> | ||||
|           <li>in English</li> | ||||
|         </ul> | ||||
|         <div class="m-2 p-2 w-full"> | ||||
| 
 | ||||
|           <ValidatedInput type="id" value={newLayerId} feedback={layerIdFeedback} on:submit={() => createNewLayer()} /> | ||||
|         </div> | ||||
|       {:else } | ||||
|         <NextButton clss="primary" on:click={() => createNewLayer()}> | ||||
|           Create layer {$newLayerId} | ||||
|         </NextButton> | ||||
|       {/if} | ||||
|     </div> | ||||
|   {:else if state === "loading"} | ||||
|     <div class="w-8 h-8"> | ||||
|       <Loading /> | ||||
|     </div> | ||||
|   {:else if state === "editing_layer"} | ||||
|     <EditLayer {initialLayerConfig} state={editLayerState} /> | ||||
|   {/if} | ||||
| </LoginToggle> | ||||
|         {#if $layerIdFeedback !== undefined} | ||||
|           <div class="alert"> | ||||
|             {$layerIdFeedback} | ||||
|           </div> | ||||
|         {:else } | ||||
|           <NextButton clss="primary" on:click={() => createNewLayer()}> | ||||
|             Create layer {$newLayerId} | ||||
|           </NextButton> | ||||
|         {/if} | ||||
|       </div> | ||||
|     {:else if state === "loading"} | ||||
|       <div class="w-8 h-8"> | ||||
|         <Loading /> | ||||
|       </div> | ||||
|     {:else if state === "editing_layer"} | ||||
|       <EditLayer {initialLayerConfig} state={editLayerState} /> | ||||
|     {/if} | ||||
|   </LoginToggle> | ||||
| </If> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue