UX: improve 'plantnet detection' UI, fix #1989 (technically, the text was there all along)

This commit is contained in:
Pieter Vander Vennet 2025-05-06 13:57:29 +02:00
parent 01d924374e
commit 6c730f5581
2 changed files with 73 additions and 54 deletions

View file

@ -734,7 +734,8 @@
"li1": "take a picture which shows a single leaf",
"li2": "take a picture which shows the bark",
"li3": "take a picture of the flowers",
"li4": "take a picture of the fruits"
"li4": "take a picture of the fruits",
"title": "What pictures to take for automatic detection?"
},
"loadingWikidata": "Loading information about {species}…",
"matchPercentage": "{match}% match",

View file

@ -11,6 +11,7 @@
import WikipediaPanel from "../Wikipedia/WikipediaPanel.svelte"
import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte"
import ArrowPath from "@babeard/svelte-heroicons/mini/ArrowPath"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
/**
* The main entry point for the plantnet wizard
@ -23,7 +24,6 @@
*/
export let imageUrls: Store<string[]>
export let onConfirm: (wikidataId: string) => void
let collapsedMode = true
let options: UIEventSource<PlantNetSpeciesMatch[]> = new UIEventSource<PlantNetSpeciesMatch[]>(
undefined
)
@ -35,6 +35,8 @@
*/
let selectedOption: string
let running = false
let expandExplanation = false
let done = false
function speciesSelected(species: string) {
@ -44,69 +46,52 @@
async function detectSpecies() {
error = undefined
collapsedMode = false
running = true
try {
const result: PlantNetResult | "no_plant_detected" = await PlantNet.query(imageUrls.data.slice(0, 5))
if (result === "no_plant_detected") {
error = "no_plant_detected"
expandExplanation = true
return
}
options.set(result.results.filter((r) => r.score > 0.005).slice(0, 8))
const filteredResults = result.results.filter((r) => r.score > 0.005).slice(0, 8)
expandExplanation = filteredResults.length == 0
options.set(filteredResults)
} catch (e) {
console.error("Caught", e)
error = e
running = false
}
}
</script>
<div class="flex flex-col">
{#if collapsedMode}
<button class="w-full" on:click={detectSpecies}>
<Tr t={t.button} />
</button>
{:else if error === "no_plant_detected"}
<div class="flex flex-col mb-4">
<!-- Error message -->
{#if error === "no_plant_detected"}
<Tr cls="alert" t={t.noPlantDetected} />
<button on:click={() => detectSpecies()}>
<ArrowPath class="h-6 w-6" />
<Tr t={Translations.t.general.retry} />
</button>
{:else if error !== undefined}
<Tr cls="alert" t={t.error.Subs({ error })} />
<button on:click={() => detectSpecies()}>
<ArrowPath class="h-6 w-6" />
<Tr t={Translations.t.general.retry} />
{/if}
<!-- Button. If 'done==false', the button should be shown, otherwise <PlantnetSpeciesList> will handle the flow -->
{#if !done && !running && !selectedOption}
<button class="w-full flex" on:click={detectSpecies}>
<Plantnet_logo class="mr-2 shrink-0 h-8 w-8 rounded-full bg-white p-1" />
<span class="flex flex-col items-center">
<Tr t={t.button} />
{#if error !== undefined}
<div class="flex subtle mt-2">
<ArrowPath class="h-6 w-6" />
<Tr t={Translations.t.general.retry} />
</div>
{/if}
</span>
</button>
{:else if $imageUrls.length === 0}
<!-- No urls are available, show the explanation instead-->
<div class=" border-region relative mb-1 p-2">
<XCircleIcon
class="absolute right-0 top-0 m-4 h-8 w-8 cursor-pointer"
on:click={() => {
collapsedMode = true
}}
/>
<Tr t={t.takeImages} />
<Tr t={t.howTo.intro} />
<ul>
<li>
<Tr t={t.howTo.li0} />
</li>
<li>
<Tr t={t.howTo.li1} />
</li>
<li>
<Tr t={t.howTo.li2} />
</li>
<li>
<Tr t={t.howTo.li3} />
</li>
<li>
<Tr t={t.howTo.li4} />
</li>
</ul>
</div>
{:else if selectedOption === undefined}
{:else if !selectedOption}
<PlantNetSpeciesList
{options}
numberOfImages={$imageUrls.length}
@ -115,9 +100,7 @@
<XCircleIcon
slot="upper-right"
class="m-4 h-8 w-8 cursor-pointer"
on:click={() => {
collapsedMode = true
}}
on:click={() => {running = false}}
/>
</PlantNetSpeciesList>
{:else if !done}
@ -158,8 +141,43 @@
<Tr t={t.tryAgain} />
</BackButton>
{/if}
<div class="low-interaction flex self-end rounded-xl p-2">
<Plantnet_logo class="mr-1 h-8 w-8 rounded-full bg-white p-1" />
<Tr t={t.poweredByPlantnet} />
<!-- Explanation -->
<div class="my-1 low-interaction">
<AccordionSingle noBorder expanded={expandExplanation}>
<div class="subtle flex justify-start text-sm p-1" slot="header">
<Tr t={t.howTo.title} />
</div>
<div class="p-4 border-low-interaction border-l border-r border-b flex flex-col">
<Tr t={t.takeImages} />
<Tr t={t.howTo.intro} />
<ul>
<li>
<Tr t={t.howTo.li0} />
</li>
<li>
<Tr t={t.howTo.li1} />
</li>
<li>
<Tr t={t.howTo.li2} />
</li>
<li>
<Tr t={t.howTo.li3} />
</li>
<li>
<Tr t={t.howTo.li4} />
</li>
</ul>
<!-- Attribution -->
<div class="low-interaction flex items-center self-end rounded-xl p-2 px-3 mt-4 w-fit">
<Plantnet_logo class="mr-2 h-8 w-8 rounded-full bg-white p-1" />
<Tr t={t.poweredByPlantnet} />
</div>
</div>
</AccordionSingle>
</div>
</div>