UI fixes
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public extension Binding {
|
||||
func withSetter(_ setter: @escaping (Value) -> Void) -> Binding<Value> {
|
||||
Binding {
|
||||
wrappedValue
|
||||
} set: { [setter] newValue, _ in
|
||||
setter(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import SwiftUI
|
||||
public struct ActiveDashboardView: View {
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@Environment(\.selection) private var parentSelection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var profile: ExtensionProfile
|
||||
@State private var isLoading = true
|
||||
@State private var profileList: [Profile] = []
|
||||
@State private var selectedProfileID: Int64!
|
||||
@State private var profileList: [ProfilePreview] = []
|
||||
@State private var selectedProfileID: Int64 = 0
|
||||
@State private var alert: Alert?
|
||||
@State private var selection = DashboardPage.overview
|
||||
@State private var systemProxyAvailable = false
|
||||
@@ -73,22 +74,19 @@ public struct ActiveDashboardView: View {
|
||||
OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled)
|
||||
#endif
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.onChangeCompat(of: scenePhase) { newValue in
|
||||
if newValue == .active {
|
||||
Task {
|
||||
await doReload()
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
.onReceive(environments.selectedProfileUpdate) { _ in
|
||||
Task {
|
||||
selectedProfileID = await SharedPreferences.selectedProfileID.get()
|
||||
if profile.status.isConnected {
|
||||
await doReloadSystemProxy()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChangeCompat(of: parentSelection.wrappedValue) { newValue in
|
||||
if newValue == .dashboard {
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alertBinding($alert)
|
||||
}
|
||||
|
||||
@@ -98,8 +96,8 @@ public struct ActiveDashboardView: View {
|
||||
}
|
||||
if ApplicationLibrary.inPreview {
|
||||
profileList = [
|
||||
Profile(id: 0, name: "profile local", type: .local, path: ""),
|
||||
Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0)),
|
||||
ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")),
|
||||
ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))),
|
||||
]
|
||||
systemProxyAvailable = true
|
||||
systemProxyEnabled = true
|
||||
@@ -107,7 +105,7 @@ public struct ActiveDashboardView: View {
|
||||
|
||||
} else {
|
||||
do {
|
||||
profileList = try await ProfileManager.list()
|
||||
profileList = try await ProfileManager.list().map { ProfilePreview($0) }
|
||||
if profileList.isEmpty {
|
||||
return
|
||||
}
|
||||
@@ -116,7 +114,7 @@ public struct ActiveDashboardView: View {
|
||||
profile.id == selectedProfileID
|
||||
})
|
||||
.isEmpty {
|
||||
selectedProfileID = profileList[0].id!
|
||||
selectedProfileID = profileList[0].id
|
||||
await SharedPreferences.selectedProfileID.set(selectedProfileID)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public extension DashboardPage {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func contentView(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View {
|
||||
func contentView(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View {
|
||||
viewBuilder {
|
||||
switch self {
|
||||
case .overview:
|
||||
|
||||
@@ -5,19 +5,26 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct OverviewView: View {
|
||||
public static let NotificationUpdateSelectedProfile = Notification.Name("update-selected-profile")
|
||||
|
||||
@Environment(\.selection) private var selection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var profile: ExtensionProfile
|
||||
@Binding private var profileList: [Profile]
|
||||
@Binding private var selectedProfileID: Int64!
|
||||
@Binding private var profileList: [ProfilePreview]
|
||||
@Binding private var selectedProfileID: Int64
|
||||
@Binding private var systemProxyAvailable: Bool
|
||||
@Binding private var systemProxyEnabled: Bool
|
||||
@State private var alert: Alert?
|
||||
@State private var reasserting = false
|
||||
@State private var observer: Any?
|
||||
|
||||
public init(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) {
|
||||
private var selectedProfileIDLocal: Binding<Int64> {
|
||||
$selectedProfileID.withSetter { newValue in
|
||||
reasserting = true
|
||||
Task { [self] in
|
||||
await switchProfile(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) {
|
||||
_profileList = profileList
|
||||
_selectedProfileID = selectedProfileID
|
||||
_systemProxyAvailable = systemProxyAvailable
|
||||
@@ -45,7 +52,7 @@ public struct OverviewView: View {
|
||||
}
|
||||
}
|
||||
Section("Profile") {
|
||||
Picker(selection: $selectedProfileID) {
|
||||
Picker(selection: selectedProfileIDLocal) {
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
Text(profile.name).tag(profile.id)
|
||||
}
|
||||
@@ -63,7 +70,7 @@ public struct OverviewView: View {
|
||||
}
|
||||
Section("Profile") {
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
Picker(profile.name, selection: $selectedProfileID) {
|
||||
Picker(profile.name, selection: selectedProfileIDLocal) {
|
||||
Text("").tag(profile.id)
|
||||
}
|
||||
}
|
||||
@@ -74,44 +81,24 @@ public struct OverviewView: View {
|
||||
}
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.onChangeCompat(of: selectedProfileID) {
|
||||
reasserting = true
|
||||
Task {
|
||||
await switchProfile(selectedProfileID!)
|
||||
}
|
||||
}
|
||||
.disabled(!ApplicationLibrary.inPreview && (!profile.status.isSwitchable || reasserting))
|
||||
#if os(macOS)
|
||||
.onAppear {
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: OverviewView.NotificationUpdateSelectedProfile, object: nil, queue: nil, using: { newProfileID in
|
||||
selectedProfileID = newProfileID.object as! Int64
|
||||
})
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private nonisolated func switchProfile(_ newProfileID: Int64) async {
|
||||
private func switchProfile(_ newProfileID: Int64) async {
|
||||
await SharedPreferences.selectedProfileID.set(newProfileID)
|
||||
NotificationCenter.default.post(name: OverviewView.NotificationUpdateSelectedProfile, object: newProfileID)
|
||||
if await profile.status.isConnected {
|
||||
environments.selectedProfileUpdate.send()
|
||||
if profile.status.isConnected {
|
||||
do {
|
||||
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
||||
try await serviceReload()
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
}
|
||||
alert = Alert(error)
|
||||
}
|
||||
}
|
||||
await MainActor.run {
|
||||
reasserting = false
|
||||
}
|
||||
reasserting = false
|
||||
}
|
||||
|
||||
private nonisolated func serviceReload() async throws {
|
||||
try LibboxNewStandaloneCommandClient()?.serviceReload()
|
||||
}
|
||||
|
||||
private nonisolated func setSystemProxyEnabled(_ isEnabled: Bool) async {
|
||||
|
||||
@@ -5,53 +5,30 @@ public struct LogView: View {
|
||||
@Environment(\.selection) private var selection
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
|
||||
private let logFont = Font.system(.caption2, design: .monospaced)
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
if ApplicationLibrary.inPreview {
|
||||
let logList = [
|
||||
"(packet-tunnel) log server started",
|
||||
"INFO[0000] router: loaded geoip database: 250 codes",
|
||||
"INFO[0000] router: loaded geosite database: 1400 codes",
|
||||
"INFO[0000] router: updated default interface en0, index 11",
|
||||
"inbound/tun[0]: started at utun3",
|
||||
"sing-box started (1.666s)",
|
||||
]
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
.focusable()
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
} else if environments.logClient.logList.isEmpty {
|
||||
VStack {
|
||||
if environments.logClient.isConnected {
|
||||
Text("Empty logs")
|
||||
} else {
|
||||
Text("Service not started").onAppear {
|
||||
environments.connectLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScrollViewReader { reader in
|
||||
LogView0().environmentObject(environments.logClient)
|
||||
}
|
||||
|
||||
private struct LogView0: View {
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var logClient: CommandClient
|
||||
private let logFont = Font.system(.caption2, design: .monospaced)
|
||||
|
||||
var body: some View {
|
||||
if ApplicationLibrary.inPreview {
|
||||
let logList = [
|
||||
"(packet-tunnel) log server started",
|
||||
"INFO[0000] router: loaded geoip database: 250 codes",
|
||||
"INFO[0000] router: loaded geosite database: 1400 codes",
|
||||
"INFO[0000] router: updated default interface en0, index 11",
|
||||
"inbound/tun[0]: started at utun3",
|
||||
"sing-box started (1.666s)",
|
||||
]
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(environments.logClient.logList.enumerated()), id: \.offset) { it in
|
||||
ForEach(Array(logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
@@ -59,12 +36,6 @@ public struct LogView: View {
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
|
||||
.onChangeCompat(of: environments.logClient.logList.count) { newCount in
|
||||
withAnimation {
|
||||
reader.scrollTo(newCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
@@ -73,8 +44,45 @@ public struct LogView: View {
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
.onAppear {
|
||||
reader.scrollTo(environments.logClient.logList.count - 1)
|
||||
} else if logClient.logList.isEmpty {
|
||||
VStack {
|
||||
if logClient.isConnected {
|
||||
Text("Empty logs")
|
||||
} else {
|
||||
Text("Service not started").onAppear {
|
||||
environments.connectLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScrollViewReader { reader in
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(Array(logClient.logList.enumerated()), id: \.offset) { it in
|
||||
Text(it.element)
|
||||
.font(logFont)
|
||||
#if os(tvOS)
|
||||
.focusable()
|
||||
#endif
|
||||
Spacer(minLength: 8)
|
||||
}
|
||||
|
||||
.onChangeCompat(of: logClient.logList.count) { newCount in
|
||||
withAnimation {
|
||||
reader.scrollTo(newCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.padding()
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focusEffectDisabled()
|
||||
.focusSection()
|
||||
#endif
|
||||
.onAppear {
|
||||
reader.scrollTo(logClient.logList.count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,15 @@ public struct EditProfileView: View {
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
#endif
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var profile: Profile
|
||||
|
||||
@State private var isLoading = false
|
||||
@State private var isChanged = false
|
||||
@State private var alert: Alert?
|
||||
private let updateCallback: (() -> Void)?
|
||||
public init(_ updateCallback: (() -> Void)? = nil) {
|
||||
self.updateCallback = updateCallback
|
||||
}
|
||||
|
||||
public init() {}
|
||||
public var body: some View {
|
||||
FormView {
|
||||
FormItem("Name") {
|
||||
@@ -170,7 +168,7 @@ public struct EditProfileView: View {
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
|
||||
try await profile.updateRemoteProfile()
|
||||
await performCallback()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
}
|
||||
@@ -183,7 +181,7 @@ public struct EditProfileView: View {
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
await performCallback()
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -201,14 +199,6 @@ public struct EditProfileView: View {
|
||||
}
|
||||
isChanged = false
|
||||
isLoading = false
|
||||
await performCallback()
|
||||
}
|
||||
|
||||
private func performCallback() async {
|
||||
if let updateCallback {
|
||||
updateCallback()
|
||||
} else {
|
||||
NotificationCenter.default.post(name: ProfileView.notificationName, object: nil)
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public struct NewProfileView: View {
|
||||
public static let windowID = "new-profile"
|
||||
#endif
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var isSaving = false
|
||||
@@ -27,9 +28,7 @@ public struct NewProfileView: View {
|
||||
public let url: String
|
||||
}
|
||||
|
||||
private let callback: (() async -> Void)?
|
||||
public init(_ importRequest: ImportRequest? = nil, _ callback: (() async -> Void)? = nil) {
|
||||
self.callback = callback
|
||||
public init(_ importRequest: ImportRequest? = nil) {
|
||||
if let importRequest {
|
||||
_profileName = .init(initialValue: importRequest.name)
|
||||
_profileType = .init(initialValue: .remote)
|
||||
@@ -156,12 +155,9 @@ public struct NewProfileView: View {
|
||||
alert = Alert(error)
|
||||
return
|
||||
}
|
||||
if let callback {
|
||||
await callback()
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.post(name: ProfileView.notificationName, object: nil)
|
||||
resetFields()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct ProfileView: View {
|
||||
public static let notificationName = Notification.Name("\(FilePath.packageName).update-profile")
|
||||
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@Environment(\.importProfile) private var importProfile
|
||||
@Environment(\.importRemoteProfile) private var importRemoteProfile
|
||||
@State private var importRemoteProfileRequest: NewProfileView.ImportRequest?
|
||||
@@ -17,7 +16,7 @@ public struct ProfileView: View {
|
||||
@State private var isUpdating = false
|
||||
|
||||
@State private var alert: Alert?
|
||||
@State private var profileList: [Profile] = []
|
||||
@State private var profileList: [ProfilePreview] = []
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
@State private var editMode = EditMode.inactive
|
||||
@@ -29,10 +28,7 @@ public struct ProfileView: View {
|
||||
@Environment(\.devicePickerSupports) private var devicePickerSupports
|
||||
#endif
|
||||
|
||||
@State private var observer: Any?
|
||||
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
if isLoading {
|
||||
@@ -46,17 +42,13 @@ public struct ProfileView: View {
|
||||
ZStack {
|
||||
if let importRemoteProfileRequest {
|
||||
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
|
||||
NewProfileView(importRemoteProfileRequest) {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView(importRemoteProfileRequest)
|
||||
}
|
||||
}
|
||||
FormView {
|
||||
#if os(iOS)
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
@@ -64,9 +56,7 @@ public struct ProfileView: View {
|
||||
#elseif os(tvOS)
|
||||
Section {
|
||||
NavigationLink {
|
||||
NewProfileView {
|
||||
await doReload()
|
||||
}
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
@@ -85,7 +75,7 @@ public struct ProfileView: View {
|
||||
Text("Empty profiles")
|
||||
} else {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
viewBuilder {
|
||||
if editMode.isEditing == true {
|
||||
Text(profile.name)
|
||||
@@ -106,7 +96,7 @@ public struct ProfileView: View {
|
||||
} else {
|
||||
FormView {
|
||||
List {
|
||||
ForEach(profileList, id: \.mustID) { profile in
|
||||
ForEach(profileList, id: \.id) { profile in
|
||||
ProfileItem(self, profile)
|
||||
}
|
||||
.onMove(perform: moveProfile)
|
||||
@@ -128,15 +118,6 @@ public struct ProfileView: View {
|
||||
importRemoteProfile.wrappedValue = nil
|
||||
createImportRemoteProfileDialog(remoteProfile)
|
||||
}
|
||||
#if os(macOS)
|
||||
if observer == nil {
|
||||
observer = NotificationCenter.default.addObserver(forName: ProfileView.notificationName, object: nil, queue: .main) { _ in
|
||||
Task {
|
||||
await doReload()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onChangeCompat(of: importProfile.wrappedValue) { newValue in
|
||||
if let newValue {
|
||||
@@ -150,23 +131,14 @@ public struct ProfileView: View {
|
||||
createImportRemoteProfileDialog(newValue)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
.onDisappear {
|
||||
if let observer {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
observer = nil
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
profileList = []
|
||||
isLoading = true
|
||||
// not updated, but why?
|
||||
// Task {
|
||||
// await doReload()
|
||||
// }
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button {
|
||||
openWindow(id: NewProfileView.windowID)
|
||||
} label: {
|
||||
Label("New Profile", systemImage: "plus.square.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -232,18 +204,15 @@ public struct ProfileView: View {
|
||||
private func doReload() async {
|
||||
if ApplicationLibrary.inPreview {
|
||||
profileList = [
|
||||
Profile(id: 0, name: "profile local", type: .local, path: ""),
|
||||
Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0)),
|
||||
ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")),
|
||||
ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))),
|
||||
]
|
||||
} else {
|
||||
defer {
|
||||
isLoading = false
|
||||
}
|
||||
do {
|
||||
if !profileList.isEmpty {
|
||||
profileList.removeAll()
|
||||
}
|
||||
profileList = try await ProfileManager.list()
|
||||
profileList = try await ProfileManager.list().map { ProfilePreview($0) }
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
return
|
||||
@@ -279,11 +248,12 @@ public struct ProfileView: View {
|
||||
private func moveProfile(from source: IndexSet, to destination: Int) {
|
||||
profileList.move(fromOffsets: source, toOffset: destination)
|
||||
for (index, profile) in profileList.enumerated() {
|
||||
profile.order = UInt32(index)
|
||||
profileList[index].order = UInt32(index)
|
||||
profile.origin.order = UInt32(index)
|
||||
}
|
||||
Task {
|
||||
do {
|
||||
try await ProfileManager.update(profileList)
|
||||
try await ProfileManager.update(profileList.map(\.origin))
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
}
|
||||
@@ -292,7 +262,7 @@ public struct ProfileView: View {
|
||||
|
||||
private func deleteProfile(where profileIndex: IndexSet) {
|
||||
let profileToDelete = profileIndex.map { index in
|
||||
profileList[index]
|
||||
profileList[index].origin
|
||||
}
|
||||
profileList.remove(atOffsets: profileIndex)
|
||||
Task {
|
||||
@@ -306,16 +276,16 @@ public struct ProfileView: View {
|
||||
|
||||
public struct ProfileItem: View {
|
||||
private let parent: ProfileView
|
||||
private let profile: Profile
|
||||
public init(_ parent: ProfileView, _ profile: Profile) {
|
||||
@State private var profile: ProfilePreview
|
||||
public init(_ parent: ProfileView, _ profile: ProfilePreview) {
|
||||
self.parent = parent
|
||||
self.profile = profile
|
||||
_profile = State(initialValue: profile)
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
#if os(iOS) || os(macOS)
|
||||
if #available(iOS 16.0, macOS 13.0,*) {
|
||||
body0.draggable(profile)
|
||||
body0.draggable(profile.origin)
|
||||
} else {
|
||||
body0
|
||||
}
|
||||
@@ -329,23 +299,20 @@ public struct ProfileView: View {
|
||||
viewBuilder {
|
||||
#if !os(macOS)
|
||||
NavigationLink {
|
||||
EditProfileView {
|
||||
Task {
|
||||
await parent.doReload()
|
||||
}
|
||||
}.environmentObject(profile)
|
||||
EditProfileView().environmentObject(profile.origin)
|
||||
} label: {
|
||||
Text(profile.name)
|
||||
}
|
||||
.contextMenu {
|
||||
ProfileShareButton(parent.$alert, profile) {
|
||||
ProfileShareButton(parent.$alert, profile.origin) {
|
||||
Label("Share", systemImage: "square.and.arrow.up.fill")
|
||||
}
|
||||
if profile.type == .remote {
|
||||
Button {
|
||||
parent.isUpdating = true
|
||||
Task {
|
||||
await parent.updateProfile(profile)
|
||||
await parent.updateProfile(profile.origin)
|
||||
profile = ProfilePreview(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Label("Update", systemImage: "arrow.clockwise")
|
||||
@@ -353,7 +320,7 @@ public struct ProfileView: View {
|
||||
}
|
||||
Button(role: .destructive) {
|
||||
Task {
|
||||
await parent.deleteProfile(profile)
|
||||
await parent.deleteProfile(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash.fill")
|
||||
@@ -365,7 +332,7 @@ public struct ProfileView: View {
|
||||
Text(profile.name)
|
||||
if profile.type == .remote {
|
||||
Spacer(minLength: 4)
|
||||
Text("Last Updated: \(profile.lastUpdatedString)").font(.caption)
|
||||
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
@@ -373,23 +340,24 @@ public struct ProfileView: View {
|
||||
Button {
|
||||
parent.isUpdating = true
|
||||
Task {
|
||||
await parent.updateProfile(profile)
|
||||
await parent.updateProfile(profile.origin)
|
||||
profile = ProfilePreview(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
}
|
||||
ProfileShareButton(parent.$alert, profile) {
|
||||
ProfileShareButton(parent.$alert, profile.origin) {
|
||||
Image(systemName: "square.and.arrow.up.fill")
|
||||
}
|
||||
Button {
|
||||
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.mustID)
|
||||
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.id)
|
||||
} label: {
|
||||
Image(systemName: "pencil")
|
||||
}
|
||||
Button {
|
||||
Task {
|
||||
await parent.deleteProfile(profile)
|
||||
await parent.deleteProfile(profile.origin)
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "trash.fill")
|
||||
|
||||
Reference in New Issue
Block a user