mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 16:06:32 +00:00
fix: ask before signing out
This commit is contained in:
@@ -1129,6 +1129,11 @@
|
||||
"remove_account_message": "Von {account} auf diesem Gerät abmelden. Ihre E-Mails bleiben auf dem Server.",
|
||||
"remove_account_title": "Konto entfernen?",
|
||||
"show_unread": "Ungelesene anzeigen",
|
||||
"sign_out_all_confirm_message": "Alle Konten werden von diesem Gerät entfernt. Sie können sich jederzeit wieder anmelden.",
|
||||
"sign_out_all_confirm_title": "Von allen Konten abmelden?",
|
||||
"sign_out_confirm_message": "Das Konto wird von diesem Gerät entfernt. Sie können sich jederzeit wieder anmelden.",
|
||||
"sign_out_confirm_title": "Abmelden?",
|
||||
"sign_out_confirm_title_account": "Von {account} abmelden?",
|
||||
"tab_unavailable": "{name} (nicht verfügbar)"
|
||||
},
|
||||
"unified_mailbox": {
|
||||
|
||||
@@ -1148,6 +1148,11 @@
|
||||
"remove_account_message": "Sign out of {account} on this device. Your mail stays on the server.",
|
||||
"remove_account_title": "Remove account?",
|
||||
"show_unread": "Show unread",
|
||||
"sign_out_all_confirm_message": "Every account is removed from this device. You can sign in again at any time.",
|
||||
"sign_out_all_confirm_title": "Sign out of all accounts?",
|
||||
"sign_out_confirm_message": "The account is removed from this device. You can sign in again at any time.",
|
||||
"sign_out_confirm_title": "Sign out?",
|
||||
"sign_out_confirm_title_account": "Sign out of {account}?",
|
||||
"tab_unavailable": "{name} (unavailable)"
|
||||
},
|
||||
"unified_mailbox": {
|
||||
|
||||
@@ -38,6 +38,7 @@ import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import type { RootStackParamList } from '../navigation/types';
|
||||
import type { Mailbox } from '../api/types';
|
||||
import { MODAL_ORIENTATIONS } from '../lib/modal-orientations';
|
||||
import { confirmSignOut } from '../lib/sign-out-confirm';
|
||||
|
||||
const CHEVRON_SLOT = 20;
|
||||
const INDENT_STEP = 12;
|
||||
@@ -919,7 +920,10 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side
|
||||
styles.accountMenuAction,
|
||||
pressed && styles.accountMenuActionPressed,
|
||||
]}
|
||||
onPress={() => { setAccountMenuOpen(false); onClose(); void logout(); }}
|
||||
onPress={() => {
|
||||
setAccountMenuOpen(false);
|
||||
confirmSignOut(t, { account: accountEmailFull || undefined }, () => { onClose(); void logout(); });
|
||||
}}
|
||||
>
|
||||
<LogOut size={16} color={c.textSecondary} />
|
||||
<Text style={styles.accountMenuActionText} numberOfLines={1}>
|
||||
@@ -932,7 +936,10 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side
|
||||
styles.accountMenuAction,
|
||||
pressed && styles.accountMenuActionPressed,
|
||||
]}
|
||||
onPress={() => { setAccountMenuOpen(false); onClose(); void logoutAll(); }}
|
||||
onPress={() => {
|
||||
setAccountMenuOpen(false);
|
||||
confirmSignOut(t, { all: true }, () => { onClose(); void logoutAll(); });
|
||||
}}
|
||||
>
|
||||
<LogOut size={16} color={c.error} />
|
||||
<Text style={[styles.accountMenuActionText, { color: c.error }]}>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
const alert = vi.fn();
|
||||
vi.mock('react-native', () => ({ Alert: { alert: (...a: unknown[]) => alert(...a) } }));
|
||||
|
||||
import { confirmSignOut } from '../sign-out-confirm';
|
||||
|
||||
const t = (_key: string, fallback: string, params?: Record<string, string | number>) =>
|
||||
fallback.replace(/\{(\w+)\}/g, (_, k) => String(params?.[k] ?? ''));
|
||||
|
||||
describe('confirmSignOut', () => {
|
||||
it('asks first and signs out only from the destructive button', () => {
|
||||
const onConfirm = vi.fn();
|
||||
confirmSignOut(t, { account: 'usera@example.org' }, onConfirm);
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
const [title, , buttons] = alert.mock.calls[0] as [string, string, Array<{ style?: string; onPress?: () => void }>];
|
||||
expect(title).toBe('Sign out of usera@example.org?');
|
||||
buttons.find((b) => b.style === 'cancel')?.onPress?.();
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
buttons.find((b) => b.style === 'destructive')!.onPress!();
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Alert } from 'react-native';
|
||||
|
||||
type Translate = (key: string, fallback: string, params?: Record<string, string | number>) => string;
|
||||
|
||||
/**
|
||||
* Asks before signing out: it drops the account's cached mail and push
|
||||
* registration on this device, and a stray tap in the account menu or at the
|
||||
* end of Settings did it straight away.
|
||||
*/
|
||||
export function confirmSignOut(
|
||||
t: Translate,
|
||||
{ account, all = false }: { account?: string; all?: boolean },
|
||||
onConfirm: () => void,
|
||||
): void {
|
||||
const title = all
|
||||
? t('sidebar.sign_out_all_confirm_title', 'Sign out of all accounts?')
|
||||
: account
|
||||
? t('sidebar.sign_out_confirm_title_account', 'Sign out of {account}?', { account })
|
||||
: t('sidebar.sign_out_confirm_title', 'Sign out?');
|
||||
const message = all
|
||||
? t('sidebar.sign_out_all_confirm_message', 'Every account is removed from this device. You can sign in again at any time.')
|
||||
: t('sidebar.sign_out_confirm_message', 'The account is removed from this device. You can sign in again at any time.');
|
||||
Alert.alert(title, message, [
|
||||
{ text: t('common.cancel', 'Cancel'), style: 'cancel' },
|
||||
{
|
||||
text: all ? t('sidebar.sign_out_all', 'Sign out of all accounts') : t('sidebar.sign_out', 'Sign out'),
|
||||
style: 'destructive',
|
||||
onPress: onConfirm,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView, Pressable, TextInput, Keyboard } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useLayout } from '../lib/use-layout';
|
||||
import { confirmSignOut } from '../lib/sign-out-confirm';
|
||||
import {
|
||||
ArrowLeft, LogOut, Settings, ChevronRight, Search, X,
|
||||
Palette, User, Shield, UserPen, Palmtree, Calendar,
|
||||
@@ -506,7 +507,8 @@ export default function SettingsScreen({ onLogout, onBack, onTabSelect }: Settin
|
||||
{/* Logout - matches webmail: border-t, destructive text, icon + label */}
|
||||
<View style={styles.logoutSection}>
|
||||
<Pressable
|
||||
onPress={onLogout}
|
||||
onPress={() => { if (onLogout) confirmSignOut(t, {}, onLogout); }}
|
||||
accessibilityRole="button"
|
||||
style={({ pressed }) => [styles.logoutBtn, pressed && styles.logoutBtnPressed]}
|
||||
>
|
||||
<LogOut size={16} color={c.error} />
|
||||
|
||||
Reference in New Issue
Block a user