| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  | import { Translation } from "../../i18n/Translation" | 
					
						
							|  |  |  | import OsmObjectDownloader from "../../../Logic/Osm/OsmObjectDownloader" | 
					
						
							|  |  |  | import { UIEventSource } from "../../../Logic/UIEventSource" | 
					
						
							|  |  |  | import { OsmId } from "../../../Models/OsmFeature" | 
					
						
							|  |  |  | import { OsmConnection } from "../../../Logic/Osm/OsmConnection" | 
					
						
							|  |  |  | import { SpecialVisualizationState } from "../../SpecialVisualization" | 
					
						
							|  |  |  | import Translations from "../../i18n/Translations" | 
					
						
							|  |  |  | import Constants from "../../../Models/Constants" | 
					
						
							| 
									
										
										
										
											2023-06-01 23:40:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class DeleteFlowState { | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |     public readonly canBeDeleted: UIEventSource<boolean | undefined> = new UIEventSource< | 
					
						
							|  |  |  |         boolean | undefined | 
					
						
							|  |  |  |     >(undefined) | 
					
						
							|  |  |  |     public readonly canBeDeletedReason: UIEventSource<Translation | undefined> = | 
					
						
							|  |  |  |         new UIEventSource<Translation>(undefined) | 
					
						
							| 
									
										
										
										
											2023-06-01 23:40:05 +02:00
										 |  |  |     private readonly objectDownloader: OsmObjectDownloader | 
					
						
							|  |  |  |     private readonly _id: OsmId | 
					
						
							|  |  |  |     private readonly _allowDeletionAtChangesetCount: number | 
					
						
							|  |  |  |     private readonly _osmConnection: OsmConnection | 
					
						
							|  |  |  |     private readonly state: SpecialVisualizationState | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor( | 
					
						
							|  |  |  |         id: OsmId, | 
					
						
							|  |  |  |         state: SpecialVisualizationState, | 
					
						
							|  |  |  |         allowDeletionAtChangesetCount?: number | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         this.state = state | 
					
						
							|  |  |  |         this.objectDownloader = state.osmObjectDownloader | 
					
						
							|  |  |  |         this._id = id | 
					
						
							|  |  |  |         this._osmConnection = state.osmConnection | 
					
						
							|  |  |  |         this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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 { | 
					
						
							|  |  |  |         console.log("Checking deleteability (internet?", useTheInternet, ")") | 
					
						
							|  |  |  |         const t = Translations.t.delete | 
					
						
							|  |  |  |         const id = this._id | 
					
						
							|  |  |  |         const self = this | 
					
						
							|  |  |  |         if (!id.startsWith("node")) { | 
					
						
							|  |  |  |             this.canBeDeleted.setData(false) | 
					
						
							|  |  |  |             this.canBeDeletedReason.setData(t.isntAPoint) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Does the currently logged in user have enough experience to delete this point?
 | 
					
						
							|  |  |  |         const deletingPointsOfOtherAllowed = this._osmConnection.userDetails.map((ud) => { | 
					
						
							|  |  |  |             if (ud === undefined) { | 
					
						
							|  |  |  |                 return undefined | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (!ud.loggedIn) { | 
					
						
							|  |  |  |                 return false | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return ( | 
					
						
							|  |  |  |                 ud.csCount >= | 
					
						
							|  |  |  |                 Math.min( | 
					
						
							|  |  |  |                     Constants.userJourney.deletePointsOfOthersUnlock, | 
					
						
							|  |  |  |                     this._allowDeletionAtChangesetCount | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const previousEditors = new UIEventSource<number[]>(undefined) | 
					
						
							|  |  |  |         const allByMyself = previousEditors.map( | 
					
						
							|  |  |  |             (previous) => { | 
					
						
							|  |  |  |                 if (previous === null || previous === undefined) { | 
					
						
							|  |  |  |                     // Not yet downloaded
 | 
					
						
							|  |  |  |                     return null | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 const userId = self._osmConnection.userDetails.data.uid | 
					
						
							|  |  |  |                 return !previous.some((editor) => editor !== userId) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             [self._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!
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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
 | 
					
						
							|  |  |  |                     const hist = this.objectDownloader | 
					
						
							|  |  |  |                         .DownloadHistory(id) | 
					
						
							|  |  |  |                         .map((versions) => | 
					
						
							|  |  |  |                             versions.map((version) => | 
					
						
							|  |  |  |                                 Number(version.tags["_last_edit:contributor:uid"]) | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                     hist.addCallbackAndRunD((hist) => previousEditors.setData(hist)) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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) => { | 
					
						
							|  |  |  |             if (deletetionAllowed === false) { | 
					
						
							|  |  |  |                 // Nope, we are not allowed to delete
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.canBeDeleted.setData(false) | 
					
						
							|  |  |  |                 this.canBeDeletedReason.setData(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
 | 
					
						
							|  |  |  |             this.objectDownloader.DownloadReferencingRelations(id).then((rels) => { | 
					
						
							|  |  |  |                 hasRelations.setData(rels.length > 0) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.objectDownloader.DownloadReferencingWays(id).then((ways) => { | 
					
						
							|  |  |  |                 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 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 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 deleteable by mapcomplete
 | 
					
						
							|  |  |  |                 this.canBeDeleted.setData(false) | 
					
						
							|  |  |  |                 this.canBeDeletedReason.setData(t.partOfOthers) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // alright, this point can be safely deleted!
 | 
					
						
							|  |  |  |                 this.canBeDeleted.setData(true) | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |                 this.canBeDeletedReason.setData( | 
					
						
							|  |  |  |                     allByMyself.data ? t.onlyEditedByLoggedInUser : t.safeDelete | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2023-06-01 23:40:05 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |