You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Log screen in RN app
This commit is contained in:
		| @@ -41,6 +41,13 @@ class ScreenHeaderComponent extends Component { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log_press() { | ||||
| 		this.props.dispatch({ | ||||
| 			type: 'Navigation/NAVIGATE', | ||||
| 			routeName: 'Log', | ||||
| 		});	 | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| 		let key = 0; | ||||
| 		let menuOptionComponents = []; | ||||
| @@ -56,10 +63,10 @@ class ScreenHeaderComponent extends Component { | ||||
| 			menuOptionComponents.push(<View key={'menuOption_' + key++} style={styles.divider}/>); | ||||
| 		} | ||||
|  | ||||
| 		// menuOptionComponents.push( | ||||
| 		// 	<MenuOption value={1} key={'menuOption_' + key++}> | ||||
| 		// 		<Text>{_('Configuration')}</Text> | ||||
| 		// 	</MenuOption>); | ||||
| 		menuOptionComponents.push( | ||||
| 			<MenuOption value={() => this.log_press()} key={'menuOption_' + key++}> | ||||
| 				<Text>{_('Log')}</Text> | ||||
| 			</MenuOption>); | ||||
|  | ||||
| 		let title = 'title' in this.props && this.props.title !== null ? this.props.title : _(this.props.navState.routeName); | ||||
|  | ||||
|   | ||||
							
								
								
									
										62
									
								
								ReactNativeClient/lib/components/screens/log.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								ReactNativeClient/lib/components/screens/log.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import React, { Component } from 'react'; | ||||
| import { ListView, View, Text } from 'react-native'; | ||||
| import { connect } from 'react-redux' | ||||
| import { Log } from 'lib/log.js' | ||||
| import { reg } from 'lib/registry.js' | ||||
| import { ScreenHeader } from 'lib/components/screen-header.js'; | ||||
| import { time } from 'lib/time-utils' | ||||
|  | ||||
| class LogScreenComponent extends React.Component { | ||||
| 	 | ||||
| 	static navigationOptions(options) { | ||||
| 		return { header: null }; | ||||
| 	} | ||||
|  | ||||
| 	constructor() { | ||||
| 		super(); | ||||
| 		const ds = new ListView.DataSource({ | ||||
| 			rowHasChanged: (r1, r2) => { return r1 !== r2; } | ||||
| 		}); | ||||
| 		this.state = { | ||||
| 			dataSource: ds, | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	componentWillMount() { | ||||
| 		reg.logger().lastEntries(1000).then((entries) => { | ||||
| 			const newDataSource = this.state.dataSource.cloneWithRows(entries); | ||||
| 			this.setState({ dataSource: newDataSource }); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| 		let renderRow = (item) => { | ||||
| 			return ( | ||||
| 				<View style={{flexDirection: 'row', paddingLeft: 1, paddingRight: 1, paddingTop:0, paddingBottom:0 }}> | ||||
| 					<Text style={{fontFamily: 'monospace', fontSize: 10}}>{time.unixMsToIsoSec(item.timestamp) + ': ' + item.message}</Text> | ||||
| 				</View> | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		// `enableEmptySections` is to fix this warning: https://github.com/FaridSafi/react-native-gifted-listview/issues/39 | ||||
| 		return ( | ||||
| 			<View style={{flex: 1}}> | ||||
| 				<ScreenHeader navState={this.props.navigation.state} /> | ||||
| 				<ListView | ||||
| 					dataSource={this.state.dataSource} | ||||
| 					renderRow={renderRow} | ||||
| 					enableEmptySections={true} | ||||
| 				/> | ||||
| 			</View> | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| const LogScreen = connect( | ||||
| 	(state) => { | ||||
| 		return {}; | ||||
| 	} | ||||
| )(LogScreenComponent) | ||||
|  | ||||
| export { LogScreen }; | ||||
| @@ -52,12 +52,20 @@ class Logger { | ||||
| 			output = object; | ||||
| 		} | ||||
|  | ||||
| 		return output; | ||||
| 		return output;		 | ||||
| 	} | ||||
|  | ||||
| 	objectsToString(...object) { | ||||
| 		let output = []; | ||||
| 		for (let i = 0; i < object.length; i++) { | ||||
| 			output.push('"' + this.objectToString(object[i]) + '"'); | ||||
| 		} | ||||
| 		return output.join(', '); | ||||
| 	} | ||||
|  | ||||
| 	static databaseCreateTableSql() { | ||||
| 		let output = ` | ||||
| 		CREATE TABLE logs ( | ||||
| 		CREATE TABLE IF NOT EXISTS logs ( | ||||
| 			id INTEGER PRIMARY KEY, | ||||
| 			source TEXT, | ||||
| 			level INT NOT NULL, | ||||
| @@ -68,14 +76,25 @@ class Logger { | ||||
| 		return output.split("\n").join(' '); | ||||
| 	} | ||||
|  | ||||
| 	log(level, object) { | ||||
| 	// Only for database at the moment | ||||
| 	async lastEntries(limit = 100) { | ||||
| 		for (let i = 0; i < this.targets_.length; i++) { | ||||
| 			const target = this.targets_[i]; | ||||
| 			if (target.type == 'database') { | ||||
| 				return await target.database.selectAll('SELECT * FROM logs ORDER BY timestamp DESC LIMIT ' + limit); | ||||
| 			} | ||||
| 		} | ||||
| 		return []; | ||||
| 	} | ||||
|  | ||||
| 	log(level, ...object) { | ||||
| 		if (this.level() < level || !this.targets_.length) return; | ||||
|  | ||||
| 		let levelString = ''; | ||||
| 		if (this.level() == Logger.LEVEL_INFO) levelString = '[info] '; | ||||
| 		if (this.level() == Logger.LEVEL_WARN) levelString = '[warn] '; | ||||
| 		if (this.level() == Logger.LEVEL_ERROR) levelString = '[error] '; | ||||
| 		let line = moment().format('YYYY-MM-DD HH:mm:ss') + ': ' + levelString; | ||||
| 		let line = moment().format('YYYY-MM-DD HH:mm:ss') + ': '; | ||||
|  | ||||
| 		if (level == Logger.LEVEL_WARN) levelString += '[warn] '; | ||||
| 		if (level == Logger.LEVEL_ERROR) levelString += '[error] '; | ||||
|  | ||||
| 		for (let i = 0; i < this.targets_.length; i++) { | ||||
| 			let target = this.targets_[i]; | ||||
| @@ -84,27 +103,23 @@ class Logger { | ||||
| 				if (level = Logger.LEVEL_ERROR) fn = 'error'; | ||||
| 				if (level = Logger.LEVEL_WARN) fn = 'warn'; | ||||
| 				if (level = Logger.LEVEL_INFO) fn = 'info'; | ||||
| 				if (typeof object === 'object') { | ||||
| 					console[fn](line, object); | ||||
| 				} else { | ||||
| 					console[fn](line + object); | ||||
| 				} | ||||
| 				console[fn](line + this.objectsToString(...object)); | ||||
| 			} else if (target.type == 'file') { | ||||
| 				let serializedObject = this.objectToString(object); | ||||
| 				let serializedObject = this.objectsToString(...object); | ||||
| 				Logger.fsDriver().appendFileSync(target.path, line + serializedObject + "\n"); | ||||
| 			} else if (target.type == 'vorpal') { | ||||
| 				target.vorpal.log(object); | ||||
| 				target.vorpal.log(...object); | ||||
| 			} else if (target.type == 'database') { | ||||
| 				let msg = this.objectToString(object); | ||||
| 				let msg = this.objectsToString(...object); | ||||
| 				target.database.exec('INSERT INTO logs (`source`, `level`, `message`, `timestamp`) VALUES (?, ?, ?, ?)', [target.source, level, msg, time.unixMs()]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	error(object) { return this.log(Logger.LEVEL_ERROR, object); } | ||||
| 	warn(object)  { return this.log(Logger.LEVEL_WARN, object); } | ||||
| 	info(object)  { return this.log(Logger.LEVEL_INFO, object); } | ||||
| 	debug(object) { return this.log(Logger.LEVEL_DEBUG, object); } | ||||
| 	error(...object) { return this.log(Logger.LEVEL_ERROR, ...object); } | ||||
| 	warn(...object)  { return this.log(Logger.LEVEL_WARN, ...object); } | ||||
| 	info(...object)  { return this.log(Logger.LEVEL_INFO, ...object); } | ||||
| 	debug(...object) { return this.log(Logger.LEVEL_DEBUG, ...object); } | ||||
|  | ||||
| 	static levelStringToId(s) { | ||||
| 		if (s == 'none') return Logger.LEVEL_NONE; | ||||
|   | ||||
							
								
								
									
										9
									
								
								ReactNativeClient/lib/react-logger.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ReactNativeClient/lib/react-logger.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| import { Logger } from 'lib/logger.js'; | ||||
|  | ||||
| class ReactLogger extends Logger { | ||||
|  | ||||
| 	 | ||||
|  | ||||
| } | ||||
|  | ||||
| export { ReactLogger } | ||||
| @@ -18,6 +18,10 @@ let time = { | ||||
| 		return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z'; | ||||
| 	}, | ||||
|  | ||||
| 	unixMsToIsoSec(ms) { | ||||
| 		return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss') + 'Z'; | ||||
| 	}, | ||||
|  | ||||
| 	msleep(ms) { | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			setTimeout(() => { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import { StackNavigator } from 'react-navigation'; | ||||
| import { addNavigationHelpers } from 'react-navigation'; | ||||
| import { shim } from 'lib/shim.js'; | ||||
| import { Log } from 'lib/log.js' | ||||
| import { Logger } from 'lib/logger.js' | ||||
| import { Note } from 'lib/models/note.js' | ||||
| import { Folder } from 'lib/models/folder.js' | ||||
| import { Resource } from 'lib/models/resource.js' | ||||
| @@ -16,6 +17,7 @@ import { NoteTag } from 'lib/models/note-tag.js' | ||||
| import { BaseItem } from 'lib/models/base-item.js' | ||||
| import { BaseModel } from 'lib/base-model.js' | ||||
| import { JoplinDatabase } from 'lib/joplin-database.js' | ||||
| import { Database } from 'lib/database.js' | ||||
| import { ItemList } from 'lib/components/item-list.js' | ||||
| import { NotesScreen } from 'lib/components/screens/notes.js' | ||||
| import { NotesScreenUtils } from 'lib/components/screens/notes-utils.js' | ||||
| @@ -23,6 +25,7 @@ import { NoteScreen } from 'lib/components/screens/note.js' | ||||
| import { FolderScreen } from 'lib/components/screens/folder.js' | ||||
| import { FoldersScreen } from 'lib/components/screens/folders.js' | ||||
| import { LoginScreen } from 'lib/components/screens/login.js' | ||||
| import { LogScreen } from 'lib/components/screens/log.js' | ||||
| import { LoadingScreen } from 'lib/components/screens/loading.js' | ||||
| import { OneDriveLoginScreen } from 'lib/components/screens/onedrive-login.js' | ||||
| import { Setting } from 'lib/models/setting.js' | ||||
| @@ -45,7 +48,7 @@ let defaultState = { | ||||
| }; | ||||
|  | ||||
| const reducer = (state = defaultState, action) => { | ||||
| 	Log.info('Reducer action', action.type); | ||||
| 	reg.logger().info('Reducer action', action.type); | ||||
|  | ||||
| 	let newState = state; | ||||
|  | ||||
| @@ -58,8 +61,8 @@ const reducer = (state = defaultState, action) => { | ||||
| 			const currentRoute = r.length ? r[r.length - 1] : null; | ||||
| 			const currentRouteName = currentRoute ? currentRoute.routeName : ''; | ||||
|  | ||||
| 			Log.info('Current route name', currentRouteName); | ||||
| 			Log.info('New route name', action.routeName); | ||||
| 			reg.logger().info('Current route name', currentRouteName); | ||||
| 			reg.logger().info('New route name', action.routeName); | ||||
|  | ||||
| 			newState = Object.assign({}, state); | ||||
|  | ||||
| @@ -178,7 +181,7 @@ const reducer = (state = defaultState, action) => { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// Log.info('newState.selectedFolderId', newState.selectedFolderId); | ||||
| 	// reg.logger().info('newState.selectedFolderId', newState.selectedFolderId); | ||||
|  | ||||
| 	return newState; | ||||
| } | ||||
| @@ -193,6 +196,7 @@ const AppNavigator = StackNavigator({ | ||||
| 	Login: { screen: LoginScreen }, | ||||
| 	Loading: { screen: LoadingScreen }, | ||||
| 	OneDriveLogin: { screen: OneDriveLoginScreen }, | ||||
| 	Log: { screen: LogScreen }, | ||||
| }); | ||||
|  | ||||
| class AppComponent extends React.Component { | ||||
| @@ -233,6 +237,16 @@ class AppComponent extends React.Component { | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		Setting.setConstant('appId', 'net.cozic.joplin-android'); | ||||
| 		Setting.setConstant('appType', 'mobile'); | ||||
|  | ||||
| 		const logDatabase = new Database(new DatabaseDriverReactNative()); | ||||
| 		await logDatabase.open({ name: 'log.sqlite' }); | ||||
| 		await logDatabase.exec(Logger.databaseCreateTableSql()); | ||||
| 		reg.logger().addTarget('database', { database: logDatabase, source: 'm' }); | ||||
|  | ||||
| 		reg.logger().info('Starting application' + Setting.value('appId')); | ||||
|  | ||||
| 		let db = new JoplinDatabase(new DatabaseDriverReactNative()); | ||||
| 		reg.setDb(db); | ||||
|  | ||||
| @@ -247,8 +261,8 @@ class AppComponent extends React.Component { | ||||
| 		BaseItem.loadClass('NoteTag', NoteTag); | ||||
|  | ||||
| 		try { | ||||
| 			await db.open({ name: '/storage/emulated/0/Download/joplin-48.sqlite' }) | ||||
| 			Log.info('Database is ready.'); | ||||
| 			await db.open({ name: 'joplin-50.sqlite' }) | ||||
| 			reg.logger().info('Database is ready.'); | ||||
|  | ||||
| 			//await db.exec('DELETE FROM notes'); | ||||
| 			//await db.exec('DELETE FROM folders'); | ||||
| @@ -257,14 +271,12 @@ class AppComponent extends React.Component { | ||||
| 			//await db.exec('DELETE FROM resources'); | ||||
| 			//await db.exec('DELETE FROM deleted_items'); | ||||
|  | ||||
| 			Log.info('Loading settings...'); | ||||
| 			reg.logger().info('Loading settings...'); | ||||
| 			await Setting.load(); | ||||
| 			 | ||||
| 			Setting.setConstant('appId', 'net.cozic.joplin-android'); | ||||
| 			Setting.setConstant('appType', 'mobile'); | ||||
| 			Setting.setConstant('resourceDir', RNFetchBlob.fs.dirs.DocumentDir); | ||||
|  | ||||
| 			Log.info('Loading folders...'); | ||||
| 			reg.logger().info('Loading folders...'); | ||||
|  | ||||
| 			let folders = await Folder.all(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user