Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bluetooth common reducer #17008

Closed
wants to merge 4 commits into from
Closed

Conversation

peter-sanderson
Copy link
Contributor

@peter-sanderson peter-sanderson commented Feb 14, 2025

This PR introduces first version of the shared reducers and state (for desktop & mobile) for connecting a Bluetooth device.

Copy link

github-actions bot commented Feb 14, 2025

🚀 Expo preview is ready!

  • Project → trezor-suite-preview
  • Platforms → android, ios
  • Scheme → trezorsuitelite
  • Runtime Version → 26
  • More info

Learn more about 𝝠 Expo Github Action

@peter-sanderson peter-sanderson added the no-project This label is used to specify that PR doesn't need to be added to a project label Feb 14, 2025
@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 11546ec to 5dbe6c7 Compare February 14, 2025 14:51
@peter-sanderson peter-sanderson changed the base branch from bluetooth-flag to develop February 14, 2025 14:51
@peter-sanderson peter-sanderson marked this pull request as ready for review February 14, 2025 14:53
Copy link

coderabbitai bot commented Feb 14, 2025

Walkthrough

The changes integrate Bluetooth functionality across several modules. In the device package, a private Bluetooth property is added to the Device class and included in its messaging output. The corresponding type definitions are updated to optionally include Bluetooth properties. The transport package now supports a new device type with the addition of an enum value for Bluetooth devices and an updated descriptor that accepts a Bluetooth identifier. Additionally, a new Bluetooth package is introduced under the suite-common directory, which provides a dedicated set of Redux actions and a reducer creator to manage Bluetooth device states. A new TypeScript configuration file for the package is also included, specifying compiler options and project references.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (2)

46-52: Initialize adapter state carefully.

Setting isAdapterEnabled to true and scanStatus to 'running' by default could lead to UI confusion if the real adapter state differs. Consider initializing them to false or a neutral state until a real event updates them.


57-59: Avoid silencing type errors with @ts-expect-error.

Using @ts-expect-error may hide legitimate type issues. Investigate whether a properly typed action or an explicit type guard can eliminate this error while preserving type safety.

packages/transport/src/types/index.ts (1)

31-32: Confirm naming consistency for the new UUID property.

Adding uuid to the Descriptor type is sensible. However, confirm it aligns with other device identifiers (e.g., path, session) and consider adopting a consistent naming convention if additional Bluetooth-specific fields are added later.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8304a7b and 5dbe6c7.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • packages/connect/src/device/Device.ts (4 hunks)
  • packages/connect/src/types/device.ts (4 hunks)
  • packages/transport/src/api/abstract.ts (1 hunks)
  • packages/transport/src/types/index.ts (1 hunks)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothActions.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • suite-common/bluetooth/package.json
  • suite-common/bluetooth/tsconfig.json
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: Type Checking
  • GitHub Check: Build libs for publishing
  • GitHub Check: Linting and formatting
  • GitHub Check: Unit Tests
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: test
  • GitHub Check: prepare_android_test_app
  • GitHub Check: EAS Update
🔇 Additional comments (7)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

67-82: Ensure no concurrency issues when merging devices.

When merging known devices and scanned devices, ensure no concurrency issues arise if multiple updates arrive in quick succession. The Map + sort approach is solid, but consider whether stale device data from parallel scans or re-connection cycles could overwrite newer data.

suite-common/bluetooth/src/bluetoothActions.ts (1)

1-45: Actions are well-structured and consistent.

The action creators and payload definitions are clear and type-safe. Aggregating them into allBluetoothActions is a convenient approach for maintainability.

packages/transport/src/api/abstract.ts (1)

26-26: LGTM! The new Bluetooth device type is properly added.

The addition follows the existing pattern and is well-documented with a reference to the trezord-go implementation.

packages/connect/src/types/device.ts (2)

84-86: LGTM! The Bluetooth properties type is well-defined.

The type is properly exported and uses a UUID for unique device identification, which is a standard practice for Bluetooth devices.


112-112: LGTM! Bluetooth properties are consistently added across device types.

The optional bluetoothProps property is uniformly applied to all device types (KnownDevice, UnknownDevice, and UnreadableDevice), maintaining type consistency.

Also applies to: 134-134, 156-156

packages/connect/src/device/Device.ts (2)

