2021-07-01 02:26:45 +02:00
|
|
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
|
|
import Toggle from "../Input/Toggle"
|
|
|
|
import Translations from "../i18n/Translations"
|
|
|
|
import Svg from "../../Svg"
|
2021-07-15 20:47:28 +02:00
|
|
|
import DeleteAction from "../../Logic/Osm/Actions/DeleteAction"
|
2022-06-05 02:24:14 +02:00
|
|
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
2021-07-01 02:26:45 +02:00
|
|
|
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
|
|
|
import Combine from "../Base/Combine"
|
|
|
|
import { SubtleButton } from "../Base/SubtleButton"
|
|
|
|
import { Translation } from "../i18n/Translation"
|
|
|
|
import BaseUIElement from "../BaseUIElement"
|
2021-07-03 14:35:44 +02:00
|
|
|
import Constants from "../../Models/Constants"
|
2021-08-07 23:11:34 +02:00
|
|
|
import DeleteConfig from "../../Models/ThemeConfig/DeleteConfig"
|
2022-01-19 20:34:04 +01:00
|
|
|
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
2022-05-01 04:17:40 +02:00
|
|
|
import OsmChangeAction from "../../Logic/Osm/Actions/OsmChangeAction"
|
|
|
|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
|
|
|
import { InputElement } from "../Input/InputElement"
|
|
|
|
import { RadioButton } from "../Input/RadioButton"
|
|
|
|
import { FixedInputElement } from "../Input/FixedInputElement"
|
|
|
|
import Title from "../Base/Title"
|
|
|
|
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
2023-04-14 02:42:57 +02:00
|
|
|
import { OsmId, OsmTags } from "../../Models/OsmFeature"
|
2023-01-06 03:37:22 +01:00
|
|
|
import { LoginToggle } from "./LoginButton"
|
2023-04-14 02:42:57 +02:00
|
|
|
import { SpecialVisualizationState } from "../SpecialVisualization"
|
2023-04-20 03:58:31 +02:00
|
|
|
import SvelteUIElement from "../Base/SvelteUIElement"
|
|
|
|
import TagHint from "./TagHint.svelte"
|
|
|
|
import OsmObjectDownloader from "../../Logic/Osm/OsmObjectDownloader"
|
2021-07-01 02:26:45 +02:00
|
|
|
|
|
|
|
export default class DeleteWizard extends Toggle {
|
|
|
|
/**
|
|
|
|
* The UI-element which triggers 'deletion' (either soft or hard).
|
|
|
|
*
|
|
|
|
* - A 'hard deletion' is if the point is actually deleted from the OSM database
|
|
|
|
* - A 'soft deletion' is if the point is not deleted, but the tagging is modified which will result in the point not being picked up by the filters anymore.
|
|
|
|
* Apart having needing theme-specific tags added (which must be supplied by the theme creator), fixme='marked for deletion' will be added too
|
|
|
|
*
|
|
|
|
* A deletion is only possible if the user is logged in.
|
|
|
|
* A soft deletion is only possible if tags are provided
|
|
|
|
* A hard deletion is only possible if the user has sufficient rigts
|
|
|
|
*
|
|
|
|
* There is also the possibility to have a 'trojan horse' option. If the user selects that option, it is NEVER removed, but the tags are applied.
|
|
|
|
* Ideal for the case of "THIS PATH IS ON MY GROUND AND SHOULD BE DELETED IMMEDIATELY OR I WILL GET MY LAWYER" but to mark it as private instead.
|
|
|
|
* (Note that _delete_reason is used as trigger to do actual deletion - setting such a tag WILL delete from the database with that as changeset comment)
|
|
|
|
*
|
|
|
|
*/
|
2023-04-14 02:42:57 +02:00
|
|
|
constructor(
|
|
|
|
id: OsmId,
|
|
|
|
tagsSource: UIEventSource<OsmTags>,
|
|
|
|
state: SpecialVisualizationState,
|
|
|
|
options: DeleteConfig
|
|
|
|
) {
|
|
|
|
const deleteAbility = new DeleteabilityChecker(
|
|
|
|
id,
|
|
|
|
state.osmConnection,
|
2023-04-20 03:58:31 +02:00
|
|
|
state.osmObjectDownloader,
|
2023-04-14 02:42:57 +02:00
|
|
|
options.neededChangesets
|
|
|
|
)
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2021-10-04 03:12:42 +02:00
|
|
|
const isDeleted = new UIEventSource(false)
|
2021-07-03 14:35:44 +02:00
|
|
|
const allowSoftDeletion = !!options.softDeletionTags
|
2021-07-01 02:26:45 +02:00
|
|
|
|
|
|
|
const confirm = new UIEventSource<boolean>(false)
|
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
/**
|
|
|
|
* This function is the actual delete function
|
|
|
|
*/
|
|
|
|
function doDelete(selected: { deleteReason: string } | { retagTo: TagsFilter }) {
|
|
|
|
let actionToTake: OsmChangeAction
|
|
|
|
if (selected["retagTo"] !== undefined) {
|
|
|
|
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
|
|
|
actionToTake = new ChangeTagAction(id, selected["retagTo"], tagsSource.data, {
|
2023-04-14 02:42:57 +02:00
|
|
|
theme: state?.layout?.id ?? "unkown",
|
2022-05-01 04:17:40 +02:00
|
|
|
changeType: "special-delete",
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
actionToTake = new DeleteAction(
|
|
|
|
id,
|
|
|
|
options.softDeletionTags,
|
|
|
|
{
|
2023-04-14 02:42:57 +02:00
|
|
|
theme: state?.layout?.id ?? "unkown",
|
2022-05-01 04:17:40 +02:00
|
|
|
specialMotivation: selected["deleteReason"],
|
|
|
|
},
|
|
|
|
deleteAbility.canBeDeleted.data.canBeDeleted
|
|
|
|
)
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|
2022-05-01 04:17:40 +02:00
|
|
|
state.changes?.applyAction(actionToTake)
|
2021-10-04 03:12:42 +02:00
|
|
|
isDeleted.setData(true)
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const t = Translations.t.delete
|
2022-05-01 04:17:40 +02:00
|
|
|
const cancelButton = t.cancel
|
|
|
|
.SetClass("block btn btn-secondary")
|
|
|
|
.onClick(() => confirm.setData(false))
|
2021-07-01 02:26:45 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The button which is shown first. Opening it will trigger the check for deletions
|
|
|
|
*/
|
2021-07-03 14:35:44 +02:00
|
|
|
const deleteButton = new SubtleButton(
|
2022-05-01 04:17:40 +02:00
|
|
|
Svg.delete_icon_svg().SetStyle("width: 1.5rem; height: 1.5rem;"),
|
|
|
|
t.delete
|
|
|
|
).onClick(() => {
|
|
|
|
deleteAbility.CheckDeleteability(true)
|
|
|
|
confirm.setData(true)
|
|
|
|
})
|
|
|
|
|
2022-06-05 02:24:14 +02:00
|
|
|
const isShown: Store<boolean> = tagsSource.map((tgs) => tgs.id.indexOf("-") < 0)
|
2022-05-01 04:17:40 +02:00
|
|
|
|
|
|
|
const deleteOptionPicker = DeleteWizard.constructMultipleChoice(options, tagsSource, state)
|
|
|
|
const deleteDialog = new Combine([
|
|
|
|
new Title(
|
2023-05-09 00:06:51 +02:00
|
|
|
new SubstitutedTranslation(t.whyDelete, tagsSource, state),
|
2022-05-01 04:17:40 +02:00
|
|
|
3
|
|
|
|
),
|
|
|
|
deleteOptionPicker,
|
|
|
|
new Combine([
|
|
|
|
DeleteWizard.constructExplanation(
|
|
|
|
deleteOptionPicker.GetValue(),
|
|
|
|
deleteAbility,
|
|
|
|
tagsSource,
|
|
|
|
state
|
|
|
|
),
|
|
|
|
new Combine([
|
|
|
|
cancelButton,
|
|
|
|
DeleteWizard.constructConfirmButton(deleteOptionPicker.GetValue()).onClick(() =>
|
|
|
|
doDelete(deleteOptionPicker.GetValue().data)
|
2022-09-08 21:40:48 +02:00
|
|
|
),
|
2022-05-01 04:17:40 +02:00
|
|
|
]).SetClass("flex justify-end flex-wrap-reverse"),
|
|
|
|
]).SetClass("flex mt-2 justify-between"),
|
|
|
|
]).SetClass("question")
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2023-01-06 03:37:22 +01:00
|
|
|
const deleteFlow = new Toggle(
|
|
|
|
new Toggle(
|
|
|
|
new Toggle(
|
|
|
|
deleteDialog,
|
2023-05-08 01:55:21 +02:00
|
|
|
new SubtleButton(Svg.envelope_svg(), t.readMessages),
|
2023-01-06 03:37:22 +01:00
|
|
|
state.osmConnection.userDetails.map(
|
|
|
|
(ud) =>
|
|
|
|
ud.csCount >
|
|
|
|
Constants.userJourney.addNewPointWithUnreadMessagesUnlock ||
|
|
|
|
ud.unreadMessages == 0
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
deleteButton,
|
|
|
|
confirm
|
|
|
|
),
|
|
|
|
new VariableUiElement(
|
|
|
|
deleteAbility.canBeDeleted.map((cbd) =>
|
|
|
|
new Combine([
|
|
|
|
Svg.delete_not_allowed_svg()
|
|
|
|
.SetStyle("height: 2rem; width: auto")
|
|
|
|
.SetClass("mr-2"),
|
|
|
|
new Combine([
|
|
|
|
t.cannotBeDeleted,
|
|
|
|
cbd.reason.SetClass("subtle"),
|
|
|
|
t.useSomethingElse.SetClass("subtle"),
|
|
|
|
]).SetClass("flex flex-col"),
|
|
|
|
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200 bg-gray-200")
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
deleteAbility.canBeDeleted.map((cbd) => allowSoftDeletion || cbd.canBeDeleted !== false)
|
|
|
|
)
|
|
|
|
|
2021-07-01 02:26:45 +02:00
|
|
|
super(
|
2021-07-03 14:35:44 +02:00
|
|
|
new Toggle(
|
2021-09-09 00:05:51 +02:00
|
|
|
new Combine([
|
|
|
|
Svg.delete_icon_svg().SetClass(
|
|
|
|
"h-16 w-16 p-2 m-2 block bg-gray-300 rounded-full"
|
|
|
|
),
|
2022-05-01 04:17:40 +02:00
|
|
|
t.isDeleted,
|
|
|
|
]).SetClass("flex m-2 rounded-full"),
|
2023-01-06 03:37:22 +01:00
|
|
|
new LoginToggle(deleteFlow, undefined, state),
|
2021-10-04 03:12:42 +02:00
|
|
|
isDeleted
|
|
|
|
),
|
2021-07-03 14:35:44 +02:00
|
|
|
undefined,
|
|
|
|
isShown
|
|
|
|
)
|
2023-01-04 19:12:54 +01:00
|
|
|
|
|
|
|
const self = this
|
|
|
|
confirm.addCallbackAndRunD((dialogIsOpened) => {
|
|
|
|
if (dialogIsOpened) {
|
|
|
|
self.ScrollIntoView()
|
|
|
|
}
|
|
|
|
})
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
private static constructConfirmButton(
|
|
|
|
deleteReasons: UIEventSource<any | undefined>
|
|
|
|
): BaseUIElement {
|
2021-07-01 02:26:45 +02:00
|
|
|
const t = Translations.t.delete
|
|
|
|
const btn = new Combine([
|
2023-05-08 01:55:21 +02:00
|
|
|
Svg.delete_icon_svg().SetClass("w-6 h-6 mr-3 block"),
|
2022-05-01 04:17:40 +02:00
|
|
|
t.delete,
|
2021-07-01 02:26:45 +02:00
|
|
|
]).SetClass("flex btn bg-red-500")
|
|
|
|
|
|
|
|
const btnNonActive = new Combine([
|
2023-05-08 01:55:21 +02:00
|
|
|
Svg.delete_icon_svg().SetClass("w-6 h-6 mr-3 block"),
|
2022-05-01 04:17:40 +02:00
|
|
|
t.delete,
|
2021-07-01 02:26:45 +02:00
|
|
|
]).SetClass("flex btn btn-disabled bg-red-200")
|
|
|
|
|
|
|
|
return new Toggle(
|
|
|
|
btn,
|
|
|
|
btnNonActive,
|
|
|
|
deleteReasons.map((reason) => reason !== undefined)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
private static constructExplanation(
|
|
|
|
selectedOption: UIEventSource<{ deleteReason: string } | { retagTo: TagsFilter }>,
|
|
|
|
deleteAction: DeleteabilityChecker,
|
|
|
|
currentTags: UIEventSource<object>,
|
|
|
|
state?: { osmConnection?: OsmConnection }
|
2022-09-08 21:40:48 +02:00
|
|
|
) {
|
2021-07-01 02:26:45 +02:00
|
|
|
const t = Translations.t.delete
|
2022-05-01 04:17:40 +02:00
|
|
|
return new VariableUiElement(
|
|
|
|
selectedOption.map(
|
|
|
|
(selectedOption) => {
|
|
|
|
if (selectedOption === undefined) {
|
|
|
|
return t.explanations.selectReason.SetClass("subtle")
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
const retag: TagsFilter | undefined = selectedOption["retagTo"]
|
|
|
|
if (retag !== undefined) {
|
|
|
|
// This is a retagging, not a deletion of any kind
|
|
|
|
return new Combine([
|
|
|
|
t.explanations.retagNoOtherThemes,
|
2023-04-20 03:58:31 +02:00
|
|
|
new SvelteUIElement(TagHint, {
|
|
|
|
osmConnection: state.osmConnection,
|
|
|
|
tags: retag,
|
|
|
|
}),
|
2022-05-01 04:17:40 +02:00
|
|
|
])
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
const deleteReason = selectedOption["deleteReason"]
|
|
|
|
if (deleteReason !== undefined) {
|
|
|
|
return new VariableUiElement(
|
|
|
|
deleteAction.canBeDeleted.map(({ canBeDeleted, reason }) => {
|
|
|
|
if (canBeDeleted) {
|
|
|
|
// This is a hard delete for which we give an explanation
|
|
|
|
return t.explanations.hardDelete
|
|
|
|
}
|
|
|
|
// This is a soft deletion: we explain _why_ the deletion is soft
|
2022-12-16 13:45:07 +01:00
|
|
|
return t.explanations.softDelete.Subs({ reason: reason })
|
2022-05-01 04:17:40 +02:00
|
|
|
})
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2022-05-01 04:17:40 +02:00
|
|
|
}
|
2021-07-01 02:26:45 +02:00
|
|
|
},
|
|
|
|
[deleteAction.canBeDeleted]
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2021-07-01 02:26:45 +02:00
|
|
|
).SetClass("block")
|
|
|
|
}
|
|
|
|
|
2022-08-20 12:46:33 +02:00
|
|
|
private static constructMultipleChoice(
|
|
|
|
config: DeleteConfig,
|
|
|
|
tagsSource: UIEventSource<Record<string, string>>,
|
2023-04-14 02:42:57 +02:00
|
|
|
state: SpecialVisualizationState
|
2022-05-01 04:17:40 +02:00
|
|
|
): InputElement<{ deleteReason: string } | { retagTo: TagsFilter }> {
|
|
|
|
const elements: InputElement<{ deleteReason: string } | { retagTo: TagsFilter }>[] = []
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
for (const nonDeleteOption of config.nonDeleteMappings) {
|
|
|
|
elements.push(
|
|
|
|
new FixedInputElement(
|
|
|
|
new SubstitutedTranslation(nonDeleteOption.then, tagsSource, state),
|
|
|
|
{
|
|
|
|
retagTo: nonDeleteOption.if,
|
|
|
|
}
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2022-05-01 04:17:40 +02:00
|
|
|
)
|
2021-07-03 14:35:44 +02:00
|
|
|
}
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2022-12-06 00:49:34 +01:00
|
|
|
for (const extraDeleteReason of config.deleteReasons ?? []) {
|
2022-05-01 04:17:40 +02:00
|
|
|
elements.push(
|
|
|
|
new FixedInputElement(
|
|
|
|
new SubstitutedTranslation(extraDeleteReason.explanation, tagsSource, state),
|
|
|
|
{
|
|
|
|
deleteReason: extraDeleteReason.changesetMessage,
|
|
|
|
}
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2022-05-01 04:17:40 +02:00
|
|
|
)
|
|
|
|
}
|
2021-07-01 02:26:45 +02:00
|
|
|
|
2022-05-01 04:17:40 +02:00
|
|
|
return new RadioButton(elements, { selectFirstAsDefault: false })
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|
2021-10-04 03:12:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class DeleteabilityChecker {
|
|
|
|
public readonly canBeDeleted: UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>
|
2023-04-20 03:58:31 +02:00
|
|
|
private readonly objectDownloader: OsmObjectDownloader
|
2023-04-14 02:42:57 +02:00
|
|
|
private readonly _id: OsmId
|
2021-10-04 03:12:42 +02:00
|
|
|
private readonly _allowDeletionAtChangesetCount: number
|
2023-04-14 02:42:57 +02:00
|
|
|
private readonly _osmConnection: OsmConnection
|
2021-10-04 03:12:42 +02:00
|
|
|
|
2023-04-20 03:58:31 +02:00
|
|
|
constructor(
|
|
|
|
id: OsmId,
|
|
|
|
osmConnection: OsmConnection,
|
|
|
|
objectDownloader: OsmObjectDownloader,
|
|
|
|
allowDeletionAtChangesetCount?: number
|
|
|
|
) {
|
|
|
|
this.objectDownloader = objectDownloader
|
2021-10-04 03:12:42 +02:00
|
|
|
this._id = id
|
2023-04-14 02:42:57 +02:00
|
|
|
this._osmConnection = osmConnection
|
2021-10-04 03:12:42 +02:00
|
|
|
this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE
|
|
|
|
|
|
|
|
this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({
|
|
|
|
canBeDeleted: undefined,
|
|
|
|
reason: Translations.t.delete.loading,
|
|
|
|
})
|
|
|
|
this.CheckDeleteability(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the currently logged in user can delete the current point.
|
|
|
|
* State is written into this._canBeDeleted
|
|
|
|
* @constructor
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
public CheckDeleteability(useTheInternet: boolean): void {
|
|
|
|
const t = Translations.t.delete
|
|
|
|
const id = this._id
|
|
|
|
const state = this.canBeDeleted
|
2022-01-26 21:40:38 +01:00
|
|
|
const self = this
|
2021-10-04 03:12:42 +02:00
|
|
|
if (!id.startsWith("node")) {
|
|
|
|
this.canBeDeleted.setData({
|
|
|
|
canBeDeleted: false,
|
|
|
|
reason: t.isntAPoint,
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does the currently logged in user have enough experience to delete this point?
|
2023-04-14 02:42:57 +02:00
|
|
|
const deletingPointsOfOtherAllowed = this._osmConnection.userDetails.map((ud) => {
|
2021-10-04 03:12:42 +02:00
|
|
|
if (ud === undefined) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
if (!ud.loggedIn) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
ud.csCount >=
|
|
|
|
Math.min(
|
|
|
|
Constants.userJourney.deletePointsOfOthersUnlock,
|
|
|
|
this._allowDeletionAtChangesetCount
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
|
|
|
)
|
2021-10-04 03:12:42 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
const previousEditors = new UIEventSource<number[]>(undefined)
|
|
|
|
const allByMyself = previousEditors.map(
|
|
|
|
(previous) => {
|
|
|
|
if (previous === null || previous === undefined) {
|
|
|
|
// Not yet downloaded
|
|
|
|
return null
|
|
|
|
}
|
2023-04-14 02:42:57 +02:00
|
|
|
const userId = self._osmConnection.userDetails.data.uid
|
2021-10-04 03:12:42 +02:00
|
|
|
return !previous.some((editor) => editor !== userId)
|
2022-01-19 20:34:04 +01:00
|
|
|
},
|
2023-04-14 02:42:57 +02:00
|
|
|
[self._osmConnection.userDetails]
|
2022-01-19 20:34:04 +01:00
|
|
|
)
|
2021-10-04 03:12:42 +02:00
|
|
|
|
|
|
|
// User allowed OR only edited by self?
|
|
|
|
const deletetionAllowed = deletingPointsOfOtherAllowed.map(
|
|
|
|
(isAllowed) => {
|
|
|
|
if (isAllowed === undefined) {
|
|
|
|
// No logged in user => definitively not allowed to delete!
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (isAllowed === true) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this point, the logged in user is not allowed to delete points created/edited by _others_
|
|
|
|
// however, we query OSM and if it turns out the current point has only be edited by the current user, deletion is allowed after all!
|
2022-09-08 21:40:48 +02:00
|
|
|
|
2021-10-04 03:12:42 +02:00
|
|
|
if (allByMyself.data === null && useTheInternet) {
|
|
|
|
// We kickoff the download here as it hasn't yet been downloaded. Note that this is mapped onto 'all by myself' above
|
2023-04-20 03:58:31 +02:00
|
|
|
const hist = this.objectDownloader
|
|
|
|
.DownloadHistory(id)
|
|
|
|
.map((versions) =>
|
|
|
|
versions.map((version) =>
|
|
|
|
Number(version.tags["_last_edit:contributor:uid"])
|
|
|
|
)
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2022-06-05 02:24:14 +02:00
|
|
|
hist.addCallbackAndRunD((hist) => previousEditors.setData(hist))
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2021-10-04 03:12:42 +02:00
|
|
|
|
|
|
|
if (allByMyself.data === true) {
|
|
|
|
// Yay! We can download!
|
|
|
|
return true
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2021-10-04 03:12:42 +02:00
|
|
|
if (allByMyself.data === false) {
|
|
|
|
// Nope, downloading not allowed...
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-07-01 02:26:45 +02:00
|
|
|
// At this point, we don't have enough information yet to decide if the user is allowed to delete the current point...
|
2022-05-01 04:17:40 +02:00
|
|
|
return undefined
|
2022-09-08 21:40:48 +02:00
|
|
|
},
|
2021-10-04 03:12:42 +02:00
|
|
|
[allByMyself]
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2021-10-04 03:12:42 +02:00
|
|
|
|
|
|
|
const hasRelations: UIEventSource<boolean> = new UIEventSource<boolean>(null)
|
|
|
|
const hasWays: UIEventSource<boolean> = new UIEventSource<boolean>(null)
|
|
|
|
deletetionAllowed.addCallbackAndRunD((deletetionAllowed) => {
|
|
|
|
if (deletetionAllowed === false) {
|
|
|
|
// Nope, we are not allowed to delete
|
|
|
|
state.setData({
|
|
|
|
canBeDeleted: false,
|
|
|
|
reason: t.notEnoughExperience,
|
|
|
|
})
|
|
|
|
return true // unregister this caller!
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!useTheInternet) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// All right! We have arrived at a point that we should query OSM again to check that the point isn't a part of ways or relations
|
2023-04-20 03:58:31 +02:00
|
|
|
this.objectDownloader.DownloadReferencingRelations(id).then((rels) => {
|
2021-10-04 03:12:42 +02:00
|
|
|
hasRelations.setData(rels.length > 0)
|
|
|
|
})
|
|
|
|
|
2023-04-20 03:58:31 +02:00
|
|
|
this.objectDownloader.DownloadReferencingWays(id).then((ways) => {
|
2021-10-04 03:12:42 +02:00
|
|
|
hasWays.setData(ways.length > 0)
|
|
|
|
})
|
|
|
|
return true // unregister to only run once
|
|
|
|
})
|
|
|
|
|
|
|
|
const hasWaysOrRelations = hasRelations.map(
|
|
|
|
(hasRelationsData) => {
|
|
|
|
if (hasRelationsData === true) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (hasWays.data === true) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (hasWays.data === null || hasRelationsData === null) {
|
|
|
|
return null
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2021-10-04 03:12:42 +02:00
|
|
|
if (hasWays.data === false && hasRelationsData === false) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return null
|
2022-09-08 21:40:48 +02:00
|
|
|
},
|
|
|
|
[hasWays]
|
2021-10-04 03:12:42 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
hasWaysOrRelations.addCallbackAndRun((waysOrRelations) => {
|
|
|
|
if (waysOrRelations == null) {
|
|
|
|
// Not yet loaded - we still wait a little bit
|
2022-09-08 21:40:48 +02:00
|
|
|
return
|
|
|
|
}
|
2021-10-04 03:12:42 +02:00
|
|
|
if (waysOrRelations) {
|
2022-12-06 00:49:34 +01:00
|
|
|
// not deleteable by mapcomplete
|
2021-10-04 03:12:42 +02:00
|
|
|
state.setData({
|
|
|
|
canBeDeleted: false,
|
|
|
|
reason: t.partOfOthers,
|
2022-09-08 21:40:48 +02:00
|
|
|
})
|
|
|
|
} else {
|
2021-10-04 03:12:42 +02:00
|
|
|
// alright, this point can be safely deleted!
|
|
|
|
state.setData({
|
|
|
|
canBeDeleted: true,
|
|
|
|
reason: allByMyself.data === true ? t.onlyEditedByLoggedInUser : t.safeDelete,
|
2022-09-08 21:40:48 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2021-10-04 03:12:42 +02:00
|
|
|
}
|
2021-07-01 02:26:45 +02:00
|
|
|
}
|