Prepare airdrop support
This commit is contained in:
@@ -26,10 +26,7 @@ public struct EditProfileContentView: View {
|
||||
@State private var profile: Profile!
|
||||
@State private var profileContent: String = ""
|
||||
@State private var isChanged = false
|
||||
|
||||
@State private var errorPresented = false
|
||||
@State private var errorMessage = ""
|
||||
@State private var fatalError = false
|
||||
@State private var alert: Alert?
|
||||
|
||||
public var body: some View {
|
||||
viewBuilder {
|
||||
@@ -60,17 +57,7 @@ public struct EditProfileContentView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $errorPresented) {
|
||||
Alert(
|
||||
title: Text("Error"),
|
||||
message: Text(errorMessage),
|
||||
dismissButton: .default(Text("Ok"), action: {
|
||||
if fatalError {
|
||||
dismiss()
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.navigationTitle(navigationTitle)
|
||||
#if os(macOS)
|
||||
.toolbar {
|
||||
@@ -115,9 +102,7 @@ public struct EditProfileContentView: View {
|
||||
do {
|
||||
try loadContent0()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
fatalError = true
|
||||
errorPresented = true
|
||||
alert = Alert(error, dismiss.callAsFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +125,7 @@ public struct EditProfileContentView: View {
|
||||
do {
|
||||
try profile.write(profileContent)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
isChanged = false
|
||||
|
||||
@@ -10,8 +10,7 @@ public struct EditProfileView: View {
|
||||
|
||||
@State private var isLoading = false
|
||||
@State private var isChanged = false
|
||||
@State private var errorPresented = false
|
||||
@State private var errorMessage = ""
|
||||
@State private var alert: Alert?
|
||||
|
||||
public init() {}
|
||||
|
||||
@@ -69,6 +68,17 @@ public struct EditProfileView: View {
|
||||
}
|
||||
.disabled(isLoading)
|
||||
}
|
||||
if #available(iOS 16.0, *) {
|
||||
ShareLink(item: profile.shareLink) {
|
||||
Text("Share")
|
||||
}
|
||||
} else {
|
||||
Button("Share") {
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
||||
windowScene.keyWindow?.rootViewController?.present(UIActivityViewController(activityItems: [profile.shareLink], applicationActivities: nil), animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -132,13 +142,7 @@ public struct EditProfileView: View {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alert(isPresented: $errorPresented) {
|
||||
Alert(
|
||||
title: Text("Error"),
|
||||
message: Text(errorMessage),
|
||||
dismissButton: .default(Text("Ok"))
|
||||
)
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.navigationTitle("Edit Profile")
|
||||
}
|
||||
|
||||
@@ -150,8 +154,7 @@ public struct EditProfileView: View {
|
||||
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
|
||||
try profile.updateRemoteProfile()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +162,7 @@ public struct EditProfileView: View {
|
||||
do {
|
||||
_ = try ProfileManager.update(profile)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
isChanged = false
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import Library
|
||||
import SwiftUI
|
||||
|
||||
@@ -16,8 +15,7 @@ import SwiftUI
|
||||
|
||||
@State private var isLoading = true
|
||||
@State private var profile: Profile!
|
||||
@State private var errorPresented = false
|
||||
@State private var errorMessage = ""
|
||||
@State private var alert: Alert?
|
||||
|
||||
public var body: some View {
|
||||
viewBuilder {
|
||||
@@ -27,19 +25,11 @@ import SwiftUI
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $errorPresented) {
|
||||
Alert(
|
||||
title: Text("Error"),
|
||||
message: Text(errorMessage),
|
||||
dismissButton: .default(Text("Ok"), action: {
|
||||
dismiss()
|
||||
})
|
||||
)
|
||||
}
|
||||
} else {
|
||||
EditProfileView().environmentObject(profile!)
|
||||
}
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.onExitCommand {
|
||||
dismiss()
|
||||
}
|
||||
@@ -47,20 +37,17 @@ import SwiftUI
|
||||
|
||||
private func doReload() async {
|
||||
guard let profileID else {
|
||||
errorMessage = "Context destroyed"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Context destroyed")
|
||||
return
|
||||
}
|
||||
do {
|
||||
profile = try ProfileManager.get(profileID)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
if profile == nil {
|
||||
errorMessage = "Profile deleted"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Profile deleted")
|
||||
return
|
||||
}
|
||||
isLoading = false
|
||||
|
||||
@@ -17,12 +17,21 @@ public struct NewProfileView: View {
|
||||
@State private var fileURL: URL!
|
||||
@State private var remotePath = ""
|
||||
@State private var pickerPresented = false
|
||||
@State private var errorPresented = false
|
||||
@State private var errorMessage = ""
|
||||
@State private var alert: Alert?
|
||||
|
||||
public struct ImportRequest: Codable, Hashable {
|
||||
public let name: String
|
||||
public let url: String
|
||||
}
|
||||
|
||||
private let callback: (() -> Void)?
|
||||
public init(_ callback: (() -> Void)? = nil) {
|
||||
public init(_ importRequest: ImportRequest? = nil, _ callback: (() -> Void)? = nil) {
|
||||
self.callback = callback
|
||||
if let importRequest {
|
||||
_profileName = .init(initialValue: importRequest.name)
|
||||
_profileType = .init(initialValue: .remote)
|
||||
_remotePath = .init(initialValue: importRequest.url)
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -88,13 +97,7 @@ public struct NewProfileView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("New Profile")
|
||||
.alert(isPresented: $errorPresented) {
|
||||
Alert(
|
||||
title: Text("Error"),
|
||||
message: Text(errorMessage),
|
||||
dismissButton: .default(Text("Ok"))
|
||||
)
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.fileImporter(
|
||||
isPresented: $pickerPresented,
|
||||
allowedContentTypes: [.json],
|
||||
@@ -106,8 +109,7 @@ public struct NewProfileView: View {
|
||||
fileURL = urls[0]
|
||||
}
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -118,26 +120,22 @@ public struct NewProfileView: View {
|
||||
isSaving = false
|
||||
}
|
||||
if profileName.isEmpty {
|
||||
errorMessage = "Missing profile name"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Missing profile name")
|
||||
return
|
||||
}
|
||||
if remotePath.isEmpty {
|
||||
if profileType == .icloud {
|
||||
errorMessage = "Missing path"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Missing path")
|
||||
return
|
||||
} else if profileType == .remote {
|
||||
errorMessage = "Missing URL"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Missing URL")
|
||||
return
|
||||
}
|
||||
}
|
||||
do {
|
||||
try createProfile0()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
await MainActor.run {
|
||||
@@ -173,13 +171,11 @@ public struct NewProfileView: View {
|
||||
let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json")
|
||||
if fileImport {
|
||||
guard let fileURL else {
|
||||
errorMessage = "Missing file"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Missing file")
|
||||
return
|
||||
}
|
||||
if !fileURL.startAccessingSecurityScopedResource() {
|
||||
errorMessage = "Missing access to selected file"
|
||||
errorPresented = true
|
||||
alert = Alert(errorMessage: "Missing access to selected file")
|
||||
return
|
||||
}
|
||||
defer {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import Foundation
|
||||
import Libbox
|
||||
import Library
|
||||
import SwiftUI
|
||||
|
||||
public struct ProfileView: View {
|
||||
public static let notificationName = Notification.Name("\(FilePath.packageName).update-profile")
|
||||
|
||||
@Environment(\.importRemoteProfile) private var importRemoteProfile
|
||||
@State private var importRemoteProfileRequest: NewProfileView.ImportRequest?
|
||||
@State private var importRemoteProfilePresented = false
|
||||
|
||||
@State private var isLoading = true
|
||||
@State private var isUpdating = false
|
||||
|
||||
@State private var errorPresented = false
|
||||
@State private var errorMessage = ""
|
||||
|
||||
@State private var alert: Alert?
|
||||
@State private var profileList: [Profile] = []
|
||||
|
||||
#if os(iOS)
|
||||
@@ -33,36 +36,51 @@ public struct ProfileView: View {
|
||||
}
|
||||
} else {
|
||||
#if os(iOS)
|
||||
FormView {
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
Task.detached {
|
||||
doReload()
|
||||
ZStack {
|
||||
if let importRemoteProfileRequest {
|
||||
NavigationLink(
|
||||
destination: NewProfileView(importRemoteProfileRequest) {
|
||||
Task.detached {
|
||||
doReload()
|
||||
}
|
||||
},
|
||||
isActive: $importRemoteProfilePresented,
|
||||
label: {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
)
|
||||
}
|
||||
.disabled(editMode.isEditing)
|
||||
if profileList.isEmpty {
|
||||
Text("Empty Profiles")
|
||||
} else {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
viewBuilder {
|
||||
if editMode.isEditing == true {
|
||||
Text(profile.name)
|
||||
} else {
|
||||
NavigationLink {
|
||||
EditProfileView().environmentObject(profile)
|
||||
} label: {
|
||||
FormView {
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
Task.detached {
|
||||
doReload()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
.disabled(editMode.isEditing)
|
||||
if profileList.isEmpty {
|
||||
Text("Empty Profiles")
|
||||
} else {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
viewBuilder {
|
||||
if editMode.isEditing == true {
|
||||
Text(profile.name)
|
||||
} else {
|
||||
NavigationLink {
|
||||
EditProfileView().environmentObject(profile)
|
||||
} label: {
|
||||
Text(profile.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onMove(perform: moveProfile)
|
||||
.onDelete(perform: deleteProfile)
|
||||
}
|
||||
.onMove(perform: moveProfile)
|
||||
.onDelete(perform: deleteProfile)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +110,9 @@ public struct ProfileView: View {
|
||||
}, label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
})
|
||||
ShareLink(item: profile.shareLink) {
|
||||
Image(systemName: "square.and.arrow.up.fill")
|
||||
}
|
||||
}
|
||||
Button(action: {
|
||||
openWindow(id: EditProfileWindowView.windowID, value: profile.mustID)
|
||||
@@ -119,8 +140,13 @@ public struct ProfileView: View {
|
||||
}
|
||||
.disabled(isUpdating)
|
||||
.navigationTitle("Profiles")
|
||||
#if os(macOS)
|
||||
.onAppear {
|
||||
.alertBinding($alert, $isLoading)
|
||||
.onAppear {
|
||||
if let remoteProfile = importRemoteProfile.wrappedValue {
|
||||
importRemoteProfile.wrappedValue = nil
|
||||
createImportRemoteProfileDialog(remoteProfile)
|
||||
}
|
||||
#if os(macOS)
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: ProfileView.notificationName, object: nil, queue: .main) { _ in
|
||||
Task.detached {
|
||||
@@ -128,40 +154,63 @@ public struct ProfileView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onChange(of: importRemoteProfile.wrappedValue) { newValue in
|
||||
if let newValue {
|
||||
importRemoteProfile.wrappedValue = nil
|
||||
createImportRemoteProfileDialog(newValue)
|
||||
}
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
observer = nil
|
||||
}
|
||||
#if os(macOS)
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button(action: {
|
||||
openWindow(id: NewProfileView.windowID)
|
||||
}, label: {
|
||||
Label("New Profile", systemImage: "plus.square.fill")
|
||||
})
|
||||
}
|
||||
observer = nil
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button(action: {
|
||||
openWindow(id: NewProfileView.windowID)
|
||||
}, label: {
|
||||
Label("New Profile", systemImage: "plus.square.fill")
|
||||
})
|
||||
}
|
||||
}
|
||||
#elseif os(iOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
EditButton().disabled(profileList.isEmpty)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
EditButton().disabled(profileList.isEmpty)
|
||||
}
|
||||
.environment(\.editMode, $editMode)
|
||||
}
|
||||
.environment(\.editMode, $editMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func createImportRemoteProfileDialog(_ newValue: LibboxImportRemoteProfile) {
|
||||
importRemoteProfileRequest = .init(name: newValue.name, url: newValue.url)
|
||||
alert = Alert(
|
||||
title: Text("Import Remote Profile"),
|
||||
message: Text("Are you sure to import remote configuration \(newValue.name)? You will connect to \(newValue.host) to download the configuration."),
|
||||
primaryButton: .default(Text("Import")) {
|
||||
#if os(iOS)
|
||||
importRemoteProfilePresented = true
|
||||
#elseif os(macOS)
|
||||
openWindow(id: NewProfileView.windowID, value: importRemoteProfileRequest!)
|
||||
#endif
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
|
||||
private func deleteSelectedProfiles(_ profileID: [Int64]) {
|
||||
do {
|
||||
if try ProfileManager.delete(by: profileID) > 0 {
|
||||
isLoading = true
|
||||
}
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +227,7 @@ public struct ProfileView: View {
|
||||
do {
|
||||
profileList = try ProfileManager.list()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -189,8 +237,7 @@ public struct ProfileView: View {
|
||||
do {
|
||||
_ = try profile.updateRemoteProfile()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
}
|
||||
isUpdating = false
|
||||
}
|
||||
@@ -200,8 +247,7 @@ public struct ProfileView: View {
|
||||
do {
|
||||
_ = try ProfileManager.delete(profile)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
isLoading = true
|
||||
@@ -216,8 +262,7 @@ public struct ProfileView: View {
|
||||
do {
|
||||
try ProfileManager.update(profileList)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -231,8 +276,7 @@ public struct ProfileView: View {
|
||||
do {
|
||||
_ = try ProfileManager.delete(profileToDelete)
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
errorPresented = true
|
||||
alert = Alert(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user