diff --git a/dashpilot-ios/dashpilot/UI/HomeView.swift b/dashpilot-ios/dashpilot/UI/HomeView.swift index dba7eba..0e9f36f 100644 --- a/dashpilot-ios/dashpilot/UI/HomeView.swift +++ b/dashpilot-ios/dashpilot/UI/HomeView.swift @@ -24,7 +24,7 @@ struct HomeView: View { pinnedSection actionButtons Spacer().frame(height: 12) - dataSourceMenu + DataSourceMenu(navigationPath: $navigationPath) Spacer() } .padding(DashMetrics.screenPadding) @@ -67,58 +67,6 @@ struct HomeView: View { } } - // MARK: - Data source menu - - /// Centered "Data source" menu below the actions (port of the Android - /// `DebugDataSourceMenu`): pick a source while idle, disconnect while a - /// session is up. Selecting DashKit retries the BLE link; comma leads to - /// the WiFi connect/setup flow. - private var dataSourceMenu: some View { - VStack(spacing: 4) { - Menu { - if isIdle { - Button("comma") { navigationPath.append(AppRoute.setup) } - Button("DashKit") { connectionVM.connectDashKit() } - } else { - Button("Disconnect") { connectionVM.disconnect() } - } - } label: { - HStack(spacing: 4) { - Text(dataSourceLabel) - .foregroundColor(.dashTextMuted) - .font(.system(size: 13)) - Image(systemName: "arrowtriangle.down.fill") - .font(.system(size: 9)) - .foregroundColor(.dashTextMuted) - } - .padding(.horizontal, 12) - .frame(height: 36) - } - if case .error(let message) = connectionVM.connectionStatus { - Text(message) - .foregroundColor(.dashError) - .font(.system(size: 12)) - .multilineTextAlignment(.center) - } - } - .frame(maxWidth: .infinity) - } - - private var isIdle: Bool { - switch connectionVM.connectionStatus { - case .disconnected, .error: return true - case .connecting, .connected: return false - } - } - - private var dataSourceLabel: String { - switch connectionVM.connectionStatus { - case .connected: return "Data source: connected" - case .connecting: return "Data source: connecting…" - case .disconnected, .error: return "Data source" - } - } - // MARK: - Widget grid private var widgetGrid: some View { @@ -224,6 +172,70 @@ struct HomeView: View { } } +// MARK: - Data source menu + +/// Centered "Data source" menu below the actions (port of the Android +/// `DebugDataSourceMenu`): pick a source while idle, disconnect while a +/// session is up. Selecting DashKit retries the BLE link; comma leads to +/// the WiFi connect/setup flow. +/// +/// Kept as its own view on purpose: its body depends only on +/// `connectionStatus`, not on the live dash state. Inside `HomeView`'s body +/// the ~25 Hz dash updates rebuilt the presented `Menu` on every tick, which +/// detached the visible items from their actions — tapping "Disconnect" did +/// nothing. +private struct DataSourceMenu: View { + + @Binding var navigationPath: NavigationPath + @Environment(ConnectionViewModel.self) var connectionVM + + var body: some View { + VStack(spacing: 4) { + Menu { + if isIdle { + Button("comma") { navigationPath.append(AppRoute.setup) } + Button("DashKit") { connectionVM.connectDashKit() } + } else { + Button("Disconnect") { connectionVM.disconnect() } + } + } label: { + HStack(spacing: 4) { + Text(dataSourceLabel) + .foregroundColor(.dashTextMuted) + .font(.system(size: 13)) + Image(systemName: "arrowtriangle.down.fill") + .font(.system(size: 9)) + .foregroundColor(.dashTextMuted) + } + .padding(.horizontal, 12) + .frame(height: 36) + } + if case .error(let message) = connectionVM.connectionStatus { + Text(message) + .foregroundColor(.dashError) + .font(.system(size: 12)) + .multilineTextAlignment(.center) + } + } + .frame(maxWidth: .infinity) + } + + private var isIdle: Bool { + switch connectionVM.connectionStatus { + case .disconnected, .error: return true + case .connecting, .connected: return false + } + } + + private var dataSourceLabel: String { + switch connectionVM.connectionStatus { + case .connected: return "Data source: connected" + case .connecting: return "Data source: connecting…" + case .disconnected, .error: return "Data source" + } + } +} + // MARK: - Info widget /// Square-ish stat card: accent icon at top, big value + muted label at bottom.