mapcomplete/UI/Popup/NearbyImageVis.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

148 lines
6 KiB
TypeScript
Raw Normal View History

import FeaturePipelineState from "../../Logic/State/FeaturePipelineState";
import {UIEventSource} from "../../Logic/UIEventSource";
import {DefaultGuiState} from "../DefaultGuiState";
import BaseUIElement from "../BaseUIElement";
import Translations from "../i18n/Translations";
import {GeoOperations} from "../../Logic/GeoOperations";
import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage} from "./NearbyImages";
import {SubstitutedTranslation} from "../SubstitutedTranslation";
import {Tag} from "../../Logic/Tags/Tag";
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
import {And} from "../../Logic/Tags/And";
import {SaveButton} from "./SaveButton";
import Lazy from "../Base/Lazy";
import {CheckBox} from "../Input/Checkboxes";
import Slider from "../Input/Slider";
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders";
import Combine from "../Base/Combine";
import {VariableUiElement} from "../Base/VariableUIElement";
import Toggle from "../Input/Toggle";
import Title from "../Base/Title";
import {MapillaryLinkVis} from "./MapillaryLinkVis";
import {SpecialVisualization} from "../SpecialVisualization";
export class NearbyImageVis implements SpecialVisualization {
args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
{
name: "mode",
defaultValue: "expandable",
doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them.",
},
{
name: "mapillary",
defaultValue: "true",
doc: "If 'true', includes a link to mapillary on this location.",
},
]
docs =
"A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"
funcName = "nearby_images"
constr(
state: FeaturePipelineState,
tagSource: UIEventSource<any>,
args: string[],
guistate: DefaultGuiState
): BaseUIElement {
const t = Translations.t.image.nearbyPictures
const mode: "open" | "expandable" | "collapsable" = <any>args[0]
const feature = state.allElements.ContainingFeatures.get(tagSource.data.id)
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
const id: string = tagSource.data["id"]
const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+")
const selectedImage = new UIEventSource<P4CPicture>(undefined)
let saveButton: BaseUIElement = undefined
if (canBeEdited) {
const confirmText: BaseUIElement = new SubstitutedTranslation(
t.confirm,
tagSource,
state
)
const onSave = async () => {
console.log("Selected a picture...", selectedImage.data)
const osmTags = selectedImage.data.osmTags
const tags: Tag[] = []
for (const key in osmTags) {
tags.push(new Tag(key, osmTags[key]))
}
await state?.changes?.applyAction(
new ChangeTagAction(id, new And(tags), tagSource.data, {
theme: state?.layoutToUse.id,
changeType: "link-image",
})
)
}
saveButton = new SaveButton(
selectedImage,
state.osmConnection,
confirmText,
t.noImageSelected
)
.onClick(onSave)
.SetClass("flex justify-end")
}
const nearby = new Lazy(() => {
const towardsCenter = new CheckBox(t.onlyTowards, false)
const radiusValue =
state?.osmConnection?.GetPreference("nearby-images-radius", "300").sync(
(s) => Number(s),
[],
(i) => "" + i
) ?? new UIEventSource(300)
const radius = new Slider(25, 500, {
value: radiusValue,
step: 25,
})
const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource)
const options: NearbyImageOptions & { value } = {
lon,
lat,
searchRadius: 500,
shownRadius: radius.GetValue(),
value: selectedImage,
blacklist: alreadyInTheImage,
towardscenter: towardsCenter.GetValue(),
maxDaysOld: 365 * 5,
}
const slideshow = canBeEdited
? new SelectOneNearbyImage(options, state)
: new NearbyImages(options, state)
const controls = new Combine([
towardsCenter,
new Combine([
new VariableUiElement(
radius.GetValue().map((radius) => t.withinRadius.Subs({radius}))
),
radius,
]).SetClass("flex justify-between"),
]).SetClass("flex flex-col")
return new Combine([
slideshow,
controls,
saveButton,
new MapillaryLinkVis().constr(state, tagSource, []).SetClass("mt-6"),
])
})
let withEdit: BaseUIElement = nearby
if (canBeEdited) {
withEdit = new Combine([t.hasMatchingPicture, nearby]).SetClass("flex flex-col")
}
if (mode === "open") {
return withEdit
}
const toggleState = new UIEventSource<boolean>(mode === "collapsable")
return new Toggle(
new Combine([new Title(t.title), withEdit]),
new Title(t.browseNearby).onClick(() => toggleState.setData(true)),
toggleState
)
}
}