diff --git a/app/mobile/src/constants/Colors.js b/app/mobile/src/constants/Colors.js index eea75364..3a601893 100644 --- a/app/mobile/src/constants/Colors.js +++ b/app/mobile/src/constants/Colors.js @@ -21,6 +21,8 @@ const LightColors = { headerBar: '#eeeeee', primaryButton: '#448866', primaryButtonText: '#ffffff', + cancelButton: '#888888', + cancelButtonText: '#aaaaaa', disabledButton: '#dddddd', disabledButtonText: '#aaaaaa', dangerButton: '#ff5555', @@ -65,6 +67,8 @@ const DarkColors = { headerBar: '#555555', primaryButton: '#448866', primaryButtonText: '#ffffff', + cancelButton: '#888888', + cancelButtonText: '#eeeeee', disabledButton: '#888888', disabledButtonText: '#eeeeee', dangerButton: '#ff5555', @@ -113,6 +117,8 @@ export const Colors = { headerBar: getColor('headerBar'), primaryButton: getColor('primaryButton'), primaryButtonText: getColor('primaryButtonText'), + cancelButton: getColor('cancelButton'), + cancelButtonText: getColor('cancelButtonText'), disabledButton: getColor('disabledButton'), disabledButtonText: getColor('disabledButtonText'), dangerButton: getColor('dangerButton'), diff --git a/app/mobile/src/constants/Strings.js b/app/mobile/src/constants/Strings.js index 3bba26ae..7ac4da85 100644 --- a/app/mobile/src/constants/Strings.js +++ b/app/mobile/src/constants/Strings.js @@ -2,6 +2,7 @@ import { NativeModules, Platform } from 'react-native' const Strings = [ { + // settings screen visibleRegistry: 'Visible in Registry', edit: 'Edit', enableNotifications: 'Push Notifications', @@ -26,6 +27,7 @@ const Strings = [ monthStart: 'mm/dd', monthEnd: 'dd/mm', + // seal wizard sealUnset: 'Generate a key to enable end-to-end encrypted topics.', sealUnlocked: 'Disabling the sealing key will block 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.', @@ -47,6 +49,13 @@ const Strings = [ update: 'Update', changeKey: 'Change Key Password', delayMessage: 'Key generation can take several minutes.', + + // settings modals + cancel: 'Cancel', + confirmLogout: 'Logout', + loggingOut: 'Logging Out', + username: 'Username', + save: 'Save', }, { visibleRegistry: 'Visible dans le Registre', @@ -57,8 +66,8 @@ const Strings = [ hourMode: 'Heure', dateMode: 'Date', language: 'Langue', - logout: 'Se Déconnecter', - changeLogin: 'Changer le Mot de Passe', + logout: 'Déconnecter', + changeLogin: 'Modifier l\'Accès', deleteAccount: 'Supprimer le Compte', contacts: 'Contacts', topics: 'Sujets', @@ -94,6 +103,12 @@ const Strings = [ update: 'Mise à jour', changeKey: 'Changer le mot de passe clé', delayMessage: 'La génération de clé peut prendre plusieurs minutes.', + + cancel: 'Annuler', + confirmLogout: 'Déconnecter', + loggingOut: 'Confirmation de la Déconnexion', + username: 'Nom d\'Utilisateur', + save: 'Engegistrer', }, { visibleRegistry: 'Visible en el Registro', @@ -141,6 +156,12 @@ const Strings = [ update: 'Actualizar', changeKey: 'Cambiar clave Contraseña', delayMessage: 'La generación de claves puede tardar varios minutos.', + + cancel: 'Cancelar', + confirmLogout: 'Cerrar', + loggingOut: 'Confirmando cierre de sesión', + username: 'Nombre de Usuario', + save: 'Guardar', }, { visibleRegistry: 'Sichtbar in der Registrierung', @@ -188,6 +209,12 @@ const Strings = [ update: 'Aktualisieren', changeKey: 'Schlüsselpasswort ändern', delayMessage: 'Die Schlüsselgenerierung kann mehrere Minuten dauern.', + + cancel: 'Stornieren', + confirmlogout: 'Ausloggen', + loggingOut: 'Abmelden bestätigen', + username: 'Nutzername', + save: 'Speichern', } ]; diff --git a/app/mobile/src/session/settings/Settings.jsx b/app/mobile/src/session/settings/Settings.jsx index 4eb08961..8d5067da 100644 --- a/app/mobile/src/session/settings/Settings.jsx +++ b/app/mobile/src/session/settings/Settings.jsx @@ -42,6 +42,41 @@ export function Settings() { } } + const logout = async () => { + if (!busy) { + try { + setBusy(true); + await actions.logout(); + } + catch (err) { + console.log(err); + Alert.alert( + 'Failed to Logout', + 'Please try again.', + ); + } + setBusy(false); + } + } + + const changeLogin = async () => { + if (!busy) { + try { + setBusy(true); + await actions.changeLogin(); + actions.hideLogin(); + } + catch (err) { + console.log(err); + Alert.alert( + 'Failed to Change Login', + 'Please try again.', + ); + } + setBusy(false); + } + } + return ( @@ -134,7 +169,7 @@ export function Settings() { { state.strings.account } - + @@ -144,7 +179,7 @@ export function Settings() { - + @@ -401,6 +436,113 @@ export function Settings() { + + + + { state.strings.loggingOut } + + + { state.strings.cancel } + + + { state.strings.confirmLogout } + + + + + + + + + + + + + + + { state.strings.changeLogin } + + + + + { !state.validated && ( + + + + )} + { state.validated && state.available && ( + + + + )} + { state.validated && !state.available && ( + + + + )} + + + + + { state.hidePassword && ( + + + + )} + { !state.hidePassword && ( + + + + )} + + + + { state.hideConfirm && ( + + + + )} + { !state.hideConfirm && ( + + + + )} + + + + { (state.password !== state.confirm || !state.password || !state.validated || !state.username) && ( + + { state.strings.update } + + )} + { state.password === state.confirm && state.password && state.validated && state.username && ( + + { state.strings.update } + + )} + + + + + ); diff --git a/app/mobile/src/session/settings/Settings.styled.js b/app/mobile/src/session/settings/Settings.styled.js index 8107424b..79f40e64 100644 --- a/app/mobile/src/session/settings/Settings.styled.js +++ b/app/mobile/src/session/settings/Settings.styled.js @@ -203,8 +203,8 @@ export const styles = StyleSheet.create({ fontFamily: 'Roboto', }, disabledButton: { - marginTop: 32, - marginBottom: 16, + marginTop: 8, + marginBottom: 8, paddingTop: 8, paddingBottom: 8, paddingLeft: 32, @@ -230,5 +230,28 @@ export const styles = StyleSheet.create({ color: Colors.dangerButtonText, fontFamily: 'Roboto', }, + cancelButton: { + margin: 8, + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 32, + paddingRight: 32, + borderRadius: 4, + backgroundColor: Colors.cancelButton, + }, + promptButton: { + margin: 8, + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 32, + paddingRight: 32, + borderRadius: 4, + backgroundColor: Colors.primaryButton, + }, + buttons: { + display: 'flex', + flexDirection: 'row', + padding: 8, + }, }); diff --git a/app/mobile/src/session/settings/useSettings.hook.js b/app/mobile/src/session/settings/useSettings.hook.js index 1149d463..71df2d23 100644 --- a/app/mobile/src/session/settings/useSettings.hook.js +++ b/app/mobile/src/session/settings/useSettings.hook.js @@ -2,12 +2,17 @@ import { useState, useEffect, useRef, useContext } from 'react'; import { getLanguageStrings } from 'constants/Strings'; import { ProfileContext } from 'context/ProfileContext'; import { AccountContext } from 'context/AccountContext'; +import { AppContext } from 'context/AppContext'; import { generateSeal, updateSeal, unlockSeal } from 'context/sealUtil'; export function useSettings() { const profile = useContext(ProfileContext); const account = useContext(AccountContext); + const app = useContext(AppContext); + + const debounce = useRef(null); + const checking = useRef(null); const [state, setState] = useState({ strings: getLanguageStrings(), @@ -15,6 +20,14 @@ export function useSettings() { monthLast: false, pushEnabled: null, + login: false, + username: null, + validated: false, + available: true, + password: null, + confirm: null, + + logout: false, editSeal: false, sealEnabled: false, sealUnlocked: false, @@ -33,8 +46,9 @@ export function useSettings() { useEffect(() => { const { timeFull, monthLast } = profile.state; - updateState({ timeFull, monthLast }); - }, [profile.state.timeFull, profile.state.monthLast]); + const handle = profile.state.identity.handle; + updateState({ timeFull, monthLast, handle }); + }, [profile.state]); useEffect(() => { const { seal, sealable, pushEnabled } = account.state.status; @@ -42,7 +56,6 @@ export function useSettings() { const sealEnabled = seal?.publicKey != null; const sealUnlocked = seal?.publicKey === sealKey?.public && sealKey?.private && sealKey?.public; updateState({ sealable, seal, sealKey, sealEnabled, sealUnlocked, pushEnabled }); - }, [account.state]); const unlockKey = async () => { @@ -80,6 +93,42 @@ export function useSettings() { setNotifications: async (flag) => { await account.actions.setNotifications(flag); }, + showLogin: () => { + updateState({ login: true, username: state.handle, password: null, confirm: null, validated: true }); + }, + hideLogin: () => { + updateState({ login: false }); + }, + changeLogin: async () => { + await account.actions.setLogin(state.username, state.password); + }, + setUsername: (username) => { + clearTimeout(debounce.current); + checking.current = username; + updateState({ username, validated: false }); + debounce.current = setTimeout(async () => { + const cur = JSON.parse(JSON.stringify(username)); + const available = await profile.actions.getHandleStatus(cur); + if (checking.current === cur) { + updateState({ available, validated: true }); + } + }, 1000); + }, + setPassword: (password) => { + updateState({ password }); + }, + setConfirm: (confirm) => { + updateState({ confirm }); + }, + logout: async () => { + await app.actions.logout(); + }, + showLogout: () => { + updateState({ logout: true }); + }, + hideLogout: () => { + updateState({ logout: false }); + }, showEditSeal: () => { updateState({ editSeal: true, sealPassword: null, sealConfirm: null, hidePassword: true, hideConfirm: true, sealDelete: null, sealRemove: false, sealUpdate: false });