import {VariableUiElement} from "../Base/VariableUIElement"; import {Store, UIEventSource} from "../../Logic/UIEventSource"; import PlantNet from "../../Logic/Web/PlantNet"; import Loading from "../Base/Loading"; import Wikidata from "../../Logic/Web/Wikidata"; import WikidataPreviewBox from "../Wikipedia/WikidataPreviewBox"; import {Button} from "../Base/Button"; import Combine from "../Base/Combine"; import Title from "../Base/Title"; import WikipediaBox from "../Wikipedia/WikipediaBox"; import Translations from "../i18n/Translations"; import List from "../Base/List"; import Svg from "../../Svg"; export default class PlantNetSpeciesSearch extends VariableUiElement { /*** * Given images, queries plantnet to search a species matching those images. * A list of species will be presented to the user, after which they can confirm an item. * The wikidata-url is returned in the callback when the user selects one */ constructor(images: Store, onConfirm: (wikidataUrl: string) => Promise) { const t = Translations.t.plantDetection super( images .bind(images => { if (images.length === 0) { return null } return UIEventSource.FromPromiseWithErr(PlantNet.query(images.slice(0,5))); }) .map(result => { if (images.data.length === 0) { return new Combine([t.takeImages, t.howTo.intro, new List( [ t.howTo.li0, t.howTo.li1, t.howTo.li2, t.howTo.li3 ] )]).SetClass("flex flex-col") } if (result === undefined) { return new Loading(t.querying.Subs(images.data)) } if (result["error"] !== undefined) { return t.error.Subs(result).SetClass("alert") } console.log(result) const success = result["success"] const selectedSpecies = new UIEventSource(undefined) const speciesInformation = success.results .filter(species => species.score >= 0.005) .map(species => { const wikidata = UIEventSource.FromPromise(Wikidata.Sparql<{ species }>(["?species", "?speciesLabel"], ["?species wdt:P846 \"" + species.gbif.id + "\""])); const confirmButton = new Button(t.seeInfo, async() => { await selectedSpecies.setData(wikidata.data[0].species?.value) }).SetClass("btn") const match = t.matchPercentage.Subs({match: Math.round(species.score * 100)}).SetClass("font-bold") const extraItems = new Combine([match, confirmButton]).SetClass("flex flex-col") return new WikidataPreviewBox(wikidata.map(wd => wd == undefined ? undefined : wd[0]?.species?.value), { whileLoading: new Loading( t.loadingWikidata.Subs({species: species.species.scientificNameWithoutAuthor})), extraItems: [new Combine([extraItems])], imageStyle: "max-width: 8rem; width: unset; height: 8rem" }) .SetClass("border-2 border-subtle rounded-xl block mb-2") } ); const plantOverview = new Combine([ new Title(t.overviewTitle), t.overviewIntro, t.overviewVerify.SetClass("font-bold"), ...speciesInformation]).SetClass("flex flex-col") return new VariableUiElement(selectedSpecies.map(wikidataSpecies => { if (wikidataSpecies === undefined) { return plantOverview } const buttons = new Combine([ new Button( new Combine([ Svg.back_svg().SetClass("w-6 mr-1 bg-white rounded-full p-1"), t.back]).SetClass("flex"), () => { selectedSpecies.setData(undefined) }).SetClass("btn btn-secondary"), new Button( new Combine([Svg.confirm_svg().SetClass("w-6 mr-1"), t.confirm]).SetClass("flex") , () => { onConfirm(wikidataSpecies) }).SetClass("btn"), ]).SetClass("flex justify-between"); return new Combine([ new WikipediaBox([wikidataSpecies], { firstParagraphOnly: false, noImages: false, addHeader: false }).SetClass("h-96"), buttons ]).SetClass("flex flex-col self-end") })) } )) } }