| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | import {UIEventSource} from "../../UIEventSource"; | 
					
						
							|  |  |  | import {Translation} from "../../../UI/i18n/Translation"; | 
					
						
							|  |  |  | import State from "../../../State"; | 
					
						
							|  |  |  | import {OsmObject} from "../OsmObject"; | 
					
						
							|  |  |  | import Translations from "../../../UI/i18n/Translations"; | 
					
						
							|  |  |  | import Constants from "../../../Models/Constants"; | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class DeleteAction { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public readonly canBeDeleted: UIEventSource<{ canBeDeleted?: boolean, reason: Translation }>; | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |     public readonly isDeleted = new UIEventSource<boolean>(false); | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |     private readonly _id: string; | 
					
						
							| 
									
										
										
										
											2021-07-01 02:43:49 +02:00
										 |  |  |     private readonly _allowDeletionAtChangesetCount: number; | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:43:49 +02:00
										 |  |  |     constructor(id: string, allowDeletionAtChangesetCount?: number) { | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |         this._id = id; | 
					
						
							| 
									
										
										
										
											2021-07-01 02:43:49 +02:00
										 |  |  |         this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE; | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({ | 
					
						
							|  |  |  |             canBeDeleted: undefined, | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |             reason: Translations.t.delete.loading | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         this.CheckDeleteability(false) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Does actually delete the feature; returns the event source 'this.isDeleted' | 
					
						
							|  |  |  |      * If deletion is not allowed, triggers the callback instead | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     public DoDelete(reason: string, onNotAllowed : () => void): void { | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |         const isDeleted = this.isDeleted | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |         const self = this; | 
					
						
							|  |  |  |         let deletionStarted = false; | 
					
						
							|  |  |  |         this.canBeDeleted.addCallbackAndRun( | 
					
						
							|  |  |  |             canBeDeleted => { | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                 if (isDeleted.data || deletionStarted) { | 
					
						
							|  |  |  |                     // Already deleted...
 | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                  | 
					
						
							|  |  |  |                 if(canBeDeleted.canBeDeleted === false){ | 
					
						
							|  |  |  |                     // We aren't allowed to delete
 | 
					
						
							|  |  |  |                     deletionStarted = true; | 
					
						
							|  |  |  |                     onNotAllowed(); | 
					
						
							|  |  |  |                     isDeleted.setData(true); | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                  | 
					
						
							|  |  |  |                 if (!canBeDeleted) { | 
					
						
							|  |  |  |                     // We are not allowed to delete (yet), this might change in the future though
 | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                 deletionStarted = true; | 
					
						
							|  |  |  |                 OsmObject.DownloadObject(self._id).addCallbackAndRun(obj => { | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                     if (obj === undefined) { | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                         return; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     State.state.osmConnection.changesetHandler.DeleteElement( | 
					
						
							|  |  |  |                         obj, | 
					
						
							|  |  |  |                         State.state.layoutToUse.data, | 
					
						
							|  |  |  |                         reason, | 
					
						
							|  |  |  |                         State.state.allElements, | 
					
						
							|  |  |  |                         () => { | 
					
						
							|  |  |  |                             isDeleted.setData(true) | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Checks if the currently logged in user can delete the current point. | 
					
						
							|  |  |  |      * State is written into this._canBeDeleted | 
					
						
							|  |  |  |      * @constructor | 
					
						
							|  |  |  |      * @private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |     public CheckDeleteability(useTheInternet: boolean): void { | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |         const t = Translations.t.delete; | 
					
						
							|  |  |  |         const id = this._id; | 
					
						
							|  |  |  |         const state = this.canBeDeleted | 
					
						
							|  |  |  |         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?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const deletingPointsOfOtherAllowed = State.state.osmConnection.userDetails.map(ud => { | 
					
						
							|  |  |  |             if (ud === undefined) { | 
					
						
							|  |  |  |                 return undefined; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |             if (!ud.loggedIn) { | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:43:49 +02:00
										 |  |  |             return ud.csCount >= Math.min(Constants.userJourney.deletePointsOfOthersUnlock, this._allowDeletionAtChangesetCount); | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const previousEditors = new UIEventSource<number[]>(undefined) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const allByMyself = previousEditors.map(previous => { | 
					
						
							|  |  |  |             if (previous === null || previous === undefined) { | 
					
						
							|  |  |  |                 // Not yet downloaded
 | 
					
						
							|  |  |  |                 return null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const userId = State.state.osmConnection.userDetails.data.uid; | 
					
						
							|  |  |  |             return !previous.some(editor => editor !== userId) | 
					
						
							|  |  |  |         }, [State.state.osmConnection.userDetails]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 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!
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |             if (allByMyself.data === null && useTheInternet) { | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                 // We kickoff the download here as it hasn't yet been downloaded. Note that this is mapped onto 'all by myself' above
 | 
					
						
							|  |  |  |                 OsmObject.DownloadHistory(id).map(versions => versions.map(version => version.tags["_last_edit:contributor:uid"])).syncWith(previousEditors) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (allByMyself.data === true) { | 
					
						
							|  |  |  |                 // Yay! We can download!
 | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (allByMyself.data === false) { | 
					
						
							|  |  |  |                 // Nope, downloading not allowed...
 | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 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]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const hasRelations: UIEventSource<boolean> = new UIEventSource<boolean>(null) | 
					
						
							|  |  |  |         const hasWays: UIEventSource<boolean> = new UIEventSource<boolean>(null) | 
					
						
							|  |  |  |         deletetionAllowed.addCallbackAndRunD(deletetionAllowed => { | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |             if (deletetionAllowed === false) { | 
					
						
							|  |  |  |                 // Nope, we are not allowed to delete
 | 
					
						
							|  |  |  |                 state.setData({ | 
					
						
							|  |  |  |                     canBeDeleted: false, | 
					
						
							|  |  |  |                     reason: t.notEnoughExperience | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |             if (!useTheInternet) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +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
 | 
					
						
							|  |  |  |             OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(rels => { | 
					
						
							|  |  |  |                 hasRelations.setData(rels.length > 0) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             OsmObject.DownloadReferencingWays(id).addCallbackAndRunD(ways => { | 
					
						
							|  |  |  |                 hasWays.setData(ways.length > 0) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const hasWaysOrRelations = hasRelations.map(hasRelationsData => { | 
					
						
							|  |  |  |             if (hasRelationsData === true) { | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (hasWays.data === true) { | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |             if (hasWays.data === null || hasRelationsData === null) { | 
					
						
							|  |  |  |                 return null; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |             if (hasWays.data === false && hasRelationsData === false) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         }, [hasWays]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         hasWaysOrRelations.addCallbackAndRun( | 
					
						
							|  |  |  |             waysOrRelations => { | 
					
						
							|  |  |  |                 if (waysOrRelations == null) { | 
					
						
							|  |  |  |                     // Not yet loaded - we still wait a little bit
 | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (waysOrRelations) { | 
					
						
							|  |  |  |                     // not deleteble by mapcomplete
 | 
					
						
							|  |  |  |                     state.setData({ | 
					
						
							|  |  |  |                         canBeDeleted: false, | 
					
						
							|  |  |  |                         reason: t.partOfOthers | 
					
						
							|  |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                 }else{ | 
					
						
							|  |  |  |                     // alright, this point can be safely deleted!
 | 
					
						
							|  |  |  |                     state.setData({ | 
					
						
							|  |  |  |                         canBeDeleted: true, | 
					
						
							|  |  |  |                         reason: allByMyself.data === true ? t.onlyEditedByLoggedInUser : t.safeDelete | 
					
						
							|  |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 02:26:45 +02:00
										 |  |  |                | 
					
						
							| 
									
										
										
										
											2021-06-30 18:48:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |