mirror of
https://github.com/seemoo-lab/openhaystack.git
synced 2026-08-19 19:46:24 +00:00
Adding a button that shows if the mail plug-in is active. The button turns red if the plug-in is not active.
Architectural changes discussed with @schmittner: Moving the FindMyController out of the environment and using the AccessoryController as the main entry point, also for downloading reports The AccessoryController is now passed as an Environment Object again
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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(()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.importAccessories, label: {
|
||||
Label("Export accessories", systemImage: "square.and.arrow.down")
|
||||
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("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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user