Files
sing-box-for-apple/ApplicationLibrary/Views/Setting/ServiceLogView.swift
T

119 lines
3.5 KiB
Swift

import Foundation
import Library
import SwiftUI
import UniformTypeIdentifiers
@MainActor
public struct ServiceLogView: View {
#if os(macOS)
public static let windowID = "service-log"
#endif
@Environment(\.dismiss) private var dismiss
@State private var isLoading = true
@State private var content = ""
@State private var fileExporterPresented = false
private let logFont = Font.system(.caption, design: .monospaced)
public init() {}
public var body: some View {
viewBuilder {
if isLoading {
ProgressView().onAppear {
Task {
await loadContent()
}
}
} else {
if content.isEmpty {
Text("Empty content")
} else {
ScrollView {
Text(content)
.font(logFont)
.frame(maxWidth: .infinity, alignment: .topLeading)
}
.padding()
}
}
}
#if !os(tvOS)
.toolbar {
if !content.isEmpty {
Button("Export") {
fileExporterPresented = true
}
Button("Delete", role: .destructive) {
Task {
await deleteContent()
}
}
}
}
#endif
#if !os(tvOS)
.fileExporter(
isPresented: $fileExporterPresented,
document: LogDocument(content),
contentType: .text,
defaultFilename: "service-log.txt",
onCompletion: { _ in }
)
#endif
.navigationTitle("Service Log")
#if os(tvOS)
.focusable()
#endif
}
private nonisolated func loadContent() async {
var content = ""
do {
content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log"))
} catch {}
if content.isEmpty {
do {
content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old"))
} catch {}
}
await MainActor.run { [content] in
self.content = content
isLoading = false
}
}
private nonisolated func deleteContent() async {
try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log"))
try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old"))
await MainActor.run {
dismiss()
isLoading = true
}
}
#if !os(tvOS)
private struct LogDocument: FileDocument {
static var readableContentTypes = [UTType.text]
let content: String
init(_ content: String) {
self.content = content
}
init(configuration: ReadConfiguration) throws {
if let data = configuration.file.regularFileContents {
content = String(decoding: data, as: UTF8.self)
} else {
content = ""
}
}
func fileWrapper(configuration _: WriteConfiguration) throws -> FileWrapper {
FileWrapper(regularFileWithContents: Data(content.utf8))
}
}
#endif
}