mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
Secure backup fixes (#2037)
* Fix logout alert title and message * Disable auto correction and capitalization on the recovery confirmation screen * Allow scrolling in the key verification screen for when the key needs to be confirmed and the keyboard is up. * Address PR comments
This commit is contained in:
@@ -370,8 +370,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
guard isLastSession, appSettings.chatBackupEnabled else {
|
guard isLastSession, appSettings.chatBackupEnabled else {
|
||||||
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(),
|
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(),
|
||||||
title: L10n.screenSignoutRecoveryDisabledTitle,
|
title: L10n.screenSignoutConfirmationDialogTitle,
|
||||||
message: L10n.screenSignoutRecoveryDisabledSubtitle,
|
message: L10n.screenSignoutConfirmationDialogContent,
|
||||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||||
self?.actionsSubject.send(.logout)
|
self?.actionsSubject.send(.logout)
|
||||||
})
|
})
|
||||||
|
|||||||
+66
-44
@@ -20,15 +20,31 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SecureBackupRecoveryKeyScreen: View {
|
struct SecureBackupRecoveryKeyScreen: View {
|
||||||
@ObservedObject var context: SecureBackupRecoveryKeyScreenViewModel.Context
|
@ObservedObject var context: SecureBackupRecoveryKeyScreenViewModel.Context
|
||||||
|
@FocusState private var focused
|
||||||
|
private let textFieldIdentifier = "textFieldIdentifier"
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
mainContent
|
ScrollView {
|
||||||
.padding()
|
ScrollViewReader { reader in
|
||||||
.interactiveDismissDisabled()
|
mainContent
|
||||||
.toolbar { toolbar }
|
.padding(16)
|
||||||
.toolbar(.visible, for: .navigationBar)
|
.onChange(of: focused) { newValue in
|
||||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
guard newValue == true else { return }
|
||||||
.alert(item: $context.alertInfo)
|
reader.scrollTo(textFieldIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.safeAreaInset(edge: .bottom) {
|
||||||
|
footer
|
||||||
|
.padding([.horizontal, .bottom], 16)
|
||||||
|
.padding(.top, 8)
|
||||||
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||||
|
}
|
||||||
|
.interactiveDismissDisabled()
|
||||||
|
.toolbar { toolbar }
|
||||||
|
.toolbar(.visible, for: .navigationBar)
|
||||||
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||||
|
.alert(item: $context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@@ -37,7 +53,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
|||||||
switch context.viewState.mode {
|
switch context.viewState.mode {
|
||||||
case .setupRecovery, .changeRecovery:
|
case .setupRecovery, .changeRecovery:
|
||||||
header
|
header
|
||||||
newRecoveryKeySection
|
generateRecoveryKeySection
|
||||||
case .fixRecovery:
|
case .fixRecovery:
|
||||||
header
|
header
|
||||||
confirmRecoveryKeySection
|
confirmRecoveryKeySection
|
||||||
@@ -45,6 +61,38 @@ struct SecureBackupRecoveryKeyScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var footer: some View {
|
||||||
|
switch context.viewState.mode {
|
||||||
|
case .setupRecovery, .changeRecovery:
|
||||||
|
if let recoveryKey = context.viewState.recoveryKey {
|
||||||
|
ShareLink(item: recoveryKey) {
|
||||||
|
Label(L10n.screenRecoveryKeySaveAction, icon: \.download)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
.simultaneousGesture(TapGesture().onEnded { _ in
|
||||||
|
context.send(viewAction: .keySaved)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
context.send(viewAction: .done)
|
||||||
|
} label: {
|
||||||
|
Text(L10n.actionDone)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
.disabled(context.viewState.recoveryKey == nil || context.viewState.doneButtonEnabled == false)
|
||||||
|
case .fixRecovery:
|
||||||
|
Button {
|
||||||
|
context.send(viewAction: .confirmKey)
|
||||||
|
} label: {
|
||||||
|
Text(L10n.actionConfirm)
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.primary))
|
||||||
|
.disabled(context.confirmationRecoveryKey.isEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbar: some ToolbarContent {
|
private var toolbar: some ToolbarContent {
|
||||||
if context.viewState.recoveryKey == nil {
|
if context.viewState.recoveryKey == nil {
|
||||||
@@ -72,31 +120,6 @@ struct SecureBackupRecoveryKeyScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var newRecoveryKeySection: some View {
|
|
||||||
generateRecoveryKeySection
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if let recoveryKey = context.viewState.recoveryKey {
|
|
||||||
ShareLink(item: recoveryKey) {
|
|
||||||
Label(L10n.screenRecoveryKeySaveAction, icon: \.download)
|
|
||||||
}
|
|
||||||
.buttonStyle(.compound(.primary))
|
|
||||||
.simultaneousGesture(TapGesture().onEnded { _ in
|
|
||||||
context.send(viewAction: .keySaved)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
context.send(viewAction: .done)
|
|
||||||
} label: {
|
|
||||||
Text(L10n.actionDone)
|
|
||||||
}
|
|
||||||
.buttonStyle(.compound(.primary))
|
|
||||||
.disabled(context.viewState.recoveryKey == nil || context.viewState.doneButtonEnabled == false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var generateRecoveryKeySection: some View {
|
private var generateRecoveryKeySection: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(L10n.commonRecoveryKey)
|
Text(L10n.commonRecoveryKey)
|
||||||
@@ -155,25 +178,24 @@ struct SecureBackupRecoveryKeyScreen: View {
|
|||||||
.foregroundColor(.compound.textPrimary)
|
.foregroundColor(.compound.textPrimary)
|
||||||
.font(.compound.bodySM)
|
.font(.compound.bodySM)
|
||||||
|
|
||||||
TextField(L10n.screenRecoveryKeyConfirmKeyPlaceholder, text: $context.confirmationRecoveryKey, axis: .vertical)
|
TextField(L10n.screenRecoveryKeyConfirmKeyPlaceholder, text: $context.confirmationRecoveryKey)
|
||||||
|
.textContentType(.password) // Not ideal but stops random suggestions
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disableAutocorrection(true)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color.compound.bgSubtleSecondaryLevel0)
|
.background(Color.compound.bgSubtleSecondaryLevel0)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||||
|
.id(textFieldIdentifier)
|
||||||
|
.focused($focused)
|
||||||
|
.submitLabel(.done)
|
||||||
|
.onSubmit {
|
||||||
|
context.send(viewAction: .confirmKey)
|
||||||
|
}
|
||||||
|
|
||||||
Text(context.viewState.recoveryKeySubtitle)
|
Text(context.viewState.recoveryKeySubtitle)
|
||||||
.foregroundColor(.compound.textSecondary)
|
.foregroundColor(.compound.textSecondary)
|
||||||
.font(.compound.bodySM)
|
.font(.compound.bodySM)
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button {
|
|
||||||
context.send(viewAction: .confirmKey)
|
|
||||||
} label: {
|
|
||||||
Text(L10n.actionConfirm)
|
|
||||||
}
|
|
||||||
.buttonStyle(.compound(.primary))
|
|
||||||
.disabled(context.confirmationRecoveryKey.isEmpty)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:04b0e56fb858bb91536de8a6235b00885e85a717f05ee5c386017a53105f7857
|
oid sha256:6e555a2dc897f2b010e6d62f0595d51f892810ea81382d55bc067355d4118ec7
|
||||||
size 106661
|
size 106260
|
||||||
|
|||||||
Reference in New Issue
Block a user