ios: show local authentication notice; ios & android: retry authentication button (#706)
* advertisement * refactor * advertisement state machine * simplify * ios: retry * remove log * android: retry * Update apps/ios/Shared/ContentView.swift Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> * Update apps/ios/Shared/Views/UserSettings/SettingsView.swift Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
@@ -11,7 +11,13 @@ struct ContentView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
@ObservedObject var callController = CallController.shared
|
||||
@Binding var userAuthorized: Bool?
|
||||
@Binding var doAuthenticate: Bool
|
||||
@Binding var enteredBackground: Double?
|
||||
@State private var userAuthorized: Bool?
|
||||
@State private var laFailed: Bool = false
|
||||
@AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false
|
||||
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
@@ -25,6 +31,12 @@ struct ContentView: View {
|
||||
NtfManager.shared.requestAuthorization(onDeny: {
|
||||
alertManager.showAlert(notificationAlert())
|
||||
})
|
||||
// Local Authentication notice is to be shown on next start after onboarding is complete
|
||||
if (!prefLANoticeShown && prefShowLANotice) {
|
||||
prefLANoticeShown = true
|
||||
alertManager.showAlert(laNoticeAlert())
|
||||
}
|
||||
prefShowLANotice = true
|
||||
}
|
||||
if chatModel.showCallView, let call = chatModel.activeCall {
|
||||
ActiveCallView(call: call)
|
||||
@@ -35,11 +47,77 @@ struct ContentView: View {
|
||||
OnboardingView(onboarding: step)
|
||||
}
|
||||
}
|
||||
} else if prefPerformLA && laFailed {
|
||||
retryAuthView()
|
||||
}
|
||||
}
|
||||
.onChange(of: doAuthenticate) { doAuth in
|
||||
if doAuth, authenticationExpired() {
|
||||
runAuthenticate()
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
|
||||
}
|
||||
|
||||
private func retryAuthView() -> some View {
|
||||
Button {
|
||||
laFailed = false
|
||||
runAuthenticate()
|
||||
} label: { Label("Retry", systemImage: "arrow.counterclockwise") }
|
||||
}
|
||||
|
||||
private func runAuthenticate() {
|
||||
if !prefPerformLA {
|
||||
userAuthorized = true
|
||||
} else {
|
||||
userAuthorized = false
|
||||
authenticate(reason: "Unlock") { laResult in
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
userAuthorized = true
|
||||
case .failed:
|
||||
laFailed = true
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
case .unavailable:
|
||||
userAuthorized = true
|
||||
prefPerformLA = false
|
||||
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func authenticationExpired() -> Bool {
|
||||
if let enteredBackground = enteredBackground {
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackground >= 30
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func laNoticeAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("SimpleX Lock"),
|
||||
message: Text("To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled."),
|
||||
primaryButton: .default(Text("Turn on")) {
|
||||
authenticate(reason: "Enable SimpleX Lock") { laResult in
|
||||
switch laResult {
|
||||
case .success:
|
||||
prefPerformLA = true
|
||||
alertManager.showAlert(laTurnedOnAlert())
|
||||
case .failed:
|
||||
prefPerformLA = false
|
||||
alertManager.showAlert(laFailedAlert())
|
||||
case .unavailable:
|
||||
prefPerformLA = false
|
||||
alertManager.showAlert(laUnavailableInstructionAlert())
|
||||
}
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
|
||||
func notificationAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("Notifications are disabled!"),
|
||||
|
||||
@@ -14,7 +14,6 @@ import WebKit
|
||||
final class ChatModel: ObservableObject {
|
||||
@Published var onboardingStage: OnboardingStage?
|
||||
@Published var currentUser: User?
|
||||
@Published var performLA: Bool = false
|
||||
// list of chat "previews"
|
||||
@Published var chats: [Chat] = []
|
||||
// current chat
|
||||
|
||||
@@ -16,9 +16,9 @@ struct SimpleXApp: App {
|
||||
@StateObject private var chatModel = ChatModel.shared
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var performLA = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@State private var userAuthorized: Bool? = nil
|
||||
@State private var doAuthenticate: Bool = true
|
||||
@State private var doAuthenticate: Bool = false
|
||||
@State private var enteredBackground: Double? = nil
|
||||
|
||||
init() {
|
||||
@@ -30,14 +30,13 @@ struct SimpleXApp: App {
|
||||
|
||||
var body: some Scene {
|
||||
return WindowGroup {
|
||||
ContentView(userAuthorized: $userAuthorized)
|
||||
ContentView(doAuthenticate: $doAuthenticate, enteredBackground: $enteredBackground)
|
||||
.environmentObject(chatModel)
|
||||
.onOpenURL { url in
|
||||
logger.debug("ContentView.onOpenURL: \(url)")
|
||||
chatModel.appOpenUrl = url
|
||||
}
|
||||
.onAppear() {
|
||||
chatModel.performLA = performLA
|
||||
initializeChat()
|
||||
}
|
||||
.onChange(of: scenePhase) { phase in
|
||||
@@ -46,50 +45,14 @@ struct SimpleXApp: App {
|
||||
switch (phase) {
|
||||
case .background:
|
||||
BGManager.shared.schedule()
|
||||
doAuthenticate = true
|
||||
doAuthenticate = false
|
||||
enteredBackground = ProcessInfo.processInfo.systemUptime
|
||||
case .inactive:
|
||||
authenticateOnPhaseChange()
|
||||
case .active:
|
||||
authenticateOnPhaseChange()
|
||||
doAuthenticate = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func authenticateOnPhaseChange() {
|
||||
if doAuthenticate {
|
||||
doAuthenticate = false
|
||||
if !performLA {
|
||||
userAuthorized = true
|
||||
} else {
|
||||
if authenticationExpired() {
|
||||
userAuthorized = false
|
||||
authenticate(reason: "Unlock") { laResult in
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
userAuthorized = true
|
||||
case .failed:
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
case .unavailable:
|
||||
userAuthorized = true
|
||||
performLA = false
|
||||
chatModel.performLA = false
|
||||
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func authenticationExpired() -> Bool {
|
||||
if let enteredBackground = enteredBackground {
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackground >= 30
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,14 @@ import SwiftUI
|
||||
struct SettingsButton: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@State private var showSettings = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
|
||||
var body: some View {
|
||||
Button { showSettings = true } label: {
|
||||
Image(systemName: "gearshape")
|
||||
}
|
||||
.sheet(isPresented: $showSettings, content: {
|
||||
SettingsView(showSettings: $showSettings)
|
||||
SettingsView(showSettings: $showSettings, performLA: prefPerformLA)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,16 @@ let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionS
|
||||
|
||||
let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String
|
||||
|
||||
let DEFAULT_SHOW_LA_NOTICE = "showLocalAuthenticationNotice"
|
||||
let DEFAULT_LA_NOTICE_SHOWN = "localAuthenticationNoticeShown"
|
||||
let DEFAULT_PERFORM_LA = "performLocalAuthentication"
|
||||
let DEFAULT_USE_NOTIFICATIONS = "useNotifications"
|
||||
let DEFAULT_PENDING_CONNECTIONS = "pendingConnections"
|
||||
let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
|
||||
|
||||
let appDefaults: [String:Any] = [
|
||||
DEFAULT_SHOW_LA_NOTICE: false,
|
||||
DEFAULT_LA_NOTICE_SHOWN: false,
|
||||
DEFAULT_PERFORM_LA: false,
|
||||
DEFAULT_USE_NOTIFICATIONS: false,
|
||||
DEFAULT_PENDING_CONNECTIONS: true,
|
||||
@@ -32,10 +36,12 @@ struct SettingsView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Binding var showSettings: Bool
|
||||
@State var performLA: Bool = false
|
||||
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@State private var performLAToggleReset = false
|
||||
@AppStorage(DEFAULT_USE_NOTIFICATIONS) private var useNotifications = false
|
||||
@AppStorage(DEFAULT_PENDING_CONNECTIONS) private var pendingConnections = true
|
||||
@State private var performLAToggleReset = false
|
||||
@State var showNotificationsAlert: Bool = false
|
||||
@State var whichNotificationsAlert = NotificationAlert.enable
|
||||
@State var alert: SettingsViewAlert? = nil
|
||||
@@ -72,7 +78,7 @@ struct SettingsView: View {
|
||||
|
||||
Section("Settings") {
|
||||
settingsRow("lock") {
|
||||
Toggle("SimpleX Lock", isOn: $chatModel.performLA)
|
||||
Toggle("SimpleX Lock", isOn: $performLA)
|
||||
}
|
||||
settingsRow("link") {
|
||||
Toggle("Show pending connections", isOn: $pendingConnections)
|
||||
@@ -150,7 +156,8 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Your settings")
|
||||
.onChange(of: chatModel.performLA) { performLAToggle in
|
||||
.onChange(of: performLA) { performLAToggle in
|
||||
prefLANoticeShown = true
|
||||
if performLAToggleReset {
|
||||
performLAToggleReset = false
|
||||
} else {
|
||||
@@ -181,14 +188,14 @@ struct SettingsView: View {
|
||||
case .failed:
|
||||
prefPerformLA = false
|
||||
withAnimation() {
|
||||
chatModel.performLA = false
|
||||
performLA = false
|
||||
}
|
||||
performLAToggleReset = true
|
||||
alert = .laFailedAlert
|
||||
case .unavailable:
|
||||
prefPerformLA = false
|
||||
withAnimation() {
|
||||
chatModel.performLA = false
|
||||
performLA = false
|
||||
}
|
||||
performLAToggleReset = true
|
||||
alert = .laUnavailableInstructionAlert
|
||||
@@ -204,7 +211,7 @@ struct SettingsView: View {
|
||||
case .failed:
|
||||
prefPerformLA = true
|
||||
withAnimation() {
|
||||
chatModel.performLA = true
|
||||
performLA = true
|
||||
}
|
||||
performLAToggleReset = true
|
||||
alert = .laFailedAlert
|
||||
|
||||
Reference in New Issue
Block a user