| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | import { VariableUiElement } from "../Base/VariableUIElement" | 
					
						
							|  |  |  | import Toggle from "../Input/Toggle" | 
					
						
							|  |  |  | import Translations from "../i18n/Translations" | 
					
						
							|  |  |  | import Svg from "../../Svg" | 
					
						
							|  |  |  | import DeleteAction from "../../Logic/Osm/Actions/DeleteAction" | 
					
						
							|  |  |  | import { Store, UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							|  |  |  | 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" | 
					
						
							|  |  |  | import Constants from "../../Models/Constants" | 
					
						
							|  |  |  | import DeleteConfig from "../../Models/ThemeConfig/DeleteConfig" | 
					
						
							|  |  |  | import { OsmConnection } from "../../Logic/Osm/OsmConnection" | 
					
						
							|  |  |  | 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 }) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             let actionToTake: OsmChangeAction | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |             if (selected["retagTo"] !== undefined) { | 
					
						
							|  |  |  |                 // no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 actionToTake = new ChangeTagAction(id, selected["retagTo"], tagsSource.data, { | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |                     theme: state?.layout?.id ?? "unkown", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     changeType: "special-delete", | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 actionToTake = new DeleteAction( | 
					
						
							|  |  |  |                     id, | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |                     options.softDeletionTags, | 
					
						
							|  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |                         theme: state?.layout?.id ?? "unkown", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                         specialMotivation: selected["deleteReason"], | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |                     }, | 
					
						
							|  |  |  |                     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-09-08 21:40:48 +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-09-08 21:40:48 +02:00
										 |  |  |             Svg.delete_icon_svg().SetStyle("width: 1.5rem; height: 1.5rem;"), | 
					
						
							|  |  |  |             t.delete | 
					
						
							|  |  |  |         ).onClick(() => { | 
					
						
							|  |  |  |             deleteAbility.CheckDeleteability(true) | 
					
						
							|  |  |  |             confirm.setData(true) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const isShown: Store<boolean> = tagsSource.map((tgs) => tgs.id.indexOf("-") < 0) | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const deleteOptionPicker = DeleteWizard.constructMultipleChoice(options, tagsSource, state) | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |         const deleteDialog = new Combine([ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             new Title( | 
					
						
							|  |  |  |                 new SubstitutedTranslation(t.whyDelete, tagsSource, state).SetClass( | 
					
						
							|  |  |  |                     "question-text" | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 3 | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |             deleteOptionPicker, | 
					
						
							|  |  |  |             new Combine([ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 DeleteWizard.constructExplanation( | 
					
						
							|  |  |  |                     deleteOptionPicker.GetValue(), | 
					
						
							|  |  |  |                     deleteAbility, | 
					
						
							|  |  |  |                     tagsSource, | 
					
						
							|  |  |  |                     state | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |                 new Combine([ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     cancelButton, | 
					
						
							|  |  |  |                     DeleteWizard.constructConfirmButton(deleteOptionPicker.GetValue()).onClick(() => | 
					
						
							|  |  |  |                         doDelete(deleteOptionPicker.GetValue().data) | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                 ]).SetClass("flex justify-end flex-wrap-reverse"), | 
					
						
							|  |  |  |             ]).SetClass("flex mt-2 justify-between"), | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |         ]).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, | 
					
						
							|  |  |  |                     new SubtleButton(Svg.envelope_ui(), t.readMessages), | 
					
						
							|  |  |  |                     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( | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 new Combine([ | 
					
						
							|  |  |  |                     Svg.delete_icon_svg().SetClass( | 
					
						
							|  |  |  |                         "h-16 w-16 p-2 m-2 block bg-gray-300 rounded-full" | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     t.isDeleted, | 
					
						
							|  |  |  |                 ]).SetClass("flex m-2 rounded-full"), | 
					
						
							| 
									
										
										
										
											2023-01-06 03:37:22 +01:00
										 |  |  |                 new LoginToggle(deleteFlow, undefined, state), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 isDeleted | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2021-07-03 14:35:44 +02:00
										 |  |  |             undefined, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             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-09-08 21:40:48 +02:00
										 |  |  |     private static constructConfirmButton( | 
					
						
							|  |  |  |         deleteReasons: UIEventSource<any | undefined> | 
					
						
							|  |  |  |     ): BaseUIElement { | 
					
						
							|  |  |  |         const t = Translations.t.delete | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         const btn = new Combine([ | 
					
						
							|  |  |  |             Svg.delete_icon_ui().SetClass("w-6 h-6 mr-3 block"), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             t.delete, | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         ]).SetClass("flex btn bg-red-500") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const btnNonActive = new Combine([ | 
					
						
							|  |  |  |             Svg.delete_icon_ui().SetClass("w-6 h-6 mr-3 block"), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             deleteReasons.map((reason) => reason !== undefined) | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     private static constructExplanation( | 
					
						
							|  |  |  |         selectedOption: UIEventSource<{ deleteReason: string } | { retagTo: TagsFilter }>, | 
					
						
							|  |  |  |         deleteAction: DeleteabilityChecker, | 
					
						
							|  |  |  |         currentTags: UIEventSource<object>, | 
					
						
							|  |  |  |         state?: { osmConnection?: OsmConnection } | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         const t = Translations.t.delete | 
					
						
							|  |  |  |         return new VariableUiElement( | 
					
						
							|  |  |  |             selectedOption.map( | 
					
						
							|  |  |  |                 (selectedOption) => { | 
					
						
							|  |  |  |                     if (selectedOption === undefined) { | 
					
						
							|  |  |  |                         return t.explanations.selectReason.SetClass("subtle") | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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-09-08 21:40:48 +02:00
										 |  |  |                         ]) | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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-09-08 21:40:48 +02:00
										 |  |  |                             }) | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 [deleteAction.canBeDeleted] | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ).SetClass("block") | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     private static constructMultipleChoice( | 
					
						
							|  |  |  |         config: DeleteConfig, | 
					
						
							|  |  |  |         tagsSource: UIEventSource<Record<string, string>>, | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |         state: SpecialVisualizationState | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             elements.push( | 
					
						
							|  |  |  |                 new FixedInputElement( | 
					
						
							|  |  |  |                     new SubstitutedTranslation(nonDeleteOption.then, tagsSource, state), | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         retagTo: nonDeleteOption.if, | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											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-09-08 21:40:48 +02:00
										 |  |  |             elements.push( | 
					
						
							|  |  |  |                 new FixedInputElement( | 
					
						
							|  |  |  |                     new SubstitutedTranslation(extraDeleteReason.explanation, tagsSource, state), | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         deleteReason: extraDeleteReason.changesetMessage, | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2022-05-01 04:17:40 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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 { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this._id = id | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |         this._osmConnection = osmConnection | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({ | 
					
						
							|  |  |  |             canBeDeleted: undefined, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             reason: Translations.t.delete.loading, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  |         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 { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const t = Translations.t.delete | 
					
						
							|  |  |  |         const id = this._id | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         const state = this.canBeDeleted | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const self = this | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         if (!id.startsWith("node")) { | 
					
						
							|  |  |  |             this.canBeDeleted.setData({ | 
					
						
							|  |  |  |                 canBeDeleted: false, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 reason: t.isntAPoint, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return undefined | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             if (!ud.loggedIn) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return false | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             return ( | 
					
						
							|  |  |  |                 ud.csCount >= | 
					
						
							|  |  |  |                 Math.min( | 
					
						
							|  |  |  |                     Constants.userJourney.deletePointsOfOthersUnlock, | 
					
						
							|  |  |  |                     this._allowDeletionAtChangesetCount | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const previousEditors = new UIEventSource<number[]>(undefined) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return !previous.some((editor) => editor !== userId) | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2023-04-14 02:42:57 +02:00
										 |  |  |             [self._osmConnection.userDetails] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // User allowed OR only edited by self?
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const deletetionAllowed = deletingPointsOfOtherAllowed.map( | 
					
						
							|  |  |  |             (isAllowed) => { | 
					
						
							|  |  |  |                 if (isAllowed === undefined) { | 
					
						
							|  |  |  |                     // No logged in user => definitively not allowed to delete!
 | 
					
						
							|  |  |  |                     return false | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (isAllowed === true) { | 
					
						
							|  |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 // 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!
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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
										 |  |  |                         ) | 
					
						
							|  |  |  |                     hist.addCallbackAndRunD((hist) => previousEditors.setData(hist)) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 if (allByMyself.data === true) { | 
					
						
							|  |  |  |                     // Yay! We can download!
 | 
					
						
							|  |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (allByMyself.data === false) { | 
					
						
							|  |  |  |                     // Nope, downloading not allowed...
 | 
					
						
							|  |  |  |                     return false | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 // At this point, we don't have enough information yet to decide if the user is allowed to delete the current point...
 | 
					
						
							|  |  |  |                 return undefined | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             [allByMyself] | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const hasRelations: UIEventSource<boolean> = new UIEventSource<boolean>(null) | 
					
						
							|  |  |  |         const hasWays: UIEventSource<boolean> = new UIEventSource<boolean>(null) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         deletetionAllowed.addCallbackAndRunD((deletetionAllowed) => { | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             if (deletetionAllowed === false) { | 
					
						
							|  |  |  |                 // Nope, we are not allowed to delete
 | 
					
						
							|  |  |  |                 state.setData({ | 
					
						
							|  |  |  |                     canBeDeleted: false, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     reason: t.notEnoughExperience, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return true // unregister this caller!
 | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!useTheInternet) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 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) | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             return true // unregister to only run once
 | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const hasWaysOrRelations = hasRelations.map( | 
					
						
							|  |  |  |             (hasRelationsData) => { | 
					
						
							|  |  |  |                 if (hasRelationsData === true) { | 
					
						
							|  |  |  |                     return true | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 if (hasWays.data === true) { | 
					
						
							|  |  |  |                     return true | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 if (hasWays.data === null || hasRelationsData === null) { | 
					
						
							|  |  |  |                     return null | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (hasWays.data === false && hasRelationsData === false) { | 
					
						
							|  |  |  |                     return false | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return null | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             [hasWays] | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         hasWaysOrRelations.addCallbackAndRun((waysOrRelations) => { | 
					
						
							|  |  |  |             if (waysOrRelations == null) { | 
					
						
							|  |  |  |                 // Not yet loaded - we still wait a little bit
 | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (waysOrRelations) { | 
					
						
							| 
									
										
										
										
											2022-12-06 00:49:34 +01:00
										 |  |  |                 // not deleteable by mapcomplete
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 state.setData({ | 
					
						
							|  |  |  |                     canBeDeleted: false, | 
					
						
							|  |  |  |                     reason: t.partOfOthers, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // alright, this point can be safely deleted!
 | 
					
						
							|  |  |  |                 state.setData({ | 
					
						
							|  |  |  |                     canBeDeleted: true, | 
					
						
							|  |  |  |                     reason: allByMyself.data === true ? t.onlyEditedByLoggedInUser : t.safeDelete, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | } |