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:
JRoberts
2022-05-28 22:09:46 +04:00
committed by GitHub
parent b56ad77502
commit c3c712aa02
8 changed files with 248 additions and 139 deletions

View File

@@ -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!"),

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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)
})
}
}

View File

@@ -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