| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | import utils from "./utils.js"; | 
					
						
							| 
									
										
										
										
											2018-03-25 13:02:39 -04:00
										 |  |  | import Branch from "../entities/branch.js"; | 
					
						
							|  |  |  | import NoteShort from "../entities/note_short.js"; | 
					
						
							| 
									
										
										
										
											2019-10-20 10:00:18 +02:00
										 |  |  | import toastService from "./toast.js"; | 
					
						
							| 
									
										
										
										
											2019-08-26 20:21:43 +02:00
										 |  |  | import ws from "./ws.js"; | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | import server from "./server.js"; | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-14 18:32:56 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * TreeCache keeps a read only cache of note tree structure in frontend's memory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | class TreeCache { | 
					
						
							| 
									
										
										
										
											2018-08-16 23:00:04 +02:00
										 |  |  |     constructor() { | 
					
						
							|  |  |  |         this.init(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     init() { | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  |         /** @type {Object.<string, string>} */ | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         this.parents = {}; | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  |         /** @type {Object.<string, string>} */ | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         this.children = {}; | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @type {Object.<string, string>} */ | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |         this.childParentToBranch = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** @type {Object.<string, NoteShort>} */ | 
					
						
							|  |  |  |         this.notes = {}; | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @type {Object.<string, Branch>} */ | 
					
						
							|  |  |  |         this.branches = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 21:47:14 +02:00
										 |  |  |     load(noteRows, branchRows) { | 
					
						
							|  |  |  |         this.init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.addResp(noteRows, branchRows); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addResp(noteRows, branchRows) { | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |         for (const noteRow of noteRows) { | 
					
						
							|  |  |  |             const note = new NoteShort(this, noteRow); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.notes[note.noteId] = note; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const branchRow of branchRows) { | 
					
						
							|  |  |  |             const branch = new Branch(this, branchRow); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.addBranch(branch); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 22:56:45 +02:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |      * Reload notes and their children. | 
					
						
							| 
									
										
										
										
											2019-04-13 22:56:45 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |     async reloadNotesAndTheirChildren(noteIds) { | 
					
						
							|  |  |  |         // first load the data before clearing the cache
 | 
					
						
							|  |  |  |         const resp = await server.post('tree/load', { noteIds }); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |         for (const noteId of noteIds) { | 
					
						
							|  |  |  |             for (const childNoteId of this.children[noteId] || []) { | 
					
						
							|  |  |  |                 this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |                 const branchId = this.getBranchIdByChildParent(childNoteId, noteId); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |                 delete this.branches[branchId]; | 
					
						
							|  |  |  |                 delete this.childParentToBranch[childNoteId + '-' + noteId]; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |             this.children[noteId] = []; | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |             delete this.notes[noteId]; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 21:47:14 +02:00
										 |  |  |         this.addResp(resp.notes, resp.branches); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 22:56:45 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Reloads parents of given noteId - useful when new note is created to make sure note is loaded | 
					
						
							|  |  |  |      * in a correct order. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     async reloadParents(noteId) { | 
					
						
							|  |  |  |         // to be able to find parents we need first to make sure it is actually loaded
 | 
					
						
							|  |  |  |         await this.getNote(noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |         await this.reloadNotesAndTheirChildren(this.parents[noteId] || []); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:56:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // this is done to load the new parents for the noteId
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:29:34 +02:00
										 |  |  |         await this.reloadNotesAndTheirChildren([noteId]); | 
					
						
							| 
									
										
										
										
											2019-04-13 22:56:45 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  |     /** @return {Promise<NoteShort[]>} */ | 
					
						
							| 
									
										
										
										
											2018-08-16 23:00:04 +02:00
										 |  |  |     async getNotes(noteIds, silentNotFoundError = false) { | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         const missingNoteIds = noteIds.filter(noteId => this.notes[noteId] === undefined); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         if (missingNoteIds.length > 0) { | 
					
						
							|  |  |  |             const resp = await server.post('tree/load', { noteIds: missingNoteIds }); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 21:47:14 +02:00
										 |  |  |             this.addResp(resp.notes, resp.branches); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         return noteIds.map(noteId => { | 
					
						
							| 
									
										
										
										
											2018-08-16 23:00:04 +02:00
										 |  |  |             if (!this.notes[noteId] && !silentNotFoundError) { | 
					
						
							| 
									
										
										
										
											2019-08-26 20:21:43 +02:00
										 |  |  |                 ws.logError(`Can't find note "${noteId}"`); | 
					
						
							| 
									
										
										
										
											2018-08-06 11:30:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-12 12:59:38 +02:00
										 |  |  |                 return null; | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return this.notes[noteId]; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-08-12 12:59:38 +02:00
										 |  |  |         }).filter(note => note !== null); | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 22:10:16 +02:00
										 |  |  |     /** @return {Promise<boolean>} */ | 
					
						
							|  |  |  |     async noteExists(noteId) { | 
					
						
							|  |  |  |         const notes = await this.getNotes([noteId], true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return notes.length === 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @return {Promise<NoteShort>} */ | 
					
						
							| 
									
										
										
										
											2019-09-08 11:25:57 +02:00
										 |  |  |     async getNote(noteId, silentNotFoundError = false) { | 
					
						
							| 
									
										
										
										
											2018-05-26 16:16:34 -04:00
										 |  |  |         if (noteId === 'none') { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-08 11:25:57 +02:00
										 |  |  |         return (await this.getNotes([noteId], silentNotFoundError))[0]; | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addBranch(branch) { | 
					
						
							|  |  |  |         this.branches[branch.branchId] = branch; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         this.addBranchRelationship(branch.branchId, branch.noteId, branch.parentNoteId); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |     addBranchRelationship(branchId, childNoteId, parentNoteId) { | 
					
						
							| 
									
										
										
										
											2018-05-26 16:16:34 -04:00
										 |  |  |         if (parentNoteId === 'none') { // applies only to root element
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         this.childParentToBranch[childNoteId + '-' + parentNoteId] = branchId; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.parents[childNoteId] = this.parents[childNoteId] || []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!this.parents[childNoteId].includes(parentNoteId)) { | 
					
						
							|  |  |  |             this.parents[childNoteId].push(parentNoteId); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.children[parentNoteId] = this.children[parentNoteId] || []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!this.children[parentNoteId].includes(childNoteId)) { | 
					
						
							|  |  |  |             this.children[parentNoteId].push(childNoteId); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     add(note, branch) { | 
					
						
							|  |  |  |         this.notes[note.noteId] = note; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.addBranch(branch); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |     async getBranches(branchIds) { | 
					
						
							|  |  |  |         const missingBranchIds = branchIds.filter(branchId => this.branches[branchId] === undefined); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         if (missingBranchIds.length > 0) { | 
					
						
							|  |  |  |             const resp = await server.post('tree/load', { branchIds: branchIds }); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-25 21:47:14 +02:00
										 |  |  |             this.addResp(resp.notes, resp.branches); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         return branchIds.map(branchId => { | 
					
						
							|  |  |  |             if (!this.branches[branchId]) { | 
					
						
							|  |  |  |                 throw new Error(`Can't find branch ${branchId}`); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 return this.branches[branchId]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @return Branch */ | 
					
						
							|  |  |  |     async getBranch(branchId) { | 
					
						
							|  |  |  |         return (await this.getBranches([branchId]))[0]; | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 14:49:20 -04:00
										 |  |  |     /** @return Branch */ | 
					
						
							|  |  |  |     async getBranchByChildParent(childNoteId, parentNoteId) { | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         const branchId = this.getBranchIdByChildParent(childNoteId, parentNoteId); | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         return await this.getBranch(branchId); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getBranchIdByChildParent(childNoteId, parentNoteId) { | 
					
						
							|  |  |  |         const key = childNoteId + '-' + parentNoteId; | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         const branchId = this.childParentToBranch[key]; | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 20:40:18 -04:00
										 |  |  |         if (!branchId) { | 
					
						
							| 
									
										
										
										
											2019-10-20 10:00:18 +02:00
										 |  |  |             toastService.throwError("Cannot find branch for child-parent=" + key); | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:13:33 -04:00
										 |  |  |         return branchId; | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Move note from one parent to another. */ | 
					
						
							| 
									
										
										
										
											2019-03-18 21:59:53 +01:00
										 |  |  |     async moveNote(childNoteId, oldParentNoteId, newParentNoteId, beforeNoteId, afterNoteId) { | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  |         utils.assertArguments(childNoteId, oldParentNoteId, newParentNoteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (oldParentNoteId === newParentNoteId) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 23:03:41 +01:00
										 |  |  |         const branchId = this.childParentToBranch[childNoteId + '-' + oldParentNoteId]; | 
					
						
							| 
									
										
										
										
											2019-01-05 19:25:22 +01:00
										 |  |  |         const branch = await this.getBranch(branchId); | 
					
						
							|  |  |  |         branch.parentNoteId = newParentNoteId; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 23:03:41 +01:00
										 |  |  |         this.childParentToBranch[childNoteId + '-' + newParentNoteId] = branchId; | 
					
						
							|  |  |  |         delete this.childParentToBranch[childNoteId + '-' + oldParentNoteId]; // this is correct because we know that oldParentId isn't same as newParentId
 | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // remove old associations
 | 
					
						
							| 
									
										
										
										
											2019-03-18 23:03:41 +01:00
										 |  |  |         this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== oldParentNoteId); | 
					
						
							|  |  |  |         this.children[oldParentNoteId] = this.children[oldParentNoteId].filter(ch => ch !== childNoteId); | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // add new associations
 | 
					
						
							| 
									
										
										
										
											2019-03-18 23:03:41 +01:00
										 |  |  |         this.parents[childNoteId].push(newParentNoteId); | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 23:03:41 +01:00
										 |  |  |         const children = this.children[newParentNoteId] = this.children[newParentNoteId] || []; // this might be first child
 | 
					
						
							| 
									
										
										
										
											2019-03-18 21:59:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // we try to put the note into precise order which might be used again by lazy-loaded nodes
 | 
					
						
							|  |  |  |         if (beforeNoteId && children.includes(beforeNoteId)) { | 
					
						
							|  |  |  |             children.splice(children.indexOf(beforeNoteId), 0, childNoteId); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (afterNoteId && children.includes(afterNoteId)) { | 
					
						
							|  |  |  |             children.splice(children.indexOf(afterNoteId) + 1, 0, childNoteId); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             children.push(childNoteId); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-03-26 22:11:45 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-25 12:29:00 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const treeCache = new TreeCache(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default treeCache; |