You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	All: Improved synchronisation process and saving of models so that reducer can deal with full objects
This commit is contained in:
		| @@ -62,11 +62,12 @@ class BaseModel { | ||||
| 		return temp; | ||||
| 	} | ||||
|  | ||||
| 	static fieldType(name) { | ||||
| 	static fieldType(name, defaultValue = null) { | ||||
| 		let fields = this.fields(); | ||||
| 		for (let i = 0; i < fields.length; i++) { | ||||
| 			if (fields[i].name == name) return fields[i].type; | ||||
| 		} | ||||
| 		if (defaultValue !== null) return defaultValue; | ||||
| 		throw new Error('Unknown field: ' + name); | ||||
| 	} | ||||
|  | ||||
| @@ -238,6 +239,9 @@ class BaseModel { | ||||
| 			o.updated_time = timeNow; | ||||
| 		} | ||||
|  | ||||
| 		// The purpose of user_updated_time is to allow the user to manually set the time of a note (in which case | ||||
| 		// options.autoTimestamp will be `false`). However note that if the item is later changed, this timestamp | ||||
| 		// will be set again to the current time. | ||||
| 		if (options.autoTimestamp && this.hasField('user_updated_time')) { | ||||
| 			o.user_updated_time = timeNow; | ||||
| 		} | ||||
| @@ -278,6 +282,18 @@ class BaseModel { | ||||
| 		options = this.modOptions(options); | ||||
| 		options.isNew = this.isNew(o, options); | ||||
|  | ||||
| 		// Diff saving is an optimisation which takes a new version of the item and an old one, | ||||
| 		// do a diff and save only this diff. IMPORTANT: When using this make sure that both | ||||
| 		// models have been normalised using ItemClass.filter() | ||||
| 		const isDiffSaving = options && options.oldItem && !options.isNew; | ||||
|  | ||||
| 		if (isDiffSaving) { | ||||
| 			const newObject = BaseModel.diffObjects(options.oldItem, o); | ||||
| 			newObject.type_ = o.type_; | ||||
| 			newObject.id = o.id; | ||||
| 			o = newObject; | ||||
| 		} | ||||
|  | ||||
| 		o = this.filter(o); | ||||
|  | ||||
| 		let queries = []; | ||||
| @@ -298,6 +314,15 @@ class BaseModel { | ||||
| 			if ('user_updated_time' in saveQuery.modObject) o.user_updated_time = saveQuery.modObject.user_updated_time; | ||||
| 			if ('user_created_time' in saveQuery.modObject) o.user_created_time = saveQuery.modObject.user_created_time; | ||||
| 			o = this.addModelMd(o); | ||||
|  | ||||
| 			if (isDiffSaving) { | ||||
| 				for (let n in options.oldItem) { | ||||
| 					if (!options.oldItem.hasOwnProperty(n)) continue; | ||||
| 					if (n in o) continue; | ||||
| 					o[n] = options.oldItem[n]; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return this.filter(o); | ||||
| 		}).catch((error) => { | ||||
| 			Log.error('Cannot save model', error); | ||||
| @@ -328,9 +353,18 @@ class BaseModel { | ||||
| 		let output = Object.assign({}, model); | ||||
| 		for (let n in output) { | ||||
| 			if (!output.hasOwnProperty(n)) continue; | ||||
|  | ||||
| 			// The SQLite database doesn't have booleans so cast everything to int | ||||
| 			if (output[n] === true) output[n] = 1; | ||||
| 			if (output[n] === false) output[n] = 0; | ||||
| 			if (output[n] === true) { | ||||
| 				output[n] = 1; | ||||
| 			} else if (output[n] === false) { | ||||
| 				output[n] = 0;  | ||||
| 			} else { | ||||
| 				const t = this.fieldType(n, Database.TYPE_UNKNOWN); | ||||
| 				if (t === Database.TYPE_INT) { | ||||
| 					output[n] = !n ? 0 : parseInt(output[n], 10); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		return output; | ||||
|   | ||||
| @@ -308,6 +308,7 @@ class Database { | ||||
|  | ||||
| } | ||||
|  | ||||
| Database.TYPE_UNKNOWN = 0; | ||||
| Database.TYPE_INT = 1; | ||||
| Database.TYPE_TEXT = 2; | ||||
| Database.TYPE_NUMERIC = 3; | ||||
|   | ||||
| @@ -410,14 +410,18 @@ class Synchronizer { | ||||
| 					let action = null; | ||||
| 					let reason = ''; | ||||
| 					let local = await BaseItem.loadItemByPath(path); | ||||
| 					let ItemClass = null; | ||||
| 					let content = null; | ||||
| 					if (!local) { | ||||
| 						if (remote.isDeleted !== true) { | ||||
| 							action = 'createLocal'; | ||||
| 							reason = 'remote exists but local does not'; | ||||
| 							content = await loadContent(); | ||||
| 							ItemClass = content ? BaseItem.itemClass(content) : null; | ||||
| 						} | ||||
| 					} else { | ||||
| 						ItemClass = BaseItem.itemClass(local); | ||||
| 						local = ItemClass.filter(local); | ||||
| 						if (remote.isDeleted) { | ||||
| 							action = 'deleteLocal'; | ||||
| 							reason = 'remote has been deleted'; | ||||
| @@ -440,7 +444,6 @@ class Synchronizer { | ||||
| 							this.logger().warn('Remote has been deleted between now and the list() call? In that case it will be handled during the next sync: ' + path); | ||||
| 							continue; | ||||
| 						} | ||||
| 						let ItemClass = BaseItem.itemClass(content); | ||||
| 						content = ItemClass.filter(content); | ||||
|  | ||||
| 						// 2017-12-03: This was added because the new user_updated_time and user_created_time properties were added | ||||
| @@ -451,34 +454,20 @@ class Synchronizer { | ||||
| 						if (!content.user_updated_time) content.user_updated_time = content.updated_time; | ||||
| 						if (!content.user_created_time) content.user_created_time = content.created_time; | ||||
|  | ||||
| 						let newContent = null; | ||||
|  | ||||
| 						if (action === 'createLocal') { | ||||
| 							newContent = Object.assign({}, content);							 | ||||
| 						} else if (action === 'updateLocal') { | ||||
| 							newContent = BaseModel.diffObjects(local, content); | ||||
| 							newContent.type_ = content.type_; | ||||
| 							newContent.id = content.id; | ||||
| 						} else { | ||||
| 							throw new Error('Unknown action: ' + action); | ||||
| 						} | ||||
|  | ||||
| 						let options = { | ||||
| 							autoTimestamp: false, | ||||
| 							nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, newContent, time.unixMs()), | ||||
| 							nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, content, time.unixMs()), | ||||
| 						}; | ||||
| 						if (action == 'createLocal') options.isNew = true; | ||||
| 						if (action == 'updateLocal') options.oldItem = local; | ||||
|  | ||||
| 						if (newContent.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal') { | ||||
| 							let localResourceContentPath = Resource.fullPath(newContent); | ||||
| 							let remoteResourceContentPath = this.resourceDirName_ + '/' + newContent.id; | ||||
| 						if (content.type_ == BaseModel.TYPE_RESOURCE && action == 'createLocal') { | ||||
| 							let localResourceContentPath = Resource.fullPath(content); | ||||
| 							let remoteResourceContentPath = this.resourceDirName_ + '/' + content.id; | ||||
| 							await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: 'file' }); | ||||
| 						} | ||||
|  | ||||
| 						// if (!newContent.user_updated_time) newContent.user_updated_time = newContent.updated_time; | ||||
| 						// if (!newContent.user_created_time) newContent.user_created_time = newContent.created_time; | ||||
|  | ||||
| 						await ItemClass.save(newContent, options); | ||||
| 						await ItemClass.save(content, options); | ||||
|  | ||||
| 					} else if (action == 'deleteLocal') { | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user