fix: confirm before triple-tap panic wipe

The settings danger-zone already confirms; the logo triple-tap shortcut
wiped instantly. Gate it behind the same confirmation dialog so an
accidental triple-tap cannot erase identity.
This commit is contained in:
Taksh 2026-07-27 10:07:57 +03:00
parent 1f59e814f9
commit d18af9c569
3 changed files with 35 additions and 3 deletions

View File

@ -18,6 +18,8 @@ final class AppChromeModel: ObservableObject {
@Published var bluetoothAlertMessage = ""
@Published var bluetoothState: CBManagerState = .unknown
@Published var showScreenshotPrivacyWarning = false
/// Confirmation gate for the triple-tap logo panic shortcut (#152).
@Published var showPanicConfirmation = false
private let chatViewModel: ChatViewModel
private let onPanicWipe: () -> Void
@ -111,6 +113,20 @@ final class AppChromeModel: ObservableObject {
prepareForPanic = preparation
}
/// Triple-tap (and any other undiscoverable shortcut) must confirm first.
func requestPanicWipe() {
showPanicConfirmation = true
}
func confirmPanicWipe() {
showPanicConfirmation = false
panicClearAllData()
}
func cancelPanicWipe() {
showPanicConfirmation = false
}
func panicClearAllData() {
prepareForPanic?()
onPanicWipe()

View File

@ -120,7 +120,7 @@ struct AppInfoView: View {
static let dangerTitle = String(localized: "app_info.settings.danger.title", defaultValue: "DANGER ZONE", comment: "Section header (uppercase) for destructive actions in settings")
static let panicButton = String(localized: "app_info.settings.danger.panic_button", defaultValue: "panic wipe", comment: "Button in the settings danger zone that erases all local data after confirmation")
static let panicNote = String(localized: "app_info.settings.danger.panic_note", defaultValue: "erases all messages, keys, and identity. triple-tapping the bitchat/ logo does the same, instantly.", comment: "Caption under the panic wipe button explaining what it does and the triple-tap shortcut")
static let panicNote = String(localized: "app_info.settings.danger.panic_note", defaultValue: "erases all messages, keys, and identity. triple-tapping the bitchat/ logo asks for the same confirmation.", comment: "Caption under the panic wipe button explaining what it does and the triple-tap shortcut")
static let panicConfirmTitle = String(localized: "app_info.settings.danger.panic_confirm_title", defaultValue: "wipe all data?", comment: "Title of the confirmation dialog before a panic wipe")
static let panicConfirmAction = String(localized: "app_info.settings.danger.panic_confirm_action", defaultValue: "wipe everything", comment: "Destructive confirmation button that performs the panic wipe")
}

View File

@ -51,14 +51,15 @@ struct ContentHeaderView: View {
// cluster at priority 3 never gives up width.
.layoutPriority(2)
.onTapGesture(count: 3) {
appChromeModel.panicClearAllData()
appChromeModel.requestPanicWipe()
}
.onTapGesture(count: 1) {
appChromeModel.presentAppInfo()
}
// This is the only entry point to App Info, but it reads as
// static text; surface the tap. (The triple-tap panic wipe
// stays undiscoverable on purpose it's destructive.)
// stays undiscoverable on purpose it's destructive but
// still requires confirmation before erasing anything.)
.accessibilityAddTraits(.isButton)
.accessibilityHint(
String(localized: "content.accessibility.app_info_hint", comment: "Accessibility hint on the bitchat/ logo explaining a tap opens app info")
@ -66,6 +67,21 @@ struct ContentHeaderView: View {
.accessibilityAction {
appChromeModel.presentAppInfo()
}
.confirmationDialog(
String(localized: "app_info.settings.danger.panic_confirm_title", defaultValue: "wipe all data?", comment: "Title of the confirmation dialog before a panic wipe"),
isPresented: $appChromeModel.showPanicConfirmation,
titleVisibility: .visible
) {
Button(
String(localized: "app_info.settings.danger.panic_confirm_action", defaultValue: "wipe everything", comment: "Destructive confirmation button that performs the panic wipe"),
role: .destructive
) {
appChromeModel.confirmPanicWipe()
}
Button("common.cancel", role: .cancel) {
appChromeModel.cancelPanicWipe()
}
}
HStack(spacing: 0) {
Text(verbatim: "@")