refactor: Tab view accessory compat
This commit is contained in:
@@ -11,6 +11,8 @@ import SwiftUI
|
||||
@StateObject private var cardConfiguration = DashboardCardConfiguration()
|
||||
#if os(iOS) || os(tvOS)
|
||||
@State private var showCardManagement = false
|
||||
#endif
|
||||
#if os(tvOS)
|
||||
@State private var showGroups = false
|
||||
@State private var showConnections = false
|
||||
@State private var buttonState = ButtonVisibilityState()
|
||||
@@ -58,22 +60,7 @@ import SwiftUI
|
||||
|
||||
@ViewBuilder private var content: some View {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
if useLegacyTabView {
|
||||
if Variant.screenshotMode || profile.status.isConnectedStrict {
|
||||
VStack {
|
||||
pageSelector
|
||||
pageContent
|
||||
}
|
||||
} else {
|
||||
overviewPage
|
||||
}
|
||||
} else {
|
||||
overviewPage
|
||||
}
|
||||
#else
|
||||
overviewPage
|
||||
#endif
|
||||
overviewPage
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
.toolbar {
|
||||
@@ -110,19 +97,15 @@ import SwiftUI
|
||||
}
|
||||
}
|
||||
#else
|
||||
.sheet(isPresented: $showGroups) {
|
||||
groupsSheetContent
|
||||
}.sheet(isPresented: $showConnections) {
|
||||
connectionsSheetContent
|
||||
}.sheet(isPresented: $showCardManagement, onDismiss: {
|
||||
Task { await cardConfiguration.reload() }
|
||||
}, content: {
|
||||
if #available(iOS 16.0, *) {
|
||||
CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible)
|
||||
} else {
|
||||
CardManagementSheet()
|
||||
}
|
||||
})
|
||||
.sheet(isPresented: $showCardManagement, onDismiss: {
|
||||
Task { await cardConfiguration.reload() }
|
||||
}, content: {
|
||||
if #available(iOS 16.0, *) {
|
||||
CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible)
|
||||
} else {
|
||||
CardManagementSheet()
|
||||
}
|
||||
})
|
||||
#endif
|
||||
#endif
|
||||
.onAppear {
|
||||
@@ -149,24 +132,14 @@ import SwiftUI
|
||||
}
|
||||
}
|
||||
}
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(tvOS)
|
||||
.onReceive(environments.commandClient.$groups) { _ in
|
||||
Task { @MainActor in
|
||||
updateButtonVisibility()
|
||||
#if os(iOS)
|
||||
if useLegacyTabView, coordinator.selection == .groups, !buttonState.showGroupsButton {
|
||||
coordinator.selection = .overview
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}.onReceive(profile.$status) { _ in
|
||||
Task { @MainActor in
|
||||
updateButtonVisibility()
|
||||
#if os(iOS)
|
||||
if useLegacyTabView, coordinator.selection == .groups, !buttonState.showGroupsButton {
|
||||
coordinator.selection = .overview
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}.onAppear {
|
||||
updateButtonVisibility()
|
||||
@@ -189,58 +162,13 @@ import SwiftUI
|
||||
)
|
||||
}
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
#if os(tvOS)
|
||||
private func updateButtonVisibility() {
|
||||
buttonState.update(profile: profile, commandClient: environments.commandClient)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
private var isTabViewBottomAccessoryAvailable: Bool {
|
||||
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var useLegacyTabView: Bool {
|
||||
!isTabViewBottomAccessoryAvailable
|
||||
}
|
||||
|
||||
private var enabledPages: [DashboardPage] {
|
||||
DashboardPage.enabledCases(hasGroups: buttonState.showGroupsButton)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var pageSelector: some View {
|
||||
Picker("Page", selection: $coordinator.selection) {
|
||||
ForEach(enabledPages) { page in
|
||||
page.label
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.padding([.leading, .trailing])
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var pageContent: some View {
|
||||
TabView(selection: $coordinator.selection) {
|
||||
ForEach(enabledPages) { page in
|
||||
page.contentView(
|
||||
$coordinator.profileList,
|
||||
$coordinator.selectedProfileID,
|
||||
$coordinator.systemProxyAvailable,
|
||||
$coordinator.systemProxyEnabled,
|
||||
cardConfiguration
|
||||
)
|
||||
.tag(page)
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
@ToolbarContentBuilder private var toolbar: some ToolbarContent {
|
||||
#if os(tvOS)
|
||||
ToolbarItemGroup(placement: .topBarLeading) {
|
||||
@@ -249,23 +177,10 @@ import SwiftUI
|
||||
#endif
|
||||
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||
if #available(iOS 16.0, tvOS 17.0, *) {
|
||||
#if os(iOS)
|
||||
if !useLegacyTabView || coordinator.selection == .overview {
|
||||
cardManagementButton
|
||||
}
|
||||
#else
|
||||
cardManagementButton
|
||||
#endif
|
||||
cardManagementButton
|
||||
}
|
||||
#if os(tvOS)
|
||||
StartStopButton()
|
||||
#elseif os(iOS)
|
||||
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
|
||||
} else if !useLegacyTabView || coordinator.selection != .connections {
|
||||
StartStopButton()
|
||||
}
|
||||
#else
|
||||
StartStopButton()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -289,14 +204,6 @@ import SwiftUI
|
||||
#endif
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
private var groupsSheetContent: some View {
|
||||
GroupsSheetContent()
|
||||
}
|
||||
|
||||
private var connectionsSheetContent: some View {
|
||||
ConnectionsSheetContent()
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *) @ViewBuilder private var cardManagementButton: some View {
|
||||
#if os(iOS)
|
||||
Menu {
|
||||
|
||||
@@ -5,13 +5,17 @@ import SwiftUI
|
||||
@MainActor
|
||||
public struct StartStopButton: View {
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
private let showsRuntimeDuration: Bool
|
||||
|
||||
public init() {}
|
||||
public init(showsRuntimeDuration: Bool = false) {
|
||||
self.showsRuntimeDuration = showsRuntimeDuration
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Group {
|
||||
if let profile = environments.extensionProfile {
|
||||
ToggleConnectionButton().environmentObject(profile)
|
||||
ToggleConnectionButton(showsRuntimeDuration: showsRuntimeDuration)
|
||||
.environmentObject(profile)
|
||||
} else {
|
||||
Button {} label: {
|
||||
#if os(tvOS)
|
||||
@@ -33,6 +37,7 @@ public struct StartStopButton: View {
|
||||
@State private var alert: AlertState?
|
||||
@State private var currentTime = Date()
|
||||
@State private var isStarting = false
|
||||
let showsRuntimeDuration: Bool
|
||||
|
||||
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
||||
@@ -44,7 +49,7 @@ public struct StartStopButton: View {
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
HStack(spacing: 8) {
|
||||
if showRuntimeDuration, profile.status.isConnectedStrict, let duration = runtimeDuration {
|
||||
if showsRuntimeDuration, profile.status.isConnectedStrict, let duration = runtimeDuration {
|
||||
Text(duration)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
@@ -121,15 +126,6 @@ public struct StartStopButton: View {
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private var showRuntimeDuration: Bool {
|
||||
if #available(iOS 26.0, *), !Variant.debugNoIOS26 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
|
||||
private var runtimeDuration: String? {
|
||||
guard let connectedDate = profile.connectedDate else { return nil }
|
||||
let interval: TimeInterval
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import Foundation
|
||||
import Library
|
||||
import SwiftUI
|
||||
|
||||
public enum DashboardPage: Int, CaseIterable, Identifiable {
|
||||
public var id: Self {
|
||||
self
|
||||
}
|
||||
|
||||
case overview
|
||||
case groups
|
||||
case connections
|
||||
}
|
||||
|
||||
public extension DashboardPage {
|
||||
static func enabledCases() -> [DashboardPage] {
|
||||
[.overview, .groups, .connections]
|
||||
}
|
||||
|
||||
static func enabledCases(hasGroups: Bool) -> [DashboardPage] {
|
||||
var cases: [DashboardPage] = [.overview]
|
||||
if hasGroups {
|
||||
cases.append(.groups)
|
||||
}
|
||||
cases.append(.connections)
|
||||
return cases
|
||||
}
|
||||
}
|
||||
|
||||
public extension DashboardPage {
|
||||
var title: String {
|
||||
switch self {
|
||||
case .overview:
|
||||
return String(localized: "Overview")
|
||||
case .groups:
|
||||
return String(localized: "Groups")
|
||||
case .connections:
|
||||
return String(localized: "Connections")
|
||||
}
|
||||
}
|
||||
|
||||
var label: some View {
|
||||
switch self {
|
||||
case .overview:
|
||||
return Label(title, systemImage: "text.and.command.macwindow")
|
||||
case .groups:
|
||||
return Label(title, systemImage: "rectangle.3.group.fill")
|
||||
case .connections:
|
||||
return Label(title, systemImage: "list.bullet.rectangle.portrait.fill")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func contentView(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>, _ cardConfiguration: DashboardCardConfiguration) -> some View {
|
||||
Group {
|
||||
switch self {
|
||||
case .overview:
|
||||
OverviewView(profileList, selectedProfileID, systemProxyAvailable, systemProxyEnabled, cardConfiguration: cardConfiguration)
|
||||
case .groups:
|
||||
GroupListView()
|
||||
case .connections:
|
||||
ConnectionListView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ private let logger = Logger(category: "DashboardViewModel")
|
||||
public final class DashboardViewModel: BaseViewModel {
|
||||
@Published public var profileList: [ProfilePreview] = []
|
||||
@Published public var selectedProfileID: Int64 = 0
|
||||
@Published public var selection = DashboardPage.overview
|
||||
@Published public var systemProxyAvailable = false
|
||||
@Published public var systemProxyEnabled = false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user