Studio: Fix some usability errors

This commit is contained in:
Pieter Vander Vennet 2023-10-17 00:32:54 +02:00
parent 80b7a038cf
commit 2f8cb91d31
9 changed files with 43 additions and 30 deletions

View file

@ -28,7 +28,8 @@
</script> </script>
{#if $badge} {#if $badge}
{#if !ignoreLoading && $loadingStatus === "loading"} <slot/>
<!-- {#if !ignoreLoading && $loadingStatus === "loading"}
<slot name="loading"> <slot name="loading">
<Loading /> <Loading />
</slot> </slot>
@ -41,5 +42,5 @@
<slot /> <slot />
{:else if $loadingStatus === "not-attempted"} {:else if $loadingStatus === "not-attempted"}
<slot name="not-logged-in" /> <slot name="not-logged-in" />
{/if} {/if} -->
{/if} {/if}

View file

@ -88,7 +88,7 @@ export class LoginToggle extends VariableUiElement {
) )
}, },
[state.featureSwitchUserbadge, state.osmConnection?.apiIsOnline] [state.featureSwitchUserbadge, state.osmConnection?.apiIsOnline]
) ?? new ImmutableStore(el) // ) ?? new ImmutableStore(el)
) )
} }
} }

View file

@ -16,7 +16,7 @@
export let tags: UIEventSource<Record<string, string>> export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature | undefined export let selectedElement: Feature | undefined
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
export let layer: LayerConfig export let layer: LayerConfig = undefined
export let editingEnabled : Store<boolean> | undefined = state?.featureSwitchUserbadge export let editingEnabled : Store<boolean> | undefined = state?.featureSwitchUserbadge

View file

