From 4219b23af109f854907a5864f8eef298e0923591 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 3 Nov 2023 01:05:17 +0100 Subject: [PATCH] UX: map in 'add new point' now takes the full screen --- ...2023-10-17 User Test Studio bxl-forever.md | 2 + assets/layers/last_click/last_click.json | 1 + .../ThemeConfig/Conversion/PrepareLayer.ts | 3 + .../BigComponents/SelectedElementView.svelte | 3 +- src/UI/Popup/AddNewPoint/AddNewPoint.svelte | 528 +++++++++--------- src/UI/Popup/TagRendering/Questionbox.svelte | 2 +- .../TagRendering/TagRenderingEditable.svelte | 8 +- src/UI/Studio/ShowConversionMessage.svelte | 0 8 files changed, 281 insertions(+), 266 deletions(-) create mode 100644 src/UI/Studio/ShowConversionMessage.svelte diff --git a/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md b/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md index e54d3ddca..d980bd67c 100644 --- a/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md +++ b/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md @@ -21,3 +21,5 @@ The participant has extensive OpenStreetMap-knowledge but only used MapComplete - [x] This user had an expression with two tags in an AND. There was some confusion if the taginfo-count gave the totals for the tags individually or for the entire expression. Fix: play with padding and wording - [x] BUG: having a complex expression for tags (e.g. with `and: [key=value, key0=value0]`) fails as the JSON would be stringified +- [x] In MapComplete (not in studio): creating a new point: the buttons might dissapear under scroll if zoomed in a lot +- [x] If a layer does not have a title and a tagRenderings, it is not interpreted as 'standalone' theme diff --git a/assets/layers/last_click/last_click.json b/assets/layers/last_click/last_click.json index 1712bd3f3..c99a4ce5b 100644 --- a/assets/layers/last_click/last_click.json +++ b/assets/layers/last_click/last_click.json @@ -156,6 +156,7 @@ "tagRenderings": [ { "id": "add_new", + "classes": "h-full flex", "condition": "has_presets=yes", "render": { "*": "{add_new_point()}" diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 923fed502..a72306dd9 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -483,6 +483,9 @@ export class AddQuestionBox extends DesugaringStep { ) { return json } + if (json.source === "special") { + return json + } json = { ...json } json.tagRenderings = [...json.tagRenderings] const allSpecials: Exclude[] = ( diff --git a/src/UI/BigComponents/SelectedElementView.svelte b/src/UI/BigComponents/SelectedElementView.svelte index 924d6f7f3..c7e0c2656 100644 --- a/src/UI/BigComponents/SelectedElementView.svelte +++ b/src/UI/BigComponents/SelectedElementView.svelte @@ -28,10 +28,11 @@ {:else} -
+
{#each layer.tagRenderings as config (config.id)} {#if (config.condition?.matchesProperties($tags) ?? true) && config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true)} {#if config.IsKnown($tags)} + {config.id} - } = undefined - let checkedOfGlobalFilters: number = 0 - let confirmedCategory = false + } = undefined; + let checkedOfGlobalFilters: number = 0; + let confirmedCategory = false; $: if (selectedPreset === undefined) { - confirmedCategory = false - creating = false - checkedOfGlobalFilters = 0 + confirmedCategory = false; + creating = false; + checkedOfGlobalFilters = 0; } - let flayer: FilteredLayer = undefined - let layerIsDisplayed: UIEventSource | undefined = undefined - let layerHasFilters: Store | undefined = undefined - let globalFilter: UIEventSource = state.layerState.globalFilters - let _globalFilter: GlobalFilter[] = [] + let flayer: FilteredLayer = undefined; + let layerIsDisplayed: UIEventSource | undefined = undefined; + let layerHasFilters: Store | undefined = undefined; + let globalFilter: UIEventSource = state.layerState.globalFilters; + let _globalFilter: GlobalFilter[] = []; onDestroy( globalFilter.addCallbackAndRun((globalFilter) => { - console.log("Global filters are", globalFilter) - _globalFilter = globalFilter ?? [] + console.log("Global filters are", globalFilter); + _globalFilter = globalFilter ?? []; }) - ) + ); $: { - flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id) - layerIsDisplayed = flayer?.isDisplayed - layerHasFilters = flayer?.hasFilter + flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id); + layerIsDisplayed = flayer?.isDisplayed; + 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 - let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined) - let snappedToObject: UIEventSource = new UIEventSource(undefined) + const isLoading = state.dataIsLoading; + let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined); + let snappedToObject: UIEventSource = new UIEventSource(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 - 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. * Will delete the lastclick-location */ function abort() { - state.selectedElement.setData(undefined) + state.selectedElement.setData(undefined); // 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 - state.lastClickObject.features.setData([]) - preciseInputIsTapped = false + state.lastClickObject.features.setData([]); + preciseInputIsTapped = false; } async function confirm() { - creating = true - const location: { lon: number; lat: number } = preciseCoordinate.data - const snapTo: WayId | undefined = snappedToObject.data + creating = true; + const location: { lon: number; lat: number } = preciseCoordinate.data; + const snapTo: WayId | undefined = snappedToObject.data; const tags: Tag[] = selectedPreset.preset.tags.concat( ..._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) { - const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0) + const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0); if (downloaded !== "deleted") { - snapToWay = downloaded + snapToWay = downloaded; } } @@ -113,44 +113,44 @@ theme: state.layout?.id ?? "unkown", changeType: "create", snapOnto: snapToWay, - reusePointWithinMeters: 1, - }) - await state.changes.applyAction(newElementAction) - state.newFeatures.features.ping() + reusePointWithinMeters: 1 + }); + await state.changes.applyAction(newElementAction); + state.newFeatures.features.ping(); // The 'changes' should have created a new point, which added this into the 'featureProperties' - const newId = newElementAction.newElementId - console.log("Applied pending changes, fetching store for", newId) - const tagsStore = state.featureProperties.getStore(newId) + const newId = newElementAction.newElementId; + console.log("Applied pending changes, fetching store for", newId); + const tagsStore = state.featureProperties.getStore(newId); if (!tagsStore) { - console.error("Bug: no tagsStore found for", newId) + console.error("Bug: no tagsStore found for", newId); } { // Set some metainfo - const properties = tagsStore.data + const properties = tagsStore.data; if (snapTo) { // metatags (starting with underscore) are not uploaded, so we can safely mark this - delete properties["_referencing_ways"] - properties["_referencing_ways"] = `["${snapTo}"]` + delete properties["_referencing_ways"]; + properties["_referencing_ways"] = `["${snapTo}"]`; } - properties["_backend"] = state.osmConnection.Backend() - properties["_last_edit:timestamp"] = new Date().toISOString() - const userdetails = state.osmConnection.userDetails.data - properties["_last_edit:contributor"] = userdetails.name - properties["_last_edit:uid"] = "" + userdetails.uid - tagsStore.ping() + properties["_backend"] = state.osmConnection.Backend(); + properties["_last_edit:timestamp"] = new Date().toISOString(); + const userdetails = state.osmConnection.userDetails.data; + properties["_last_edit:contributor"] = userdetails.name; + properties["_last_edit:uid"] = "" + userdetails.uid; + tagsStore.ping(); } - const feature = state.indexedFeatures.featuresById.data.get(newId) - console.log("Selecting feature", feature, "and opening their popup") - abort() - state.selectedLayer.setData(selectedPreset.layer) - state.selectedElement.setData(feature) - tagsStore.ping() + const feature = state.indexedFeatures.featuresById.data.get(newId); + console.log("Selecting feature", feature, "and opening their popup"); + abort(); + state.selectedLayer.setData(selectedPreset.layer); + state.selectedElement.setData(feature); + tagsStore.ping(); } function confirmSync() { confirm() .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)); } @@ -162,206 +162,212 @@ - {#if $zoom < Constants.minZoomLevelToAddNewPoint} -
- -
- {:else if $isLoading} -
- - - -
- {:else if selectedPreset === undefined} - - { +
+ + {#if $zoom < Constants.minZoomLevelToAddNewPoint} +
+ +
+ {:else if $isLoading} +
+ + + +
+ {:else if selectedPreset === undefined} + + { selectedPreset = event.detail }} - /> - {:else if !$layerIsDisplayed} - -
- - -
+ {:else if !$layerIsDisplayed} + +
+ + +
-
- + > + + + - -
- {:else if $layerHasFilters} - -
- - -
-
- +
+ {:else if $layerHasFilters} + +
+ + +
+
+ - + -
- {:else if !confirmedCategory} - -

- -

+ > + + + +
+ {:else if !confirmedCategory} + +

+ +

- + - {#if selectedPreset.preset.description} - - {/if} + {#if selectedPreset.preset.description} + + {/if} - {#if selectedPreset.preset.exampleImages} -

- {#if selectedPreset.preset.exampleImages.length === 1} - - {:else} - - {/if} -

- + {#if selectedPreset.preset.exampleImages} +

+ {#if selectedPreset.preset.exampleImages.length === 1} + + {:else} + + {/if} +

+ {#each selectedPreset.preset.exampleImages as src} {/each} - {/if} - t.presetInfo.Subs({ tags })} - {state} - tags={new And(selectedPreset.preset.tags)} - /> - -
- (selectedPreset = undefined)} clss="w-full"> - - - - (confirmedCategory = true)} clss="primary w-full"> -
- - -
-
- -
-
-
- {:else if _globalFilter?.length > 0 && _globalFilter?.length > checkedOfGlobalFilters} - - { - checkedOfGlobalFilters = checkedOfGlobalFilters + 1 - }} - > - t.presetInfo.Subs({ tags })} + {state} + tags={new And(selectedPreset.preset.tags)} /> - - - { - globalFilter.setData([]) - abort() - }} - > - - - - {:else if !creating} -
-
- { - preciseInputIsTapped = true - }} - value={preciseCoordinate} - snappedTo={snappedToObject} - {state} - {coordinate} - targetLayer={selectedPreset.layer} - snapToLayers={selectedPreset.preset.preciseInput.snapToLayers} - /> -
-
- -
- +
+ (selectedPreset = undefined)} clss="w-full"> + + + + (confirmedCategory = true)} clss="primary w-full"> +
+ + +
+
+
+ {:else if _globalFilter?.length > 0 && _globalFilter?.length > checkedOfGlobalFilters} + + { + checkedOfGlobalFilters = checkedOfGlobalFilters + 1 + }} + > + + + + { + globalFilter.setData([]) + abort() + }} + > + + + + {:else if !creating} +
+
+
+ { + preciseInputIsTapped = true + }} + value={preciseCoordinate} + snappedTo={snappedToObject} + {state} + {coordinate} + targetLayer={selectedPreset.layer} + snapToLayers={selectedPreset.preset.preciseInput.snapToLayers} + /> +
-
- -
-
-
- (selectedPreset = undefined)} clss="w-full"> - - +
+ + +
+ +
+
+
- -
- +
+ +
-
-
- {:else} - Creating point... - {/if} +
+ (selectedPreset = undefined)} clss="w-full"> + + + + +
+ +
+
+
+
+ {:else} + Creating point... + {/if} +
diff --git a/src/UI/Popup/TagRendering/Questionbox.svelte b/src/UI/Popup/TagRendering/Questionbox.svelte index 2f06de0c4..f8be57489 100644 --- a/src/UI/Popup/TagRendering/Questionbox.svelte +++ b/src/UI/Popup/TagRendering/Questionbox.svelte @@ -95,7 +95,7 @@ } -
+
{#if _questionsToAsk.length === 0} {#if skipped + answered > 0}
diff --git a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte index eaadc6f05..3d240c1f7 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte @@ -19,7 +19,9 @@ export let layer: LayerConfig = undefined; export let editingEnabled: Store | undefined = state?.featureSwitchUserbadge; - + + export let clss = config.classes.join(" ") + export let highlightedRendering: UIEventSource = undefined; export let showQuestionIfUnknown: boolean = false; /** @@ -71,7 +73,7 @@ } -
+
{#if config.question && (!editingEnabled || $editingEnabled)} {#if editMode} @@ -106,7 +108,7 @@
{/if} {:else} -
+
{/if} diff --git a/src/UI/Studio/ShowConversionMessage.svelte b/src/UI/Studio/ShowConversionMessage.svelte new file mode 100644 index 000000000..e69de29bb