forked from MapComplete/MapComplete
UX: usertest + fix some surfaced issues, see #2108
This commit is contained in:
parent
45481a879d
commit
f5e71f2f1c
9 changed files with 128 additions and 5 deletions
49
Docs/UserTests/2025-09-12 Usertest Paul.md
Normal file
49
Docs/UserTests/2025-09-12 Usertest Paul.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
User test Paul
|
||||||
|
Tech Skills: High, low OSM knowledge
|
||||||
|
Demography: M, ~22
|
||||||
|
Language: NL
|
||||||
|
MapComplete App (Google-Play versie)
|
||||||
|
|
||||||
|
No predetermined goal to achieve
|
||||||
|
|
||||||
|
- Wilt taalfout fixen: ommetoer via 3 login systemen....
|
||||||
|
Out of scope voor beginners
|
||||||
|
|
||||||
|
- Loginscherm is wit:
|
||||||
|
Chrome openen ernaast
|
||||||
|
Technische bug, maar hoe te fixen?
|
||||||
|
|
||||||
|
- Toiletten: vragen over databron
|
||||||
|
|
||||||
|
- Hackerspace kaart: ik heb twee symbolen, wat is het verschil? Ik heb geen legende?
|
||||||
|
-> Achtergrondkaart selectie? Daar legende?
|
||||||
|
Aanklikken symbooltjes: softwaregericht / makerspace... Ooohhhh
|
||||||
|
- OK, is duidelijk
|
||||||
|
|
||||||
|
- "Geen idee wat panoramax is" > Open Panoramax/Mapillary hier
|
||||||
|
Geen idee wat dit is
|
||||||
|
Heeft wat meer info nodig (!)
|
||||||
|
"Allemaal links naar andere maps..."
|
||||||
|
|
||||||
|
- Zoekfunctie: de dingen staan in de weg
|
||||||
|
-> Ik kan op terug klikken. Ja nee, dat dacht ik al
|
||||||
|
-> Terug terug gaat niet terug
|
||||||
|
-> "Gouda". Ja nee, dat is niet handig...
|
||||||
|
Ik wil een ander thema... Ah ja
|
||||||
|
-> 'Active filters' staat in de weg - max height: 1/2 height
|
||||||
|
-> "Clear text"-knop staat permanent aan, opvolgtest: niet gevonden
|
||||||
|
|
||||||
|
- Kompas: "ooh, het wijzertje is mijn kompas!" -> Popup tonen met "wijst nu naar je kompas"
|
||||||
|
Fixed by adding popovers
|
||||||
|
|
||||||
|
- Trees: icoontje ontbreekt in app
|
||||||
|
Niemand heeft onze boom toegevoegd! Toevoegen
|
||||||
|
"Ja, het is een loofboom"
|
||||||
|
Locatie toevoegen gaat vlot
|
||||||
|
Voeg toe! Gaat vlot
|
||||||
|
Foto toevoegen: gaat ook vlot
|
||||||
|
Plantnet-detectie: gaat vlot, maar niet de juiste boomsoort
|
||||||
|
"Oh, ik kon er gewoon op klikken" -> duidelijk maken voor preview
|
||||||
|
|
||||||
|
- Waarom staan restaurants los van cafés?
|
||||||
|
OSM-keuze
|
|
@ -32,6 +32,17 @@
|
||||||
"intro": "Get in touch with other people to get to know them, learn from them, …",
|
"intro": "Get in touch with other people to get to know them, learn from them, …",
|
||||||
"title": "Get in touch with others"
|
"title": "Get in touch with others"
|
||||||
},
|
},
|
||||||
|
"compass": {
|
||||||
|
"E": "Map has east pointing up",
|
||||||
|
"N": "Map has north pointing up",
|
||||||
|
"NE": "Map has northeast pointing up",
|
||||||
|
"NW": "Map has northwest pointing up",
|
||||||
|
"S": "Map has south pointing up",
|
||||||
|
"SE": "Map has southeast pointing up",
|
||||||
|
"SW": "Map has southwest pointing up",
|
||||||
|
"W": "Map has west pointing up",
|
||||||
|
"toCompass": "The map is now oriented to the device orientation"
|
||||||
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
"button": "Create a copy",
|
"button": "Create a copy",
|
||||||
"confirm": "Create copy at the specified location",
|
"confirm": "Create copy at the specified location",
|
||||||
|
|
|
@ -1970,6 +1970,10 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
max-height: 16rem;
|
max-height: 16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-h-\[30vh\] {
|
||||||
|
max-height: 30vh;
|
||||||
|
}
|
||||||
|
|
||||||
.max-h-full {
|
.max-h-full {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -5346,6 +5350,13 @@ input[type="text"] {
|
||||||
* This very important section defines what the various input elements look like within the 'low-interaction' and 'interactive'-blocks
|
* This very important section defines what the various input elements look like within the 'low-interaction' and 'interactive'-blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
/* Disable the 'clear text field' button in chrome */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
/********* BUTTONS ***********/
|
/********* BUTTONS ***********/
|
||||||
|
|
||||||
button, .button {
|
button, .button {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
<div class="sidebar-unit">
|
<script lang="ts">
|
||||||
|
export let clss = ""
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={"sidebar-unit "+clss}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
import Compass_back from "../../assets/svg/Compass_back.svelte"
|
import Compass_back from "../../assets/svg/Compass_back.svelte"
|
||||||
import Compass_needle from "../../assets/svg/Compass_needle.svelte"
|
import Compass_needle from "../../assets/svg/Compass_needle.svelte"
|
||||||
import North_arrow from "../../assets/svg/North_arrow.svelte"
|
import North_arrow from "../../assets/svg/North_arrow.svelte"
|
||||||
|
import { Popover } from "flowbite-svelte"
|
||||||
|
import { Translation, TypedTranslation } from "../i18n/Translation"
|
||||||
|
import TrDyn from "../Base/TrDyn.svelte"
|
||||||
|
import Translations from "../i18n/Translations"
|
||||||
|
import Tr from "../Base/Tr.svelte"
|
||||||
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
|
|
||||||
export let mapProperties: { rotation: UIEventSource<number>; allowRotating: Store<boolean> }
|
export let mapProperties: { rotation: UIEventSource<number>; allowRotating: Store<boolean> }
|
||||||
let orientation = Orientation.singleton.alpha
|
let orientation = Orientation.singleton.alpha
|
||||||
|
@ -17,24 +23,48 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let mapRotation = mapProperties.rotation
|
let mapRotation = mapProperties.rotation
|
||||||
|
let showHint = new UIEventSource(false)
|
||||||
|
showHint.stabilized(4000).addCallback(isShown => {
|
||||||
|
if (isShown) {
|
||||||
|
showHint.set(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let hovered = new UIEventSource(false)
|
||||||
|
let focused = new UIEventSource(false)
|
||||||
|
|
||||||
|
let hint: UIEventSource<Translation> = new UIEventSource(undefined)
|
||||||
export let size = "h-10 w-10"
|
export let size = "h-10 w-10"
|
||||||
let wrapperClass = "absolute top-0 left-0 " + size
|
let wrapperClass = "absolute top-0 left-0 " + size
|
||||||
let compassLoaded = Orientation.singleton.gotMeasurement
|
let compassLoaded = Orientation.singleton.gotMeasurement
|
||||||
let allowRotation = mapProperties.allowRotating
|
let allowRotation = mapProperties.allowRotating
|
||||||
|
|
||||||
function clicked() {
|
function clicked() {
|
||||||
|
showHint.set(true)
|
||||||
if (mapProperties.rotation.data === 0) {
|
if (mapProperties.rotation.data === 0) {
|
||||||
if (mapProperties.allowRotating.data && compassLoaded.data) {
|
if (mapProperties.allowRotating.data && compassLoaded.data) {
|
||||||
mapProperties.rotation.set(orientation.data)
|
mapProperties.rotation.set(orientation.data)
|
||||||
|
hint.set(Translations.t.compass.toCompass)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mapProperties.rotation.set(0)
|
mapProperties.rotation.set(0)
|
||||||
|
hint.set(undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let orientationText = mapProperties.rotation.mapD(r => {
|
||||||
|
const key = GeoOperations.bearingToHuman(r)
|
||||||
|
return Translations.t.compass[key]
|
||||||
|
})
|
||||||
|
|
||||||
|
let deviceOrientation = Orientation.singleton.alpha
|
||||||
|
|
||||||
|
Orientation.singleton.fakeMeasurements() // TODO Remove
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $allowRotation || $gotNonZero}
|
{#if $allowRotation || $gotNonZero}
|
||||||
<button class={"as-link pointer-events-auto relative " + size} on:click={() => clicked()}>
|
<button class={"as-link pointer-events-auto relative " + size} on:click={() => clicked()}
|
||||||
|
on:mouseenter={() => hovered.set(true)} on:mouseleave={() => hovered.set(false)}
|
||||||
|
on:focus={() => focused.set(true)} on:blur={() => focused.set(false)}>
|
||||||
{#if $allowRotation && !$compassLoaded && !$gotNonZero}
|
{#if $allowRotation && !$compassLoaded && !$gotNonZero}
|
||||||
<div
|
<div
|
||||||
class={"rounded-full border-2 border-dotted border-gray-500 " + wrapperClass}
|
class={"rounded-full border-2 border-dotted border-gray-500 " + wrapperClass}
|
||||||
|
@ -61,4 +91,14 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
<Popover placement="right" open={$showHint || $hovered || $focused} trigger="null">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
{#if $hint !== undefined}
|
||||||
|
<Tr t={$hint} />
|
||||||
|
{:else if Math.abs($deviceOrientation - (($mapRotation + 360) % 360)) <= 45}
|
||||||
|
<Tr t={Translations.t.compass.toCompass} />
|
||||||
|
{/if}
|
||||||
|
<Tr t={$orientationText} />
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
|
|
||||||
let featureSwitches = state.featureSwitches
|
let featureSwitches = state.featureSwitches
|
||||||
let showHome = featureSwitches?.featureSwitchBackToThemeOverview ?? new ImmutableStore(true)
|
let showHome = featureSwitches?.featureSwitchBackToThemeOverview ?? new ImmutableStore(true)
|
||||||
|
let allowLogin = featureSwitches?.featureSwitchEnableLogin ?? new ImmutableStore(true)
|
||||||
let pg = state.guistate.pageStates
|
let pg = state.guistate.pageStates
|
||||||
let pendingChanges = state?.changes?.pendingChanges
|
let pendingChanges = state?.changes?.pendingChanges
|
||||||
export let onlyLink: boolean
|
export let onlyLink: boolean
|
||||||
|
@ -119,7 +120,7 @@
|
||||||
|
|
||||||
<div class="flex flex-col gap-y-2 overflow-y-auto px-2 sm:gap-y-3 sm:px-3">
|
<div class="flex flex-col gap-y-2 overflow-y-auto px-2 sm:gap-y-3 sm:px-3">
|
||||||
{#if $showHome || $isAndroid}
|
{#if $showHome || $isAndroid}
|
||||||
<a class="button flex" class:primary={$loggedIn} href={Utils.HomepageLink()}>
|
<a class="button flex" class:primary={$loggedIn || $allowLogin} href={Utils.HomepageLink()}>
|
||||||
<Squares2x2 class="h-10 w-10" />
|
<Squares2x2 class="h-10 w-10" />
|
||||||
{#if Utils.isIframe}
|
{#if Utils.isIframe}
|
||||||
<Tr t={Translations.t.general.seeIndex} />
|
<Tr t={Translations.t.general.seeIndex} />
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import { WithSearchState } from "../../Models/ThemeViewState/WithSearchState"
|
import { WithSearchState } from "../../Models/ThemeViewState/WithSearchState"
|
||||||
|
|
||||||
export let activeFilters: (FilterSearchResult & ActiveFilter)[]
|
export let activeFilters: (FilterSearchResult & ActiveFilter)[]
|
||||||
|
export let clss = ""
|
||||||
let language = Locale.language
|
let language = Locale.language
|
||||||
let mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
let mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
||||||
$: mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
$: mergedActiveFilters = FilterSearch.mergeSemiIdenticalLayers(activeFilters, $language)
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if mergedActiveFilters.length > 0 || $nonactiveLayers.length > 0}
|
{#if mergedActiveFilters.length > 0 || $nonactiveLayers.length > 0}
|
||||||
<SidebarUnit>
|
<SidebarUnit {clss}>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<h3><Tr t={t.activeFilters} /></h3>
|
<h3><Tr t={t.activeFilters} /></h3>
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
|
|
||||||
<div class="low-interaction flex flex-col gap-y-2 p-4">
|
<div class="low-interaction flex flex-col gap-y-2 p-4">
|
||||||
{#if $allowFilters}
|
{#if $allowFilters}
|
||||||
<ActiveFilters {state} activeFilters={$activeFilters} />
|
<ActiveFilters clss={"flex-shrink "+ ($searchTerm.length > 0 ? "max-h-[30vh]" : "")} {state} activeFilters={$activeFilters} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if $searchTerm.length === 0 && $activeFilters.length === 0}
|
{#if $searchTerm.length === 0 && $activeFilters.length === 0}
|
||||||
<div class="items-center p-8 text-center">
|
<div class="items-center p-8 text-center">
|
||||||
|
|
|
@ -186,6 +186,12 @@ input[type="text"] {
|
||||||
* This very important section defines what the various input elements look like within the 'low-interaction' and 'interactive'-blocks
|
* This very important section defines what the various input elements look like within the 'low-interaction' and 'interactive'-blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
/* Disable the 'clear text field' button in chrome */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
/********* BUTTONS ***********/
|
/********* BUTTONS ***********/
|
||||||
|
|
||||||
button, .button {
|
button, .button {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue