diff --git a/app/client/mobile/src/constants/Strings.ts b/app/client/mobile/src/constants/Strings.ts index 77c1834a..3531554c 100644 --- a/app/client/mobile/src/constants/Strings.ts +++ b/app/client/mobile/src/constants/Strings.ts @@ -31,6 +31,7 @@ const Strings = [ monthEnd: 'dd/mm', error: 'Error', tryAgain: 'Please try again.', + allDevices: 'Logout of all devices', // seal wizard sealUnset: 'Generate a key to enable end-to-end encrypted topics.', @@ -246,6 +247,7 @@ const Strings = [ monthEnd: 'jj/mm', error: 'Erreur', tryAgain: 'Veuillez réessayer.', + allDevices: 'Déconnexion de tous les appareils', messages: 'Messages', sealUnset: @@ -461,6 +463,7 @@ const Strings = [ monthEnd: 'dd/mm', error: 'Error', tryAgain: 'Inténtalo de nuevo.', + allDevices: 'Cerrar sesión en todos los dispositivos', sealUnset: 'Genere una clave para habilitar temas cifrados de un extremo a otro.', @@ -673,6 +676,7 @@ const Strings = [ monthEnd: 'dd/mm', error: 'Fehler', tryAgain: 'Bitte versuche es erneut.', + allDevices: 'Abmelden aller Geräte', sealUnset: 'Generieren Sie einen Schlüssel, um Ende-zu-Ende-verschlüsselte Themen zu ermöglichen.', @@ -1038,6 +1042,7 @@ const Strings = [ start: 'Crie Uma Conversa', started: 'Iniciar', + allDevices: 'Sair de todos os dispositivos', deleteMessage: 'Apagar Mensagem', blockMessage: 'Bloquear Mensagem', reportMessage: 'Denunciar Mensagem', @@ -1183,6 +1188,7 @@ const Strings = [ guest: 'Гость', leave: 'Покинуть', members: 'Участники', + allDevices: 'Выход из всех устройств', editSubject: 'Редактировать тему', topicMembers: 'Участники темы', leaveTopic: 'Покинуть тему', diff --git a/app/client/mobile/src/context/useAppContext.hook.ts b/app/client/mobile/src/context/useAppContext.hook.ts index 1b0caed0..a57e620c 100644 --- a/app/client/mobile/src/context/useAppContext.hook.ts +++ b/app/client/mobile/src/context/useAppContext.hook.ts @@ -77,12 +77,18 @@ export function useAppContext() { ); updateState({session: login}); }, - accountLogout: async () => { + accountLogout: async (all: boolean) => { if (state.session) { - await sdk.current.logout(state.session, false); + await sdk.current.logout(state.session, all); updateState({session: null}); } }, + accountRemove: async () => { + if (state.session) { + await sdk.current.remove(state.session); + updateState({ session: null }); + } + }, accountCreate: async ( handle: string, password: string, diff --git a/app/client/mobile/src/settings/Settings.styled.ts b/app/client/mobile/src/settings/Settings.styled.ts index b5b40ad3..a2ea1b45 100644 --- a/app/client/mobile/src/settings/Settings.styled.ts +++ b/app/client/mobile/src/settings/Settings.styled.ts @@ -28,6 +28,9 @@ export const styles = StyleSheet.create({ height: '100%', gap: 8, }, + remove: { + backgroundColor: Colors.danger, + }, surface: { padding: 16, }, @@ -211,6 +214,14 @@ export const styles = StyleSheet.create({ fontSize: 16, flexGrow: 1, }, + allControl: { + flexShrink: 1, + flexGrow: 1, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + paddingTop: 16, + }, control: { flexShrink: 1, flexGrow: 1, diff --git a/app/client/mobile/src/settings/Settings.tsx b/app/client/mobile/src/settings/Settings.tsx index b2d67d7a..ff40ac70 100644 --- a/app/client/mobile/src/settings/Settings.tsx +++ b/app/client/mobile/src/settings/Settings.tsx @@ -18,6 +18,10 @@ export function Settings({ showLogout }: { showLogout: boolean }) { const [auth, setAuth] = useState(false); const [clear, setClear] = useState(false); const [change, setChange] = useState(false); + const [logout, setLogout] = useState(false); + const [remove, setRemove] = useState(false); + const [applyingLogout, setApplyingLogout] = useState(false); + const [applyingRemove, setApplyingRemove] = useState(false); const [sealDelete, setSealDelete] = useState(false); const [sealReset, setSealReset] = useState(false); const [sealConfig, setSealConfig] = useState(false); @@ -70,6 +74,38 @@ export function Settings({ showLogout }: { showLogout: boolean }) { } } + const applyRemove = async () => { + if (!applyingRemove) { + setApplyingRemove(true); + try { + await actions.remove(); + setRemove(false); + } + catch (err) { + console.log(err); + setRemove(false); + setAlert(true); + } + setApplyingRemove(false); + } + } + + const applyLogout = async () => { + if (!applyingLogout) { + setApplyingLogout(true); + try { + await actions.logout(); + setLogout(false); + } + catch (err) { + console.log(err); + setLogout(false); + setAlert(true); + } + setApplyingLogout(false); + } + } + const setMfa = async (flag: boolean) => { if (!savingAuth) { setSavingAuth(true); @@ -404,7 +440,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) { - manageSeal}> + setLogout(true)}> {state.strings.logout} @@ -415,7 +451,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) { - manageSeal}> + setRemove(true)}> {state.strings.deleteAccount} @@ -941,6 +977,75 @@ export function Settings({ showLogout }: { showLogout: boolean }) { + setLogout(false)}> + + + + + { state.strings.loggingOut } + setLogout(false)} /> + + + {state.strings.allDevices} + + + + + + + + + + + + setRemove(false)}> + + + + + { state.strings.deleteAccount } + setRemove(false)} /> + + } + onChangeText={value => actions.setRemove(value)} + /> + + + + + + + + + ); diff --git a/app/client/mobile/src/settings/useSettings.hook.ts b/app/client/mobile/src/settings/useSettings.hook.ts index 7739c78f..c0307745 100644 --- a/app/client/mobile/src/settings/useSettings.hook.ts +++ b/app/client/mobile/src/settings/useSettings.hook.ts @@ -19,6 +19,7 @@ export function useSettings() { all: false, password: '', confirm: '', + remove: '', username: '', taken: false, checked: true, @@ -192,6 +193,9 @@ export function useSettings() { logout: async () => { await app.actions.accountLogout(state.all) }, + remove: async () => { + await app.actions.accountRemove() + }, setHandle: (handle: string) => { updateState({ handle, taken: false, checked: false }) clearTimeout(debounce.current) @@ -216,6 +220,9 @@ export function useSettings() { setConfirm: (confirm: string) => { updateState({ confirm }) }, + setRemove: (remove: string) => { + updateState({ remove }); + }, setName: (name: string) => { updateState({ name }) }, diff --git a/app/client/web/src/context/useAppContext.hook.ts b/app/client/web/src/context/useAppContext.hook.ts index c9fb2ad7..a2b01700 100644 --- a/app/client/web/src/context/useAppContext.hook.ts +++ b/app/client/web/src/context/useAppContext.hook.ts @@ -56,9 +56,9 @@ export function useAppContext() { ) updateState({ session: login }) }, - accountLogout: async () => { + accountLogout: async (all: boolean) => { if (state.session) { - await sdk.current.logout(state.session, false) + await sdk.current.logout(state.session, all) updateState({ session: null }) } }, diff --git a/app/sdk/src/api.ts b/app/sdk/src/api.ts index c777c024..1df6fb00 100644 --- a/app/sdk/src/api.ts +++ b/app/sdk/src/api.ts @@ -53,6 +53,7 @@ export interface Settings { unlockSeal(password: string): Promise; updaterSeal(password: string): Promise; forgetSeal(): Promise; + deleteAccount(): Promise; addConfigListener(ev: (config: Config) => void): void; removeConfigListener(ev: (config: Config) => void): void; diff --git a/app/sdk/src/index.ts b/app/sdk/src/index.ts index 4359e3f7..f93cc385 100644 --- a/app/sdk/src/index.ts +++ b/app/sdk/src/index.ts @@ -5,6 +5,7 @@ import { type Logging, ConsoleLogging } from './logging'; import { type Store, OfflineStore, OnlineStore, NoStore } from './store'; import { setLogin } from './net/setLogin'; import { clearLogin } from './net/clearLogin'; +import { removeAccount } from './net/removeAccount'; import { setAccess } from './net/setAccess'; import { addAccount } from './net/addAccount'; import { setAdmin } from './net/setAdmin'; @@ -79,6 +80,24 @@ export class DatabagSDK { return new SessionModule(this.store, this.crypto, this.log, guid, appToken, node, secure, created); } + public async remove(session: Session): Promise { + const sessionModule = session as SessionModule; + const params = await sessionModule.close(); + try { + await this.store.clearLogin(); + } + catch(err) { + this.log.error(err); + } + try { + const { node, secure, token } = params; + await removeAccount(node, secure, token); + } + catch(err) { + this.log.error(err); + } + } + public async logout(session: Session, all: boolean): Promise { const sessionModule = session as SessionModule; const params = await sessionModule.close(); diff --git a/app/sdk/src/net/removeAccount.ts b/app/sdk/src/net/removeAccount.ts new file mode 100644 index 00000000..1fd469d3 --- /dev/null +++ b/app/sdk/src/net/removeAccount.ts @@ -0,0 +1,7 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function removeAccount(node: string, secure: boolean, token: string): Promise { + const endpoint = `http${secure ? 's' : ''}://${node}/profile?agent=${token}`; + const { status } = await fetchWithTimeout(endpoint, { method: 'DELETE' }) + checkResponse(status); +}