2017-12-30 21:57:34 +02:00
const React = require ( 'react' ) ; const Component = React . Component ;
const { TextInput , TouchableOpacity , Linking , View , Switch , Slider , StyleSheet , Text , Button , ScrollView } = require ( 'react-native' ) ;
2017-12-31 15:58:50 +02:00
const EncryptionService = require ( 'lib/services/EncryptionService' ) ;
2017-12-30 21:57:34 +02:00
const { connect } = require ( 'react-redux' ) ;
const { ScreenHeader } = require ( 'lib/components/screen-header.js' ) ;
const { _ } = require ( 'lib/locale.js' ) ;
const { BaseScreenComponent } = require ( 'lib/components/base-screen.js' ) ;
const { Dropdown } = require ( 'lib/components/Dropdown.js' ) ;
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' ) ;
2017-12-31 15:58:50 +02:00
const { dialogs } = require ( 'lib/dialogs.js' ) ;
const DialogBox = require ( 'react-native-dialogbox' ) . default ;
2017-12-30 21:57:34 +02:00
class EncryptionConfigScreenComponent extends BaseScreenComponent {
static navigationOptions ( options ) {
return { header : null } ;
}
constructor ( ) {
super ( ) ;
2017-12-31 15:58:50 +02:00
this . state = {
passwordPromptShow : false ,
passwordPromptAnswer : '' ,
} ;
2017-12-30 21:57:34 +02:00
shared . constructor ( this ) ;
this . styles _ = { } ;
}
componentDidMount ( ) {
this . isMounted _ = true ;
}
componentWillUnmount ( ) {
this . isMounted _ = false ;
}
initState ( props ) {
return shared . initState ( this , props ) ;
}
async refreshStats ( ) {
return shared . refreshStats ( this ) ;
}
componentWillMount ( ) {
this . initState ( this . props ) ;
}
componentWillReceiveProps ( nextProps ) {
this . initState ( nextProps ) ;
}
async checkPasswords ( ) {
return shared . checkPasswords ( this ) ;
}
styles ( ) {
const themeId = this . props . theme ;
const theme = themeStyle ( themeId ) ;
if ( this . styles _ [ themeId ] ) return this . styles _ [ themeId ] ;
this . styles _ = { } ;
let styles = {
titleText : {
flex : 1 ,
fontWeight : 'bold' ,
flexDirection : 'column' ,
fontSize : theme . fontSize ,
paddingTop : 5 ,
paddingBottom : 5 ,
2017-12-31 15:58:50 +02:00
marginTop : theme . marginTop ,
marginBottom : 5 ,
2018-01-06 11:06:41 +02:00
color : theme . color ,
2017-12-30 21:57:34 +02:00
} ,
normalText : {
flex : 1 ,
fontSize : theme . fontSize ,
2018-01-06 11:06:41 +02:00
color : theme . color ,
2017-12-30 21:57:34 +02:00
} ,
container : {
flex : 1 ,
padding : theme . margin ,
} ,
}
this . styles _ [ themeId ] = StyleSheet . create ( styles ) ;
return this . styles _ [ themeId ] ;
}
renderMasterKey ( num , mk ) {
const theme = themeStyle ( this . props . theme ) ;
const onSaveClick = ( ) => {
return shared . onSavePasswordClick ( this , mk ) ;
}
const onPasswordChange = ( text ) => {
return shared . onPasswordChange ( this , mk , text ) ;
}
const password = this . state . passwords [ mk . id ] ? this . state . passwords [ mk . id ] : '' ;
const passwordOk = this . state . passwordChecks [ mk . id ] === true ? '✔' : '❌' ;
const active = this . props . activeMasterKeyId === mk . id ? '✔' : '' ;
return (
< View key = { mk . id } >
2018-01-02 21:17:14 +02:00
< Text style = { this . styles ( ) . titleText } > { _ ( 'Master Key %s' , mk . id . substr ( 0 , 6 ) ) } < / T e x t >
2017-12-30 21:57:34 +02:00
< Text style = { this . styles ( ) . normalText } > { _ ( 'Created: %s' , time . formatMsToLocal ( mk . created _time ) ) } < / T e x t >
< View style = { { flexDirection : 'row' , alignItems : 'center' } } >
2018-01-06 11:06:41 +02:00
< Text style = { { flex : 0 , fontSize : theme . fontSize , marginRight : 10 , color : theme . color } } > { _ ( 'Password:' ) } < / T e x t >
< TextInput secureTextEntry = { true } value = { password } onChangeText = { ( text ) => onPasswordChange ( text ) } style = { { flex : 1 , marginRight : 10 , color : theme . color } } > < / T e x t I n p u t >
< Text style = { { fontSize : theme . fontSize , marginRight : 10 , color : theme . color } } > { passwordOk } < / T e x t >
2017-12-30 21:57:34 +02:00
< Button title = { _ ( 'Save' ) } onPress = { ( ) => onSaveClick ( ) } > < / B u t t o n >
< / V i e w >
< / V i e w >
) ;
}
2017-12-31 15:58:50 +02:00
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 } } >
2018-01-06 11:06:41 +02:00
< Text style = { { fontSize : theme . fontSize , color : theme . color } } > { _ ( '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.' ) } < / T e x t >
< TextInput style = { { margin : 10 , color : theme . color , borderWidth : 1 , borderColor : theme . dividerColor } } secureTextEntry = { true } value = { this . state . passwordPromptAnswer } onChangeText = { ( text ) => { this . setState ( { passwordPromptAnswer : text } ) } } > < / T e x t I n p u t >
2017-12-31 15:58:50 +02:00
< View style = { { flexDirection : 'row' } } >
< View style = { { flex : 1 , marginRight : 10 } } >
< Button title = { _ ( 'Enable' ) } onPress = { ( ) => { onEnableClick ( ) } } > < / B u t t o n >
< / V i e w >
< View style = { { flex : 1 } } >
< Button title = { _ ( 'Cancel' ) } onPress = { ( ) => { this . setState ( { passwordPromptShow : false } ) } } > < / B u t t o n >
< / V i e w >
< / V i e w >
< / V i e w >
) ;
}
2017-12-30 21:57:34 +02:00
render ( ) {
2018-01-05 19:40:57 +02:00
const theme = themeStyle ( this . props . theme ) ;
2017-12-30 21:57:34 +02:00
const masterKeys = this . state . masterKeys ;
const decryptedItemsInfo = this . props . encryptionEnabled ? < Text style = { this . styles ( ) . normalText } > { shared . decryptedStatText ( this ) } < / T e x t > : n u l l ;
const mkComps = [ ] ;
for ( let i = 0 ; i < masterKeys . length ; i ++ ) {
const mk = masterKeys [ i ] ;
mkComps . push ( this . renderMasterKey ( i + 1 , mk ) ) ;
}
2017-12-31 15:58:50 +02:00
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 ( ) } > < / B u t t o n > < / V i e w > : n u l l ;
2017-12-30 21:57:34 +02:00
return (
< View style = { this . rootStyle ( this . props . theme ) . root } >
2017-12-31 15:58:50 +02:00
< ScreenHeader title = { _ ( 'Encryption Config' ) } / >
2017-12-30 21:57:34 +02:00
< ScrollView style = { this . styles ( ) . container } >
2018-01-05 19:40:57 +02:00
< View style = { { backgroundColor : theme . warningBackgroundColor , padding : 5 } } >
< Text > Important : This is a * beta * feature . It has been extensively tested and is already in use by some users , but it is possible that some bugs remain . < / T e x t >
< Text > If you wish to you use it , it is recommended that you keep a backup of your data . The simplest way is to regularly backup your notes from the desktop or terminal application . < / T e x t >
< Text > For more information about End - To - End Encryption ( E2EE ) and how it is going to work , please check the documentation : < / T e x t >
< TouchableOpacity onPress = { ( ) => { Linking . openURL ( 'http://joplin.cozic.net/help/e2ee.html' ) } } > < Text > http : //joplin.cozic.net/help/e2ee.html</Text></TouchableOpacity>
< / V i e w >
2017-12-30 21:57:34 +02:00
< Text style = { this . styles ( ) . titleText } > { _ ( 'Status' ) } < / T e x t >
< Text style = { this . styles ( ) . normalText } > { _ ( 'Encryption is: %s' , this . props . encryptionEnabled ? _ ( 'Enabled' ) : _ ( 'Disabled' ) ) } < / T e x t >
{ decryptedItemsInfo }
2017-12-31 15:58:50 +02:00
{ toggleButton }
{ passwordPromptComp }
2017-12-30 21:57:34 +02:00
{ mkComps }
2018-01-02 21:17:14 +02:00
< View style = { { flex : 1 , height : 20 } } > < / V i e w >
2017-12-30 21:57:34 +02:00
< / S c r o l l V i e w >
2017-12-31 15:58:50 +02:00
< DialogBox ref = { dialogbox => { this . dialogbox = dialogbox } } / >
2017-12-30 21:57:34 +02:00
< / V i e w >
) ;
}
}
const EncryptionConfigScreen = connect (
( state ) => {
return {
theme : state . settings . theme ,
masterKeys : state . masterKeys ,
passwords : state . settings [ 'encryption.passwordCache' ] ,
encryptionEnabled : state . settings [ 'encryption.enabled' ] ,
activeMasterKeyId : state . settings [ 'encryption.activeMasterKeyId' ] ,
} ;
}
) ( EncryptionConfigScreenComponent )
module . exports = { EncryptionConfigScreen } ;