Improve UI

This commit is contained in:
世界
2024-02-08 10:37:37 +08:00
parent 70d43abd52
commit 1f6e99f576
32 changed files with 1201 additions and 646 deletions
@@ -5,10 +5,6 @@
@MainActor
public struct EditProfileContentView: View {
#if os(macOS)
public static let windowID = "edit-profile-content"
#endif
public struct Context: Codable, Hashable {
public let profileID: Int64
public let readOnly: Bool
@@ -4,10 +4,6 @@ import SwiftUI
@MainActor
public struct EditProfileView: View {
#if os(macOS)
@Environment(\.openWindow) private var openWindow
#endif
@EnvironmentObject private var environments: ExtensionEnvironments
@Environment(\.dismiss) private var dismiss
@EnvironmentObject private var profile: Profile
@@ -58,39 +54,45 @@ public struct EditProfileView: View {
FormTextItem("Last Updated", profile.lastUpdatedString)
}
}
#if os(iOS) || os(tvOS)
Section("Action") {
if profile.type != .remote {
#if os(iOS)
NavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
} label: {
Text("Edit Content").foregroundColor(.accentColor)
}
#endif
} else {
#if os(iOS)
NavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
} label: {
Text("View Content").foregroundColor(.accentColor)
}
#endif
Button("Update") {
isLoading = true
Task {
await updateProfile()
}
Section("Action") {
if profile.type != .remote {
#if os(iOS) || os(macOS)
NavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
} label: {
Label("Edit Content", systemImage: "pencil")
.foregroundColor(.accentColor)
}
.disabled(isLoading)
}
Button("Delete", role: .destructive) {
#endif
} else {
#if os(iOS) || os(macOS)
NavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
} label: {
Label("View Content", systemImage: "doc.fill")
.foregroundColor(.accentColor)
}
#endif
FormButton {
isLoading = true
Task {
await deleteProfile()
await updateProfile()
}
} label: {
Label("Update", systemImage: "arrow.clockwise")
}
.foregroundColor(.accentColor)
.disabled(isLoading)
}
#endif
FormButton(role: .destructive) {
Task {
await deleteProfile()
}
} label: {
Label("Delete", systemImage: "trash.fill")
}
.foregroundColor(.red)
}
}
.onChangeCompat(of: profile.name) {
isChanged = true
@@ -114,30 +116,6 @@ public struct EditProfileView: View {
Image("save", bundle: ApplicationLibrary.bundle, label: Text("Save"))
}
.disabled(isLoading || !isChanged)
if profile.type != .remote {
Button {
openWindow(id: EditProfileContentView.windowID, value: EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
} label: {
Label("Edit Content", systemImage: "pencil")
}
.disabled(isLoading)
} else {
Button {
isLoading = true
Task {
await updateProfile()
}
} label: {
Label("Update", systemImage: "arrow.clockwise")
}
.disabled(isLoading)
Button {
openWindow(id: EditProfileContentView.windowID, value: EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
} label: {
Label("View Content", systemImage: "doc.text.fill")
}
.disabled(isLoading)
}
}
}
#elseif os(iOS)
@@ -1,58 +0,0 @@
import Library
import SwiftUI
#if os(macOS)
@MainActor
public struct EditProfileWindowView: View {
public static let windowID = "edit-profile"
private var profileID: Int64?
public init(_ profileID: Int64?) {
self.profileID = profileID
}
@Environment(\.dismiss) private var dismiss
@State private var isLoading = true
@State private var profile: Profile!
@State private var alert: Alert?
public var body: some View {
viewBuilder {
if isLoading {
ProgressView().onAppear {
Task {
await doReload()
}
}
} else {
EditProfileView().environmentObject(profile!)
}
}
.alertBinding($alert)
.onExitCommand {
dismiss()
}
}
private func doReload() async {
guard let profileID else {
alert = Alert(errorMessage: "Context destroyed")
return
}
do {
profile = try await ProfileManager.get(profileID)
} catch {
alert = Alert(error)
return
}
if profile == nil {
alert = Alert(errorMessage: "Profile deleted")
return
}
isLoading = false
}
}
#endif
@@ -5,10 +5,6 @@ import SwiftUI
@MainActor
public struct NewProfileView: View {
#if os(macOS)
public static let windowID = "new-profile"
#endif
@EnvironmentObject private var environments: ExtensionEnvironments
@Environment(\.dismiss) private var dismiss
@@ -100,11 +96,13 @@ public struct NewProfileView: View {
}
Section {
if !isSaving {
Button("Create") {
FormButton {
isSaving = true
Task {
await createProfile()
}
} label: {
Label("Create", systemImage: "doc.fill.badge.plus")
}
} else {
ProgressView()
@@ -21,8 +21,6 @@ public struct ProfileView: View {
#if os(iOS) || os(tvOS)
@State private var editMode = EditMode.inactive
#elseif os(macOS)
@Environment(\.openWindow) private var openWindow
#endif
#if os(tvOS)
@@ -39,73 +37,67 @@ public struct ProfileView: View {
}
}
} else {
#if os(iOS) || os(tvOS)
ZStack {
if let importRemoteProfileRequest {
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
NewProfileView(importRemoteProfileRequest)
}
ZStack {
if let importRemoteProfileRequest {
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
NewProfileView(importRemoteProfileRequest)
}
FormView {
#if os(iOS)
}
FormView {
#if os(iOS)
NavigationLink {
NewProfileView()
} label: {
Text("New Profile").foregroundColor(.accentColor)
}
.disabled(editMode.isEditing)
#elseif os(macOS)
NavigationLink {
NewProfileView()
} label: {
Text("New Profile")
}
#elseif os(tvOS)
Section {
NavigationLink {
NewProfileView()
} label: {
Text("New Profile").foregroundColor(.accentColor)
}
.disabled(editMode.isEditing)
#elseif os(tvOS)
Section {
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
NavigationLink {
NewProfileView()
} label: {
Text("New Profile").foregroundColor(.accentColor)
}
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
NavigationLink {
ImportProfileView {
await doReload()
}
} label: {
Text("Import Profile").foregroundColor(.accentColor)
ImportProfileView {
await doReload()
}
} label: {
Text("Import Profile").foregroundColor(.accentColor)
}
}
#endif
if profileList.isEmpty {
Text("Empty profiles")
} else {
List {
ForEach(profileList, id: \.id) { profile in
viewBuilder {
}
#endif
if profileList.isEmpty {
Text("Empty profiles")
} else {
List {
ForEach(profileList, id: \.id) { profile in
viewBuilder {
#if os(iOS) || os(tvOS)
if editMode.isEditing == true {
Text(profile.name)
} else {
ProfileItem(self, profile)
}
}
#else
ProfileItem(self, profile)
#endif
}
.onMove(perform: moveProfile)
.onDelete(perform: deleteProfile)
}
}
}
}
#elseif os(macOS)
if profileList.isEmpty {
Text("Empty profiles")
} else {
FormView {
List {
ForEach(profileList, id: \.id) { profile in
ProfileItem(self, profile)
}
.onMove(perform: moveProfile)
.onDelete(perform: deleteProfile)
}
}
}
#endif
}
}
}
.disabled(isUpdating)
@@ -140,17 +132,7 @@ public struct ProfileView: View {
// await doReload()
// }
}
#if os(macOS)
.toolbar {
ToolbarItem {
Button {
openWindow(id: NewProfileView.windowID)
} label: {
Label("New Profile", systemImage: "plus.square.fill")
}
}
}
#elseif os(iOS)
#if os(iOS)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton().disabled(profileList.isEmpty)
@@ -202,11 +184,7 @@ public struct ProfileView: View {
title: Text("Import Remote Profile"),
message: Text("Are you sure to import remote profile \(newValue.name)? You will connect to \(newValue.host) to download the configuration."),
primaryButton: .default(Text("Import")) {
#if os(iOS) || os(tvOS)
importRemoteProfilePresented = true
#elseif os(macOS)
openWindow(id: NewProfileView.windowID, value: importRemoteProfileRequest!)
#endif
importRemoteProfilePresented = true
},
secondaryButton: .cancel()
)
@@ -285,6 +263,7 @@ public struct ProfileView: View {
}
}
@MainActor
public struct ProfileItem: View {
private let parent: ProfileView
@State private var profile: ProfilePreview
@@ -307,7 +286,6 @@ public struct ProfileView: View {
#endif
}
@MainActor
private var body0: some View {
viewBuilder {
#if !os(macOS)
@@ -346,57 +324,63 @@ public struct ProfileView: View {
}
} label: {
Label("Delete", systemImage: "trash.fill")
.foregroundColor(.red)
}
}
#else
HStack {
VStack(alignment: .leading) {
Text(profile.name)
if profile.type == .remote {
Spacer(minLength: 4)
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
}
}
NavigationLink {
EditProfileView().environmentObject(profile.origin)
} label: {
HStack {
if profile.type == .remote {
VStack(alignment: .leading) {
Text(profile.name)
if profile.type == .remote {
Spacer(minLength: 4)
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
}
}
HStack {
if profile.type == .remote {
Button {
parent.isUpdating = true
Task {
await parent.updateProfile(profile.origin)
profile = ProfilePreview(profile.origin)
}
} label: {
Image(systemName: "arrow.clockwise")
}
.padding(.leading, 4)
Button {
shareLinkPresented = true
} label: {
Image(systemName: "qrcode")
}
.padding(.leading, 4)
.popover(isPresented: $shareLinkPresented, arrowEdge: .bottom) {
shareLinkView
}
}
ProfileShareButton(parent.$alert, profile.origin) {
Image(systemName: "square.and.arrow.up.fill")
}
.padding(.leading, 4)
Button {
parent.isUpdating = true
Task {
await parent.updateProfile(profile.origin)
profile = ProfilePreview(profile.origin)
await parent.deleteProfile(profile.origin)
}
} label: {
Image(systemName: "arrow.clockwise")
}
Button {
shareLinkPresented = true
} label: {
Image(systemName: "qrcode")
}
.popover(isPresented: $shareLinkPresented, arrowEdge: .bottom) {
shareLinkView
Image(systemName: "trash.fill")
}
.padding([.leading, .trailing], 4)
}
ProfileShareButton(parent.$alert, profile.origin) {
Image(systemName: "square.and.arrow.up.fill")
}
Button {
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.id)
} label: {
Image(systemName: "pencil")
}
Button {
Task {
await parent.deleteProfile(profile.origin)
}
} label: {
Image(systemName: "trash.fill")
}
.buttonStyle(.plain)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
#endif
}
}
@@ -412,6 +396,9 @@ public struct ProfileView: View {
shareLinkView0
}
}
#elseif os(macOS)
shareLinkView0
.frame(minWidth: 300, minHeight: 300)
#else
shareLinkView0
#endif