You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Sync in background for RN
This commit is contained in:
		| @@ -5,11 +5,6 @@ import { _ } from 'lib/locale.js'; | ||||
|  | ||||
| class AppNavComponent extends Component { | ||||
|  | ||||
| 	constructor() { | ||||
| 		super(); | ||||
| 		this.screenCache_ = []; | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| 		if (!this.props.route) throw new Error('Route must not be null'); | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { ListView, Text, TouchableHighlight, Switch, View } from 'react-native'; | ||||
| import { Log } from 'lib/log.js'; | ||||
| import { _ } from 'lib/locale.js'; | ||||
| import { Checkbox } from 'lib/components/checkbox.js'; | ||||
| import { reg } from 'lib/registry.js'; | ||||
| import { Note } from 'lib/models/note.js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
|  | ||||
| @@ -36,6 +37,8 @@ class ItemListComponent extends Component { | ||||
| 	async todoCheckbox_change(itemId, checked) {	 | ||||
| 		let note = await Note.load(itemId); | ||||
| 		await Note.save({ id: note.id, todo_completed: checked ? time.unixMs() : 0 }); | ||||
| 		reg.scheduleSync(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	listView_itemLongPress(itemId) {} | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { ActionButton } from 'lib/components/action-button.js'; | ||||
| import { Folder } from 'lib/models/folder.js' | ||||
| import { BaseModel } from 'lib/base-model.js' | ||||
| import { ScreenHeader } from 'lib/components/screen-header.js'; | ||||
| import { reg } from 'lib/registry.js'; | ||||
| import { NotesScreenUtils } from 'lib/components/screens/notes-utils.js' | ||||
| import { BaseScreenComponent } from 'lib/components/base-screen.js'; | ||||
| import { dialogs } from 'lib/dialogs.js'; | ||||
| @@ -69,6 +70,8 @@ class FolderScreenComponent extends BaseScreenComponent { | ||||
| 				duplicateCheck: true, | ||||
| 				reservedTitleCheck: true, | ||||
| 			}); | ||||
|  | ||||
| 			reg.scheduleSync(); | ||||
| 		} catch (error) { | ||||
| 			dialogs.error(this, _('The folder could not be saved: %s', error.message)); | ||||
| 			return; | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import { time } from 'lib/time-utils.js'; | ||||
| import { Checkbox } from 'lib/components/checkbox.js' | ||||
| import { _ } from 'lib/locale.js'; | ||||
| import marked from 'lib/marked.js'; | ||||
| import { reg } from 'lib/registry.js'; | ||||
| import { BaseScreenComponent } from 'lib/components/base-screen.js'; | ||||
| import { dialogs } from 'lib/dialogs.js'; | ||||
| import { NotesScreenUtils } from 'lib/components/screens/notes-utils.js' | ||||
| @@ -38,7 +39,9 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 			showNoteMetadata: false, | ||||
| 			folder: null, | ||||
| 			lastSavedNote: null, | ||||
| 		} | ||||
| 		}; | ||||
|  | ||||
| 		this.saveButtonHasBeenShown_ = false; | ||||
|  | ||||
| 		this.backHandler = () => { | ||||
| 			if (!this.state.note.id) { | ||||
| @@ -112,11 +115,9 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 	} | ||||
|  | ||||
| 	noteComponent_change(propName, propValue) { | ||||
| 		this.setState((prevState, props) => { | ||||
| 			let note = Object.assign({}, prevState.note); | ||||
| 			note[propName] = propValue; | ||||
| 			return { note: note } | ||||
| 		}); | ||||
| 		let note = Object.assign({}, this.state.note); | ||||
| 		note[propName] = propValue; | ||||
| 		this.setState({ note: note }); | ||||
| 	} | ||||
|  | ||||
| 	async refreshNoteMetadata(force = null) { | ||||
| @@ -155,6 +156,8 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 		}); | ||||
| 		if (isNew) Note.updateGeolocation(note.id); | ||||
| 		this.refreshNoteMetadata(); | ||||
|  | ||||
| 		reg.scheduleSync(); | ||||
| 	} | ||||
|  | ||||
| 	async deleteNote_onPress() { | ||||
| @@ -168,6 +171,8 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
|  | ||||
| 		await Note.delete(note.id); | ||||
| 		await NotesScreenUtils.openNoteList(folderId); | ||||
|  | ||||
| 		reg.scheduleSync(); | ||||
| 	} | ||||
|  | ||||
| 	attachFile_onPress() { | ||||
| @@ -200,6 +205,8 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 				lastSavedNote: Object.assign({}, note), | ||||
| 				note: note, | ||||
| 			}); | ||||
|  | ||||
| 			reg.scheduleSync(); | ||||
| 		} else { | ||||
| 			note[name] = value; | ||||
| 			this.setState({	note: note }); | ||||
| @@ -207,7 +214,8 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 	} | ||||
|  | ||||
| 	async todoCheckbox_change(checked) { | ||||
| 		return this.saveOneProperty('todo_completed', checked ? time.unixMs() : 0); | ||||
| 		await this.saveOneProperty('todo_completed', checked ? time.unixMs() : 0); | ||||
| 		reg.scheduleSync(); | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| @@ -307,9 +315,11 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
|  | ||||
| 		const actionButtonComp = renderActionButton(); | ||||
|  | ||||
| 		let showSaveButton = this.state.mode == 'edit'; | ||||
| 		let showSaveButton = this.state.mode == 'edit' || this.isModified() || this.saveButtonHasBeenShown_; | ||||
| 		let saveButtonDisabled = !this.isModified(); | ||||
|  | ||||
| 		if (showSaveButton) this.saveButtonHasBeenShown_ = true; | ||||
|  | ||||
| 		return ( | ||||
| 			<View style={this.styles().screen}> | ||||
| 				<ScreenHeader | ||||
| @@ -328,6 +338,8 @@ class NoteScreenComponent extends BaseScreenComponent { | ||||
| 								note: note, | ||||
| 								folder: folder, | ||||
| 							}); | ||||
|  | ||||
| 							reg.scheduleSync(); | ||||
| 						} | ||||
| 					}} | ||||
| 					navState={this.props.navigation.state} | ||||
|   | ||||
| @@ -40,7 +40,7 @@ const styles = StyleSheet.create({ | ||||
| 		paddingRight: 20, | ||||
| 		paddingTop: 14, | ||||
| 		paddingBottom: 14, | ||||
| 		marginBottom: 5	, | ||||
| 		marginBottom: 5, | ||||
| 	}, | ||||
| 	folderButtonText: { | ||||
| 		color: "#ffffff", | ||||
| @@ -65,24 +65,30 @@ class SideMenuContentComponent extends Component { | ||||
| 		NotesScreenUtils.openNoteList(folder.id); | ||||
| 	} | ||||
|  | ||||
| 	async synchronizer_progress(report) { | ||||
| 		const sync = await reg.synchronizer(); | ||||
| 		let lines = sync.reportToLines(report); | ||||
| 		this.setState({ syncReportText: lines.join("\n") }); | ||||
| 	} | ||||
|  | ||||
| 	synchronizer_complete() { | ||||
| 		FoldersScreenUtils.refreshFolders(); | ||||
| 	} | ||||
|  | ||||
| 	async componentWillMount() { | ||||
| 		reg.dispatcher().on('synchronizer_progress', this.synchronizer_progress.bind(this)); | ||||
| 		reg.dispatcher().on('synchronizer_complete', this.synchronizer_complete.bind(this)); | ||||
| 	} | ||||
|  | ||||
| 	componentWillUnmount() { | ||||
| 		reg.dispatcher().off('synchronizer_progress', this.synchronizer_progress.bind(this)); | ||||
| 		reg.dispatcher().off('synchronizer_complete', this.synchronizer_complete.bind(this)); | ||||
| 	} | ||||
|  | ||||
| 	async synchronize_press() { | ||||
| 		if (reg.oneDriveApi().auth()) { | ||||
| 			const sync = await reg.synchronizer() | ||||
|  | ||||
| 			let options = { | ||||
| 				onProgress: (report) => { | ||||
| 					let lines = sync.reportToLines(report); | ||||
| 					this.setState({ syncReportText: lines.join("\n") }); | ||||
| 				}, | ||||
| 			}; | ||||
|  | ||||
| 			try { | ||||
| 				sync.start(options).then(async () => { | ||||
| 					await FoldersScreenUtils.refreshFolders(); | ||||
| 				}); | ||||
| 			} catch (error) { | ||||
| 				Log.error(error); | ||||
| 			} | ||||
| 			sync.start(); | ||||
| 		} else { | ||||
| 			this.props.dispatch({ type: 'SIDE_MENU_CLOSE' }); | ||||
| 			 | ||||
| @@ -102,7 +108,7 @@ class SideMenuContentComponent extends Component { | ||||
| 			items.push( | ||||
| 				<TouchableOpacity key={f.id} onPress={() => { this.folder_press(f) }}> | ||||
| 					<View style={styles.folderButton}> | ||||
| 						<Text style={styles.folderButtonText}>{title}</Text> | ||||
| 						<Text numberOfLines={1} style={styles.folderButtonText}>{title}</Text> | ||||
| 					</View> | ||||
| 				</TouchableOpacity> | ||||
| 			); | ||||
|   | ||||
							
								
								
									
										35
									
								
								ReactNativeClient/lib/event-dispatcher.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								ReactNativeClient/lib/event-dispatcher.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| class EventDispatcher { | ||||
|  | ||||
| 	constructor() { | ||||
| 		this.listeners_ = []; | ||||
| 	} | ||||
|  | ||||
| 	dispatch(eventName, event = null) { | ||||
| 		if (!this.listeners_[eventName]) return; | ||||
|  | ||||
| 		let ls = this.listeners_[eventName]; | ||||
| 		for (let i = 0; i < ls.length; i++) { | ||||
| 			ls[i](event); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	on(eventName, callback) { | ||||
| 		if (!this.listeners_[eventName]) this.listeners_[eventName] = []; | ||||
| 		this.listeners_[eventName].push(callback); | ||||
| 	} | ||||
|  | ||||
| 	off(eventName, callback) { | ||||
| 		if (!this.listeners_[eventName]) return; | ||||
|  | ||||
| 		let ls = this.listeners_[eventName]; | ||||
| 		for (let i = 0; i < ls.length; i++) { | ||||
| 			if (ls[i] === callback) { | ||||
| 				ls.splice(i, 1); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { EventDispatcher }; | ||||
| @@ -5,9 +5,16 @@ import { parameters } from 'lib/parameters.js'; | ||||
| import { FileApi } from 'lib/file-api.js'; | ||||
| import { Synchronizer } from 'lib/synchronizer.js'; | ||||
| import { FileApiDriverOneDrive } from 'lib/file-api-driver-onedrive.js'; | ||||
| import { EventDispatcher } from 'lib/event-dispatcher.js'; | ||||
|  | ||||
| const reg = {}; | ||||
|  | ||||
| reg.dispatcher = () => { | ||||
| 	if (this.dispatcher_) return this.dispatcher_; | ||||
| 	this.dispatcher_ = new EventDispatcher(); | ||||
| 	return this.dispatcher_; | ||||
| } | ||||
|  | ||||
| reg.logger = () => { | ||||
| 	if (!reg.logger_) { | ||||
| 		console.warn('Calling logger before it is initialized'); | ||||
| @@ -70,9 +77,37 @@ reg.synchronizer = async () => { | ||||
| 	let fileApi = await reg.fileApi(); | ||||
| 	reg.synchronizer_ = new Synchronizer(reg.db(), fileApi, Setting.value('appType')); | ||||
| 	reg.synchronizer_.setLogger(reg.logger()); | ||||
|  | ||||
| 	reg.synchronizer_.on('progress', (report) => { | ||||
| 		reg.dispatcher().dispatch('synchronizer_progress', report); | ||||
| 	});	 | ||||
|  | ||||
| 	reg.synchronizer_.on('complete', () => { | ||||
| 		reg.dispatcher().dispatch('synchronizer_complete'); | ||||
| 	}); | ||||
|  | ||||
| 	return reg.synchronizer_; | ||||
| } | ||||
|  | ||||
| reg.scheduleSync = async () => { | ||||
| 	if (reg.scheduleSyncId_) return; | ||||
|  | ||||
| 	reg.logger().info('Scheduling sync operation...'); | ||||
|  | ||||
| 	reg.scheduleSyncId_ = setTimeout(async () => { | ||||
| 		reg.scheduleSyncId_ = null; | ||||
| 		reg.logger().info('Doing scheduled sync'); | ||||
|  | ||||
| 		if (!reg.oneDriveApi().auth()) { | ||||
| 			reg.logger().info('Synchronizer is missing credentials - manual sync required to authenticate.'); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		const sync = await reg.synchronizer(); | ||||
| 		sync.start(); | ||||
| 	}, 1000 * 10); | ||||
| } | ||||
|  | ||||
| reg.setDb = (v) => { | ||||
| 	reg.db_ = v; | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { sprintf } from 'sprintf-js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
| import { Logger } from 'lib/logger.js' | ||||
| import { _ } from 'lib/locale.js'; | ||||
| import { EventDispatcher } from 'lib/event-dispatcher.js'; | ||||
| import moment from 'moment'; | ||||
|  | ||||
| class Synchronizer { | ||||
| @@ -23,6 +24,16 @@ class Synchronizer { | ||||
|  | ||||
| 		this.onProgress_ = function(s) {}; | ||||
| 		this.progressReport_ = {}; | ||||
|  | ||||
| 		this.dispatcher_ = new EventDispatcher(); | ||||
| 	} | ||||
|  | ||||
| 	on(eventName, callback) { | ||||
| 		return this.dispatcher_.on(eventName, callback); | ||||
| 	} | ||||
|  | ||||
| 	off(eventName, callback) { | ||||
| 		return this.dispatcher_.off(eventName, callback); | ||||
| 	} | ||||
|  | ||||
| 	state() { | ||||
| @@ -55,6 +66,7 @@ class Synchronizer { | ||||
| 		if (report.deleteRemote) lines.push(_('Deleted remote items: %d.', report.deleteRemote)); | ||||
| 		if (report.state) lines.push(_('State: %s.', report.state.replace(/_/g, ' '))); | ||||
| 		if (report.errors && report.errors.length) lines.push(_('Last error: %s (stacktrace in log).', report.errors[report.errors.length-1].message)); | ||||
| 		if (report.completedTime) lines.push(_('Completed: %s', time.unixMsToLocalDateTime(report.completedTime))); | ||||
| 		return lines; | ||||
| 	} | ||||
|  | ||||
| @@ -88,6 +100,8 @@ class Synchronizer { | ||||
| 		this.progressReport_[action]++; | ||||
| 		this.progressReport_.state = this.state(); | ||||
| 		this.onProgress_(this.progressReport_); | ||||
|  | ||||
| 		this.dispatcher_.dispatch('progress', this.progressReport_); | ||||
| 	} | ||||
|  | ||||
| 	async logSyncSummary(report) { | ||||
| @@ -143,7 +157,7 @@ class Synchronizer { | ||||
| 		const syncTargetId = this.api().driver().syncTargetId(); | ||||
|  | ||||
| 		if (this.state() != 'idle') { | ||||
| 			this.logger().warn('Synchronization is already in progress. State: ' + this.state()); | ||||
| 			this.logger().info('Synchronization is already in progress. State: ' + this.state()); | ||||
| 			return; | ||||
| 		}	 | ||||
|  | ||||
| @@ -430,10 +444,14 @@ class Synchronizer { | ||||
|  | ||||
| 		this.logSyncOperation('finished', null, null, 'Synchronization finished [' + synchronizationId + ']'); | ||||
|  | ||||
| 		this.progressReport_.completedTime = time.unixMs(); | ||||
|  | ||||
| 		await this.logSyncSummary(this.progressReport_); | ||||
|  | ||||
| 		this.onProgress_ = function(s) {}; | ||||
| 		this.progressReport_ = {}; | ||||
|  | ||||
| 		this.dispatcher_.dispatch('complete'); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user