diff --git a/Magic Switch/Model/Store/BluetoothPeripheralStore.swift b/Magic Switch/Model/Store/BluetoothPeripheralStore.swift index fcd5601..c42bfb6 100644 --- a/Magic Switch/Model/Store/BluetoothPeripheralStore.swift +++ b/Magic Switch/Model/Store/BluetoothPeripheralStore.swift @@ -398,6 +398,11 @@ final class BluetoothPeripheralStore: NSObject, ObservableObject, BluetoothPerip var newPeripheral = peripheral peripherals.append(newPeripheral) + // Resolve the new row's live connection state (and register its disconnect + // observer) right away. Without this the row reads `.disconnected` until + // the next snapshot — i.e. the user has to leave and re-open the tab to + // see an already-connected peripheral show as connected. + fetchConnectedPeripherals() } /// Removes peripheral information from the system while maintaining it in the list diff --git a/Magic Switch/View/Settings/BluetoothPeripheralSettingsView.swift b/Magic Switch/View/Settings/BluetoothPeripheralSettingsView.swift index 6e62ffe..07baf29 100644 --- a/Magic Switch/View/Settings/BluetoothPeripheralSettingsView.swift +++ b/Magic Switch/View/Settings/BluetoothPeripheralSettingsView.swift @@ -137,17 +137,20 @@ private struct PeripheralListView: View { var secondaryAction: ((BluetoothPeripheral) -> Void)? var body: some View { - List { - ForEach(peripherals) { peripheral in - PeripheralRowView( - peripheral: peripheral, - showConnectionStatus: showConnectionStatus, - primaryAction: { primaryAction(peripheral) }, - secondaryAction: secondaryAction.map { action in - { action(peripheral) } - } - ) - } + // Rows go straight into the enclosing Form Section — a nested List here + // gives each row a taller default height than its content, which + // top-aligns short rows (e.g. the icon-only Available row) instead of + // centering them. Letting the Form own the row layout keeps content + // vertically centered. + ForEach(peripherals) { peripheral in + PeripheralRowView( + peripheral: peripheral, + showConnectionStatus: showConnectionStatus, + primaryAction: { primaryAction(peripheral) }, + secondaryAction: secondaryAction.map { action in + { action(peripheral) } + } + ) } } } @@ -166,24 +169,35 @@ private struct PeripheralRowView: View { } var body: some View { - HStack { - Text(peripheral.name) - Spacer() - if showConnectionStatus { - connectionButton - Button(action: { secondaryAction?() }) { - Image(systemName: "minus.circle") - .foregroundColor(.red) - } - .help("Remove this peripheral from Magic Switch's list.") - .accessibilityLabel("Remove \(peripheral.name) from list") - } else { - Button(action: primaryAction) { - Image(systemName: "plus.circle") - .foregroundColor(.blue) + VStack(alignment: .leading, spacing: 4) { + HStack { + Text(peripheral.name) + Spacer() + if showConnectionStatus { + connectionButton + Button(action: { secondaryAction?() }) { + Image(systemName: "minus.circle") + .foregroundColor(.red) + } + .help("Remove this peripheral from Magic Switch's list.") + .accessibilityLabel("Remove \(peripheral.name) from list") + } else { + Button(action: primaryAction) { + Image(systemName: "plus.circle") + .foregroundColor(.blue) + } + .help("Add this peripheral to Magic Switch's list.") + .accessibilityLabel("Add \(peripheral.name) to list") } - .help("Add this peripheral to Magic Switch's list.") - .accessibilityLabel("Add \(peripheral.name) to list") + } + + // Mirrors the dropdown's inline failure line. The store auto-fades the + // message after 5s (and clears it on a fresh attempt), so this appears + // and disappears on its own — no extra state to manage here. + if let error = store.peripheralOperationError[peripheral.id] { + Text(error) + .font(.caption) + .foregroundColor(.red) } } } @@ -192,7 +206,7 @@ private struct PeripheralRowView: View { private var connectionButton: some View { switch connectionState { case .connected: - Button("Disconnect", action: primaryAction) + Button("Release", action: primaryAction) .help( "Release this peripheral from this Mac. If a peer Mac is paired, it'll take ownership automatically." ) diff --git a/Magic Switch/View/Settings/NetworkDeviceManagementView.swift b/Magic Switch/View/Settings/NetworkDeviceManagementView.swift index ea3e86c..515f32c 100644 --- a/Magic Switch/View/Settings/NetworkDeviceManagementView.swift +++ b/Magic Switch/View/Settings/NetworkDeviceManagementView.swift @@ -327,7 +327,11 @@ private struct NetworkDeviceListView: View { } var body: some View { - List(devices) { device in + // Rows go straight into the enclosing Form Section — a nested List here + // gives each row a taller default height than its content, which + // top-aligns the row contents instead of centering them in the pill. + // Letting the Form own the row layout keeps content vertically centered. + ForEach(devices) { device in VStack(alignment: .leading, spacing: 6) { HStack { Text(device.name)