Files
sing-box-for-apple/ApplicationLibrary/Views/Profile/EditProfileContentViewModel.swift
T
2026-02-05 02:37:02 +08:00

125 lines
3.6 KiB
Swift

import Foundation
import Libbox
import Library
import SwiftUI
@MainActor
public final class EditProfileContentViewModel: BaseViewModel {
@Published public var profile: Profile?
@Published public var profileContent = ""
@Published public var isChanged = false
@Published public var configurationError: String?
private let profileID: Int64?
private var validationTask: Task<Void, Never>?
public init(profileID: Int64?) {
self.profileID = profileID
super.init()
isLoading = true
}
public func markAsChanged() {
isChanged = true
scheduleValidation()
}
public func reset() {
isLoading = true
profile = nil
profileContent = ""
isChanged = false
configurationError = nil
validationTask?.cancel()
validationTask = nil
alert = nil
}
public func scheduleValidation() {
configurationError = nil
validationTask?.cancel()
validationTask = Task {
try? await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
guard !Task.isCancelled else { return }
await checkConfiguration()
}
}
private func checkConfiguration() async {
let content = profileContent
if content.isEmpty { return }
let errorDescription: String? = await BlockingIO.run {
var error: NSError?
LibboxCheckConfig(content, &error)
return error?.localizedDescription
}
configurationError = errorDescription
}
public func formatConfiguration() async {
let content = profileContent
if content.isEmpty { return }
do {
let formatted: String? = try await BlockingIO.run {
var error: NSError?
let result = LibboxFormatConfig(content, &error)
if let error {
throw error
}
return result?.value
}
if let formatted, formatted != content {
profileContent = formatted
isChanged = true
}
} catch {
configurationError = error.localizedDescription
}
}
public func dismissConfigurationError() {
configurationError = nil
}
public func loadContent() async {
do {
try await loadContentBackground()
} catch {
alert = AlertState(error: error)
}
isLoading = false
}
private nonisolated func loadContentBackground() async throws {
guard let profileID else {
throw NSError(domain: "EditProfileContentViewModel", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Context destroyed")])
}
guard let profile = try await ProfileManager.get(profileID) else {
throw NSError(domain: "EditProfileContentViewModel", code: 0, userInfo: [NSLocalizedDescriptionKey: String(localized: "Profile missing")])
}
let profileContent = try await profile.readAsync()
await MainActor.run {
self.profile = profile
self.profileContent = profileContent
}
}
public func saveContent() async {
guard let profile else {
return
}
do {
try await saveContentBackground(profile)
} catch {
alert = AlertState(error: error)
return
}
isChanged = false
}
private nonisolated func saveContentBackground(_ profile: Profile) async throws {
let profileContent = await profileContent
try await profile.writeAsync(profileContent)
}
}