Replace sheets with navigation destinations on tvOS
This commit is contained in:
@@ -61,20 +61,53 @@ import SwiftUI
|
|||||||
#if os(iOS) || os(tvOS)
|
#if os(iOS) || os(tvOS)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
toolbar
|
toolbar
|
||||||
}.sheet(isPresented: $showGroups) {
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.navigationDestination(isPresented: $showGroups) {
|
||||||
|
GroupListView()
|
||||||
|
.navigationTitle("Groups")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(isPresented: $showConnections) {
|
||||||
|
ConnectionListView()
|
||||||
|
.navigationTitle("Connections")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(isPresented: $showCardManagement) {
|
||||||
|
CardManagementView(onDisappear: {
|
||||||
|
cardConfigurationVersion += 1
|
||||||
|
})
|
||||||
|
.navigationTitle("Dashboard Items")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
.sheet(isPresented: $showGroups) {
|
||||||
groupsSheetContent
|
groupsSheetContent
|
||||||
}.sheet(isPresented: $showConnections) {
|
}.sheet(isPresented: $showConnections) {
|
||||||
connectionsSheetContent
|
connectionsSheetContent
|
||||||
}.sheet(isPresented: $showCardManagement, onDismiss: {
|
}.sheet(isPresented: $showCardManagement, onDismiss: {
|
||||||
cardConfigurationVersion += 1
|
cardConfigurationVersion += 1
|
||||||
}, content: {
|
}, content: {
|
||||||
if #available(iOS 16.0, tvOS 17.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible)
|
CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible)
|
||||||
} else {
|
} else {
|
||||||
CardManagementSheet()
|
CardManagementSheet()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if ApplicationLibrary.inPreview {
|
if ApplicationLibrary.inPreview {
|
||||||
environments.commandClient.connect()
|
environments.commandClient.connect()
|
||||||
|
|||||||
@@ -97,6 +97,55 @@ import SwiftUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
@MainActor public struct CardManagementView: View {
|
||||||
|
@StateObject private var configuration = DashboardCardConfiguration()
|
||||||
|
private let onDisappear: (() -> Void)?
|
||||||
|
|
||||||
|
public init(onDisappear: (() -> Void)? = nil) {
|
||||||
|
self.onDisappear = onDisappear
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
Group {
|
||||||
|
if configuration.isLoading {
|
||||||
|
ProgressView()
|
||||||
|
} else {
|
||||||
|
List {
|
||||||
|
ForEach(configuration.cardOrder) { card in
|
||||||
|
CardRow(
|
||||||
|
card: card,
|
||||||
|
isEnabled: configuration.isEnabled(card),
|
||||||
|
onToggle: {
|
||||||
|
configuration.toggleCard(card)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onMove { source, destination in
|
||||||
|
Task {
|
||||||
|
await configuration.moveCard(from: source, to: destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.applyContentMargins()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
|
Button("Reset", role: .destructive) {
|
||||||
|
Task {
|
||||||
|
await configuration.resetToDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
onDisappear?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private struct CardRow: View {
|
private struct CardRow: View {
|
||||||
let card: DashboardCard
|
let card: DashboardCard
|
||||||
let isEnabled: Bool
|
let isEnabled: Bool
|
||||||
|
|||||||
@@ -32,6 +32,44 @@ public struct ProfileCard: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.disabled(viewModel.isUpdating)
|
.disabled(viewModel.isUpdating)
|
||||||
|
#if os(tvOS)
|
||||||
|
.navigationDestination(isPresented: $viewModel.showNewProfile) {
|
||||||
|
NewProfileContentView(onDisappear: {
|
||||||
|
environments.profileUpdate.send()
|
||||||
|
})
|
||||||
|
.environmentObject(environments)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(isPresented: $viewModel.showManageProfiles) {
|
||||||
|
ManageProfilesView()
|
||||||
|
.environmentObject(environments)
|
||||||
|
.navigationTitle("Manage profiles")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(item: $viewModel.profileToEdit) { profile in
|
||||||
|
EditProfileView()
|
||||||
|
.environmentObject(profile)
|
||||||
|
.environmentObject(environments)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $viewModel.showQRCode) {
|
||||||
|
if let profile = selectedProfile, let remoteURL = profile.remoteURL {
|
||||||
|
QRCodeSheet(profileName: profile.name, remoteURL: remoteURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
.sheet(isPresented: $viewModel.showNewProfile, onDismiss: {
|
.sheet(isPresented: $viewModel.showNewProfile, onDismiss: {
|
||||||
environments.profileUpdate.send()
|
environments.profileUpdate.send()
|
||||||
}, content: {
|
}, content: {
|
||||||
@@ -44,13 +82,14 @@ public struct ProfileCard: View {
|
|||||||
.sheet(item: $viewModel.profileToEdit) { profile in
|
.sheet(item: $viewModel.profileToEdit) { profile in
|
||||||
editProfileSheet(for: profile)
|
editProfileSheet(for: profile)
|
||||||
}
|
}
|
||||||
#if os(iOS) || os(tvOS)
|
#if os(iOS)
|
||||||
.sheet(isPresented: $viewModel.showQRCode) {
|
.sheet(isPresented: $viewModel.showQRCode) {
|
||||||
if let profile = selectedProfile, let remoteURL = profile.remoteURL {
|
if let profile = selectedProfile, let remoteURL = profile.remoteURL {
|
||||||
QRCodeSheet(profileName: profile.name, remoteURL: remoteURL)
|
QRCodeSheet(profileName: profile.name, remoteURL: remoteURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
.alert($viewModel.alert)
|
.alert($viewModel.alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +365,41 @@ extension ProfileCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - NewProfileContentView (tvOS)
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
extension ProfileCard {
|
||||||
|
@MainActor
|
||||||
|
struct NewProfileContentView: View {
|
||||||
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
|
@State private var createdProfile: Profile?
|
||||||
|
private let onDisappear: (() -> Void)?
|
||||||
|
|
||||||
|
init(onDisappear: (() -> Void)? = nil) {
|
||||||
|
self.onDisappear = onDisappear
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if let profile = createdProfile {
|
||||||
|
EditProfileView()
|
||||||
|
.environmentObject(profile)
|
||||||
|
.environmentObject(environments)
|
||||||
|
} else {
|
||||||
|
NewProfileView { profile in
|
||||||
|
createdProfile = profile
|
||||||
|
}
|
||||||
|
.environmentObject(environments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
onDisappear?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// MARK: - ManageProfilesView
|
// MARK: - ManageProfilesView
|
||||||
|
|
||||||
extension ProfileCard {
|
extension ProfileCard {
|
||||||
|
|||||||
@@ -35,9 +35,24 @@ public struct DashboardView: View {
|
|||||||
.onChangeCompat(of: importRemoteProfile.wrappedValue) { _ in
|
.onChangeCompat(of: importRemoteProfile.wrappedValue) { _ in
|
||||||
handleImportRemoteProfile()
|
handleImportRemoteProfile()
|
||||||
}
|
}
|
||||||
|
#if os(tvOS)
|
||||||
|
.navigationDestination(item: $importRemoteProfileRequest) { request in
|
||||||
|
NewProfileView(request)
|
||||||
|
.environmentObject(environments)
|
||||||
|
.onDisappear {
|
||||||
|
environments.profileUpdate.send()
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
BackButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
.sheet(item: $importRemoteProfileRequest) { request in
|
.sheet(item: $importRemoteProfileRequest) { request in
|
||||||
importRemoteProfileSheet(for: request)
|
importRemoteProfileSheet(for: request)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.onChangeCompat(of: controlActiveState) { state in
|
.onChangeCompat(of: controlActiveState) { state in
|
||||||
guard state != .inactive, Variant.useSystemExtension, !coordinator.isLoading else { return }
|
guard state != .inactive, Variant.useSystemExtension, !coordinator.isLoading else { return }
|
||||||
|
|||||||
Reference in New Issue
Block a user