mapcomplete/UI/Popup/NearbyImages.ts

150 lines
5.3 KiB
TypeScript

import Combine from "../Base/Combine";
import {UIEventSource} from "../../Logic/UIEventSource";
import {SlideShow} from "../Image/SlideShow";
import Toggle from "../Input/Toggle";
import Loading from "../Base/Loading";
import {AttributedImage} from "../Image/AttributedImage";
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders";
import Svg from "../../Svg";
import BaseUIElement from "../BaseUIElement";
import {InputElement} from "../Input/InputElement";
import {VariableUiElement} from "../Base/VariableUIElement";
import Translations from "../i18n/Translations";
import {Mapillary} from "../../Logic/ImageProviders/Mapillary";
import {SubtleButton} from "../Base/SubtleButton";
export interface P4CPicture {
pictureUrl: string,
date: number,
coordinates: { lat: number, lng: number },
provider: "Mapillary" | string,
author,
license,
detailsUrl: string,
direction,
osmTags: object /*To copy straight into OSM!*/
,
thumbUrl: string,
details: {
isSpherical: boolean,
}
}
export interface NearbyImageOptions {
lon: number,
lat: number,
radius: number,
maxDaysOld?: 1095 | number,
blacklist: UIEventSource<{url: string}[]>,
shownImagesCount?: UIEventSource<number>,
towardscenter?: boolean;
}
export default class NearbyImages extends VariableUiElement {
constructor(options: NearbyImageOptions) {
const t = Translations.t.image.nearbyPictures
const P4C = require("../../vendor/P4C.min")
const picManager = new P4C.PicturesManager({});
const shownImages = options.shownImagesCount ?? new UIEventSource(25);
const loadedPictures =
UIEventSource.FromPromise<P4CPicture[]>(
picManager.startPicsRetrievalAround(new P4C.LatLng(options.lat, options.lon), options.radius, {
mindate: new Date().getTime() - (options.maxDaysOld ?? (3*365)) * 24 * 60 * 60 * 1000,
towardscenter: options.towardscenter
})
).map(images => {
console.log("Images are" ,images, "blacklisted is", options.blacklist.data)
images?.sort((a, b) => b.date - a.date)
return images ?.filter(i => !(options.blacklist?.data?.some(blacklisted =>
Mapillary.sameUrl(i.pictureUrl, blacklisted.url)))
&& i.details.isSpherical === false);
}, [options.blacklist])
const loadMoreButton = new Combine([new SubtleButton(Svg.add_svg(), t.loadMore).onClick(() => {
shownImages.setData(shownImages.data + 25)
})]).SetClass("flex flex-col justify-center")
const imageElements = loadedPictures.map(imgs => {
const elements = (imgs ?? []).slice(0, shownImages.data).map(i => this.prepareElement(i));
if(imgs !== undefined && elements.length < imgs.length){
// We effectively sliced some items, so we can increase the count
elements.push(loadMoreButton)
}
return elements;
},[shownImages]);
super(loadedPictures.map(images => {
if(images === undefined){
return new Loading(t.loading);
}
if(images.length === 0){
return t.nothingFound.SetClass("alert block")
}
return new SlideShow(imageElements)
}));
}
protected prepareElement(info: P4CPicture): BaseUIElement {
const provider = AllImageProviders.byName(info.provider);
return new AttributedImage({url: info.pictureUrl, provider})
}
private asAttributedImage(info: P4CPicture): AttributedImage {
const provider = AllImageProviders.byName(info.provider);
return new AttributedImage({url: info.thumbUrl, provider, date: new Date(info.date)})
}
protected asToggle(info:P4CPicture): Toggle {
const imgNonSelected = this.asAttributedImage(info);
const imageSelected = this.asAttributedImage(info);
const nonSelected = new Combine([imgNonSelected]).SetClass("relative block")
const hoveringCheckmark =
new Combine([Svg.confirm_svg().SetClass("block w-24 h-24 -ml-12 -mt-12")]).SetClass("absolute left-1/2 top-1/2 w-0")
const selected = new Combine([
imageSelected,
hoveringCheckmark,
]).SetClass("relative block")
return new Toggle(selected, nonSelected).SetClass("").ToggleOnClick();
}
}
export class SelectOneNearbyImage extends NearbyImages implements InputElement<P4CPicture> {
private readonly value: UIEventSource<P4CPicture>;
constructor(options: NearbyImageOptions & {value?: UIEventSource<P4CPicture>}) {
super(options)
this.value = options.value ?? new UIEventSource<P4CPicture>(undefined);
}
GetValue(): UIEventSource<P4CPicture> {
return this.value;
}
IsValid(t: P4CPicture): boolean {
return false;
}
protected prepareElement(info: P4CPicture): BaseUIElement {
const toggle = super.asToggle(info)
toggle.isEnabled.addCallback(enabled => {
if (enabled) {
this.value.setData(info)
}
})
this.value.addCallback(inf => {
if(inf !== info){
toggle.isEnabled.setData(false)
}
})
return toggle
}
}