Init commit
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import Foundation
|
||||
import Libbox
|
||||
import Library
|
||||
import SwiftUI
|
||||
|
||||
public class LogClient: ObservableObject {
|
||||
private var maxLines: Int
|
||||
@Published public var isConnected: Bool
|
||||
@Published public var logList: [String]
|
||||
|
||||
private var commandClient: LibboxCommandClient!
|
||||
private var connectTask: Task<Void, Error>?
|
||||
|
||||
public init(_ maxLines: Int) {
|
||||
self.maxLines = maxLines
|
||||
isConnected = false
|
||||
logList = []
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let connectTask {
|
||||
connectTask.cancel()
|
||||
}
|
||||
if let commandClient {
|
||||
try? commandClient.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
public func reconnect() {
|
||||
if isConnected {
|
||||
return
|
||||
}
|
||||
if let connectTask {
|
||||
connectTask.cancel()
|
||||
}
|
||||
connectTask = Task.detached {
|
||||
await self.connect()
|
||||
}
|
||||
}
|
||||
|
||||
private func connect() async {
|
||||
let clientOptions = LibboxCommandClientOptions()
|
||||
clientOptions.command = LibboxCommandLog
|
||||
clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC)
|
||||
let client = LibboxNewCommandClient(FilePath.sharedDirectory.relativePath, logHandler(self), clientOptions)!
|
||||
|
||||
do {
|
||||
for i in 0 ..< 10 {
|
||||
try await Task.sleep(nanoseconds: UInt64(Double(100 + (i * 50)) * Double(NSEC_PER_MSEC)))
|
||||
try Task.checkCancellation()
|
||||
let isConnected: Bool
|
||||
do {
|
||||
try client.connect()
|
||||
isConnected = true
|
||||
} catch {
|
||||
isConnected = false
|
||||
}
|
||||
try Task.checkCancellation()
|
||||
if isConnected {
|
||||
commandClient = client
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
try? client.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private class logHandler: NSObject, LibboxCommandClientHandlerProtocol {
|
||||
private let logClient: LogClient
|
||||
|
||||
init(_ logClient: LogClient) {
|
||||
self.logClient = logClient
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func connected() {
|
||||
logClient.logList.removeAll()
|
||||
logClient.isConnected = true
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func disconnected(_ message: String?) {
|
||||
if let message {
|
||||
logClient.logList.append("(log client closed) \(message)")
|
||||
} else {
|
||||
logClient.logList.append("(log client closed)")
|
||||
}
|
||||
try? logClient.commandClient?.disconnect()
|
||||
logClient.commandClient = nil
|
||||
logClient.isConnected = false
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func writeLog(_ message: String?) {
|
||||
guard let message else {
|
||||
return
|
||||
}
|
||||
if logClient.logList.count > logClient.maxLines {
|
||||
logClient.logList.removeFirst()
|
||||
}
|
||||
logClient.logList.append(message)
|
||||
}
|
||||
|
||||
func writeStatus(_: LibboxStatusMessage?) {}
|
||||
func writeGroups(_: LibboxOutboundGroupIteratorProtocol?) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct LogView: View {
|
||||
@Environment(\.logClient) private var logClient
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
viewBuilder {
|
||||
if let logClient = logClient.wrappedValue {
|
||||
LogView0().environmentObject(logClient)
|
||||
} else {
|
||||
Text("Service not started")
|
||||
}
|
||||
}
|
||||
.navigationTitle("Logs")
|
||||
}
|
||||
|
||||
private struct LogView0: View {
|
||||
@Environment(\.selection) private var selection
|
||||
@Environment(\.extensionProfile) private var extensionProfile
|
||||
@EnvironmentObject private var logClient: LogClient
|
||||
|
||||
private let logFont = Font.system(.caption2, design: .monospaced)
|
||||
|
||||
var body: some View {
|
||||
viewBuilder {
|
||||
if logClient.logList.isEmpty {
|
||||
VStack {
|
||||
if logClient.isConnected {
|
||||
Text("Empty logs")
|
||||
} else {
|
||||
Text("Service not started").onAppear(perform: connectLog)
|
||||
}
|
||||
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||
} else {
|
||||
ScrollViewReader { reader in
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(logClient.logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
Spacer(minLength: 5)
|
||||
}
|
||||
|
||||
.onChange(of: logClient.logList.count) { newCount in
|
||||
withAnimation {
|
||||
reader.scrollTo(newCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}
|
||||
.onAppear {
|
||||
reader.scrollTo(logClient.logList.count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func connectLog() {
|
||||
guard let profile = extensionProfile.wrappedValue else {
|
||||
return
|
||||
}
|
||||
if profile.status.isConnected, !logClient.isConnected {
|
||||
logClient.reconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user