wrapped up first pass of sealing wizard

This commit is contained in:
Roland Osborne 2023-08-29 12:04:47 -07:00
parent 8491171b82
commit f4ad6c9a9f
5 changed files with 243 additions and 47 deletions

View File

@ -23,6 +23,8 @@ const LightColors = {
primaryButtonText: '#ffffff',
disabledButton: '#dddddd',
disabledButtonText: '#aaaaaa',
dangerButton: '#ff5555',
dangerButtonText: '#ffffff',
inputBase: '#eeeeee',
inputPlaceholder: '#888888',
inputText: '#444444',
@ -64,6 +66,8 @@ const DarkColors = {
primaryButtonText: '#ffffff',
disabledButton: '#aaaaaa',
disabledButtonText: '#888888',
dangerButton: '#ff888888',
dangerButtonText: '#ffffff',
inputBase: '#ffffff',
inputPlaceholder: '#888888',
inputText: '#444444',
@ -109,6 +113,8 @@ export const Colors = {
primaryButtonText: getColor('primaryButtonText'),
disabledButton: getColor('disabledButton'),
disabledButtonText: getColor('disabledButtonText'),
dangerButton: getColor('dangerButton'),
dangerButtonText: getColor('dangerButtonText'),
inputBase: getColor('inputBase'),
inputPlaceholder: getColor('inputPlaceholder'),
inputText: getColor('inputText'),

View File

@ -4,7 +4,7 @@ const Strings = [
{
visibleRegistry: 'Visible in Registry',
edit: 'Edit',
enableNotifications: 'Enable Notifications',
enableNotifications: 'Push Notifications',
sealedTopics: 'Sealed Topics',
colorMode: 'Color Mode',
hourMode: 'Hour',
@ -27,8 +27,8 @@ const Strings = [
monthEnd: 'dd/mm',
sealUnset: 'Generate a key to enable end-to-end encrypted topics.',
sealUnlocked: 'Disabling the sealing key will remove access from all end-to-end encrypted topics until the key is unlocked again.',
sealLocked: 'Unlock the sealing key to enable end-to-end encrypted topics on this device.',
sealUnlocked: 'Disabling the sealing key will prevent access to all end-to-end encrypted topics from this device until the key is unlocked again.',
sealLocked: 'Unlock the sealing key to support end-to-end encrypted topics on this device.',
sealDelete: 'Deleting the sealing key will premanently remove access to any existing end-to-end encrypted topics for ALL of your devices.',
password: 'Password',
confirmPassword: 'Confirm Password',
@ -46,11 +46,12 @@ const Strings = [
changePassword: 'Change sealing key password.',
update: 'Update',
changeKey: 'Change Key Password',
delayMessage: 'Key generation can take several minutes.',
},
{
visibleRegistry: 'Visible dans le Registre',
edit: 'Modifier',
enableNotifications: 'Activer les Notifications',
enableNotifications: 'Notifications Push',
sealedTopics: 'Sujets Sécurisés',
colorMode: 'Mode de Couleur',
hourMode: 'Heure',
@ -92,11 +93,12 @@ const Strings = [
changePassword: 'Changez le mot de passe de la clé de sécurité.',
update: 'Mise à jour',
changeKey: 'Changer le mot de passe clé',
delayMessage: 'La génération de clé peut prendre plusieurs minutes.',
},
{
visibleRegistry: 'Visible en el Registro',
edit: 'Editar',
enableNotifications: 'Permitir Notificaciones',
enableNotifications: 'Notificaciones Push',
sealedTopics: 'Temas Protegidos',
colorMode: 'Modo de Color',
hourMode: 'Hora',
@ -138,11 +140,12 @@ const Strings = [
changePassword: 'Cambiar la contraseña de la clave de seguridad.',
update: 'Actualizar',
changeKey: 'Cambiar clave Contraseña',
delayMessage: 'La generación de claves puede tardar varios minutos.',
},
{
visibleRegistry: 'Sichtbar in der Registrierung',
edit: 'Bearbeiten',
enableNotifications: 'Benachrichtigungen aktivieren',
enableNotifications: 'Mitteilungen',
sealedTopics: 'Gesicherte Themen',
colorMode: 'Farmodus',
hourMode: 'Stunde',
@ -184,6 +187,7 @@ const Strings = [
changePassword: 'Ändern Sie das Passwort des Sicherheitsschlüssels',
update: 'Aktualisieren',
changeKey: 'Schlüsselpasswort ändern',
delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.',
}
];

View File

@ -1,3 +1,4 @@
import { useState } from 'react';
import { ActivityIndicator, KeyboardAvoidingView, Modal, ScrollView, View, Switch, Text, TextInput, TouchableOpacity, Alert } from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { styles } from './Settings.styled';
@ -7,21 +8,26 @@ import Colors from 'constants/Colors';
export function Settings() {
const [ busy, setBusy ] = useState(false);
const { state, actions } = useSettings();
const saveSeal = async () => {
try {
await actions.saveSeal();
actions.hideEditSeal();
const sealAction = async (method, name) => {
if (!busy) {
try {
setBusy(true);
await method();
actions.hideEditSeal();
}
catch (err) {
console.log(err);
Alert.alert(
`Failed to ${name} Key`,
'Please try again.',
);
}
setBusy(false);
}
catch (err) {
console.log(err);
Alert.alert(
'Failed to Update Topic Sealing',
'Please try again.',
)
}
}
};
return (
<ScrollView style={styles.content}>
@ -33,10 +39,8 @@ export function Settings() {
<View style={styles.icon}>
<MatIcons name="bell-outline" size={20} color={Colors.linkText} />
</View>
<View style={styles.option}>
<View style={styles.optionControl}>
<Text style={styles.optionLink}>{ state.strings.enableNotifications }</Text>
</View>
<View style={styles.control}>
<Switch style={styles.notifications} trackColor={styles.track}/>
</View>
</TouchableOpacity>
@ -45,7 +49,7 @@ export function Settings() {
<View style={styles.icon}>
<MatIcons name="lock-outline" size={20} color={Colors.linkText} />
</View>
<View style={styles.option}>
<View style={styles.optionControl}>
{ state.sealEnabled && (
<Text style={styles.optionLink}>{ state.strings.manageTopics }</Text>
)}
@ -53,7 +57,6 @@ export function Settings() {
<Text style={styles.optionLink}>{ state.strings.enableTopics }</Text>
)}
</View>
<View style={styles.control} />
</TouchableOpacity>
</View>
@ -200,11 +203,12 @@ export function Settings() {
<KeyboardAvoidingView behavior="height" style={styles.modalOverlay}>
<View style={styles.modalContainer}>
<View style={styles.modalClose}>
<TouchableOpacity activeOpacity={1} onPress={actions.hideEditSeal}>
<TouchableOpacity style={styles.closeButton} activeOpacity={1} onPress={actions.hideEditSeal}>
<MatIcons name="close" size={20} color={Colors.descriptionText} />
</TouchableOpacity>
</View>
<Text style={styles.modalHeader}>{ state.strings.sealedTopics }</Text>
<ActivityIndicator style={styles.modalBusy} animating={busy} color={Colors.primary} />
{ !state.sealEnabled && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnset }</Text>
@ -239,7 +243,7 @@ export function Settings() {
)}
</View>
{ state.sealPassword === state.sealConfirm && state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={actions.generateKey}>
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.generateKey, 'Generate')}>
<Text style={styles.enabledButtonText}>{ state.strings.generate }</Text>
</TouchableOpacity>
)}
@ -248,14 +252,135 @@ export function Settings() {
<Text style={styles.disabledButtonText}>{ state.strings.generate }</Text>
</View>
)}
<Text style={styles.delayMessage}>{ state.strings.delayMessage }</Text>
</>
)}
{ state.sealEnabled && !state.sealUnlocked && (
<Text style={styles.modalDescription}>{ state.strings.sealLocked }</Text>
{ state.sealEnabled && !state.sealUnlocked && !state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealLocked }</Text>
<View style={styles.modalInput}>
<TextInput style={styles.inputText} value={state.sealPassword} onChangeText={actions.setSealPassword}
autoCapitalize={'none'} secureTextEntry={state.hidePassword} placeholder={state.strings.password}
placeholderTextColor={Colors.inputPlaceholder} />
{ state.hidePassword && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.showPassword}>
<MatIcons name="eye-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
{ !state.hidePassword && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.hidePassword}>
<MatIcons name="eye-off-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
</View>
{ state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.unlockKey, 'Unlock')}>
<Text style={styles.enabledButtonText}>{ state.strings.unlock }</Text>
</TouchableOpacity>
)}
{ !state.sealPassword && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.unlock }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && (
{ state.sealEnabled && state.sealUnlocked && !state.sealRemove && !state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealUnlocked }</Text>
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.disableKey, 'Disable')}>
<Text style={styles.enabledButtonText}>{ state.strings.disable }</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealUpdate}>
<Text style={styles.modeText}>{ state.strings.changeKey }</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={1} onPress={actions.showSealRemove}>
<Text style={styles.dangerText}>{ state.strings.removeSeal }</Text>
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealRemove && (
<>
<Text style={styles.modalDescription}>{ state.strings.sealDelete }</Text>
<View style={styles.modalInput}>
<TextInput style={styles.inputText} value={state.sealDelete} onChangeText={actions.setSealDelete}
autoCapitalize={'none'} placeholder={state.strings.typeDelete}
placeholderTextColor={Colors.inputPlaceholder} />
</View>
{ state.sealDelete === state.strings.deleteKey && (
<TouchableOpacity style={styles.dangerButton} activeOpacity={1} onPress={() => sealAction(actions.removeKey, 'Remove')}>
<Text style={styles.dangerButtonText}>{ state.strings.delete }</Text>
</TouchableOpacity>
)}
{ state.sealDelete !== state.strings.deleteKey && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.delete }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealRemove}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
)}
</TouchableOpacity>
</>
)}
{ state.sealEnabled && state.sealUnlocked && state.sealUpdate && (
<>
<Text style={styles.modalDescription}>{ state.strings.changePassword }</Text>
<View style={styles.modalInput}>
<TextInput style={styles.inputText} value={state.sealPassword} onChangeText={actions.setSealPassword}
autoCapitalize={'none'} secureTextEntry={state.hidePassword} placeholder={state.strings.password}
placeholderTextColor={Colors.inputPlaceholder} />
{ state.hidePassword && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.showPassword}>
<MatIcons name="eye-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
{ !state.hidePassword && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.hidePassword}>
<MatIcons name="eye-off-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
</View>
<View style={styles.modalInput}>
<TextInput style={styles.inputText} value={state.sealConfirm} onChangeText={actions.setSealConfirm}
autoCapitalize={'none'} secureTextEntry={state.hideConfirm} placeholder={state.strings.confirmPassword}
placeholderTextColor={Colors.inputPlaceholder} />
{ state.hideConfirm && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.showConfirm}>
<MatIcons name="eye-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
{ !state.hideConfirm && (
<TouchableOpacity style={styles.inputVisibility} activeOpacity={1} onPress={actions.showConfirm}>
<MatIcons name="eye-off-outline" size={16} color={Colors.inputPlaceholder} />
</TouchableOpacity>
)}
</View>
{ state.sealPassword === state.sealConfirm && state.sealPassword && (
<TouchableOpacity style={styles.enabledButton} activeOpacity={1} onPress={() => sealAction(actions.updateKey, 'Update')}>
<Text style={styles.enabledButtonText}>{ state.strings.update }</Text>
</TouchableOpacity>
)}
{ (state.sealPassword !== state.sealConfirm || !state.sealPassword) && (
<View style={styles.disabledButton}>
<Text style={styles.disabledButtonText}>{ state.strings.update }</Text>
</View>
)}
<TouchableOpacity activeOpacity={1} onPress={actions.hideSealUpdate}>
{ state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.disableSeal }</Text>
)}
{ !state.sealUnlocked && (
<Text style={styles.modeText}>{ state.strings.unlockSeal }</Text>
)}
</TouchableOpacity>
</>
)}
</View>

