Bring the SwiftUI app in line with the Apple-native mock and keep pending approvals actionable from Live Activities and watch complications.
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
#if canImport(WidgetKit)
|
||||
import WidgetKit
|
||||
#endif
|
||||
|
||||
@MainActor
|
||||
final class AppViewModel: ObservableObject {
|
||||
@@ -10,12 +15,13 @@ final class AppViewModel: ObservableObject {
|
||||
@Published var requests: [ApprovalRequest] = []
|
||||
@Published var notifications: [AppNotification] = []
|
||||
@Published var notificationPermission: NotificationPermissionState = .unknown
|
||||
@Published var selectedSection: AppSection = .overview
|
||||
@Published var selectedSection: AppSection = .inbox
|
||||
@Published var isBootstrapping = false
|
||||
@Published var isAuthenticating = false
|
||||
@Published var isIdentifying = false
|
||||
@Published var isRefreshing = false
|
||||
@Published var isNotificationCenterPresented = false
|
||||
@Published var isShowingPairingSuccess = false
|
||||
@Published var activeRequestID: ApprovalRequest.ID?
|
||||
@Published var isScannerPresented = false
|
||||
@Published var errorMessage: String?
|
||||
@@ -32,14 +38,25 @@ final class AppViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
let rawValue = String(argument.dropFirst("--mock-section=".count))
|
||||
if rawValue == "notifications" {
|
||||
return .activity
|
||||
|
||||
switch rawValue {
|
||||
case "requests", "inbox":
|
||||
return .inbox
|
||||
case "notifications", "activity":
|
||||
return .notifications
|
||||
case "devices", "account":
|
||||
return .devices
|
||||
case "identity", "overview":
|
||||
return .identity
|
||||
case "settings":
|
||||
return .settings
|
||||
default:
|
||||
return AppSection(rawValue: rawValue)
|
||||
}
|
||||
return AppSection(rawValue: rawValue)
|
||||
}
|
||||
|
||||
init(
|
||||
service: IDPServicing = MockIDPService(),
|
||||
service: IDPServicing = MockIDPService.shared,
|
||||
notificationCoordinator: NotificationCoordinating = NotificationCoordinator(),
|
||||
appStateStore: AppStateStoring = UserDefaultsAppStateStore(),
|
||||
launchArguments: [String] = ProcessInfo.processInfo.arguments
|
||||
@@ -148,15 +165,28 @@ final class AppViewModel: ObservableObject {
|
||||
isAuthenticating = true
|
||||
defer { isAuthenticating = false }
|
||||
|
||||
let wasSignedOut = session == nil
|
||||
|
||||
do {
|
||||
let result = try await service.signIn(with: normalizedRequest)
|
||||
session = result.session
|
||||
apply(snapshot: result.snapshot)
|
||||
persistCurrentState()
|
||||
notificationPermission = await notificationCoordinator.authorizationStatus()
|
||||
selectedSection = .overview
|
||||
selectedSection = .inbox
|
||||
errorMessage = nil
|
||||
isScannerPresented = false
|
||||
|
||||
if wasSignedOut {
|
||||
isShowingPairingSuccess = true
|
||||
Haptics.success()
|
||||
|
||||
Task { @MainActor [weak self] in
|
||||
try? await Task.sleep(for: .milliseconds(1200))
|
||||
guard let self, self.session != nil else { return }
|
||||
self.isShowingPairingSuccess = false
|
||||
}
|
||||
}
|
||||
} catch let error as AppError {
|
||||
errorMessage = error.errorDescription
|
||||
} catch {
|
||||
@@ -250,7 +280,7 @@ final class AppViewModel: ObservableObject {
|
||||
let snapshot = try await service.simulateIncomingRequest()
|
||||
apply(snapshot: snapshot)
|
||||
persistCurrentState()
|
||||
selectedSection = .requests
|
||||
selectedSection = .inbox
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = "Unable to create a mock identity check right now."
|
||||
@@ -296,9 +326,36 @@ final class AppViewModel: ObservableObject {
|
||||
profile = nil
|
||||
requests = []
|
||||
notifications = []
|
||||
selectedSection = .overview
|
||||
selectedSection = .inbox
|
||||
manualPairingPayload = suggestedPairingPayload
|
||||
isShowingPairingSuccess = false
|
||||
errorMessage = nil
|
||||
|
||||
Task {
|
||||
await ApprovalActivityController.endAll()
|
||||
#if canImport(WidgetKit)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func openDeepLink(_ url: URL) {
|
||||
let destination = (url.host ?? url.lastPathComponent).lowercased()
|
||||
|
||||
switch destination {
|
||||
case "inbox":
|
||||
selectedSection = .inbox
|
||||
case "notifications":
|
||||
selectedSection = .notifications
|
||||
case "devices":
|
||||
selectedSection = .devices
|
||||
case "identity":
|
||||
selectedSection = .identity
|
||||
case "settings":
|
||||
selectedSection = .settings
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func mutateRequest(_ request: ApprovalRequest, approve: Bool) async {
|
||||
@@ -352,8 +409,20 @@ final class AppViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
private func apply(snapshot: DashboardSnapshot) {
|
||||
profile = snapshot.profile
|
||||
requests = snapshot.requests.sorted { $0.createdAt > $1.createdAt }
|
||||
notifications = snapshot.notifications.sorted { $0.sentAt > $1.sentAt }
|
||||
withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) {
|
||||
self.profile = snapshot.profile
|
||||
self.requests = snapshot.requests.sorted { $0.createdAt > $1.createdAt }
|
||||
self.notifications = snapshot.notifications.sorted { $0.sentAt > $1.sentAt }
|
||||
}
|
||||
|
||||
let profileValue = snapshot.profile
|
||||
let requestsValue = snapshot.requests.sorted { $0.createdAt > $1.createdAt }
|
||||
|
||||
Task {
|
||||
await ApprovalActivityController.sync(requests: requestsValue, profile: profileValue)
|
||||
#if canImport(WidgetKit)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user