forked from MapComplete/MapComplete
Studio: WIP: add theme edit possibility
This commit is contained in:
parent
09e511ca10
commit
7520c44059
6 changed files with 275 additions and 152 deletions
|
@ -3,11 +3,16 @@
|
||||||
import NextButton from "../Base/NextButton.svelte";
|
import NextButton from "../Base/NextButton.svelte";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers";
|
import { AllSharedLayers } from "../../Customizations/AllSharedLayers";
|
||||||
|
import { AllKnownLayouts, AllKnownLayoutsLazy } from "../../Customizations/AllKnownLayouts";
|
||||||
|
|
||||||
export let layerIds : { id: string }[]
|
export let layerIds: { id: string }[];
|
||||||
const dispatch = createEventDispatcher<{layerSelected: string}>()
|
export let category: "layers" | "themes" = "layers";
|
||||||
|
const dispatch = createEventDispatcher<{ layerSelected: string }>();
|
||||||
|
|
||||||
function fetchIconDescription(layerId): any {
|
function fetchIconDescription(layerId): any {
|
||||||
|
if(category === "themes"){
|
||||||
|
return AllKnownLayouts.allKnownLayouts.get(layerId).icon
|
||||||
|
}
|
||||||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { HighlightedTagRendering } from "./EditLayerState";
|
import type { HighlightedTagRendering } from "./EditLayerState";
|
||||||
import EditLayerState, { LayerStateSender } from "./EditLayerState";
|
import EditLayerState from "./EditLayerState";
|
||||||
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json";
|
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json";
|
||||||
import Region from "./Region.svelte";
|
import Region from "./Region.svelte";
|
||||||
import TabbedGroup from "../Base/TabbedGroup.svelte";
|
import TabbedGroup from "../Base/TabbedGroup.svelte";
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||||
import type { ConfigMeta } from "./configMeta";
|
import type { ConfigMeta } from "./configMeta";
|
||||||
import { Utils } from "../../Utils";
|
import { Utils } from "../../Utils";
|
||||||
import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson";
|
|
||||||
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion";
|
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion";
|
||||||
import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte";
|
import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte";
|
||||||
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid";
|
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||||
|
@ -17,20 +16,15 @@
|
||||||
import FromHtml from "../Base/FromHtml.svelte";
|
import FromHtml from "../Base/FromHtml.svelte";
|
||||||
import AllTagsPanel from "../Popup/AllTagsPanel.svelte";
|
import AllTagsPanel from "../Popup/AllTagsPanel.svelte";
|
||||||
import QuestionPreview from "./QuestionPreview.svelte";
|
import QuestionPreview from "./QuestionPreview.svelte";
|
||||||
|
import ShowConversionMessages from "./ShowConversionMessages.svelte";
|
||||||
|
|
||||||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw;
|
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw;
|
||||||
|
|
||||||
export let state: EditLayerState;
|
export let state: EditLayerState;
|
||||||
const messages = state.messages;
|
const messages = state.messages;
|
||||||
const hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
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;
|
const configuration = state.configuration;
|
||||||
new LayerStateSender(state);
|
|
||||||
/**
|
|
||||||
* Blacklist of regions for the general area tab
|
|
||||||
* These are regions which are handled by a different tab
|
|
||||||
*/
|
|
||||||
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group));
|
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group));
|
||||||
|
|
||||||
const perRegion: Record<string, ConfigMeta[]> = {};
|
const perRegion: Record<string, ConfigMeta[]> = {};
|
||||||
|
@ -153,18 +147,11 @@
|
||||||
<div class="literal-code">
|
<div class="literal-code">
|
||||||
<FromHtml src={JSON.stringify($configuration, null, " ").replaceAll("\n","</br>")} />
|
<FromHtml src={JSON.stringify($configuration, null, " ").replaceAll("\n","</br>")} />
|
||||||
</div>
|
</div>
|
||||||
{#each $messages as message}
|
|
||||||
<li>
|
|
||||||
{message.level}
|
|
||||||
<span class="literal-code">{message.context.path.join(".")}</span>
|
|
||||||
{message.message}
|
|
||||||
<span class="literal-code">
|
|
||||||
{message.context.operation.join(".")}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
|
<ShowConversionMessages messages={$messages}/>
|
||||||
|
<div>
|
||||||
The testobject (which is used to render the questions in the 'information panel' item has the following tags:
|
The testobject (which is used to render the questions in the 'information panel' item has the following tags:
|
||||||
|
</div>
|
||||||
|
|
||||||
<AllTagsPanel tags={state.testTags}></AllTagsPanel>
|
<AllTagsPanel tags={state.testTags}></AllTagsPanel>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,13 +2,14 @@ import { ConfigMeta } from "./configMeta"
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import {
|
import {
|
||||||
|
Conversion,
|
||||||
ConversionContext,
|
ConversionContext,
|
||||||
ConversionMessage,
|
ConversionMessage,
|
||||||
DesugaringContext,
|
DesugaringContext,
|
||||||
Pipe,
|
Pipe,
|
||||||
} from "../../Models/ThemeConfig/Conversion/Conversion"
|
} from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||||
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
|
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||||
import { ValidateLayer } from "../../Models/ThemeConfig/Conversion/Validation"
|
import { ValidateLayer, ValidateTheme } from "../../Models/ThemeConfig/Conversion/Validation"
|
||||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
|
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
|
||||||
import { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
import { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||||
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||||
|
@ -18,64 +19,21 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||||
import { OsmTags } from "../../Models/OsmFeature"
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
import { Feature, Point } from "geojson"
|
import { Feature, Point } from "geojson"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
|
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson"
|
||||||
/**
|
import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme"
|
||||||
* Sends changes back to the server
|
|
||||||
*/
|
|
||||||
export class LayerStateSender {
|
|
||||||
constructor(layerState: EditLayerState) {
|
|
||||||
const layerId = layerState.configuration.map((config) => config.id)
|
|
||||||
layerState.configuration
|
|
||||||
.mapD((config) => JSON.stringify(config, null, " "))
|
|
||||||
.stabilized(100)
|
|
||||||
.addCallbackD(async (config) => {
|
|
||||||
const id = layerId.data
|
|
||||||
if (id === undefined) {
|
|
||||||
console.warn("No id found in layer, not updating")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await layerState.server.updateLayer(id, config)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HighlightedTagRendering {
|
export interface HighlightedTagRendering {
|
||||||
path: ReadonlyArray<string | number>
|
path: ReadonlyArray<string | number>
|
||||||
schema: ConfigMeta
|
schema: ConfigMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class EditLayerState {
|
export abstract class EditJsonState<T> {
|
||||||
public readonly schema: ConfigMeta[]
|
public readonly schema: ConfigMeta[]
|
||||||
|
public readonly category: "layers" | "themes"
|
||||||
public readonly featureSwitches: {
|
|
||||||
featureSwitchIsDebugging: UIEventSource<boolean>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to preview and interact with the questions
|
|
||||||
*/
|
|
||||||
public readonly testTags = new UIEventSource<OsmTags>({ id: "node/-12345" })
|
|
||||||
public readonly exampleFeature: Feature<Point> = {
|
|
||||||
type: "Feature",
|
|
||||||
properties: this.testTags.data,
|
|
||||||
geometry: {
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [3.21, 51.2],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
public readonly configuration: UIEventSource<Partial<LayerConfigJson>> = new UIEventSource<
|
|
||||||
Partial<LayerConfigJson>
|
|
||||||
>({})
|
|
||||||
public readonly messages: Store<ConversionMessage[]>
|
|
||||||
public readonly server: StudioServer
|
public readonly server: StudioServer
|
||||||
// Needed for the special visualisations
|
|
||||||
public readonly osmConnection: OsmConnection
|
public readonly configuration: UIEventSource<Partial<T>> = new UIEventSource<Partial<T>>({})
|
||||||
public readonly imageUploadManager = {
|
public readonly messages: Store<ConversionMessage[]>
|
||||||
getCountsFor() {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
}
|
|
||||||
public readonly layout: { getMatchingLayer: (key: any) => LayerConfig }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
|
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
|
||||||
|
@ -85,69 +43,25 @@ export default class EditLayerState {
|
||||||
)
|
)
|
||||||
private readonly _stores = new Map<string, UIEventSource<any>>()
|
private readonly _stores = new Map<string, UIEventSource<any>>()
|
||||||
|
|
||||||
constructor(schema: ConfigMeta[], server: StudioServer, osmConnection: OsmConnection) {
|
constructor(schema: ConfigMeta[], server: StudioServer, category: "layers" | "themes") {
|
||||||
this.schema = schema
|
this.schema = schema
|
||||||
this.server = server
|
this.server = server
|
||||||
this.osmConnection = osmConnection
|
this.category = category
|
||||||
this.featureSwitches = {
|
|
||||||
featureSwitchIsDebugging: new UIEventSource<boolean>(true),
|
|
||||||
}
|
|
||||||
let state: DesugaringContext
|
|
||||||
{
|
|
||||||
const layers = AllSharedLayers.getSharedLayersConfigs()
|
|
||||||
const questions = layers.get("questions")
|
|
||||||
const sharedQuestions = new Map<string, QuestionableTagRenderingConfigJson>()
|
|
||||||
for (const question of questions.tagRenderings) {
|
|
||||||
sharedQuestions.set(question["id"], <QuestionableTagRenderingConfigJson>question)
|
|
||||||
}
|
|
||||||
state = {
|
|
||||||
tagRenderings: sharedQuestions,
|
|
||||||
sharedLayers: layers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.highlightedItem.addCallback((h) => console.log("Highlighted is now", h))
|
this.messages = this.setupErrorsForLayers()
|
||||||
|
|
||||||
const prepare = new Pipe(
|
const layerId = this.getId()
|
||||||
new PrepareLayer(state),
|
this.configuration
|
||||||
new ValidateLayer("dynamic", false, undefined, true)
|
.mapD((config) => JSON.stringify(config, null, " "))
|
||||||
)
|
.stabilized(100)
|
||||||
this.messages = this.configuration.mapD((config) => {
|
.addCallbackD(async (config) => {
|
||||||
const trs = Utils.NoNull(config.tagRenderings ?? [])
|
const id = layerId.data
|
||||||
for (let i = 0; i < trs.length; i++) {
|
if (id === undefined) {
|
||||||
const tr = trs[i]
|
console.warn("No id found in layer, not updating")
|
||||||
if (typeof tr === "string") {
|
return
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if (!tr["id"] && !tr["override"]) {
|
await server.update(id, config, category)
|
||||||
const qtr = <QuestionableTagRenderingConfigJson>tr
|
|
||||||
let id = "" + i
|
|
||||||
if (qtr?.freeform?.key) {
|
|
||||||
id = qtr?.freeform?.key
|
|
||||||
} else if (qtr.mappings?.[0]?.if) {
|
|
||||||
id =
|
|
||||||
qtr.freeform?.key ??
|
|
||||||
TagUtils.Tag(qtr.mappings[0].if).usedKeys()?.[0] ??
|
|
||||||
"" + i
|
|
||||||
}
|
|
||||||
qtr["id"] = id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = ConversionContext.construct([], ["prepare"])
|
|
||||||
prepare.convert(<LayerConfigJson>config, context)
|
|
||||||
return context.messages
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.layout = {
|
|
||||||
getMatchingLayer: (_) => {
|
|
||||||
try {
|
|
||||||
return new LayerConfig(<LayerConfigJson>this.configuration.data, "dynamic")
|
|
||||||
} catch (e) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined {
|
public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined {
|
||||||
|
@ -273,4 +187,130 @@ export default class EditLayerState {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract buildValidation(state: DesugaringContext): Conversion<T, any>
|
||||||
|
|
||||||
|
protected abstract getId(): Store<string>
|
||||||
|
|
||||||
|
private setupErrorsForLayers(): Store<ConversionMessage[]> {
|
||||||
|
const layers = AllSharedLayers.getSharedLayersConfigs()
|
||||||
|
const questions = layers.get("questions")
|
||||||
|
const sharedQuestions = new Map<string, QuestionableTagRenderingConfigJson>()
|
||||||
|
for (const question of questions.tagRenderings) {
|
||||||
|
sharedQuestions.set(question["id"], <QuestionableTagRenderingConfigJson>question)
|
||||||
|
}
|
||||||
|
let state: DesugaringContext = {
|
||||||
|
tagRenderings: sharedQuestions,
|
||||||
|
sharedLayers: layers,
|
||||||
|
}
|
||||||
|
const prepare = this.buildValidation(state)
|
||||||
|
return this.configuration.mapD((config) => {
|
||||||
|
const context = ConversionContext.construct([], ["prepare"])
|
||||||
|
try {
|
||||||
|
prepare.convert(<T>config, context)
|
||||||
|
} catch (e) {
|
||||||
|
context.err(e)
|
||||||
|
}
|
||||||
|
return context.messages
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
||||||
|
// Needed for the special visualisations
|
||||||
|
public readonly osmConnection: OsmConnection
|
||||||
|
public readonly imageUploadManager = {
|
||||||
|
getCountsFor() {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
public readonly layout: { getMatchingLayer: (key: any) => LayerConfig }
|
||||||
|
public readonly featureSwitches: {
|
||||||
|
featureSwitchIsDebugging: UIEventSource<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to preview and interact with the questions
|
||||||
|
*/
|
||||||
|
public readonly testTags = new UIEventSource<OsmTags>({ id: "node/-12345" })
|
||||||
|
public readonly exampleFeature: Feature<Point> = {
|
||||||
|
type: "Feature",
|
||||||
|
properties: this.testTags.data,
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [3.21, 51.2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(schema: ConfigMeta[], server: StudioServer, osmConnection: OsmConnection) {
|
||||||
|
super(schema, server, "layers")
|
||||||
|
this.osmConnection = osmConnection
|
||||||
|
this.layout = {
|
||||||
|
getMatchingLayer: (_) => {
|
||||||
|
try {
|
||||||
|
return new LayerConfig(<LayerConfigJson>this.configuration.data, "dynamic")
|
||||||
|
} catch (e) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.featureSwitches = {
|
||||||
|
featureSwitchIsDebugging: new UIEventSource<boolean>(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addMissingTagRenderingIds()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buildValidation(state: DesugaringContext) {
|
||||||
|
return new Pipe(
|
||||||
|
new PrepareLayer(state),
|
||||||
|
new ValidateLayer("dynamic", false, undefined, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getId(): Store<string> {
|
||||||
|
return this.configuration.mapD((config) => config.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private addMissingTagRenderingIds() {
|
||||||
|
this.configuration.addCallbackD((config) => {
|
||||||
|
const trs = Utils.NoNull(config.tagRenderings ?? [])
|
||||||
|
for (let i = 0; i < trs.length; i++) {
|
||||||
|
const tr = trs[i]
|
||||||
|
if (typeof tr === "string") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!tr["id"] && !tr["override"]) {
|
||||||
|
const qtr = <QuestionableTagRenderingConfigJson>tr
|
||||||
|
let id = "" + i
|
||||||
|
if (qtr?.freeform?.key) {
|
||||||
|
id = qtr?.freeform?.key
|
||||||
|
} else if (qtr.mappings?.[0]?.if) {
|
||||||
|
id =
|
||||||
|
qtr.freeform?.key ??
|
||||||
|
TagUtils.Tag(qtr.mappings[0].if).usedKeys()?.[0] ??
|
||||||
|
"" + i
|
||||||
|
}
|
||||||
|
qtr["id"] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
||||||
|
protected buildValidation(state: DesugaringContext): Conversion<LayoutConfigJson, any> {
|
||||||
|
return new Pipe(
|
||||||
|
new PrepareTheme(state),
|
||||||
|
new ValidateTheme(undefined, "", false, new Set(state.tagRenderings.keys()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(schema: ConfigMeta[], server: StudioServer) {
|
||||||
|
super(schema, server, "themes")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getId(): Store<string> {
|
||||||
|
return this.configuration.mapD((config) => config.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { EditThemeState } from "./EditLayerState";
|
||||||
|
import type { ConfigMeta } from "./configMeta";
|
||||||
|
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||||
|
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion";
|
||||||
|
import TabbedGroup from "../Base/TabbedGroup.svelte";
|
||||||
|
import ShowConversionMessages from "./ShowConversionMessages.svelte";
|
||||||
|
import Region from "./Region.svelte";
|
||||||
|
|
||||||
|
export let state: EditThemeState;
|
||||||
|
let schema: ConfigMeta[] = state.schema.filter(schema => schema.path.length > 0 && schema.path[0] !== "layers");
|
||||||
|
let config = state.configuration;
|
||||||
|
const messages = state.messages;
|
||||||
|
const hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
||||||
|
let title = state.getStoreFor(["id"]);
|
||||||
|
const wl = window.location;
|
||||||
|
const baseUrl = wl.protocol + "//" + wl.host + "/theme.html?userlayout=";
|
||||||
|
|
||||||
|
const perRegion: Record<string, ConfigMeta[]> = {};
|
||||||
|
for (const schemaElement of schema) {
|
||||||
|
const key = schemaElement.hints.group ?? "no-group"
|
||||||
|
const list = perRegion[key] ?? (perRegion[key] = [])
|
||||||
|
list.push(schemaElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full flex justify-between my-2">
|
||||||
|
<slot />
|
||||||
|
<h3>Editing theme {$title}</h3>
|
||||||
|
{#if $hasErrors > 0}
|
||||||
|
<div class="alert">{$hasErrors} errors detected</div>
|
||||||
|
{:else}
|
||||||
|
<a class="primary button" href={baseUrl+state.server.urlFor($title, "themes")} target="_blank" rel="noopener">
|
||||||
|
Try it out
|
||||||
|
<ChevronRightIcon class="h-6 w-6 shrink-0" />
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m4 h-full overflow-y-auto">
|
||||||
|
{Object.keys(perRegion).join(";")}
|
||||||
|
<TabbedGroup>
|
||||||
|
<div slot="title0">Basic properties</div>
|
||||||
|
<div slot="content0">
|
||||||
|
<Region {state} configs={perRegion["basic"]} path={[]}></Region>
|
||||||
|
<Region {state} configs={perRegion["no-group"]} path={[]}></Region>
|
||||||
|
</div>
|
||||||
|
<div slot="title1">Feature switches</div>
|
||||||
|
<div slot="content1">
|
||||||
|
<Region {state} configs={perRegion["feature_switches"]} path={[]}></Region>
|
||||||
|
</div>
|
||||||
|
<div slot="title2">Configuration file</div>
|
||||||
|
<div slot="content2">
|
||||||
|
<div class="literal-code">
|
||||||
|
{JSON.stringify($config)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ShowConversionMessages messages={$messages}></ShowConversionMessages>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</TabbedGroup>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion";
|
||||||
|
|
||||||
|
export let messages: ConversionMessage[];
|
||||||
|
console.log(messages)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if messages.length === 0}
|
||||||
|
<div class="thanks">
|
||||||
|
No errors, warnings or messages
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each messages as message}
|
||||||
|
<li>
|
||||||
|
{message.level}
|
||||||
|
<span class="literal-code">{message.context.path.join(".")}</span>
|
||||||
|
{message.message}
|
||||||
|
<span class="literal-code">
|
||||||
|
{message.context.operation.join(".")}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
|
@ -12,10 +12,11 @@ export default class StudioServer {
|
||||||
this._userId = userId
|
this._userId = userId
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchLayerOverview(): Promise<
|
public async fetchOverview(): Promise<
|
||||||
{
|
{
|
||||||
id: string
|
id: string
|
||||||
owner: number
|
owner: number
|
||||||
|
category: "layers" | "themes"
|
||||||
}[]
|
}[]
|
||||||
> {
|
> {
|
||||||
const uid = this._userId.data
|
const uid = this._userId.data
|
||||||
|
@ -29,6 +30,7 @@ export default class StudioServer {
|
||||||
const layerOverview: {
|
const layerOverview: {
|
||||||
id: string
|
id: string
|
||||||
owner: number | undefined
|
owner: number | undefined
|
||||||
|
category: "layers" | "themes"
|
||||||
}[] = []
|
}[] = []
|
||||||
for (let file of allFiles) {
|
for (let file of allFiles) {
|
||||||
let owner = undefined
|
let owner = undefined
|
||||||
|
@ -36,31 +38,29 @@ export default class StudioServer {
|
||||||
owner = uid
|
owner = uid
|
||||||
file = file.substring(file.indexOf("/") + 1)
|
file = file.substring(file.indexOf("/") + 1)
|
||||||
}
|
}
|
||||||
if (!file.startsWith("layers/")) {
|
const category = <"layers" | "themes">file.substring(0, file.indexOf("/"))
|
||||||
continue
|
|
||||||
}
|
|
||||||
const id = file.substring(file.lastIndexOf("/") + 1, file.length - ".json".length)
|
const id = file.substring(file.lastIndexOf("/") + 1, file.length - ".json".length)
|
||||||
if (Constants.priviliged_layers.indexOf(<any>id) > 0) {
|
if (Constants.priviliged_layers.indexOf(<any>id) > 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
layerOverview.push({ id, owner })
|
layerOverview.push({ id, owner, category })
|
||||||
}
|
}
|
||||||
return layerOverview
|
return layerOverview
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchLayer(layerId: string): Promise<LayerConfigJson> {
|
async fetch(layerId: string, category: "layers" | "themes"): Promise<LayerConfigJson> {
|
||||||
try {
|
try {
|
||||||
return await Utils.downloadJson(this.layerUrl(layerId))
|
return await Utils.downloadJson(this.urlFor(layerId, category))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateLayer(id: string, config: string) {
|
async update(id: string, config: string, category: "layers" | "themes") {
|
||||||
if (id === undefined || id === "") {
|
if (id === undefined || id === "") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await fetch(this.layerUrl(id), {
|
await fetch(this.urlFor(id, category), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json;charset=utf-8",
|
"Content-Type": "application/json;charset=utf-8",
|
||||||
|
@ -70,8 +70,12 @@ export default class StudioServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public layerUrl(id: string) {
|
public layerUrl(id: string) {
|
||||||
|
return this.urlFor(id, "layers")
|
||||||
|
}
|
||||||
|
|
||||||
|
public urlFor(id: string, category: "layers" | "themes") {
|
||||||
const uid = this._userId.data
|
const uid = this._userId.data
|
||||||
const uidStr = uid !== undefined ? "/" + uid : ""
|
const uidStr = uid !== undefined ? "/" + uid : ""
|
||||||
return `${this.url}${uidStr}/layers/${id}/${id}.json`
|
return `${this.url}${uidStr}/${category}/${id}/${id}.json`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue