You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Mobile: Done UI for E2EE
This commit is contained in:
		| @@ -101,9 +101,9 @@ class EncryptionConfigScreenComponent extends React.Component { | ||||
|  | ||||
| 			let answer = null; | ||||
| 			if (isEnabled) { | ||||
| 				answer = await dialogs.confirm(_('Disabling encryption means <b>all</b> your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); | ||||
| 				answer = await dialogs.confirm(_('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); | ||||
| 			} else { | ||||
| 				answer = await dialogs.prompt(_('Enabling encryption means <b>all</b> your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the <b>only</b> way to decrypt the data! To enable encryption, please enter your password below.'), '', '', { type: 'password' }); | ||||
| 				answer = await dialogs.prompt(_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.'), '', '', { type: 'password' }); | ||||
| 			} | ||||
|  | ||||
| 			if (!answer) return; | ||||
|   | ||||
| @@ -346,7 +346,7 @@ class ScreenHeaderComponent extends Component { | ||||
|  | ||||
| 			menuOptionComponents.push( | ||||
| 				<MenuOption value={() => this.encryptionConfig_press()} key={'menuOption_encryptionConfig'} style={this.styles().contextMenuItem}> | ||||
| 					<Text style={this.styles().contextMenuItemText}>{_('Encryption Configuration')}</Text> | ||||
| 					<Text style={this.styles().contextMenuItemText}>{_('Encryption Config')}</Text> | ||||
| 				</MenuOption>); | ||||
|  | ||||
| 			menuOptionComponents.push( | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| const React = require('react'); const Component = React.Component; | ||||
| const { TextInput, TouchableOpacity, Linking, View, Switch, Slider, StyleSheet, Text, Button, ScrollView } = require('react-native'); | ||||
| const EncryptionService = require('lib/services/EncryptionService'); | ||||
| const { connect } = require('react-redux'); | ||||
| const { ScreenHeader } = require('lib/components/screen-header.js'); | ||||
| const { _ } = require('lib/locale.js'); | ||||
| @@ -9,6 +10,8 @@ const { themeStyle } = require('lib/components/global-style.js'); | ||||
| const { time } = require('lib/time-utils.js'); | ||||
| const Setting = require('lib/models/Setting.js'); | ||||
| const shared = require('lib/components/shared/encryption-config-shared.js'); | ||||
| const { dialogs } = require('lib/dialogs.js'); | ||||
| const DialogBox = require('react-native-dialogbox').default; | ||||
|  | ||||
| class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 	 | ||||
| @@ -19,6 +22,11 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 	constructor() { | ||||
| 		super(); | ||||
|  | ||||
| 		this.state = { | ||||
| 			passwordPromptShow: false, | ||||
| 			passwordPromptAnswer: '', | ||||
| 		}; | ||||
|  | ||||
| 		shared.constructor(this); | ||||
|  | ||||
| 		this.styles_ = {}; | ||||
| @@ -67,6 +75,8 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 				fontSize: theme.fontSize, | ||||
| 				paddingTop: 5, | ||||
| 				paddingBottom: 5, | ||||
| 				marginTop: theme.marginTop, | ||||
| 				marginBottom: 5, | ||||
| 			}, | ||||
| 			normalText: { | ||||
| 				flex: 1, | ||||
| @@ -103,7 +113,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 				<Text style={this.styles().normalText}>{_('Created: %s', time.formatMsToLocal(mk.created_time))}</Text> | ||||
| 				<View style={{flexDirection: 'row', alignItems: 'center'}}> | ||||
| 					<Text style={{flex:0, fontSize: theme.fontSize, marginRight: 10}}>{_('Password:')}</Text> | ||||
| 					<TextInput value={password} onChangeText={(text) => onPasswordChange(text)} style={{flex:1, marginRight: 10}}></TextInput> | ||||
| 					<TextInput secureTextEntry={true} value={password} onChangeText={(text) => onPasswordChange(text)} style={{flex:1, marginRight: 10}}></TextInput> | ||||
| 					<Text style={{fontSize: theme.fontSize, marginRight: 10}}>{passwordOk}</Text> | ||||
| 					<Button title={_('Save')} onPress={() => onSaveClick()}></Button> | ||||
| 				</View> | ||||
| @@ -111,6 +121,36 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	passwordPromptComponent() { | ||||
| 		const theme = themeStyle(this.props.theme); | ||||
|  | ||||
| 		const onEnableClick = async () => { | ||||
| 			try { | ||||
| 				const password = this.state.passwordPromptAnswer; | ||||
| 				if (!password) throw new Error(_('Password cannot be empty')); | ||||
| 				await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password); | ||||
| 				this.setState({ passwordPromptShow: false }); | ||||
| 			} catch (error) { | ||||
| 				await dialogs.error(this, error.message); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return ( | ||||
| 			<View style={{flex:1, borderColor: theme.dividerColor, borderWidth: 1, padding: 10, marginTop: 10, marginBottom: 10}}> | ||||
| 				<Text style={{fontSize: theme.fontSize}}>{_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.')}</Text> | ||||
| 				<TextInput secureTextEntry={true} value={this.state.passwordPromptAnswer} onChangeText={(text) => { this.setState({ passwordPromptAnswer: text }) }}></TextInput> | ||||
| 				<View style={{flexDirection: 'row'}}> | ||||
| 					<View style={{flex:1 , marginRight:10}} > | ||||
| 						<Button title={_('Enable')} onPress={() => { onEnableClick() }}></Button> | ||||
| 					</View> | ||||
| 					<View style={{flex:1}} > | ||||
| 						<Button title={_('Cancel')} onPress={() => { this.setState({ passwordPromptShow: false}) }}></Button> | ||||
| 					</View> | ||||
| 				</View> | ||||
| 			</View> | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
| 		const masterKeys = this.state.masterKeys; | ||||
| 		const decryptedItemsInfo = this.props.encryptionEnabled ? <Text style={this.styles().normalText}>{shared.decryptedStatText(this)}</Text> : null; | ||||
| @@ -121,15 +161,40 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { | ||||
| 			mkComps.push(this.renderMasterKey(i+1, mk)); | ||||
| 		} | ||||
|  | ||||
| 		const onToggleButtonClick = async () => { | ||||
| 			if (this.props.encryptionEnabled) { | ||||
| 				const ok = await dialogs.confirm(this, _('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); | ||||
| 				if (!ok) return; | ||||
|  | ||||
| 				try { | ||||
| 					await EncryptionService.instance().disableEncryption(); | ||||
| 				} catch (error) { | ||||
| 					await dialogs.error(this, error.message); | ||||
| 				} | ||||
| 			} else { | ||||
| 				this.setState({ | ||||
| 					passwordPromptShow: true, | ||||
| 					passwordPromptAnswer: '', | ||||
| 				}); | ||||
| 				return; | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 		const passwordPromptComp = this.state.passwordPromptShow ? this.passwordPromptComponent() : null; | ||||
| 		const toggleButton = !this.state.passwordPromptShow ? <View style={{marginTop: 10}}><Button title={this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')} onPress={() => onToggleButtonClick()}></Button></View> : null; | ||||
|  | ||||
| 		return ( | ||||
| 			<View style={this.rootStyle(this.props.theme).root}> | ||||
| 				<ScreenHeader title={_('Configuration')}/> | ||||
| 				<ScreenHeader title={_('Encryption Config')}/> | ||||
| 				<ScrollView style={this.styles().container}> | ||||
| 					<Text style={this.styles().titleText}>{_('Status')}</Text> | ||||
| 					<Text style={this.styles().normalText}>{_('Encryption is: %s', this.props.encryptionEnabled ? _('Enabled') : _('Disabled'))}</Text> | ||||
| 					{decryptedItemsInfo} | ||||
| 					{toggleButton} | ||||
| 					{passwordPromptComp} | ||||
| 					{mkComps} | ||||
| 				</ScrollView> | ||||
| 				<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/> | ||||
| 			</View> | ||||
| 		); | ||||
| 	} | ||||
|   | ||||
| @@ -58,7 +58,6 @@ shared.checkPasswords = async function(comp) { | ||||
| 		const ok = password ? await EncryptionService.instance().checkMasterKeyPassword(mk, password) : false; | ||||
| 		passwordChecks[mk.id] = ok; | ||||
| 	} | ||||
| 	console.info(passwordChecks); | ||||
| 	comp.setState({ passwordChecks: passwordChecks }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -214,7 +214,9 @@ class Synchronizer { | ||||
| 					let ItemClass = BaseItem.itemClass(local); | ||||
| 					let path = BaseItem.systemPath(local); | ||||
|  | ||||
| 					// Safety check to avoid infinite loops: | ||||
| 					// Safety check to avoid infinite loops. | ||||
| 					// In fact this error is possible if the item is marked for sync (via sync_time or force_sync) while synchronisation is in | ||||
| 					// progress. In that case exit anyway to be sure we aren't in a loop and the item will be re-synced next time. | ||||
| 					if (donePaths.indexOf(path) > 0) throw new Error(sprintf('Processing a path that has already been done: %s. sync_time was not updated?', path)); | ||||
|  | ||||
| 					let remote = await this.api().stat(path); | ||||
|   | ||||
| @@ -281,8 +281,12 @@ async function initialize(dispatch) { | ||||
|  | ||||
| 	const mainLogger = new Logger(); | ||||
| 	mainLogger.addTarget('database', { database: logDatabase, source: 'm' }); | ||||
| 	if (Setting.value('env') == 'dev') mainLogger.addTarget('console'); | ||||
| 	mainLogger.setLevel(Logger.LEVEL_DEBUG); | ||||
| 	mainLogger.setLevel(Logger.LEVEL_INFO); | ||||
| 	 | ||||
| 	if (Setting.value('env') == 'dev') { | ||||
| 		mainLogger.addTarget('console'); | ||||
| 		mainLogger.setLevel(Logger.LEVEL_DEBUG); | ||||
| 	} | ||||
|  | ||||
| 	reg.setLogger(mainLogger); | ||||
| 	reg.setShowErrorMessageBoxHandler((message) => { alert(message) }); | ||||
| @@ -345,7 +349,7 @@ async function initialize(dispatch) { | ||||
| 		await Setting.load(); | ||||
|  | ||||
| 		if (Setting.value('firstStart')) { | ||||
| 			const locale = NativeModules.I18nManager.localeIdentifier | ||||
| 			let locale = NativeModules.I18nManager.localeIdentifier | ||||
| 			if (!locale) locale = defaultLocale(); | ||||
| 			Setting.setValue('locale', closestSupportedLocale(locale)); | ||||
| 			if (Setting.value('env') === 'dev') Setting.setValue('sync.target', SyncTargetRegistry.nameToId('onedrive_dev')); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user