From 837de249cc90bb40710d10002348d2a10789c2cc Mon Sep 17 00:00:00 2001 From: JJTech0130 Date: Tue, 26 Mar 2024 21:32:20 -0400 Subject: [PATCH] log view --- ValidationRelay.xcodeproj/project.pbxproj | 4 + ValidationRelay/ContentView.swift | 114 ++++++++++++---------- ValidationRelay/LogView.swift | 61 ++++++++++++ ValidationRelay/Relay.swift | 12 +++ 4 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 ValidationRelay/LogView.swift diff --git a/ValidationRelay.xcodeproj/project.pbxproj b/ValidationRelay.xcodeproj/project.pbxproj index 0c0acac..3e50379 100644 --- a/ValidationRelay.xcodeproj/project.pbxproj +++ b/ValidationRelay.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 48D289112BB3579000EA9DEC /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 48D289102BB3579000EA9DEC /* libMobileGestalt.tbd */; }; 48D289132BB3743F00EA9DEC /* ApplicationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D289122BB3743F00EA9DEC /* ApplicationMonitor.swift */; }; 48D289152BB3745000EA9DEC /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D289142BB3745000EA9DEC /* LocationManager.swift */; }; + 48D289182BB3A5A700EA9DEC /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D289172BB3A5A700EA9DEC /* LogView.swift */; }; 48D8604A2BB0DD110092EF79 /* ValidationData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D860492BB0DD110092EF79 /* ValidationData.swift */; }; 48D8604D2BB1B8CC0092EF79 /* NWWebSocket in Frameworks */ = {isa = PBXBuildFile; productRef = 48D8604C2BB1B8CC0092EF79 /* NWWebSocket */; }; 48D8604F2BB1B8EB0092EF79 /* Relay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48D8604E2BB1B8EB0092EF79 /* Relay.swift */; }; @@ -33,6 +34,7 @@ 48D289122BB3743F00EA9DEC /* ApplicationMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMonitor.swift; sourceTree = ""; }; 48D289142BB3745000EA9DEC /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; 48D289162BB374FA00EA9DEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 48D289172BB3A5A700EA9DEC /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = ""; }; 48D860482BB0D7BF0092EF79 /* build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build.sh; sourceTree = ""; }; 48D860492BB0DD110092EF79 /* ValidationData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationData.swift; sourceTree = ""; }; 48D8604E2BB1B8EB0092EF79 /* Relay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Relay.swift; sourceTree = ""; }; @@ -84,6 +86,7 @@ 48D8604E2BB1B8EB0092EF79 /* Relay.swift */, 48D289122BB3743F00EA9DEC /* ApplicationMonitor.swift */, 48D289142BB3745000EA9DEC /* LocationManager.swift */, + 48D289172BB3A5A700EA9DEC /* LogView.swift */, ); path = ValidationRelay; sourceTree = ""; @@ -186,6 +189,7 @@ 480989B32BB0C19300B49AE8 /* absd.defs in Sources */, 48D289152BB3745000EA9DEC /* LocationManager.swift in Sources */, 480989A42BB0C01900B49AE8 /* ContentView.swift in Sources */, + 48D289182BB3A5A700EA9DEC /* LogView.swift in Sources */, 480989A22BB0C01900B49AE8 /* ValidationRelayApp.swift in Sources */, 48D8604F2BB1B8EB0092EF79 /* Relay.swift in Sources */, ); diff --git a/ValidationRelay/ContentView.swift b/ValidationRelay/ContentView.swift index b2e18b6..247b9d3 100644 --- a/ValidationRelay/ContentView.swift +++ b/ValidationRelay/ContentView.swift @@ -48,66 +48,76 @@ struct ContentView: View { } var body: some View { - List { - Section { - Toggle("Relay", isOn: $wantRelayConnected) - .onChange(of: wantRelayConnected) { newValue in - // Connect or disconnect the relay - if newValue { - relayConnectionManager.connect(getCurrentRelayURL()) - } else { - relayConnectionManager.disconnect() + NavigationView { + List { + Section { + Toggle("Relay", isOn: $wantRelayConnected) + .onChange(of: wantRelayConnected) { newValue in + // Connect or disconnect the relay + if newValue { + relayConnectionManager.connect(getCurrentRelayURL()) + } else { + relayConnectionManager.disconnect() + } } + HStack { + Text("Registration Code") + Spacer() + Text(relayConnectionManager.registrationCode) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) } - HStack { - Text("Registration Code") - Spacer() - Text(relayConnectionManager.registrationCode) - .foregroundColor(.secondary) + } footer: { + Text(relayConnectionManager.connectionStatusMessage) } - } footer: { - Text(relayConnectionManager.connectionStatusMessage) - } - Section { - //Toggle("Run in Background", isOn: .constant(false)) - // .disabled(true) - Picker("Relay", selection: $selectedRelay) { - Text("Beeper").tag("Beeper") - //Text("pypush").tag("pypush") - Text("Custom").tag("Custom") - } - .pickerStyle(.segmented) - .onChange(of: selectedRelay) { newValue in - // Disconnect when the user is switching relay servers - wantRelayConnected = false - } - if (selectedRelay == "Custom") { - TextField("Custom Relay Server URL", text: $customRelayURL) - .textContentType(.URL) - .autocorrectionDisabled() - .autocapitalization(.none) - } - } header: { - Text("Connection settings") - } footer: { - Text("Beeper's relay server is recommended for most users") - } - - Section { - Button("Reset Registration Code") { - relayConnectionManager.savedRegistrationURL = "" - relayConnectionManager.savedRegistrationCode = "" - relayConnectionManager.savedRegistrationSecret = "" - relayConnectionManager.registrationCode = "None" - wantRelayConnected = false + Section { + //Toggle("Run in Background", isOn: .constant(false)) + // .disabled(true) + Picker("Relay", selection: $selectedRelay) { + Text("Beeper").tag("Beeper") + //Text("pypush").tag("pypush") + Text("Custom").tag("Custom") + } + .pickerStyle(.segmented) + .onChange(of: selectedRelay) { newValue in + // Disconnect when the user is switching relay servers + wantRelayConnected = false + } + if (selectedRelay == "Custom") { + TextField("Custom Relay Server URL", text: $customRelayURL) + .textContentType(.URL) + .autocorrectionDisabled() + .autocapitalization(.none) + } + } header: { + Text("Connection settings") + } footer: { + Text("Beeper's relay server is recommended for most users") } + + Section { + // Navigation to Log page + NavigationLink(destination: LogView(logItems: relayConnectionManager.logItems)) { + Text("Log") + } + Button("Reset Registration Code") { + relayConnectionManager.savedRegistrationURL = "" + relayConnectionManager.savedRegistrationCode = "" + relayConnectionManager.savedRegistrationSecret = "" + relayConnectionManager.registrationCode = "None" + wantRelayConnected = false + } .foregroundColor(.red) .frame(maxWidth: .infinity) - } footer: { - Text("You will need to re-enter the code on your other devices") + } footer: { + Text("You will need to re-enter the code on your other devices") + } } + .listStyle(.grouped) + .navigationBarHidden(true) + .navigationBarTitle("", displayMode: .inline) } - .listStyle(.grouped) + } } diff --git a/ValidationRelay/LogView.swift b/ValidationRelay/LogView.swift new file mode 100644 index 0000000..808cc86 --- /dev/null +++ b/ValidationRelay/LogView.swift @@ -0,0 +1,61 @@ +// +// LogView.swift +// ValidationRelay +// +// Created by James Gill on 3/26/24. +// + +import SwiftUI + +struct LogItem: Identifiable, Hashable { + var id = UUID() + var message: String + var isError: Bool + var date: Date +} + +class LogItems: ObservableObject { + @Published var items: [LogItem] = [] + + func log(_ message: String, isError: Bool = false) { + let item = LogItem(message: message, isError: isError, date: Date()) + items.append(item) + } +} + +struct LogView: View { + @ObservedObject var logItems: LogItems + + var body: some View { + List { + ForEach(logItems.items, id: \.self) { item in + HStack { + Text(item.message) + .foregroundColor(item.isError ? .red : .black) + Spacer() + Text(item.date, style: .time) + .font(.caption) + .foregroundColor(.gray) + } + } + }.listStyle(.grouped) + .navigationTitle("Event Log") + // Clear log button + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Clear") { + logItems.items = [] + } + } + } + } +} + +#Preview { + LogView(logItems: { + let testLog = LogItems() + testLog.log("Test message") + testLog.log("Test error", isError: true) + return testLog + }()) +} diff --git a/ValidationRelay/Relay.swift b/ValidationRelay/Relay.swift index d8bde62..05fa319 100644 --- a/ValidationRelay/Relay.swift +++ b/ValidationRelay/Relay.swift @@ -41,6 +41,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { @Published var registrationCode: String = "None" @Published var connectionStatusMessage: String = "" + @Published var logItems = LogItems() // These must all be saved together @AppStorage("savedRegistrationSecret") public var savedRegistrationSecret = "" @@ -50,6 +51,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { var currentURL: URL? = nil func connect(_ url: URL) { + logItems.log("Connecting to \(url)") connectionStatusMessage = "Connecting..." currentURL = url let connection = NWWebSocket(url: url, connectAutomatically: true) @@ -61,15 +63,18 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { } func disconnect() { + logItems.log("Disconnecting on request") connection?.disconnect(closeCode: .protocolCode(.normalClosure)) currentURL = nil } func webSocketDidConnect(connection: WebSocketConnection) { + logItems.log("Websocket did connect") connectionStatusMessage = "Connected" var registerCommand = ["command": "register", "data": ["": ""]] as [String : Any] if currentURL?.absoluteString == savedRegistrationURL { print("Using saved registration code") + logItems.log("Using saved registration code \(savedRegistrationCode)") registerCommand["data"] = ["code": savedRegistrationCode, "secret": savedRegistrationSecret] } let data = try! JSONSerialization.data(withJSONObject: registerCommand) @@ -77,6 +82,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { connection.send(string: String(data: data, encoding: .utf8)!) } func webSocketDidDisconnect(connection: WebSocketConnection, closeCode: NWProtocolWebSocket.CloseCode, reason: Data?) { + logItems.log("Websocket did disconnect", isError: true) print("Disconnected") // Check if "error" is in the current status message, if so don't clear it if !connectionStatusMessage.contains("Error") { @@ -97,6 +103,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { func webSocketDidReceiveError(connection: WebSocketConnection, error: NWError) { print(error) + logItems.log("Websocket error: \(error)", isError: true) connectionStatusMessage = "Error connecting to relay: \(error)" } @@ -115,6 +122,9 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { if let data = jsonDict["data"] as? [String: Any] { if let code = data["code"] as? String { registrationCode = code + if savedRegistrationCode != code { + logItems.log("New registration code \(code)") + } savedRegistrationCode = code savedRegistrationSecret = data["secret"] as? String ?? "" savedRegistrationURL = currentURL?.absoluteString ?? "" @@ -124,6 +134,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { if command == "get-version-info" { let versionInfo = ["command": "response", "data": ["versions": getIdentifiers()], "id": jsonDict["id"]!] as [String : Any] print("Sending version info: \(versionInfo)") + logItems.log("Sending version info: \(versionInfo)") let data = try! JSONSerialization.data(withJSONObject: versionInfo) connection.send(string: String(data: data, encoding: .utf8)!) } @@ -131,6 +142,7 @@ class RelayConnectionManager: WebSocketConnectionDelegate, ObservableObject { print("Sending val data") let v = generateValidationData() print("Validation data: \(v.base64EncodedString())") + logItems.log("Generated validation data: \(v.base64EncodedString())") let validationData = ["command": "response", "data": ["data": v.base64EncodedString()], "id": jsonDict["id"]!] as [String : Any] let data = try! JSONSerialization.data(withJSONObject: validationData) connection.send(string: String(data: data, encoding: .utf8)!)