MapComplete/UI/Popup/MoveWizard.ts

208 lines
7.7 KiB
TypeScript
Raw Normal View History

2021-10-11 00:54:35 +02:00
import {SubtleButton} from "../Base/SubtleButton";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
import Toggle from "../Input/Toggle";
import {UIEventSource} from "../../Logic/UIEventSource";
import Translations from "../i18n/Translations";
import {VariableUiElement} from "../Base/VariableUIElement";
import {Translation} from "../i18n/Translation";
import BaseUIElement from "../BaseUIElement";
import LocationInput from "../Input/LocationInput";
import Loc from "../../Models/Loc";
import {GeoOperations} from "../../Logic/GeoOperations";
2021-10-13 03:09:37 +02:00
import {OsmObject} from "../../Logic/Osm/OsmObject";
import {Changes} from "../../Logic/Osm/Changes";
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
2021-10-11 00:54:35 +02:00
2021-10-13 03:09:37 +02:00
interface MoveReason {
text: Translation | string,
2021-10-11 00:54:35 +02:00
icon: string | BaseUIElement,
changesetCommentValue: string,
lockBounds: true | boolean,
background: undefined | "map" | "photo",
2021-10-13 03:09:37 +02:00
startZoom: number,
minZoom: number
}
2021-10-11 00:54:35 +02:00
export default class MoveWizard extends Toggle {
/**
* The UI-element which helps moving a point
*/
constructor(
2021-10-13 03:09:37 +02:00
featureToMove: any,
2021-10-11 00:54:35 +02:00
state: {
2021-10-13 03:09:37 +02:00
osmConnection: OsmConnection,
featureSwitchUserbadge: UIEventSource<boolean>,
changes: Changes,
layoutToUse: LayoutConfig
}, options?: {
reasons?: MoveReason[]
disableDefaultReasons?: false | boolean
}) {
2021-10-11 00:54:35 +02:00
options = options ?? {}
2021-10-13 03:09:37 +02:00
const t = Translations.t.move
2021-10-11 00:54:35 +02:00
const loginButton = new Toggle(
2021-10-13 03:09:37 +02:00
t.loginToMove.Clone().SetClass("btn").onClick(() => state.osmConnection.AttemptLogin()),
2021-10-11 00:54:35 +02:00
undefined,
state.featureSwitchUserbadge
)
2021-10-13 03:09:37 +02:00
const currentStep = new UIEventSource<"start" | "reason" | "pick_location" | "moved">("start")
2021-10-11 00:54:35 +02:00
const moveReason = new UIEventSource<MoveReason>(undefined)
const moveButton = new SubtleButton(
Svg.move_ui(),
t.inviteToMove.Clone()
).onClick(() => {
currentStep.setData("reason")
})
2021-10-13 03:09:37 +02:00
const moveAgainButton = new SubtleButton(
Svg.move_ui(),
t.inviteToMoveAgain.Clone()
).onClick(() => {
currentStep.setData("reason")
})
const reasons: MoveReason[] = []
if (!options.disableDefaultReasons) {
2021-10-11 00:54:35 +02:00
reasons.push({
text: t.reasonRelocation.Clone(),
icon: Svg.relocation_svg(),
changesetCommentValue: "relocated",
lockBounds: false,
background: undefined,
2021-10-13 03:09:37 +02:00
startZoom: 12,
minZoom: 6
2021-10-11 00:54:35 +02:00
})
2021-10-13 03:09:37 +02:00
2021-10-11 00:54:35 +02:00
reasons.push({
text: t.reasonInaccurate.Clone(),
icon: Svg.crosshair_svg(),
changesetCommentValue: "improve_accuracy",
lockBounds: true,
background: "photo",
2021-10-13 03:09:37 +02:00
startZoom: 17,
minZoom: 16
2021-10-11 00:54:35 +02:00
})
}
for (const reason of options.reasons ?? []) {
reasons.push({
text: reason.text,
icon: reason.icon,
changesetCommentValue: reason.changesetCommentValue,
lockBounds: reason.lockBounds ?? true,
background: reason.background,
2021-10-13 03:09:37 +02:00
startZoom: reason.startZoom ?? 15,
minZoom: reason.minZoom
2021-10-11 00:54:35 +02:00
})
}
2021-10-13 03:09:37 +02:00
2021-10-11 00:54:35 +02:00
const selectReason = new Combine(reasons.map(r => new SubtleButton(r.icon, r.text).onClick(() => {
moveReason.setData(r)
currentStep.setData("pick_location")
})))
2021-10-13 03:09:37 +02:00
2021-10-11 00:54:35 +02:00
const cancelButton = new SubtleButton(Svg.close_svg(), t.cancel).onClick(() => currentStep.setData("start"))
2021-10-13 03:09:37 +02:00
2021-10-11 00:54:35 +02:00
const [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove)
2021-10-13 03:09:37 +02:00
const locationInput = moveReason.map(reason => {
if (reason === undefined) {
return undefined
}
const loc = new UIEventSource<Loc>({
2021-10-11 00:54:35 +02:00
lon: lon,
lat: lat,
2021-10-13 03:09:37 +02:00
zoom: reason?.startZoom ?? 16
})
const locationInput = new LocationInput({
minZoom: reason.minZoom,
centerLocation: loc
})
if (reason.lockBounds) {
locationInput.installBounds(0.05, true)
}
locationInput.SetStyle("height: 17.5rem")
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
confirmMove.onClick(() => {
state.changes.applyAction(new ChangeLocationAction(featureToMove.properties.id, [locationInput.GetValue().data.lon, locationInput.GetValue().data.lat], {
reason: Translations.WT(reason.text).textFor("en"),
theme: state.layoutToUse.icon
}))
currentStep.setData("moved")
})
const zoomInFurhter = t.zoomInFurther.Clone().SetClass("alert block m-6")
return new Combine([
locationInput,
new Toggle(confirmMove, zoomInFurhter, locationInput.GetValue().map(l => l.zoom >= 19))
]).SetClass("flex flex-col")
});
const dialogClasses = "p-2 md:p-4 m-2 border border-gray-400 rounded-xl flex flex-col"
const moveFlow = new Toggle(
2021-10-11 00:54:35 +02:00
new VariableUiElement(currentStep.map(currentStep => {
2021-10-13 03:09:37 +02:00
switch (currentStep) {
2021-10-11 00:54:35 +02:00
case "start":
return moveButton;
case "reason":
2021-10-13 03:09:37 +02:00
return new Combine([t.whyMove.Clone().SetClass("text-lg font-bold"), selectReason, cancelButton]).SetClass(dialogClasses);
2021-10-11 00:54:35 +02:00
case "pick_location":
2021-10-13 03:09:37 +02:00
return new Combine([t.moveTitle.Clone().SetClass("text-lg font-bold"), new VariableUiElement(locationInput), cancelButton]).SetClass(dialogClasses)
case "moved":
return new Combine([t.pointIsMoved.Clone().SetClass("thanks"), moveAgainButton]).SetClass("flex flex-col");
2021-10-11 00:54:35 +02:00
}
2021-10-13 03:09:37 +02:00
2021-10-11 00:54:35 +02:00
})),
loginButton,
state.osmConnection.isLoggedIn
)
2021-10-13 03:09:37 +02:00
let id = featureToMove.properties.id
const backend = state.osmConnection._oauth_config.url
if (id.startsWith(backend)) {
id = id.substring(backend.length)
}
const moveDisallowedReason = new UIEventSource<BaseUIElement>(undefined)
if (id.startsWith("way")) {
moveDisallowedReason.setData(t.isWay.Clone())
} else if (id.startsWith("relation")) {
moveDisallowedReason.setData(t.isRelation.Clone())
} else {
OsmObject.DownloadReferencingWays(id).then(referencing => {
if (referencing.length > 0) {
console.log("Got a referencing way, move not allowed")
moveDisallowedReason.setData(t.partOfAWay.Clone())
}
})
OsmObject.DownloadReferencingRelations(id).then(partOf => {
if(partOf.length > 0){
moveDisallowedReason.setData(t.partOfRelation.Clone())
}
})
}
super(
moveFlow,
new Combine([
Svg.move_not_allowed_svg().SetStyle("height: 2rem"),
new Combine([t.cannotBeMoved.Clone(),
new VariableUiElement(moveDisallowedReason).SetClass("subtle")
]).SetClass("flex flex-col")
]).SetClass("flex"),
moveDisallowedReason.map(r => r === undefined)
)
2021-10-11 00:54:35 +02:00
}
}