Files
UUVPN/iOS-SwiftUI-Code/ApplicationLibrary/Views/Profile/EditProfileContentView.swift
T
2025-01-22 14:09:10 +08:00

155 lines
5.3 KiB
Swift

#if os(iOS) || os(macOS)
import Foundation
import Library
import SwiftUI
@MainActor
public struct EditProfileContentView: View {
public struct Context: Codable, Hashable {
public let profileID: Int64
public let readOnly: Bool
}
private let profileID: Int64?
private let readOnly: Bool
public init(_ context: Context?) {
profileID = context?.profileID
readOnly = context?.readOnly == true
}
@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 {
ProgressView().onAppear {
Task {
await loadContent()
}
}
} else {
viewBuilder {
if readOnly {
TextEditor(text: .constant(profileContent))
} else {
TextEditor(text: $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)
.textInputAutocapitalization(.none)
.background(Color(UIColor.secondarySystemGroupedBackground))
#elseif os(macOS)
.padding()
#endif
.onChangeCompat(of: profileContent) {
isChanged = true
}
}
}
.alertBinding($alert)
.navigationTitle(navigationTitle)
#if os(macOS)
.toolbar {
ToolbarItemGroup(placement: .navigation) {
if !readOnly {
Button {
Task {
await saveContent()
}
} label: {
Label("Save", image: "save")
}
.disabled(!isChanged)
} else {
Button {
NSPasteboard.general.setString(profileContent, forType: .fileContents)
} label: {
Label("Copy", systemImage: "clipboard.fill")
}
}
}
}
#elseif os(iOS)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
if !readOnly {
Button("Save") {
Task {
await saveContent()
}
}.disabled(!isChanged)
} else {
Button("Copy") {
UIPasteboard.general.string = profileContent
}
}
}
}
.navigationBarTitleDisplayMode(.inline)
#endif
}
private var navigationTitle: String {
if readOnly {
return "View Content"
} else {
return "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