View File

@ -76,8 +76,8 @@ export const styles = StyleSheet.create({
alignItems: 'center',
},
control: {
flex: 2,
alignItems: 'center',
flex: 4,
display: 'flex',
},
labelText: {
fontSize: 14,
@ -129,7 +129,6 @@ export const styles = StyleSheet.create({
},
modalHeader: {
fontSize: 18,
paddingBottom: 16,
paddingTop: 16,
color: Colors.text,
fontFamily: 'Roboto',
@ -139,17 +138,25 @@ export const styles = StyleSheet.create({
width: '100%',
display: 'flex',
alignItems: 'flex-end',
paddingRight: 8,
paddingTop: 8,
},
closeButton: {
padding: 12,
},
modalBusy: {
padding: 8,
},
modalDescription: {
fontSize: 14,
color: Colors.descriptionText,
paddingTop: 16,
paddingLeft: 16,
paddingRight: 16,
paddingBottom: 16,
},
delayMessage: {
fontSize: 12,
color: Colors.descriptionText,
paddingBottom: 16,
},
modalInput: {
marginRight: 32,
marginLeft: 32,
@ -158,12 +165,21 @@ export const styles = StyleSheet.create({
justifyContent: 'center',
flexDirection: 'row',
},
modeText: {
fontSize: 14,
paddingBottom: 16,
color: Colors.linkText,
fontFamily: 'Roboto',
},
dangerText: {
fontSize: 14,
paddingBottom: 16,
color: Colors.dangerText,
fontFamily: 'Roboto',
},
inputText: {
flex: 1,
paddingLeft: 8,
paddingRight: 8,
paddingTop: 4,
paddingBottom: 4,
padding: 8,
borderRadius: 4,
margin: 4,
backgroundColor: Colors.inputBase,
@ -200,5 +216,19 @@ export const styles = StyleSheet.create({
color: Colors.disabledButtonText,
fontFamily: 'Roboto',
},
dangerButton: {
marginTop: 32,
marginBottom: 16,
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 32,
paddingRight: 32,
borderRadius: 4,
backgroundColor: Colors.dangerButton,
},
dangerButtonText: {
color: Colors.dangerButtonText,
fontFamily: 'Roboto',
},
});

View File

@ -19,8 +19,11 @@ export function useSettings() {
sealUnlocked: false,
sealPassword: null,
sealConfirm: null,
sealDelete: null,
hideConfirm: true,
hidePassword: true,
sealRemove: false,
sealUpdate: false,
});
const updateState = (value) => {
@ -40,26 +43,26 @@ export function useSettings() {
updateState({ sealable, seal, sealKey, sealEnabled, sealUnlocked });
}, [account.state]);
const unlock = async () => {
const sealKey = unlockSeal(state.seal, state.sealUnlock);
const unlockKey = async () => {
const sealKey = unlockSeal(state.seal, state.sealPassword);
await account.actions.unlockAccountSeal(sealKey);
};
const forget = async () => {
const disableKey = async () => {
await account.actions.unlockAccountSeal({});
}
const update = async () => {
const updateKey = async () => {
const updated = updateSeal(state.seal, state.sealKey, state.sealPassword);
await account.actions.setAccountSeal(updated.seal, updated.sealKey);
}
const enable = async () => {
const generateKey = async () => {
const created = await generateSeal(state.sealPassword);
await account.actions.setAccountSeal(created.seal, created.sealKey);
}
const disable = async () => {
const removeKey = async () => {
await account.actions.setAccountSeal({}, {});
}
@ -73,17 +76,33 @@ export function useSettings() {
await profile.actions.setMonthLast(flag);
},
showEditSeal: () => {
updateState({ editSeal: true, sealPassword: null, sealConfirm: null });
updateState({ editSeal: true, sealPassword: null, sealConfirm: null, hidePassword: true, hideConfirm: true,
sealDelete: null, sealRemove: false, sealUpdate: false });
},
hideEditSeal: () => {
updateState({ editSeal: false });
},
showSealRemove: () => {
updateState({ sealRemove: true });
},
hideSealRemove: () => {
updateState({ sealRemove: false });
},
showSealUpdate: () => {
updateState({ sealUpdate: true });
},
hideSealUpdate: () => {
updateState({ sealUpdate: false });
},
setSealPassword: (sealPassword) => {
updateState({ sealPassword });
},
setSealConfirm: (sealConfirm) => {
updateState({ sealConfirm });
},
setSealDelete: (sealDelete) => {
updateState({ sealDelete });
},
showPassword: () => {
updateState({ hidePassword: false });
},
@ -96,8 +115,20 @@ export function useSettings() {
hideConfirm: () => {
updateState({ hideConfirm: true });
},
generateKey: () => {
console.log("GENERATE KEY");
generateKey: async () => {
await generateKey();
},
disableKey: async () => {
await disableKey();
},
unlockKey: async () => {
await unlockKey();
},
updateKey: async () => {
await updateKey();
},
removeKey: async () => {
await removeKey();
},
};