import Foundation import Libbox import Library import SwiftUI @MainActor public struct ActiveDashboardView: View { @Environment(\.scenePhase) private var scenePhase @EnvironmentObject private var environments: ExtensionEnvironments @EnvironmentObject private var profile: ExtensionProfile @ObservedObject private var coordinator: DashboardViewModel @State private var cardConfigurationVersion = 0 #if os(iOS) || os(tvOS) @State private var showCardManagement = false @State private var showGroups = false @State private var showConnections = false @State private var buttonState = ButtonVisibilityState() #endif private let externalCardConfigurationVersion: Int? public init(coordinator: DashboardViewModel, externalCardConfigurationVersion: Int? = nil) { _coordinator = ObservedObject(wrappedValue: coordinator) self.externalCardConfigurationVersion = externalCardConfigurationVersion } public var body: some View { viewContent } @ViewBuilder private var viewContent: some View { if coordinator.isLoading { ProgressView() #if os(iOS) || os(tvOS) .onAppear { Task { await coordinator.reload() } } #endif } else { content.onAppear { guard !ApplicationLibrary.inPreview, profile.status.isConnected else { return } Task { await coordinator.reloadSystemProxy() } }.onChangeCompat(of: profile.status) { status in guard !ApplicationLibrary.inPreview, status == .connected else { return } Task { await coordinator.reloadSystemProxy() } } } } @ViewBuilder private var content: some View { Group { #if os(iOS) if useLegacyTabView { if ApplicationLibrary.inPreview || profile.status.isConnectedStrict { VStack { pageSelector pageContent } } else { overviewPage } } else { overviewPage } #else overviewPage #endif } #if os(iOS) || os(tvOS) .toolbar { toolbar } #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 }.sheet(isPresented: $showConnections) { connectionsSheetContent }.sheet(isPresented: $showCardManagement, onDismiss: { cardConfigurationVersion += 1 }, content: { if #available(iOS 16.0, *) { CardManagementSheet().presentationDetents([.large]).presentationDragIndicator(.visible) } else { CardManagementSheet() } }) #endif #endif .onAppear { if ApplicationLibrary.inPreview { environments.commandClient.connect() } else { environments.connect() } }.onChangeCompat(of: scenePhase) { phase in guard phase == .active else { return } environments.connect() }.onChangeCompat(of: profile.status) { status in guard status.isConnected else { return } environments.connect() }.onReceive(environments.profileUpdate) { _ in Task { await coordinator.reload() } }.onReceive(environments.selectedProfileUpdate) { _ in Task { await coordinator.updateSelectedProfile() if profile.status.isConnected { await coordinator.reloadSystemProxy() } } } #if os(iOS) || os(tvOS) .onReceive(environments.commandClient.$groups) { _ in updateButtonVisibility() #if os(iOS) if useLegacyTabView, coordinator.selection == .groups, !buttonState.showGroupsButton { coordinator.selection = .overview } #endif }.onReceive(environments.commandClient.$connections) { _ in updateButtonVisibility() }.onReceive(profile.$status) { _ in updateButtonVisibility() #if os(iOS) if useLegacyTabView, coordinator.selection == .groups, !buttonState.showGroupsButton { coordinator.selection = .overview } #endif }.onAppear { updateButtonVisibility() } #endif } @ViewBuilder private var overviewPage: some View { OverviewView( $coordinator.profileList, $coordinator.selectedProfileID, $coordinator.systemProxyAvailable, $coordinator.systemProxyEnabled, cardConfigurationVersion: externalCardConfigurationVersion ?? cardConfigurationVersion ) } #if os(iOS) || os(tvOS) private func updateButtonVisibility() { buttonState.update(profile: profile, commandClient: environments.commandClient, requireAnyConnection: true) } #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, externalCardConfigurationVersion ?? cardConfigurationVersion ) .tag(page) } } .navigationBarTitleDisplayMode(.inline) .tabViewStyle(.page(indexDisplayMode: .never)) } #endif @ToolbarContentBuilder private var toolbar: some ToolbarContent { #if os(tvOS) ToolbarItem(placement: .topBarLeading) { navigationButtons } #endif ToolbarItemGroup(placement: .topBarTrailing) { if #available(iOS 16.0, tvOS 17.0, *) { #if os(iOS) if !useLegacyTabView || coordinator.selection == .overview { cardManagementButton } #else cardManagementButton #endif } #if os(tvOS) StartStopButton() #else if #available(iOS 26.0, *), !Variant.debugNoIOS26 { } else if !useLegacyTabView || coordinator.selection != .connections { StartStopButton() } #endif } } #if os(tvOS) private var navigationButtons: some View { NavigationButtonsView( showGroupsButton: buttonState.showGroupsButton, showConnectionsButton: buttonState.showConnectionsButton, groupsCount: buttonState.groupsCount, connectionsCount: buttonState.connectionsCount, onGroupsTap: { showGroups = true }, onConnectionsTap: { showConnections = true } ) } #endif #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 { Button { showCardManagement = true } label: { Label("Dashboard Items", systemImage: "square.grid.2x2") } } label: { Label("Others", systemImage: "line.3.horizontal.circle") } #elseif os(tvOS) Button { showCardManagement = true } label: { Image(systemName: "line.3.horizontal.circle") } #endif } #endif }