Fix for tvOS

This commit is contained in:
世界
2025-11-27 18:55:10 +08:00
parent 0bfb6d2516
commit 22de256d13
4 changed files with 155 additions and 104 deletions
@@ -128,7 +128,6 @@ public func FormNavigationLink(@ViewBuilder destination: () -> some View, @ViewB
.toolbar { .toolbar {
ToolbarItemGroup(placement: .topBarLeading) { ToolbarItemGroup(placement: .topBarLeading) {
BackButton() BackButton()
.tint(.accentColor)
} }
} }
}, label: label) }, label: label)
@@ -26,35 +26,64 @@ public struct NavigationButtonsView: View {
} }
public var body: some View { public var body: some View {
HStack(spacing: 12) { #if os(tvOS)
if showConnectionsButton { tvOSBody
Divider() #else
Text(verbatim: "\(connectionsCount)") iOSBody
.font(.subheadline) #endif
.foregroundStyle(.secondary) }
.fixedSize()
Button { #if os(tvOS)
onConnectionsTap() private var tvOSBody: some View {
} label: { viewBuilder {
Label("Connections", systemImage: "list.bullet.rectangle.portrait.fill") if showConnectionsButton {
Button {
onConnectionsTap()
} label: {
Image(systemName: "list.bullet.rectangle.portrait.fill")
}
} }
.labelStyle(.iconOnly) if showGroupsButton {
.foregroundStyle(.primary) Button {
} onGroupsTap()
if showGroupsButton { } label: {
Divider() Image(systemName: "rectangle.3.group.fill")
Text(verbatim: "\(groupsCount)") }
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize()
Button {
onGroupsTap()
} label: {
Label("Groups", systemImage: "rectangle.3.group.fill")
} }
.labelStyle(.iconOnly)
.foregroundStyle(.primary)
} }
} }
} #else
private var iOSBody: some View {
HStack(spacing: 12) {
if showConnectionsButton {
Divider()
Text(verbatim: "\(connectionsCount)")
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize()
Button {
onConnectionsTap()
} label: {
Label("Connections", systemImage: "list.bullet.rectangle.portrait.fill")
}
.labelStyle(.iconOnly)
.foregroundStyle(.primary)
}
if showGroupsButton {
Divider()
Text(verbatim: "\(groupsCount)")
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize()
Button {
onGroupsTap()
} label: {
Label("Groups", systemImage: "rectangle.3.group.fill")
}
.labelStyle(.iconOnly)
.foregroundStyle(.primary)
}
}
}
#endif
} }
@@ -3,8 +3,7 @@ import Libbox
import Library import Library
import SwiftUI import SwiftUI
@MainActor @MainActor public struct ActiveDashboardView: View {
public struct ActiveDashboardView: View {
@Environment(\.scenePhase) private var scenePhase @Environment(\.scenePhase) private var scenePhase
@EnvironmentObject private var environments: ExtensionEnvironments @EnvironmentObject private var environments: ExtensionEnvironments
@EnvironmentObject private var profile: ExtensionProfile @EnvironmentObject private var profile: ExtensionProfile
@@ -25,35 +24,48 @@ public struct ActiveDashboardView: View {
public var body: some View { public var body: some View {
if coordinator.isLoading { if coordinator.isLoading {
ProgressView() ProgressView().onAppear {
.onAppear { coordinator.onEmptyProfilesChange = {
coordinator.onEmptyProfilesChange = { environments.emptyProfiles = $0 } environments.emptyProfiles = $0
Task { await coordinator.reload() }
} }
Task {
await coordinator.reload()
}
}
} else { } else {
content content.onAppear {
.onAppear { guard !ApplicationLibrary.inPreview else {
guard !ApplicationLibrary.inPreview else { return } return
Task { await coordinator.reloadSystemProxy() }
} }
.onChangeCompat(of: profile.status) { status in Task {
guard !ApplicationLibrary.inPreview, status == .connected else { return } await coordinator.reloadSystemProxy()
Task { await coordinator.reloadSystemProxy() }
} }
}.onChangeCompat(of: profile.status) {
status in
guard !ApplicationLibrary.inPreview, status == .connected else {
return
}
Task {
await coordinator.reloadSystemProxy()
}
}
} }
} }
@ViewBuilder @ViewBuilder private var content: some View {
private var content: some View {
overviewPage overviewPage
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
.toolbar { toolbar } .toolbar {
.sheet(isPresented: $showGroups) { toolbar
}.sheet(isPresented: $showGroups) {
groupsSheetContent groupsSheetContent
} }.sheet(isPresented: $showConnections) {
.sheet(isPresented: $showConnections) {
connectionsSheetContent connectionsSheetContent
} }.sheet(isPresented: $showCardManagement, onDismiss: {
cardConfigurationVersion += 1
}, content: {
CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible)
})
#endif #endif
.onAppear { .onAppear {
if ApplicationLibrary.inPreview { if ApplicationLibrary.inPreview {
@@ -61,19 +73,25 @@ public struct ActiveDashboardView: View {
} else { } else {
environments.connect() environments.connect()
} }
} }.onChangeCompat(of: scenePhase) {
.onChangeCompat(of: scenePhase) { phase in phase in
guard phase == .active else { return } guard phase == .active else {
return
}
environments.connect() environments.connect()
} }.onChangeCompat(of: profile.status) {
.onChangeCompat(of: profile.status) { status in status in
guard status.isConnected else { return } guard status.isConnected else {
return
}
environments.connect() environments.connect()
} }.onReceive(environments.profileUpdate) {
.onReceive(environments.profileUpdate) { _ in _ in
Task { await coordinator.reload() } Task {
} await coordinator.reload()
.onReceive(environments.selectedProfileUpdate) { _ in }
}.onReceive(environments.selectedProfileUpdate) {
_ in
Task { Task {
await coordinator.updateSelectedProfile() await coordinator.updateSelectedProfile()
if profile.status.isConnected { if profile.status.isConnected {
@@ -82,24 +100,23 @@ public struct ActiveDashboardView: View {
} }
} }
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
.onReceive(environments.commandClient.$groups) { _ in .onReceive(environments.commandClient.$groups) {
_ in
updateButtonVisibility() updateButtonVisibility()
} }.onReceive(environments.commandClient.$connections) {
.onReceive(environments.commandClient.$connections) { _ in _ in
updateButtonVisibility() updateButtonVisibility()
} }.onReceive(profile.$status) {
.onReceive(profile.$status) { _ in _ in
updateButtonVisibility() updateButtonVisibility()
} }.onAppear {
.onAppear {
updateButtonVisibility() updateButtonVisibility()
} }
#endif #endif
.alertBinding($coordinator.alert) .alertBinding($coordinator.alert)
} }
@ViewBuilder @ViewBuilder private var overviewPage: some View {
private var overviewPage: some View {
OverviewView( OverviewView(
$coordinator.profileList, $coordinator.profileList,
$coordinator.selectedProfileID, $coordinator.selectedProfileID,
@@ -123,28 +140,21 @@ public struct ActiveDashboardView: View {
} }
#endif #endif
@ToolbarContentBuilder @ToolbarContentBuilder private var toolbar: some ToolbarContent {
private var toolbar: some ToolbarContent {
#if os(tvOS) #if os(tvOS)
ToolbarItem(placement: .topBarLeading) { ToolbarItem(placement: .topBarLeading) {
navigationButtons navigationButtons
} }
#endif #endif
ToolbarItem(placement: .topBarTrailing) { ToolbarItemGroup(placement: .topBarTrailing) {
if #available(iOS 16.0, tvOS 17.0, *) { if #available(iOS 16.0, tvOS 17.0, *) {
cardManagementButton cardManagementButton
} }
} #if os(tvOS)
ToolbarItem(placement: .topBarTrailing) { StartStopButton()
#if os(iOS)
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
EmptyView()
} else {
StartStopButton()
}
#else #else
HStack(spacing: 12) { if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
Divider() } else {
StartStopButton() StartStopButton()
} }
#endif #endif
@@ -158,8 +168,12 @@ public struct ActiveDashboardView: View {
showConnectionsButton: buttonState.showConnectionsButton, showConnectionsButton: buttonState.showConnectionsButton,
groupsCount: buttonState.groupsCount, groupsCount: buttonState.groupsCount,
connectionsCount: buttonState.connectionsCount, connectionsCount: buttonState.connectionsCount,
onGroupsTap: { showGroups = true }, onGroupsTap: {
onConnectionsTap: { showConnections = true } showGroups = true
},
onConnectionsTap: {
showConnections = true
}
) )
} }
#endif #endif
@@ -174,11 +188,9 @@ public struct ActiveDashboardView: View {
ConnectionsSheetContent() ConnectionsSheetContent()
} }
@available(iOS 16.0, tvOS 17.0, *) @available(iOS 16.0, *) @ViewBuilder private var cardManagementButton: some View {
@ViewBuilder #if os(iOS)
private var cardManagementButton: some View { Menu {
Menu {
#if os(iOS)
if !isTabViewBottomAccessoryAvailable { if !isTabViewBottomAccessoryAvailable {
if buttonState.showGroupsButton { if buttonState.showGroupsButton {
Button { Button {
@@ -198,22 +210,21 @@ public struct ActiveDashboardView: View {
Divider() Divider()
} }
} }
#endif Button {
showCardManagement = true
} label: {
Label("Dashboard Items", systemImage: "square.grid.2x2")
}
} label: {
Label("Others", systemImage: "line.3.horizontal.circle")
}
#elseif os(tvOS)
Button { Button {
showCardManagement = true showCardManagement = true
} label: { } label: {
Label("Dashboard Items", systemImage: "square.grid.2x2") Image(systemName: "line.3.horizontal.circle")
} }
} label: { #endif
Label("Others", systemImage: "line.3.horizontal.circle")
}
.sheet(isPresented: $showCardManagement, onDismiss: {
cardConfigurationVersion += 1
}, content: {
CardManagementSheet()
.presentationDetents([.large])
.presentationDragIndicator(.visible)
})
} }
#endif #endif
} }
@@ -12,14 +12,22 @@ public struct StartStopButton: View {
viewBuilder { viewBuilder {
if ApplicationLibrary.inPreview { if ApplicationLibrary.inPreview {
Button {} label: { Button {} label: {
Label("Stop", systemImage: "stop.fill") #if os(tvOS)
Image(systemName: "stop.fill")
#else
Label("Stop", systemImage: "stop.fill")
#endif
} }
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
} else if let profile = environments.extensionProfile { } else if let profile = environments.extensionProfile {
ToggleConnectionButton().environmentObject(profile) ToggleConnectionButton().environmentObject(profile)
} else { } else {
Button {} label: { Button {} label: {
Label("Start", systemImage: "play.fill") #if os(tvOS)
Image(systemName: "play.fill")
#else
Label("Start", systemImage: "play.fill")
#endif
} }
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
.disabled(true) .disabled(true)
@@ -63,6 +71,12 @@ public struct StartStopButton: View {
} }
} }
.animation(.spring(response: 0.35, dampingFraction: 0.75), value: profile.status.isConnectedStrict) .animation(.spring(response: 0.35, dampingFraction: 0.75), value: profile.status.isConnectedStrict)
#elseif os(tvOS)
if !profile.status.isConnected {
Image(systemName: "play.fill")
} else {
Image(systemName: "stop.fill")
}
#else #else
HStack(spacing: 8) { HStack(spacing: 8) {
if profile.status.isConnectedStrict, let duration = runtimeDuration { if profile.status.isConnectedStrict, let duration = runtimeDuration {
@@ -89,8 +103,6 @@ public struct StartStopButton: View {
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
#if os(iOS) #if os(iOS)
.modifier(PrimaryTintModifier()) .modifier(PrimaryTintModifier())
#else
.tint(.primary)
#endif #endif
.disabled(!profile.status.isEnabled) .disabled(!profile.status.isEnabled)
.alertBinding($alert) .alertBinding($alert)