@ -986,6 +986,9 @@ export default class SpecialVisualizations {
constr: (state, tagsSource) => constr: (state, tagsSource) =>
new VariableUiElement( new VariableUiElement(
tagsSource.map((tags) => { tagsSource.map((tags) => {
if (state.layout === undefined) {
return "<feature title>"
}
const layer = state.layout.getMatchingLayer(tags) const layer = state.layout.getMatchingLayer(tags)
const title = layer?.title?.GetRenderValue(tags) const title = layer?.title?.GetRenderValue(tags)
if (title === undefined) { if (title === undefined) {
@ -1335,10 +1338,10 @@ export default class SpecialVisualizations {
tagSource.map((tags) => { tagSource.map((tags) => {
const v = tags[argument[0] ?? "value"] const v = tags[argument[0] ?? "value"]
try { try {
const tr = JSON.parse(v) const tr = typeof v === "string" ? JSON.parse(v) : v
return new Translation(tr).SetClass("font-bold") return new Translation(tr).SetClass("font-bold")
} catch (e) { } catch (e) {
return v return JSON.stringify(v)
} }
}) })
) )

View file

@ -15,6 +15,7 @@ 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"
import StudioServer from "./StudioServer" import StudioServer from "./StudioServer"
import { Utils } from "../../Utils"
/** /**
* Sends changes back to the server * Sends changes back to the server
@ -72,9 +73,9 @@ export default class EditLayerState {
} }
this.messages = this.configuration.mapD((config) => { this.messages = this.configuration.mapD((config) => {
const context = ConversionContext.construct([], ["prepare"]) const context = ConversionContext.construct([], ["prepare"])
const trs = Utils.NoNull(config.tagRenderings ?? [])
for (let i = 0; i < (config.tagRenderings ?? []).length; i++) { for (let i = 0; i < trs.length; i++) {
const tr = config.tagRenderings[i] const tr = trs[i]
if (typeof tr === "string") { if (typeof tr === "string") {
continue continue
} }
@ -116,11 +117,19 @@ export default class EditLayerState {
return entry return entry
} }
public getStoreFor(path: ReadonlyArray<string | number>): UIEventSource<any | undefined> { private readonly _stores = new Map<string, UIEventSource<any>>()
public getStoreFor<T>(path: ReadonlyArray<string | number>): UIEventSource<T | undefined> {
const key = path.join(".")
// TODO check if this gives problems when changing the order of e.g. mappings and questions
if (this._stores.has(key)) {
return this._stores.get(key)
}
const store = new UIEventSource<any>(this.getCurrentValueFor(path)) const store = new UIEventSource<any>(this.getCurrentValueFor(path))
store.addCallback((v) => { store.addCallback((v) => {
this.setValueAt(path, v) this.setValueAt(path, v)
}) })
this._stores.set(key, store)
return store return store
} }
@ -170,19 +179,24 @@ export default class EditLayerState {
public setValueAt(path: ReadonlyArray<string | number>, v: any) { public setValueAt(path: ReadonlyArray<string | number>, v: any) {
let entry = this.configuration.data let entry = this.configuration.data
const isUndefined =
v !== undefined &&
v !== null &&
v !== "" &&
!(typeof v === "object" && Object.keys({}).length === 0)
for (let i = 0; i < path.length - 1; i++) { for (let i = 0; i < path.length - 1; i++) {
const breadcrumb = path[i] const breadcrumb = path[i]
if (entry[breadcrumb] === undefined) { if (entry[breadcrumb] === undefined) {
entry[breadcrumb] = typeof path[i + 1] === "number" ? [] : {} entry[breadcrumb] = typeof path[i + 1] === "number" ? [] : {}
} }
entry = entry[breadcrumb] entry = entry[breadcrumb]
if (entry === undefined && isUndefined) {
// Nothing to do anymore: we cannot traverse the object, but don't have to set something anyway
return
} }
if ( }
v !== undefined && if (isUndefined) {
v !== null &&
v !== "" &&
!(typeof v === "object" && Object.keys({}).length === 0)
) {
entry[path.at(-1)] = v entry[path.at(-1)] = v
} else if (entry) { } else if (entry) {
delete entry[path.at(-1)] delete entry[path.at(-1)]

View file

@ -29,7 +29,7 @@
const configJson: QuestionableTagRenderingConfigJson = { const configJson: QuestionableTagRenderingConfigJson = {
id: path.join("_"), id: path.join("_"),
render: schema.type === "boolean" ? undefined : ((schema.hints.inline ?? schema.path.at(-1) )+ ": <b>{translated(value)}</b>"), render: schema.type === "boolean" ? undefined : ((schema.hints.inline ?? schema.path.at(-1) )+ ": translated value: <b>{translated(value)}</b>"),
question: schema.hints.question, question: schema.hints.question,
questionHint: nmd(schema.description), questionHint: nmd(schema.description),
freeform: schema.type === "boolean" ? undefined : { freeform: schema.type === "boolean" ? undefined : {
@ -100,7 +100,9 @@
try { try {
onDestroy(state.register(path, tags.map(tgs => { onDestroy(state.register(path, tags.map(tgs => {
const v = tgs["value"]; const v = tgs["value"];
if(v !== ""){
console.log("Registering",path,"setting value to", v) console.log("Registering",path,"setting value to", v)
}
if(typeof v !== "string"){ if(typeof v !== "string"){
return v return v
} }

View file

@ -113,7 +113,6 @@
for (const requiredAttribute of type.required) { for (const requiredAttribute of type.required) {
if (existingValue[requiredAttribute] === undefined) { if (existingValue[requiredAttribute] === undefined) {
console.log(path.join("."), " does not have required field", requiredAttribute, " so it cannot be type ", type);
// The 'existingValue' does _not_ have this required attribute, so it cannot be of this type // The 'existingValue' does _not_ have this required attribute, so it cannot be of this type
continue outer; continue outer;
} }
@ -128,7 +127,6 @@
} }
possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches); possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches);
possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount); possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount);
console.log("Possible types are", possibleTypes)
if (possibleTypes.length > 0) { if (possibleTypes.length > 0) {
chosenOption = possibleTypes[0].index chosenOption = possibleTypes[0].index
tags.setData({ chosen_type_index: "" + chosenOption}); tags.setData({ chosen_type_index: "" + chosenOption});
@ -157,27 +155,23 @@
let subSchemas: ConfigMeta[] = []; let subSchemas: ConfigMeta[] = [];
let subpath = path; let subpath = path;
console.log("Initial chosen option for",path.join("."),"is", chosenOption); const store = state.getStoreFor(path)
onDestroy(tags.addCallbackAndRun(tags => { onDestroy(tags.addCallbackAndRun(tags => {
if (tags["value"] !== undefined && tags["value"] !== "") { if (tags["value"] !== undefined && tags["value"] !== "") {
chosenOption = undefined; chosenOption = undefined;
console.log("Resetting chosenOption as `value` is present in the tags:", tags["value"])
return; return;
} }
const oldOption = chosenOption; const oldOption = chosenOption;
console.log("Updating chosenOption based on", tags, oldOption)
chosenOption = tags["chosen_type_index"] ? Number(tags["chosen_type_index"]) : defaultOption; chosenOption = tags["chosen_type_index"] ? Number(tags["chosen_type_index"]) : defaultOption;
const type = schema.type[chosenOption]; const type = schema.type[chosenOption];
if (chosenOption !== oldOption) { if (chosenOption !== oldOption) {
// Reset the values beneath // Reset the values beneath
subSchemas = []; subSchemas = [];
const o = state.getCurrentValueFor(path) ?? {}; const o = state.getCurrentValueFor(path) ?? {};
console.log({ o });
for (const key of type?.required ?? []) { for (const key of type?.required ?? []) {
console.log(key);
o[key] ??= {}; o[key] ??= {};
} }
state.setValueAt(path, o); store.setData(o)
} }
if (!type) { if (!type) {
return; return;
@ -187,7 +181,6 @@
if (type["$ref"] === "#/definitions/Record<string,string>") { if (type["$ref"] === "#/definitions/Record<string,string>") {
// The subtype is a translation object // The subtype is a translation object
const schema = state.getTranslationAt(cleanPath); const schema = state.getTranslationAt(cleanPath);
console.log("Got a translation,", schema);
subSchemas.push(schema); subSchemas.push(schema);
subpath = path.slice(0, path.length - 2); subpath = path.slice(0, path.length - 2);
return; return;

View file

@ -44,9 +44,9 @@ const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson
const tags = new UIEventSource({ value }); const tags = new UIEventSource({ value });
const store = state.getStoreFor(path)
tags.addCallbackAndRunD(tgs => { tags.addCallbackAndRunD(tgs => {
state.setValueAt(path, tgs["value"]); store.setData(tgs["value"])
}); });
let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"]); let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"]);

View file

@ -17,7 +17,7 @@
import layerSchemaRaw from "../../src/assets/schemas/layerconfigmeta.json"; import layerSchemaRaw from "../../src/assets/schemas/layerconfigmeta.json";
import If from "./Base/If.svelte"; import If from "./Base/If.svelte";
export let studioUrl = /* "https://studio.mapcomplete.org"; /*/ "http://127.0.0.1:1235"; //*/ export let studioUrl = window.location.hostname === "127.0.0.1" ? "http://127.0.0.1:1235" : "https://studio.mapcomplete.org";
const studio = new StudioServer(studioUrl); const studio = new StudioServer(studioUrl);
let layersWithErr = UIEventSource.FromPromiseWithErr(studio.fetchLayerOverview()); let layersWithErr = UIEventSource.FromPromiseWithErr(studio.fetchLayerOverview());
let layers = layersWithErr.mapD(l => l.success); let layers = layersWithErr.mapD(l => l.success);