132-132: LGTM! The Bluetooth property is properly initialized.

The bluetoothProps property is correctly marked as readonly and initialized using a clean conditional pattern in the constructor.

Also applies to: 224-224


1222-1222: LGTM! The Bluetooth properties are consistently included in device messages.

The bluetoothProps is properly included in both unacquired and acquired device message objects, maintaining consistency in the device's public interface.

Also applies to: 1248-1248

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch 2 times, most recently from 44f7c48 to 3192dde Compare February 14, 2025 15:20
},
},
) => {
if (bluetoothProps && bluetoothProps.uuid in state.devices) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets make sure that suite-native also provides this value, also change uuid => id

Copy link
Contributor Author

@peter-sanderson peter-sanderson Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7b8e6b2

This will be provided by connect right? I suppose this will be same on mobile and desktop as it uses same connect, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but connect needs to know it from the transport, and mobile uses different transport

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

108-108: ⚠️ Potential issue

Use array methods instead of the in operator.

Using bluetoothProps.id in state.devices for an array check is incorrect. The in operator checks object properties, not array elements.

- if (bluetoothProps && bluetoothProps.id in state.devices) {
+ if (bluetoothProps && state.devices.some(it => it.id === bluetoothProps.id)) {
🧹 Nitpick comments (2)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (2)

48-53: Reconsider initial scanStatus value.

Setting scanStatus to 'running' initially might be misleading as no scan is actually in progress when the reducer is initialized. Consider setting it to 'done' or adding a new status like 'initial' to better represent the true state.


76-78: Consider reversing the sort order for better UX.

The current sort order puts oldest devices first. Consider reversing the order to show recently updated devices at the beginning of the list.

- (a, b) => a.lastUpdate - b.lastUpdate,
+ (a, b) => b.lastUpdate - a.lastUpdate,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dbe6c7 and 4d84d5f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • packages/connect/src/device/Device.ts (4 hunks)
  • packages/connect/src/types/device.ts (4 hunks)
  • packages/transport/src/types/index.ts (1 hunks)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/transport/src/types/index.ts
  • suite-common/bluetooth/package.json
  • suite-common/bluetooth/tsconfig.json
  • packages/connect/src/types/device.ts
  • packages/connect/src/device/Device.ts
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=passphrase, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: transport-e2e-test
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: build-web
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: test
  • GitHub Check: build-web
  • GitHub Check: Analyze with CodeQL (javascript)
🔇 Additional comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

15-21: Document the purpose of the data field.

The data field's type (number[]) and purpose are not immediately clear. Consider adding a JSDoc comment explaining what this array represents and its expected format.

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 4d84d5f to 7b8e6b2 Compare February 14, 2025 15:52
@szymonlesisz
Copy link
Contributor

do not merge until tested in rust-bluetooth branch

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 7b8e6b2 to dc44955 Compare February 17, 2025 13:21
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

108-108: ⚠️ Potential issue

Use array methods instead of the 'in' operator.

The 'in' operator is not correct for array checks. Use array methods like .find() or .some() instead.

Apply this fix:

- if (bluetoothProps && bluetoothProps.id in state.devices) {
+ if (bluetoothProps && state.devices.some(device => device.id === bluetoothProps.id)) {
🧹 Nitpick comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

76-78: Consider optimizing device sorting.

The current implementation sorts devices on every update. For better performance with large device lists, consider:

  1. Moving the sort operation to the selector level
  2. Using a memoized selector to prevent unnecessary re-sorting

Example implementation using reselect:

import { createSelector } from '@reduxjs/toolkit';

export const selectSortedDevices = createSelector(
  (state: BluetoothState<T>) => state.devices,
  (devices) => [...devices].sort((a, b) => a.lastUpdate - b.lastUpdate)
);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d84d5f and dc44955.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • packages/connect/src/device/Device.ts (4 hunks)
  • packages/connect/src/types/device.ts (4 hunks)
  • packages/transport/src/api/abstract.ts (1 hunks)
  • packages/transport/src/types/index.ts (1 hunks)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothActions.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/transport/src/types/index.ts
  • packages/transport/src/api/abstract.ts
  • suite-common/bluetooth/package.json
  • packages/connect/src/types/device.ts
  • suite-common/bluetooth/tsconfig.json
  • packages/connect/src/device/Device.ts
⏰ Context from checks skipped due to timeout of 90000ms (17)
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=passphrase, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: test
  • GitHub Check: prepare_android_test_app
  • GitHub Check: build-web
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: build-web
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (3)
suite-common/bluetooth/src/bluetoothActions.ts (2)

35-38: Consider renaming 'done' status to 'idle'.

Based on previous feedback, the term 'done' in BluetoothScanStatus might be confusing as it implies a final state. Consider using 'idle' instead to better represent a state that can transition back to 'running'.


1-46: Well-structured Redux actions implementation!

The implementation follows Redux best practices with:

  • Clear action naming
  • Type-safe payloads
  • Good separation of concerns
  • Consistent use of action creators
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

47-133: Solid reducer implementation with good state management!

The implementation:

  • Handles all Bluetooth-related actions properly
  • Maintains clear state transitions
  • Includes proper error handling
  • Has good TypeScript type safety

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from dc44955 to 1348459 Compare February 17, 2025 13:24
@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 1348459 to 9e47312 Compare February 17, 2025 13:49
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
suite-common/bluetooth/src/bluetoothActions.ts (1)

28-33: Consider consistent parameter ordering in the payload.

The parameters in the type definition are ordered as id, connectionStatus but in the payload they're ordered as connectionStatus, id. Consider maintaining consistent ordering for better maintainability.

Apply this diff to maintain consistent parameter ordering:

 export const bluetoothConnectDeviceEventAction = createAction(
     `${BLUETOOTH_PREFIX}/device-connection-status`,
     ({ connectionStatus, id }: { id: string; connectionStatus: DeviceBluetoothStatus }) => ({
-        payload: { id, connectionStatus },
+        payload: { connectionStatus, id },
     }),
 );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc44955 and 9e47312.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (7)
  • packages/connect/src/device/Device.ts (4 hunks)
  • packages/connect/src/types/device.ts (4 hunks)
  • packages/transport/src/types/index.ts (1 hunks)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothActions.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/transport/src/types/index.ts
  • suite-common/bluetooth/package.json
  • packages/connect/src/device/Device.ts
  • suite-common/bluetooth/tsconfig.json
  • packages/connect/src/types/device.ts
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: prepare_android_test_app
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=passphrase, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: test
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: build-web
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: Analyze with CodeQL (javascript)
🔇 Additional comments (5)
suite-common/bluetooth/src/bluetoothActions.ts (5)

1-10: LGTM!

The imports are well-structured, and the prefix follows Redux naming conventions.


11-14: LGTM!

The action creator is well-implemented with proper typing and follows Redux Toolkit best practices.


16-26: Consider renaming devices to nearbyDevices for better clarity.

As suggested in a previous review, renaming devices to nearbyDevices would make the distinction between discovered and known devices more clear.

Apply this diff to improve naming:

 type BluetoothDeviceListUpdatePayload = {
-    devices: BluetoothDevice[];
+    nearbyDevices: BluetoothDevice[];
     knownDevices: BluetoothDevice[];
 };

 export const bluetoothDeviceListUpdate = createAction(
     `${BLUETOOTH_PREFIX}/device-list-update`,
-    ({ devices, knownDevices }: BluetoothDeviceListUpdatePayload) => ({
-        payload: { devices, knownDevices },
+    ({ nearbyDevices, knownDevices }: BluetoothDeviceListUpdatePayload) => ({
+        payload: { nearbyDevices, knownDevices },
     }),
 );

35-38: LGTM!

The action creator is well-implemented with proper typing and follows Redux Toolkit best practices.


40-45: LGTM!

The export is well-structured and follows the common pattern of exporting all actions as a single object.

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch 2 times, most recently from 76327ce to 1c56f4c Compare February 17, 2025 14:31
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

97-115: ⚠️ Potential issue

Fix array membership check.

The in operator is not appropriate for checking array membership:

-if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
+if (bluetoothProps && state.nearbyDevices.some(device => device.id === bluetoothProps.id)) {
🧹 Nitpick comments (2)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (2)

28-28: Consider defining a structured type for device data.

The TODO comment suggests a need for a more specific type for the data field.

Would you like me to help define a typed data structure for the device data?


62-66: Consider memoizing the sorted device list.

The current implementation sorts the list on every update. Consider memoizing the sorted list or sorting only when necessary.

+const sortDevicesByTimestamp = <T extends BluetoothDevice>(devices: T[]) =>
+    devices.sort((a, b) => a.lastUpdatedTimestamp - b.lastUpdatedTimestamp);

 .addCase(bluetoothDeviceListUpdateAction, (state, { payload: { devices } }) => {
-    state.nearbyDevices = devices.sort(
-        (a, b) => a.lastUpdatedTimestamp - b.lastUpdatedTimestamp,
-    ) as Draft<T>[];
+    state.nearbyDevices = sortDevicesByTimestamp(devices) as Draft<T>[];
 })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e47312 and 1c56f4c.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothActions.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • suite-common/bluetooth/package.json
  • suite-common/bluetooth/tsconfig.json
⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: PR-check / node-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: PR-check / web-override init-api-flaky
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=passphrase, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: transport-e2e-test
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: prepare_android_test_app
  • GitHub Check: build-web
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: test
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: Analyze with CodeQL (javascript)
🔇 Additional comments (8)
suite-common/bluetooth/src/bluetoothActions.ts (4)

1-10: LGTM!

The Redux action prefix and imports are well-structured and follow best practices.


11-14: LGTM!

The action is well-defined with proper typing for the power status.


16-25: Consider renaming for consistency with state structure.

The payload type uses devices while the state in reducer uses nearbyDevices. For better consistency:

-type BluetoothDeviceListUpdatePayload = {
-    devices: BluetoothDevice[];
-};
+type BluetoothDeviceListUpdatePayload = {
+    nearbyDevices: BluetoothDevice[];
+};

 export const bluetoothDeviceListUpdateAction = createAction(
     `${BLUETOOTH_PREFIX}/device-list-update`,
-    ({ devices }: BluetoothDeviceListUpdatePayload) => ({
-        payload: { devices },
+    ({ nearbyDevices }: BluetoothDeviceListUpdatePayload) => ({
+        payload: { nearbyDevices },
     }),
 );

27-37: LGTM!

Both actions are well-structured with proper typing for their respective payloads.

suite-common/bluetooth/src/bluetoothReducerCreator.ts (4)

13-43: LGTM!

The type definitions are comprehensive and well-structured, providing good type safety for the Bluetooth functionality.


45-52: LGTM!

The reducer creator is well-structured with proper generic type support and initial state definition.


55-61: LGTM!

The adapter event handling includes proper cleanup of device list and scan status when powered off.


117-122: LGTM!

The storage load handling includes proper fallback for undefined values.

Comment on lines 39 to 44
export const allBluetoothActions = {
bluetoothAdapterEventAction,
bluetoothDeviceListUpdate: bluetoothDeviceListUpdateAction,
bluetoothConnectDeviceEventAction,
bluetoothScanStatusAction,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix naming inconsistency in exported actions.

The bluetoothDeviceListUpdate key doesn't match the action creator name:

 export const allBluetoothActions = {
     bluetoothAdapterEventAction,
-    bluetoothDeviceListUpdate: bluetoothDeviceListUpdateAction,
+    bluetoothDeviceListUpdateAction,
     bluetoothConnectDeviceEventAction,
     bluetoothScanStatusAction,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const allBluetoothActions = {
bluetoothAdapterEventAction,
bluetoothDeviceListUpdate: bluetoothDeviceListUpdateAction,
bluetoothConnectDeviceEventAction,
bluetoothScanStatusAction,
};
export const allBluetoothActions = {
bluetoothAdapterEventAction,
bluetoothDeviceListUpdateAction,
bluetoothConnectDeviceEventAction,
bluetoothScanStatusAction,
};

@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 1c56f4c to 3cbf279 Compare February 17, 2025 14:36
@peter-sanderson peter-sanderson force-pushed the bluetooth-common-reducer branch from 3cbf279 to d0a484d Compare February 17, 2025 14:40
},
},
) => {
if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition seems to always evaluate to false since nearbyDevices is no longer an object but an array.

Suggested change
if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
if (!bluetoothProps || !bluetoothProps.id) {
return;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


// Once device is fully connected, we save it to the list of paired devices
// so next time user opens suite
const foundPairedDevice = state.knownDevices.find(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const foundPairedDevice = state.knownDevices.find(
const foundKnownDevice = state.knownDevices.find(

if (deviceState !== undefined) {
deviceState.status = null;

// Once device is fully connected, we save it to the list of paired devices
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paired 👉 known

);

if (deviceState !== undefined) {
deviceState.status = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is status set to null here? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@szymonlesisz szymonlesisz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've added and initialized this reducer in suite (in rust-bluetooth branch) and the AppState types shows bluetooth: never

import { BluetoothDevice } from '@trezor/transport-bluetooth';
import { bluetoothReducerCreator } from '@suite-common/bluetooth';

const rootReducer = combineReducers({
   ....,
   bluetooth: bluetoothReducerCreator<BluetoothDevice>(),
});

name: string;
data: number[]; // Todo: consider typed data-structure for this
lastUpdatedTimestamp: number;
status: DeviceBluetoothStatus | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status should not be persistent, its related only to connection/pairing phase

"private": true,
"license": "See LICENSE.md in repo root",
"sideEffects": false,
"main": "src/index",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing src/index file

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
suite-common/bluetooth/src/bluetoothSelectors.ts (2)

3-5: Add documentation for the generic type parameter.

Consider adding JSDoc comments to explain the purpose of the generic type parameter T and its constraints.

+/**
+ * Represents the Redux state shape containing Bluetooth state.
+ * @template T Type of Bluetooth device, must extend BluetoothDevice
+ */
 type State<T extends BluetoothDevice> = {
     bluetooth: BluetoothState<T>;
 };

7-17: Consider using createSelector for memoization.

These selectors might benefit from memoization using createSelector from Redux Toolkit to prevent unnecessary re-renders when the state hasn't changed.

+import { createSelector } from '@reduxjs/toolkit';

-export const selectAdapterStatus = <T extends BluetoothDevice>(state: State<T>) =>
-    state.bluetooth.adapterStatus;
+export const selectAdapterStatus = createSelector(
+    <T extends BluetoothDevice>(state: State<T>) => state.bluetooth,
+    bluetooth => bluetooth.adapterStatus
+);

-export const selectKnownDevices = <T extends BluetoothDevice>(state: State<T>) =>
-    state.bluetooth.knownDevices;
+export const selectKnownDevices = createSelector(
+    <T extends BluetoothDevice>(state: State<T>) => state.bluetooth,
+    bluetooth => bluetooth.knownDevices
+);
suite-common/bluetooth/src/bluetoothActions.ts (2)

16-18: Add documentation for the action payload type.

Consider adding JSDoc comments to explain the purpose and structure of the payload type.

+/**
+ * Payload type for the bluetoothNearbyDevicesUpdateAction.
+ * Contains an array of nearby Bluetooth devices discovered during scanning.
+ */
 type BluetoothNearbyDevicesUpdateActionPayload = {
     nearbyDevices: BluetoothDevice[];
 };

27-32: Consider using a dedicated type for the action payload.

Extract the payload type to improve type safety and reusability.

+type BluetoothConnectDeviceEventActionPayload = {
+    id: string;
+    connectionStatus: DeviceBluetoothStatus;
+};

 export const bluetoothConnectDeviceEventAction = createAction(
     `${BLUETOOTH_PREFIX}/connect-device-event`,
-    ({ connectionStatus, id }: { id: string; connectionStatus: DeviceBluetoothStatus }) => ({
+    ({ connectionStatus, id }: BluetoothConnectDeviceEventActionPayload) => ({
         payload: { id, connectionStatus },
     }),
 );
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

65-67: Consider sorting by signal strength or name.

The current sorting by lastUpdatedTimestamp might not provide the best user experience. Consider sorting by signal strength (if available) or device name for better usability.

 state.nearbyDevices = nearbyDevices.sort(
-    (a, b) => a.lastUpdatedTimestamp - b.lastUpdatedTimestamp,
+    (a, b) => a.name.localeCompare(b.name),
 ) as Draft<T>[];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c56f4c and 0e86c1a.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • suite-common/bluetooth/package.json (1 hunks)
  • suite-common/bluetooth/src/bluetoothActions.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothReducerCreator.ts (1 hunks)
  • suite-common/bluetooth/src/bluetoothSelectors.ts (1 hunks)
  • suite-common/bluetooth/src/index.ts (1 hunks)
  • suite-common/bluetooth/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • suite-common/bluetooth/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • suite-common/bluetooth/package.json
  • suite-common/bluetooth/tsconfig.json
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: PR-check / node-override init-api-flaky
  • GitHub Check: PR-check / web-authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy checkFirmwareAuthenticity keepSession cancel.test info.test resetDevice-api
  • GitHub Check: build-deploy
  • GitHub Check: build-deploy
  • GitHub Check: run-desktop-tests (@group=wallet, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: run-desktop-tests (@group=other, trezor-user-env-unix)
  • GitHub Check: run-desktop-tests (@group=passphrase, trezor-user-env-unix)
  • GitHub Check: EAS Update
  • GitHub Check: run-desktop-tests (@group=settings, trezor-user-env-unix bitcoin-regtest)
  • GitHub Check: Setup and Cache Dependencies
  • GitHub Check: run-desktop-tests (@group=device-management, trezor-user-env-unix)
  • GitHub Check: transport-e2e-test
  • GitHub Check: test
  • GitHub Check: run-desktop-tests (@group=suite, trezor-user-env-unix)
  • GitHub Check: build-web
  • GitHub Check: prepare_android_test_app
  • GitHub Check: build-web
  • GitHub Check: Analyze with CodeQL (javascript)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (1)
suite-common/bluetooth/src/bluetoothReducerCreator.ts (1)

25-31: Consider using a typed data structure for the data array.

The TODO comment suggests that the data property needs a more specific type. Consider defining a proper type or interface for the data structure.

Could you provide more context about what kind of data is stored in this array? This would help in suggesting an appropriate type structure.

Comment on lines +100 to +117
if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
const deviceState = state.nearbyDevices.find(
it => it.id === bluetoothProps.id,
);

if (deviceState !== undefined) {
deviceState.status = null;

// Once device is fully connected, we save it to the list of paired devices
// so next time user opens suite
const foundPairedDevice = state.knownDevices.find(
it => it.id === bluetoothProps.id,
);
if (foundPairedDevice === undefined) {
state.knownDevices.push(deviceState);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix array membership check and potential race condition.

The code has two issues:

  1. Using in operator for array membership check is incorrect
  2. Setting device status to null before checking known devices could lead to a race condition
-if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
+if (bluetoothProps?.id) {
     const deviceState = state.nearbyDevices.find(
         it => it.id === bluetoothProps.id,
     );

     if (deviceState !== undefined) {
-        deviceState.status = null;
+        const foundKnownDevice = state.knownDevices.find(
+            it => it.id === bluetoothProps.id,
+        );
+        if (foundKnownDevice === undefined) {
+            state.knownDevices.push(deviceState);
+        }
-        // Once device is fully connected, we save it to the list of paired devices
-        // so next time user opens suite
-        const foundPairedDevice = state.knownDevices.find(
-            it => it.id === bluetoothProps.id,
-        );
-        if (foundPairedDevice === undefined) {
-            state.knownDevices.push(deviceState);
-        }
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (bluetoothProps && bluetoothProps.id in state.nearbyDevices) {
const deviceState = state.nearbyDevices.find(
it => it.id === bluetoothProps.id,
);
if (deviceState !== undefined) {
deviceState.status = null;
// Once device is fully connected, we save it to the list of paired devices
// so next time user opens suite
const foundPairedDevice = state.knownDevices.find(
it => it.id === bluetoothProps.id,
);
if (foundPairedDevice === undefined) {
state.knownDevices.push(deviceState);
}
}
}
if (bluetoothProps?.id) {
const deviceState = state.nearbyDevices.find(
it => it.id === bluetoothProps.id,
);
if (deviceState !== undefined) {
const foundKnownDevice = state.knownDevices.find(
it => it.id === bluetoothProps.id,
);
if (foundKnownDevice === undefined) {
state.knownDevices.push(deviceState);
}
}
}

@peter-sanderson
Copy link
Contributor Author

Closing in favor of: #17090

@peter-sanderson peter-sanderson deleted the bluetooth-common-reducer branch February 18, 2025 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-project This label is used to specify that PR doesn't need to be added to a project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants