Hello everyone. In this article we will create all the application's screens.
To be able to preview the final rendering of each screen, we will install the library we will use for navigation, which will let us have each screen's header.
The navigation library is react-navigation. To install it, enter the following command:
yarn add react-navigationNext, we will create the folder that will contain all the screens:
cd src && mkdir screensTo preview each screen as we go, we need to modify the index.js file at the root of src as follows:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import { StackNavigator } from 'react-navigation'
import SetupScreen from './screens/Setup'
const App = StackNavigator({
Home: {
screen: SetupScreen,
navigationOptions: ({ navigation }: Object) => ({
title: 'Hello title',
headerStyle: { backgroundColor: '#01D88D' },
headerTintColor: 'white',
//header:null if we want to hide the default header
}),
},
})
export default AppHere we use a simple StackNavigator with a single route. Then we will only need to replace the screen with the one we are working on.
Creating the screens
We can now move on to creating the screens.
Setup
The first screen we will create is the application's initialization screen, which appears on first use. It lets the user define the main password that will later be used to unlock the application and access their passwords.
In the screens folder, add the Setup.js file:
/*
* @flow
*/
import React, { Component } from 'react'
import { ScrollView, Text, Button, View } from 'react-native'
import TextField from '../components/TextField'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import { ANDROID_MARGIN, IOS_MARGIN } from '../constants/dimensions'
import { PRIMARY_TEXT, DELETE_COLOR, PRIMARY, WHITE } from '../constants/colors'
import strings from '../locales/strings'
type State = {
password: string,
confirmation: string,
}
export default class SetupScreen extends Component<void, void, State> {
confirmation: Object
state = {
password: '',
confirmation: '',
}
submit() {
const { password, confirmation } = this.state
console.log('====================================')
console.log(`password : ${password} confirmation : ${confirmation}`)
console.log('====================================')
}
render() {
return (
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.instruction}>{strings.instructions}</Text>
<TextField
iconName="lock"
iosOutline
placeholder={strings.password}
secureTextEntry
value={this.state.password}
onChangeText={text => this.setState({ password: text })}
onSubmitEditing={() => {
this.confirmation.focus()
}}
returnKeyType="next"
/>
<TextField
icon="lock"
ref={c => {
this.confirmation = c
}}
placeholder={strings.confirmation}
secureTextEntry
value={this.state.confirmation}
onChangeText={text => this.setState({ confirmation: text })}
/>
<Text style={[styles.instruction, { color: DELETE_COLOR }]}>This text will be replaced when we add redux</Text>
<View style={styles.submit}>
<Button title={strings.save} color={PRIMARY} onPress={() => this.submit()} />
</View>
</ScrollView>
)
}
}
const styles = PlateformStyleSheet({
container: {
flex: 1,
paddingTop: 18,
backgroundColor: WHITE,
},
instruction: {
android: { padding: ANDROID_MARGIN },
ios: { padding: IOS_MARGIN },
color: PRIMARY_TEXT,
textAlign: 'justify',
marginBottom: 18,
},
submit: {
width: 150,
alignSelf: 'center',
},
})Then we will add the missing resources:
fr.js
...
// Setup screen,
setup: 'Initialisation',
instructions:
'Pour utiliser Vault vous devez définir seulement un mot de passe. Vous devez le garder en sécurité car si vous le perdez vous ne pourrez pas restaurer vos données',
password: 'Mot de passe',
confirmation: 'Confirmation du mot de passe',
save: 'Enregistrer',en.js
...
// Setup screen,
setup: 'Initialization',
instructions:
"To use Vault you need to set only one password. You need to keep it safe because if you loose you can't recover your data ",
password: 'Password',
confirmation: 'Password confirmation',
save: 'Save',strings.js
...
setup: I18n.t('setup'),
instructions: I18n.t('instructions'),
password: I18n.t('password'),
confirmation: I18n.t('confirmation'),
save: I18n.t('save'),Unlock
Let's move on to the application unlock screen. Add Unlock.js:
/*
* @flow
*/
import React, { Component } from 'react'
import { ScrollView, View, Image, Button, Text } from 'react-native'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import TextField from '../components/TextField'
import strings from '../locales/strings'
import { ANDROID_MARGIN, IOS_MARGIN } from '../constants/dimensions'
import { PRIMARY, WHITE, DELETE_COLOR } from '../constants/colors'
type State = {
password: string,
}
const image = require('../img/book.png')
export default class SetupScreen extends Component<void, void, State> {
state = {
password: '',
}
submit() {
console.log('====================================')
console.log(`password : ${this.state.password}`)
console.log('====================================')
}
render() {
return (
<ScrollView contentContainerStyle={styles.container}>
<Image source={image} style={styles.logo} />
<TextField
icon="lock"
placeholder={strings.password}
secureTextEntry
value={this.state.password}
onChangeText={text => this.setState({ password: text })}
/>
<Text style={styles.error}>This text will be replaced when we add redux</Text>
<View style={styles.submit}>
<Button title={strings.unlock} color={PRIMARY} onPress={() => this.submit()} />
</View>
</ScrollView>
)
}
}
const styles = PlateformStyleSheet({
container: {
flex: 1,
backgroundColor: WHITE,
justifyContent: 'center',
android: { padding: ANDROID_MARGIN },
ios: { padding: IOS_MARGIN },
},
logo: {
marginBottom: 32,
alignSelf: 'center',
},
error: {
marginVertical: 16,
color: DELETE_COLOR,
textAlign: 'justify',
},
submit: {
width: 150,
alignSelf: 'center',
},
})The resources:
fr.js
...
// Unlock Screen
unlock: 'Déverrouiller',en.js
...
// Unlock Screen
unlock: 'Unlock',strings.js
...
save: I18n.t('unlock'),Passwords
Now we will add the screen that lists all passwords and also allows adding and searching passwords.
Before creating the screen, in the previous article where we created the components, I omitted a few components that we will need to create in this article. The first is the button that lets us add passwords on Android. In the components folder, add the ActionButton.js file and add the following code:
/*
* @flow
*/
import React from 'react'
import { StyleSheet, TouchableNativeFeedback, View } from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import { ACTION_BUTTON } from '../constants/dimensions'
import { WHITE, PRIMARY } from '../constants/colors'
type Props = {
onPress: () => void,
}
const ActionButton = (props: Props) => (
<View style={[styles.container, { elevation: 4 }]}>
<TouchableNativeFeedback
background={TouchableNativeFeedback.SelectableBackgroundBorderless()}
onPress={() => props.onPress()}
>
<View style={styles.container}>
<Icon name="md-add" size={24} color={WHITE} />
</View>
</TouchableNativeFeedback>
</View>
)
ActionButton.defaultProps = {
onPress: () => console.log('onPress'),
}
export default ActionButton
const styles = StyleSheet.create({
container: {
height: ACTION_BUTTON,
width: ACTION_BUTTON,
borderRadius: ACTION_BUTTON,
backgroundColor: PRIMARY,
alignItems: 'center',
justifyContent: 'center',
},
})Then we move on to the screen. In the screens folder, add Passwords.js:
/*
* @flow
*/
import React, { Component } from 'react'
import { View, Platform, StyleSheet } from 'react-native'
import _ from 'lodash'
import SearchBar from '../components/SearchBar'
import PasswordList from '../components/PasswordList'
import ActionButton from '../components/ActionButton'
import { WHITE } from '../constants/colors'
import { ANDROID_MARGIN } from '../constants/dimensions'
import type { Password } from '../types/Password'
type State = {
passwords: Array<Password>,
searchResults: Array<Password>,
searchValue: string,
}
class PassworsScreen extends Component<void, void, State> {
// The state values are temporary and will change when we add redux
state = {
passwords: [
{ name: 'Twitter', key: 'uuid', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-2', icon: 'facebook', color: 'blue' },
{ name: 'Twitter', key: 'uuid-3', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-4', icon: 'facebook', color: 'blue' },
{ name: 'Twitter', key: 'uuid-5', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-6', icon: 'facebook', color: 'blue' },
],
searchValue: '',
searchResults: [
{ name: 'Twitter', key: 'uuid', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-2', icon: 'facebook', color: 'blue' },
{ name: 'Twitter', key: 'uuid-3', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-4', icon: 'facebook', color: 'blue' },
{ name: 'Twitter', key: 'uuid-5', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-6', icon: 'facebook', color: 'blue' },
],
}
showPassword(password: Password) {
console.log('====================================')
console.log(JSON.stringify(password))
console.log('====================================')
}
searchPassword(search: string) {
const result = _.filter(this.state.passwords, data => data.name.toLowerCase().includes(search.toLowerCase()))
this.setState({
searchResults: search.length > 0 ? result : this.state.passwords,
searchValue: search,
})
}
clearSearch() {
this.setState({
searchResults: this.state.passwords,
searchValue: '',
})
}
addNewItem() {
console.log('====================================')
console.log('add new item')
console.log('====================================')
}
openMenu() {
console.log('====================================')
console.log('open drawer')
console.log('====================================')
}
renderActionButton() {
if (Platform.OS === 'android') {
return (
<View style={styles.action}>
<ActionButton onPress={() => this.addNewItem()} />
</View>
)
}
return null
}
render() {
const { searchResults, searchValue } = this.state
return (
<View style={styles.container}>
<SearchBar
onChangeText={text => this.searchPassword(text)}
addItem={() => this.addNewItem()}
onClear={() => this.clearSearch()}
openMenu={() => this.openMenu()}
/>
<PasswordList
data={[]}
onItemPress={item => this.showPassword(item)}
fromSearch={searchValue.length > 0}
emptyOnPress={() => this.addNewItem()}
/>
{this.renderActionButton()}
</View>
)
}
}
export default PassworsScreen
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: WHITE,
},
action: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
marginBottom: ANDROID_MARGIN,
alignItems: 'center',
},
})Here we can clearly see that the screens are only an assembly of the components created earlier. All the states and actions we see in the rest of this article will be modified when we add redux to the project.
As you can see, the PasswordList component has an emptyOnPress property. If you remember, when we created the Empty component that is displayed when the password list is empty, we added a boolean property named fromSearch. This property is meant to define whether the list is empty because a search returned no results, or because the list contains no passwords by default.
When we are in the second case, the component displays a button for adding passwords. However, while creating the components, I forgot to add the button's onPress property to the PasswordList component, so we will do that now.
In the components/PasswordList index.js file, first add the emptyOnPress property to the Flow Props type:
type Props = {
data: Array<Password>,
onItemPress: (item: Password) => void,
fromSearch: boolean,
emptyOnPress: () => void,
}Then add it when using our EmptyComponent in the FlatList's "ListEmptyComponent" property:
<FlatList
numColumns={2}
data={props.data}
renderItem={({ item }) => renderItem(item, () => props.onItemPress(item))}
keyExtractor={item => item.key}
contentInset={defineContentInset()}
automaticallyAdjustContentInsets={false}
ListEmptyComponent={<EmptyComponent fromSearch={props.fromSearch} onPress={props.emptyOnPress} />}
/>Finally, define a default value for this property:
PasswordList.defaultProps = {
data: [
{ name: 'Twitter', key: 'uuid', icon: 'twitter', color: 'red' },
{ name: 'Facebook', key: 'uuid-2', icon: 'facebook', color: 'blue' },
],
onItemPress: item => console.log(`item : ${item.name}`),
fromSearch: false,
emptyOnPress: console.log('empty on press'),
};Settings
The Settings screen is the screen that allows the user to configure the application, for example to define the default length of generated passwords or choose whether passwords should be defined automatically during creation.
Still in the screens folder, add Settings.js:
/*
* @flow
*/
import React, { Component } from 'react'
import { View, Alert } from 'react-native'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import SettingRow from '../components/SettingRow'
import SliderRow from '../components/SliderRow'
import CheckBoxRow from '../components/CheckBoxRow'
import strings from '../locales/strings'
import { IOS_BACKGROUND, WHITE, DELETE_COLOR } from '../constants/colors'
type State = {
passwordLength: number,
autoGeneration: boolean,
}
class SettingsScreen extends Component<void, void, State> {
state = {
passwordLength: 18,
autoGeneration: true,
}
setPasswordLength(length: number) {
this.setState({
passwordLength: length,
})
}
setAutoGeneration(value: boolean) {
this.setState({
autoGeneration: value,
})
}
deleteAllPasswords() {
Alert.alert(strings.clear, strings.clearConfirmation, [
{ text: strings.cancel, style: 'cancel' },
{
text: strings.delete,
onPress: () => console.log('all password delete'),
},
])
}
render() {
return (
<View style={styles.container}>
<SliderRow
label={strings.passwordLength}
onValueChange={value => this.setPasswordLength(value)}
selectedValue={this.state.passwordLength}
/>
<CheckBoxRow
label={strings.passwordAuto}
iconName="star"
isChecked={this.state.autoGeneration}
switchValueChange={value => this.setAutoGeneration(value)}
/>
<View style={styles.itemContainer}>
<SettingRow
label={strings.deleteAllPasswords}
iconName="trash"
iconBackground={DELETE_COLOR}
iosOultine
onPress={() => this.deleteAllPasswords()}
/>
</View>
</View>
)
}
}
export default SettingsScreen
const styles = PlateformStyleSheet({
container: {
flex: 1,
ios: {
backgroundColor: IOS_BACKGROUND,
},
android: {
backgroundColor: WHITE,
},
},
itemContainer: {
ios: {
marginTop: 16,
},
},
})Then the resources:
fr.js
...
passwordAuto: 'Génération automatique',
clear: 'Supprimer les données',
clearConfirmation: "Êtes-vous sûr de vouloir supprimer l'intégralité des mots de passe ?",
passwordLength: 'Longeur du mot de passe',
deleteAllPasswords: 'Supprimer tous les mots de passe',
delete: 'Supprimer',
cancel: 'Annuler',en.js
...
// Settings screen
passwordAuto: 'Automatic generation',
clear: 'Clear data',
clearConfirmation: 'Are you sure you want to delete all passwords ?',
passwordLength: 'Password length',
deleteAllPasswords: 'Delete all passwords',
delete: 'Delete',
cancel: 'Cancel',strings.js
...
passwordAuto: I18n.t('passwordAuto'),
clear: I18n.t('clear'),
clearConfirmation: I18n.t('clearConfirmation'),
passwordLength: I18n.t('passwordLength'),
deleteAllPasswords: I18n.t('deleteAllPasswords'),
delete: I18n.t('delete'),
cancel: I18n.t('cancel'),Synchronization & InitSynchronization
Now let's move on to synchronization, which allows the user to save their encrypted data to Dropbox, restore it, or delete it.
This part is divided into two screens: the initialization screen, which will be displayed the first time the user goes to the synchronization section so they can link their Dropbox account to the application; and the second screen, which lets the user save, retrieve, and delete the backup.
Let's start with the initialization screen. Add the InitSynchronization.js file to the screens folder:
/*
* @flow
*/
import React, { Component } from 'react'
import { Text, ScrollView, View, Image, Button } from 'react-native'
import { WHITE, PRIMARY_TEXT } from '../constants/colors'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import strings from '../locales/strings'
const image = require('../img/paper_plane.png')
export default class InitSynchronizationScreen extends Component {
render() {
return (
<ScrollView contentContainerStyle={styles.container}>
<Image style={styles.image} source={image} />
<Text style={styles.title}>{strings.synchInstruction}</Text>
<View style={styles.login}>
<Button title={strings.login} onPress={() => console.log('log in dropbox')} />
</View>
</ScrollView>
)
}
}
const styles = PlateformStyleSheet({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
ios: {
backgroundColor: WHITE,
},
android: {
backgroundColor: WHITE,
},
},
image: {
marginBottom: 32,
},
title: {
fontWeight: 'bold',
marginTop: 12,
marginBottom: 12,
textAlign: 'center',
color: PRIMARY_TEXT,
},
login: {
width: 150,
alignSelf: 'center',
},
})Next we move on to the Synchronization screen. Add the Synchonization.js file:
/*
* @flow
*/
import React, { Component } from 'react'
import { View, Alert, ActivityIndicator } from 'react-native'
import SettingRow from '../components/SettingRow'
import strings from '../locales/strings'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import { IOS_BACKGROUND, WHITE, DELETE_COLOR, PRIMARY } from '../constants/colors'
export default class SynchronizationScreen extends Component {
uploadBackup() {
console.log('====================================')
console.log('upload data')
console.log('====================================')
}
downloadBackup() {
console.log('====================================')
console.log('download data')
console.log('====================================')
}
deleteBackup() {
Alert.alert(strings.clear, strings.clearConfirmation, [
{ text: strings.cancel, style: 'cancel' },
{ text: strings.delete, onPress: () => console.log('delete data') },
])
}
render() {
return (
<View style={styles.container}>
<SettingRow
label={strings.publish}
iconName="cloud-upload"
withSeparator
iosOultine
iosSeparator
iconBackground={PRIMARY}
onPress={() => this.uploadBackup()}
/>
<SettingRow
label={strings.pull}
iconName="cloud-download"
iosOultine
iconBackground={PRIMARY}
onPress={() => this.downloadBackup()}
/>
<View style={styles.itmCtnr}>
<SettingRow
label={strings.deleteBackup}
iconName="trash"
iconBackground={DELETE_COLOR}
iosOultine
onPress={() => this.deleteBackup()}
/>
</View>
</View>
)
}
}
const styles = PlateformStyleSheet({
container: {
flex: 1,
ios: {
backgroundColor: IOS_BACKGROUND,
},
android: {
backgroundColor: WHITE,
},
},
itmCtnr: {
ios: {
marginTop: 16,
},
},
loaderCtnr: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba( 0, 0, 0, 0.7 )',
justifyContent: 'center',
alignItems: 'center',
},
})And finally the resources:
fr.js
...
// Synchronization screen
synchInstruction:
'Avant de pouvoir synchroniser vos données. Vous devez vous connecter à DropBox.',
login: 'Se connecter',
publish: 'Publier les données',
pull: 'Récupérer les données',
deleteBackup: 'Supprimer la sauvegarde',en.js
...
// Synchronization screen
synchInstruction: 'Before you can synchronize your data. You must log in in DropBox.',
login: 'Log in',
publish: 'Publish data',
pull: 'Download data',
deleteBackup: 'Delete backup',strings.js
...
synchInstruction: I18n.t('synchInstruction'),
login: I18n.t('login'),
publish: I18n.t('publish'),
pull: I18n.t('pull'),
deleteBackup: I18n.t('deleteBackup'),ReadOnly & Edition
Finally, to finish with the screens, we will create a screen for viewing the password record and a screen for creation and editing.
We will start with the ReadOnly.js screen, but before that we will add a component to display the information.
In the components folder, add ReadOnlyRow.js:
/*
* @flow
*/
import React from 'react'
import { View, Text } from 'react-native'
import { ANDROID_MARGIN, IOS_MARGIN } from '../constants/dimensions'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import { PRIMARY_TEXT, IOS_SLIDER_LABEL } from '../constants/colors'
type Props = {
label: string,
value: string,
}
const ReadOnlyRow = (props: Props) => (
<View>
<Text style={styles.label}>{props.label} :</Text>
<Text style={styles.value}>{props.value}</Text>
</View>
)
ReadOnlyRow.defaultProps = {
label: 'Label',
value: 'value',
}
const styles = PlateformStyleSheet({
container: {
alignItems: 'flex-start',
},
value: {
color: PRIMARY_TEXT,
ios: { padding: IOS_MARGIN },
android: { padding: ANDROID_MARGIN },
fontWeight: 'bold',
fontSize: 16,
},
label: {
color: IOS_SLIDER_LABEL,
ios: { paddingHorizontal: IOS_MARGIN },
android: { paddingHorizontal: ANDROID_MARGIN },
fontSize: 16,
},
})
export default ReadOnlyRowNow, in the screens folder, add ReadOnly.js:
/*
* @flow
*/
import React, { Component } from 'react'
import { View, TouchableOpacity, Text, ScrollView } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import ReadOnlyRow from '../components/ReadOnlyRow'
import strings from '../locales/strings'
import { IOS_BACKGROUND, WHITE, DELETE_COLOR, PRIMARY } from '../constants/colors'
import { ANDROID_MARGIN, IOS_MARGIN } from '../constants/dimensions'
type State = {
key: string,
name: string,
color: string,
password: string,
icon: string,
login: string,
url: string,
}
class ReadOnlyScreen extends Component<void, void, State> {
state = {
key: '9939diz-cd',
name: 'Facebook',
color: '#64A1F6',
password: 'example-password-value',
icon: 'facebook',
login: '[email protected]',
url: 'https://www.facebook.com/',
}
editPassword() {
console.log('====================================')
console.log('copy password')
console.log('====================================')
}
copyPassword() {
console.log('====================================')
console.log('copy password')
console.log('====================================')
}
deletePassword() {
console.log('====================================')
console.log('delete password')
console.log('====================================')
}
render() {
const { icon, color, name, password, login, url } = this.state
return (
<ScrollView style={styles.scrollContent}>
<View style={styles.container}>
<View style={styles.iconCtnr}>
<View style={styles.icon}>
<Icon name={icon} size={35} color={color} />
</View>
</View>
<ReadOnlyRow label={strings.siteName} value={name} />
<ReadOnlyRow label={strings.siteUrl} value={url} />
<ReadOnlyRow label={strings.userName} value={login} />
<ReadOnlyRow label={strings.password} value={password} />
<View style={styles.actionContainer}>
<TouchableOpacity style={styles.action} onPress={() => this.copyPassword()}>
<Text style={[styles.actionLabel, { color: '#647CF6' }]}>{strings.copy}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.action} onPress={() => this.editPassword()}>
<Text style={[styles.actionLabel, { color: PRIMARY }]}>{strings.edit}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.action} onPress={() => this.deletePassword()}>
<Text style={[styles.actionLabel, { color: DELETE_COLOR }]}>{strings.delete}</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
)
}
}
export default ReadOnlyScreen
const styles = PlateformStyleSheet({
scrollContent: {
flex: 1,
backgroundColor: IOS_BACKGROUND,
},
container: {
backgroundColor: WHITE,
marginHorizontal: ANDROID_MARGIN,
marginBottom: ANDROID_MARGIN,
android: {
marginTop: ANDROID_MARGIN,
},
ios: {
marginTop: 42,
},
},
iconCtnr: {
marginBottom: 24,
ios: { marginTop: -32 },
android: { marginTop: ANDROID_MARGIN },
alignSelf: 'center',
},
icon: {
android: {
borderWidth: 1,
borderColor: PRIMARY,
},
height: 75,
width: 75,
borderRadius: 75,
backgroundColor: WHITE,
justifyContent: 'center',
alignItems: 'center',
},
actionContainer: {
marginTop: 32,
},
action: {
ios: {
padding: IOS_MARGIN,
marginBottom: 16,
},
android: {
padding: ANDROID_MARGIN,
marginBottom: 6,
},
alignItems: 'center',
justifyContent: 'center',
},
actionLabel: {
fontSize: 18,
android: { fontWeight: 'bold' },
},
})Then add the Edition.js file:
/*
* @flow
*/
import React, { Component } from 'react'
import { View, Button, TouchableOpacity } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import { PlateformStyleSheet } from '../common/PlatformHelper'
import TextField from '../components/TextField'
import IconPicker from '../components/IconPicker'
import IconModal from '../components/IconModal'
import ColorSelector from '../components/ColorSelector'
import strings from '../locales/strings'
import { IOS_BACKGROUND, WHITE, PRIMARY } from '../constants/colors'
import { ANDROID_MARGIN, IOS_MARGIN } from '../constants/dimensions'
type State = {
key: string,
name: string,
color: string,
password: string,
icon: string,
login: string,
url: string,
modalIsOpen: boolean,
}
class ReadOnlyScreen extends Component<void, void, State> {
loginField: Object
urlField: Object
passwordField: Object
state = {
key: '9939diz-cd',
name: 'Facebook',
color: '#64A1F6',
password: 'example-password-value',
icon: 'facebook',
login: '[email protected]',
url: 'https://www.facebook.com/',
modalIsOpen: false,
}
save() {
console.log('====================================')
console.log('save password')
console.log('====================================')
}
generatePassword() {
console.log('====================================')
console.log('generate password')
console.log('====================================')
}
selectIcon(icon: string) {
this.setState({
icon,
})
}
selectColor(color: string) {
this.setState({ color })
}
toggleModal() {
this.setState({
modalIsOpen: !this.state.modalIsOpen,
})
}
render() {
const { icon, color, name, password, login, url, modalIsOpen } = this.state
return (
<View style={styles.main}>
<KeyboardAwareScrollView style={styles.scrollContent}>
<View style={styles.container}>
<View style={styles.iconCtnr}>
<View style={styles.icon}>
<IconPicker icon={icon} color={color} onPress={() => this.toggleModal()} />
</View>
</View>
<TextField
placeholder={strings.siteName}
value={name}
onSubmitEditing={() => this.urlField.focus()}
onChangeText={text => this.setState({ name: text })}
returnKeyType="next"
/>
<TextField
icon="globe"
placeholder={strings.siteUrl}
value={url}
ref={c => {
this.urlField = c
}}
onSubmitEditing={() => this.loginField.focus()}
onChangeText={text => this.setState({ url: text })}
returnKeyType="next"
/>
<TextField
icon="person"
placeholder={strings.userName}
value={login}
ref={c => {
this.loginField = c
}}
onSubmitEditing={() => this.passwordField.focus()}
onChangeText={text => this.setState({ login: text })}
returnKeyType="next"
/>
<TextField
icon="lock"
placeholder={strings.password}
value={password}
ref={c => {
this.passwordField = c
}}
onChangeText={text => this.setState({ password: text })}
secureTextEntry
/>
<View style={styles.colorSelector}>
<ColorSelector onPress={colorValue => this.selectColor(colorValue)} />
</View>
<View style={styles.actionContainer}>
<Button title={strings.save} color={PRIMARY} onPress={() => this.save()} />
</View>
</View>
</KeyboardAwareScrollView>
<TouchableOpacity style={styles.generate} onPress={() => this.generatePassword()}>
<Icon name="magic" size={32} color={PRIMARY} />
</TouchableOpacity>
<IconModal
onSelectIcon={iconName => this.selectIcon(iconName)}
isOpen={modalIsOpen}
toggleModal={() => this.toggleModal()}
/>
</View>
)
}
}
export default ReadOnlyScreen
const styles = PlateformStyleSheet({
main: {
flex: 1,
},
scrollContent: {
flex: 1,
backgroundColor: IOS_BACKGROUND,
},
generate: {
android: {
top: ANDROID_MARGIN,
padding: ANDROID_MARGIN,
},
ios: {
top: 42,
padding: IOS_MARGIN,
},
height: 60,
width: 60,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
right: ANDROID_MARGIN,
backgroundColor: WHITE,
},
container: {
backgroundColor: WHITE,
marginHorizontal: ANDROID_MARGIN,
marginBottom: ANDROID_MARGIN,
android: {
marginTop: ANDROID_MARGIN,
},
ios: {
marginTop: 42,
},
},
iconCtnr: {
marginBottom: 24,
ios: { marginTop: -32 },
android: { marginTop: ANDROID_MARGIN },
alignSelf: 'center',
},
actionContainer: {
width: 150,
alignSelf: 'center',
marginTop: 32,
android: {
marginBottom: ANDROID_MARGIN,
},
ios: {
marginBottom: IOS_MARGIN,
},
},
colorSelector: {
android: {
padding: ANDROID_MARGIN,
},
ios: {
padding: IOS_MARGIN,
},
},
})And finally the resources:
fr.js
...
// Edition
siteName: 'Nom du site',
siteUrl: 'Url du site',
userName: 'Login',
edit: 'Editer',
copy: 'Copier le mot de passe',en.js
...
siteName: 'Site Name',
siteUrl: 'Site url',
userName: 'Login',
edit: 'Edit',
copy: 'Copy password',strings.js
...
siteName: I18n.t('siteName'),
siteUrl: I18n.t('siteUrl'),
userName: I18n.t('userName'),
edit: I18n.t('edit'),
copy: I18n.t('copy'),We have now finished creating the application's screens.