Refactor to MVVM architecture

This commit is contained in:
世界
2025-11-26 22:25:48 +08:00
parent 61f1036f57
commit b71cbae382
31 changed files with 1551 additions and 1229 deletions
@@ -10,42 +10,34 @@
public let readOnly: Bool
}
private let profileID: Int64?
private let readOnly: Bool
@StateObject private var viewModel: EditProfileContentViewModel
public init(_ context: Context?) {
profileID = context?.profileID
readOnly = context?.readOnly == true
_viewModel = StateObject(wrappedValue: EditProfileContentViewModel(profileID: context?.profileID))
}
@Environment(\.dismiss) private var dismiss
@State private var isLoading = true
@State private var profile: Profile!
@State private var profileContent = ""
@State private var isChanged = false
@State private var alert: Alert?
public var body: some View {
viewBuilder {
if isLoading {
if viewModel.isLoading {
ProgressView().onAppear {
Task {
await loadContent()
await viewModel.loadContent()
}
}
} else {
viewBuilder {
if readOnly {
TextEditor(text: .constant(profileContent))
TextEditor(text: .constant(viewModel.profileContent))
} else {
TextEditor(text: $profileContent)
TextEditor(text: $viewModel.profileContent)
}
}
.font(Font.system(.caption2, design: .monospaced))
.autocorrectionDisabled(true)
// https://stackoverflow.com/questions/66721935/swiftui-how-to-disable-the-smart-quotes-in-texteditor
// https://stackoverflow.com/questions/74034171/textfield-with-autocorrectiondisabled-still-shows-predictive-text-bar
.textContentType(.init(rawValue: ""))
#if os(iOS)
.keyboardType(.asciiCapable)
@@ -54,12 +46,12 @@
#elseif os(macOS)
.padding()
#endif
.onChangeCompat(of: profileContent) {
isChanged = true
.onChangeCompat(of: viewModel.profileContent) {
viewModel.markAsChanged()
}
}
}
.alertBinding($alert)
.alertBinding($viewModel.alert)
.navigationTitle(navigationTitle)
#if os(macOS)
.toolbar {
@@ -67,15 +59,15 @@
if !readOnly {
Button {
Task {
await saveContent()
await viewModel.saveContent()
}
} label: {
Label("Save", image: "save")
}
.disabled(!isChanged)
.disabled(!viewModel.isChanged)
} else {
Button {
NSPasteboard.general.setString(profileContent, forType: .fileContents)
NSPasteboard.general.setString(viewModel.profileContent, forType: .fileContents)
} label: {
Label("Copy", systemImage: "clipboard.fill")
}
@@ -88,12 +80,12 @@
if !readOnly {
Button("Save") {
Task {
await saveContent()
await viewModel.saveContent()
}
}.disabled(!isChanged)
}.disabled(!viewModel.isChanged)
} else {
Button("Copy") {
UIPasteboard.general.string = profileContent
UIPasteboard.general.string = viewModel.profileContent
}
}
}
@@ -109,46 +101,6 @@
return String(localized: "Edit Content")
}
}
private func loadContent() async {
do {
try await loadContentBackground()
} catch {
alert = Alert(error)
}
isLoading = false
}
private nonisolated func loadContentBackground() async throws {
guard let profileID else {
throw NSError(domain: "Context destroyed", code: 0)
}
guard let profile = try await ProfileManager.get(profileID) else {
throw NSError(domain: "Profile missing", code: 0)
}
let profileContent = try profile.read()
await MainActor.run {
self.profile = profile
self.profileContent = profileContent
}
}
private func saveContent() async {
guard let profile else {
return
}
do {
try await saveContentBackground(profile)
} catch {
alert = Alert(error)
return
}
isChanged = false
}
private nonisolated func saveContentBackground(_ profile: Profile) async throws {
try await profile.write(profileContent)
}
}
#endif