another.im-ios/Monal/Classes/DebugView.swift
2024-11-18 15:53:52 +01:00

203 lines
7.7 KiB
Swift

//
// LogView.swift
// Monal
//
// Created by Zain Ashraf on 3/23/24.
// Copyright © 2024 monal-im.org. All rights reserved.
//
class DebugDefaultDB: ObservableObject {
@defaultsDB("udpLoggerEnabled")
var udpLoggerEnabled: Bool
@defaultsDB("udpLoggerPort")
var udpLoggerPort: String
@defaultsDB("udpLoggerHostname")
var udpLoggerHostname: String
@defaultsDB("udpLoggerKey")
var udpLoggerKey: String
@defaultsDB("hasCompletedOnboarding")
var hasCompletedOnboarding: Bool
}
struct LogFilesView: View {
@State private var sortedLogFileInfos: [DDLogFileInfo] = []
@State private var showShareSheet:Bool = false
@State private var fileURL: URL?
@State private var showingDBExportFailedAlert = false
func refreshSortedLogfiles() {
if let sortedLogFileInfos = HelperTools.fileLogger?.logFileManager.sortedLogFileInfos {
self.sortedLogFileInfos = sortedLogFileInfos
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
refreshSortedLogfiles()
}
}
var body: some View {
VStack(alignment: .leading) {
Text("This can be used to export logfiles.\n[Learn how to read them](https://github.com/monal-im/Monal/wiki/Introduction-to-Monal-Logging#view-the-log).")
List {
Section(header: Text("Logfiles")) {
ForEach(sortedLogFileInfos, id: \.self) { logFileInfo in
Button(logFileInfo.fileName) {
fileURL = URL(fileURLWithPath: logFileInfo.filePath)
}
}
}
Section(header: Text("Database Files")) {
Button("Main Database") {
if let dbFile = DataLayer.sharedInstance().exportDB() {
self.fileURL = URL(fileURLWithPath: dbFile)
} else {
showingDBExportFailedAlert = true
}
}
Button("IPC Database") {
if let dbFile = HelperTools.exportIPCDatabase() {
self.fileURL = URL(fileURLWithPath: dbFile)
} else {
showingDBExportFailedAlert = true
}
}
}
}
.listStyle(.grouped)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.interpolatedWindowBackground)
.alert(isPresented: $showingDBExportFailedAlert) {
Alert(title: Text("Database Export Failed"), message: Text("Failed to export the database, please check the logfile for errors and try again."), dismissButton: .default(Text("Close")))
}
.sheet(isPresented:$fileURL.optionalMappedToBool()) {
if let fileURL = fileURL {
ActivityViewController(activityItems: [fileURL])
}
}
.onAppear {
refreshSortedLogfiles()
}
}
}
struct UDPConfigView: View {
@ObservedObject var defaultDB = DebugDefaultDB()
var body: some View {
VStack(alignment: .leading) {
Text("The UDP logger allows you to livestream the log to the configured IP. Please use a secure key when streaming over the internet!\n[Learn how to receive the log stream](https://github.com/monal-im/Monal/wiki/Introduction-to-Monal-Logging#stream-the-log).")
Form {
Section(header: Text("UDP Logger Configuration")) {
Toggle(isOn: $defaultDB.udpLoggerEnabled) {
Text("Enable")
}
LabeledContent("Logserver IP:") {
TextField("Logserver IP", text: $defaultDB.udpLoggerHostname, prompt: Text("Required"))
}
LabeledContent("Logserver Port:") {
TextField("Logserver Port", text: $defaultDB.udpLoggerPort, prompt: Text("Required"))
}.keyboardType(.numberPad)
LabeledContent("AES Encryption Key:") {
TextField("AES Encryption Key", text: $defaultDB.udpLoggerKey, prompt: Text("Required"))
}
}
}
.padding(0)
.textFieldStyle(.roundedBorder)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.interpolatedWindowBackground)
}
}
struct CrashTestingView: View {
@ObservedObject var defaultDB = DebugDefaultDB()
var body: some View {
VStack(alignment:.leading, spacing: 25) {
Section(header: Text("Some debug settings.")) {
Toggle(isOn: $defaultDB.hasCompletedOnboarding) {
Text("Don't show onboarding")
}
}
Text("The following buttons allow you to forcefully crash the app using several different methods to test the crash handling.")
Group {
Button("Try to call unknown handler method") {
DispatchQueue.global(qos: .default).async(execute: {
HelperTools.flushLogs(withTimeout: 0.100)
let handler = MLHandler(delegate: self, handlerName: "IDontKnowThis", andBoundArguments: [:])
handler.call(withArguments: nil)
})
}
Button("Bad Access Crash") {
HelperTools.flushLogs(withTimeout: 0.100)
let delegate: AnyClass? = NSClassFromString("MonalAppDelegate")
print(delegate.unsafelyUnwrapped.audiovisualTypes())
}
Button("Assertion Crash") {
HelperTools.flushLogs(withTimeout: 0.100)
assert(false)
}
Button("Fatal Error Crash") {
HelperTools.flushLogs(withTimeout: 0.100)
fatalError("fatalError_example")
}
Button("Nil Crash") {
HelperTools.flushLogs(withTimeout: 0.100)
let crasher:Int? = nil
print(crasher!)
}
}.foregroundColor(.red)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.interpolatedWindowBackground)
}
}
struct DebugView: View {
@StateObject private var overlay = LoadingOverlayState()
var body: some View {
TabView {
LogFilesView()
.tabItem {
Image(systemName: "list.bullet")
Text("Logs")
}
UDPConfigView()
.tabItem {
Image(systemName: "gear")
Text("UDP Logger")
}
CrashTestingView()
.tabItem {
Image(systemName: "bolt.fill")
Text("Crash Testing")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
.addLoadingOverlay(overlay)
.navigationBarItems(trailing:Button("Reconnect All") {
showLoadingOverlay(overlay, headline: "Reconnecting", description: "Will log out and reconnect all (connected) accounts.") {
MLXMPPManager.sharedInstance().reconnectAll()
return after(seconds:3.0)
}
})
}
}
#Preview {
NavigationStack {
DebugView()
}
}