diff --git a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift index 69e7fbae..51034425 100755 --- a/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift +++ b/OpenHaystack/OpenHaystack/FindMy/FindMyController.swift @@ -15,7 +15,6 @@ import SwiftUI class FindMyController: ObservableObject { @Published var error: Error? @Published var devices = [FindMyDevice]() - @Environment(\.accessoryController) var accessories: AccessoryController func loadPrivateKeys(from data: Data, with searchPartyToken: Data, completion: @escaping (Error?) -> Void) { do { @@ -98,11 +97,6 @@ class FindMyController: ObservableObject { self.fetchReports(with: token) { error in - let reports = self.devices.compactMap({ $0.reports }).flatMap({ $0 }) - if reports.isEmpty == false { - self.accessories.updateWithDecryptedReports(devices: self.devices) - } - if let error = error { completion(.failure(error)) os_log("Error: %@", String(describing: error)) diff --git a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift index 5d9786a0..890811c8 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryController.swift @@ -16,16 +16,17 @@ class AccessoryController: ObservableObject { @Published var accessories: [Accessory] var selfObserver: AnyCancellable? var listElementsObserver = [AnyCancellable]() - @Environment(\.findMyController) var findMyController: FindMyController - - init(accessories: [Accessory]) { + let findMyController: FindMyController + + init(accessories: [Accessory], findMyController: FindMyController) { self.accessories = accessories + self.findMyController = findMyController initAccessoryObserver() initObserver() } convenience init() { - self.init(accessories: KeychainController.loadAccessoriesFromKeychain()) + self.init(accessories: KeychainController.loadAccessoriesFromKeychain(), findMyController: FindMyController()) } func initAccessoryObserver() { @@ -163,8 +164,9 @@ class AccessoryController: ObservableObject { case .failure(_): completion(.failure(.activatePlugin)) case .success(let accountData): - let token = accountData.searchPartyToken - guard token.isEmpty == false else { + + guard let token = accountData.searchPartyToken, + token.isEmpty == false else { completion(.failure(.searchPartyToken)) return } @@ -179,6 +181,7 @@ class AccessoryController: ObservableObject { if reports.isEmpty { completion(.failure(.noReportsFound)) }else { + self.updateWithDecryptedReports(devices: devices) completion(.success(())) } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/ManageAccessoriesView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/ManageAccessoriesView.swift index d28070dc..cde2cff4 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/ManageAccessoriesView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/ManageAccessoriesView.swift @@ -11,7 +11,7 @@ import SwiftUI struct ManageAccessoriesView: View { - @Environment(\.accessoryController) var accessoryController: AccessoryController + @EnvironmentObject var accessoryController: AccessoryController var accessories: [Accessory] { return self.accessoryController.accessories } @@ -21,6 +21,9 @@ struct ManageAccessoriesView: View { @Binding var focusedAccessory: Accessory? @Binding var accessoryToDeploy: Accessory? @Binding var showESP32DeploySheet: Bool + var mailPluginIsActive: Bool + + @State var showMailPopup = false var body: some View { VStack { @@ -40,17 +43,29 @@ struct ManageAccessoriesView: View { .toolbar(content: { Spacer() + Button(action: {self.showMailPopup.toggle()}, label: { + Label("Plugin state", systemImage: "envelope") + .foregroundColor(self.mailPluginIsActive ? nil : .red) + }) + .help(self.mailPluginIsActive ? "Mail plug-in is active" : "Cannot connect to Mail plug-in") + .popover(isPresented: self.$showMailPopup) { + self.mailStatePopup + } + Button(action: self.importAccessories, label: { - Label("Export accessories", systemImage: "square.and.arrow.down") + Label("Import accessories", systemImage: "square.and.arrow.down") }) + .help("Import accessories from a file") Button(action: self.exportAccessories, label: { Label("Export accessories", systemImage: "square.and.arrow.up") }) + .help("Export all accessories to a file") Button(action: self.addAccessory) { Label("Add accessory", systemImage: "plus") } + .help("Add a new accessory") }) .sheet( isPresented: self.$showESP32DeploySheet, @@ -84,6 +99,23 @@ struct ManageAccessoriesView: View { .listStyle(SidebarListStyle()) } + + var mailStatePopup: some View { + HStack { + Image(systemName: "envelope") + .foregroundColor(self.mailPluginIsActive ? .green : .red) + + if self.mailPluginIsActive { + Text("The mail plug-in is up and running") + }else { + Text("Cannot connect to the mail plug-in. Open Apple Mail and make sure the plug-in is enabled") + .lineLimit(10) + .multilineTextAlignment(.leading) + } + } + .frame(maxWidth: 250) + .padding() + } /// Delete an accessory from the list of accessories. func delete(accessory: Accessory) { @@ -135,6 +167,6 @@ struct ManageAccessoriesView_Previews: PreviewProvider { @State static var showESPSheet: Bool = true static var previews: some View { - ManageAccessoriesView(alertType: self.$alertType, focusedAccessory: self.$focussed, accessoryToDeploy: self.$deploy, showESP32DeploySheet: self.$showESPSheet) + ManageAccessoriesView(alertType: self.$alertType, focusedAccessory: self.$focussed, accessoryToDeploy: self.$deploy, showESP32DeploySheet: self.$showESPSheet, mailPluginIsActive: true) } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift index 3292cbf3..e9e2a2ce 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift @@ -14,13 +14,12 @@ import SwiftUI struct OpenHaystackMainView: View { @State var loading = false - @Environment(\.accessoryController) var accessoryController: AccessoryController - @Environment(\.findMyController) var findMyController: FindMyController + @EnvironmentObject var accessoryController: AccessoryController + var accessories: [Accessory] { return self.accessoryController.accessories } - @State var showKeyError = false @State var alertType: AlertType? @State var popUpAlertType: PopUpAlertType? @State var errorDescription: String? @@ -30,7 +29,9 @@ struct OpenHaystackMainView: View { @State var isLoading = false @State var focusedAccessory: Accessory? @State var accessoryToDeploy: Accessory? - + + @State var mailPluginIsActive = false + @State var showESP32DeploySheet = false var body: some View { @@ -41,9 +42,10 @@ struct OpenHaystackMainView: View { alertType: self.$alertType, focusedAccessory: self.$focusedAccessory, accessoryToDeploy: self.$accessoryToDeploy, - showESP32DeploySheet: self.$showESP32DeploySheet + showESP32DeploySheet: self.$showESP32DeploySheet, + mailPluginIsActive: self.mailPluginIsActive ) - .frame(minWidth: 240, idealWidth: 250, maxWidth: .infinity, minHeight: 300, idealHeight: 400, maxHeight: .infinity, alignment: .center) + .frame(minWidth: 280, idealWidth: 280, maxWidth: .infinity, minHeight: 300, idealHeight: 400, maxHeight: .infinity, alignment: .center) ZStack { AccessoryMapView(accessoryController: self.accessoryController, mapType: self.$mapType, focusedAccessory: self.focusedAccessory) @@ -186,7 +188,7 @@ struct OpenHaystackMainView: View { } } - func checkPluginIsRunning(_ completion: ((Bool) -> Void)?) { + func checkPluginIsRunning(silent: Bool=false, _ completion: ((Bool) -> Void)?) { // Check if Mail plugin is active AnisetteDataManager.shared.requestAnisetteData { (result) in DispatchQueue.main.async { @@ -194,14 +196,17 @@ struct OpenHaystackMainView: View { case .success(let accountData): withAnimation { - self.searchPartyToken = String(data: accountData.searchPartyToken, encoding: .ascii) ?? "" - if self.searchPartyToken.isEmpty == false { - self.searchPartyTokenLoaded = true + if let token = accountData.searchPartyToken { + self.searchPartyToken = String(data: token, encoding: .ascii) ?? "" + if self.searchPartyToken.isEmpty == false { + self.searchPartyTokenLoaded = true + } } } + self.mailPluginIsActive = true completion?(true) case .failure(let error): - if let error = error as? AnisetteDataError { + if let error = error as? AnisetteDataError, silent == false { switch error { case .pluginNotFound: self.alertType = .activatePlugin @@ -209,7 +214,13 @@ struct OpenHaystackMainView: View { self.alertType = .activatePlugin } } + self.mailPluginIsActive = false completion?(false) + + //Check again in 5s + DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: { + self.checkPluginIsRunning(silent: true, nil) + }) } } } @@ -330,11 +341,11 @@ struct OpenHaystackMainView: View { } struct OpenHaystackMainView_Previews: PreviewProvider { - static var accessoryController = AccessoryControllerPreview(accessories: PreviewData.accessories) as AccessoryController + static var accessoryController = AccessoryControllerPreview(accessories: PreviewData.accessories, findMyController: FindMyController()) as AccessoryController static var previews: some View { OpenHaystackMainView() - .environment(\.accessoryController, accessoryController) + .environmentObject(self.accessoryController) } } diff --git a/OpenHaystack/OpenHaystack/OpenHaystackApp.swift b/OpenHaystack/OpenHaystack/OpenHaystackApp.swift index d35273a0..0cef8038 100644 --- a/OpenHaystack/OpenHaystack/OpenHaystackApp.swift +++ b/OpenHaystack/OpenHaystack/OpenHaystackApp.swift @@ -11,14 +11,22 @@ import SwiftUI @main struct OpenHaystackApp: App { - @Environment(\.accessoryController) var accessoryController: AccessoryController - @Environment(\.findMyController) var findMyController: FindMyController + @StateObject var accessoryController: AccessoryController - init() {} + init() { + let accessoryController: AccessoryController + if ProcessInfo().arguments.contains("-preview") { + accessoryController = AccessoryControllerPreview(accessories: PreviewData.accessories, findMyController: FindMyController()) + } else { + accessoryController = AccessoryController() + } + self._accessoryController = StateObject(wrappedValue: accessoryController) + } var body: some Scene { WindowGroup { OpenHaystackMainView() + .environmentObject(self.accessoryController) } .commands { SidebarCommands() @@ -36,20 +44,9 @@ private struct FindMyControllerEnvironmentKey: EnvironmentKey { private struct AccessoryControllerEnvironmentKey: EnvironmentKey { static let defaultValue: AccessoryController = { if ProcessInfo().arguments.contains("-preview") { - return AccessoryControllerPreview(accessories: PreviewData.accessories) + return AccessoryControllerPreview(accessories: PreviewData.accessories, findMyController: FindMyController()) } else { return AccessoryController() } }() } - -extension EnvironmentValues { - var findMyController: FindMyController { - get {self[FindMyControllerEnvironmentKey]} - } - - var accessoryController: AccessoryController { - get{self[AccessoryControllerEnvironmentKey]} - set{self[AccessoryControllerEnvironmentKey] = newValue} - } -} diff --git a/OpenHaystack/OpenHaystackMail/AppleAccountData.h b/OpenHaystack/OpenHaystackMail/AppleAccountData.h index 001c587f..8e87538b 100644 --- a/OpenHaystack/OpenHaystackMail/AppleAccountData.h +++ b/OpenHaystack/OpenHaystackMail/AppleAccountData.h @@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy) NSLocale *locale; @property(nonatomic, copy) NSTimeZone *timeZone; -@property(nonatomic, copy) NSData *searchPartyToken; +@property(nonatomic, copy) NSData * _Nullable searchPartyToken; - (instancetype)initWithMachineID:(NSString *)machineID oneTimePassword:(NSString *)oneTimePassword