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

[ECO-5184] feat: update channel hooks implementation #1947

Merged
merged 2 commits into from
Jan 22, 2025

Conversation

ttypic
Copy link
Collaborator

@ttypic ttypic commented Jan 14, 2025

Because of implicit attach() hooks can produce additional errors e.g. when updating ably client

to avoid this situation, channels created inside ChannelProvider now use attachOnSubscribe = false flag and attach is happening explicitly.

Summary by CodeRabbit

  • New Features

    • Added optional attachOnSubscribe configuration for more flexible channel subscription behavior.
    • Introduced constants for inactive connection and channel states.
    • Implemented useChannelAttach hook for managing channel attachment to the Ably Realtime service.
    • Added attach method to multiple classes for future functionality.
  • Refactor

    • Centralized inactive connection and channel states in a constants module.
    • Updated channel and presence subscription logic to support new configuration options.
    • Renamed and clarified function signatures for better understanding.
  • Tests

    • Added tests for the useChannelAttach hook to ensure correct behavior.

Copy link

coderabbitai bot commented Jan 14, 2025

Walkthrough

The pull request introduces a new optional attachOnSubscribe property to the ChannelOptions interface, allowing more flexible control over channel attachment behavior. Changes span multiple files across the Ably React Hooks library, including modifications to channel subscription methods, the addition of new constants for inactive states, and updates to the ChannelProvider component. The changes aim to provide more granular control over channel and connection state management.

Changes

File Change Summary
ably.d.ts Added optional attachOnSubscribe?: boolean to ChannelOptions interface
src/common/lib/client/realtimechannel.ts Modified subscribe method to conditionally attach based on attachOnSubscribe
src/common/lib/client/realtimepresence.ts Updated subscribe method to respect attachOnSubscribe option
src/platform/react-hooks/src/AblyReactHooks.ts Renamed channelOptionsWithAgent to channelOptionsForReactHooks and added attachOnSubscribe: false
src/platform/react-hooks/src/ChannelProvider.tsx Updated import for channel options to reflect new function
src/platform/react-hooks/src/hooks/constants.ts Introduced INACTIVE_CONNECTION_STATES and INACTIVE_CHANNEL_STATES constants
src/platform/react-hooks/src/hooks/usePresence.ts Imported and used new constants for state management
src/platform/react-hooks/src/fakes/ably.ts Added attach method to ClientSingleChannelConnection, ClientSingleDerivedChannelConnection, and Channel classes
src/platform/react-hooks/src/hooks/useChannel.ts Added import for useChannelAttach and integrated it into useChannel function
src/platform/react-hooks/src/hooks/useChannelAttach.ts Introduced useChannelAttach hook for managing channel attachment
src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx Added test suite for useChannelAttach hook
src/platform/react-hooks/src/hooks/usePresenceListener.ts Integrated useChannelAttach into usePresenceListener function

Assessment against linked issues

Objective Addressed Explanation
Prevent exceptions when connection is closed (ECO-5184)
Provide more flexible channel attachment control

Poem

🐰 A rabbit's tale of code so bright,
Channels attach with newfound might
Options dance, states now refined
Flexibility, elegantly designed
Ably's hooks hop with pure delight! 🚀

✨ 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.

@github-actions github-actions bot temporarily deployed to staging/pull/1947/features January 14, 2025 12:22 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/typedoc January 14, 2025 12:22 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/bundle-report January 14, 2025 12:22 Inactive
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 (2)
ably.d.ts (1)

896-902: LGTM! Consider enhancing the documentation.

The new attachOnSubscribe property is well-defined and aligns with the PR objectives to address implicit attach() hooks. The default value and scope are clearly documented.

Consider adding:

  1. A link to the channel attachment documentation
  2. Examples showing the behavior with both true and false values
   /**
    *  A boolean which determines whether calling subscribe
    *  on a channel or presence object should trigger an implicit attach. Defaults to `true`
    *
+   *  @see https://ably.com/docs/realtime/channels#attach
+   *  @example
+   *  ```typescript
+   *  // Default behavior (attachOnSubscribe: true)
+   *  const channel1 = client.channels.get('example', { attachOnSubscribe: true });
+   *  await channel1.subscribe('event', () => {}); // Implicitly attaches
+   *
+   *  // Explicit attachment (attachOnSubscribe: false)
+   *  const channel2 = client.channels.get('example', { attachOnSubscribe: false });
+   *  await channel2.subscribe('event', () => {}); // Does not attach
+   *  await channel2.attach(); // Explicit attach required
+   *  ```
    *
    *  Note: this option is for realtime client libraries only
    */
src/platform/react-hooks/src/hooks/usePresence.ts (1)

Line range hint 63-71: Consider using constants for channel states in cleanup function.

The cleanup function uses hardcoded channel states ['attached', 'attaching'] while the rest of the code uses imported constants. Consider defining these states in the constants file for consistency.

- const canLeaveFromChannelState = ['attached', 'attaching'].includes(channel.state);
+ const ACTIVE_CHANNEL_STATES = ['attached', 'attaching'] as const;
+ const canLeaveFromChannelState = ACTIVE_CHANNEL_STATES.includes(channel.state);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between af41744 and 1758e16.

📒 Files selected for processing (7)
  • ably.d.ts (1 hunks)
  • src/common/lib/client/realtimechannel.ts (1 hunks)
  • src/common/lib/client/realtimepresence.ts (1 hunks)
  • src/platform/react-hooks/src/AblyReactHooks.ts (1 hunks)
  • src/platform/react-hooks/src/ChannelProvider.tsx (3 hunks)
  • src/platform/react-hooks/src/hooks/constants.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/usePresence.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/platform/react-hooks/src/hooks/constants.ts
🧰 Additional context used
🪛 GitHub Actions: Test (react hooks)
src/platform/react-hooks/src/ChannelProvider.tsx

[error] 57-57: TypeError: channel.attach is not a function. The channel object does not have the expected attach method, causing test failures in React hooks tests.

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-npm-package
  • GitHub Check: test-node (16.x)
🔇 Additional comments (10)
src/platform/react-hooks/src/AblyReactHooks.ts (1)

17-25: LGTM! The changes align with the PR objectives.

The modifications to channelOptionsWithAgent correctly implement the requirement to make channel attachment explicit by:

  1. Adding explicit return type for better type safety
  2. Setting attachOnSubscribe: false to prevent implicit channel attachment
src/platform/react-hooks/src/ChannelProvider.tsx (3)

1-6: LGTM! Dependencies are properly imported.

The new imports support the connection state management functionality.


13-13: LGTM! New skip property added.

The skip property enhances control over channel attachment behavior.


27-30: LGTM! Connection state management implementation.

The implementation properly tracks and updates the connection state.

src/common/lib/client/realtimepresence.ts (1)

457-461: LGTM! Conditional channel attachment implementation.

The implementation correctly:

  1. Checks attachOnSubscribe option before attaching
  2. Includes traceability comment (RTP6d)
src/common/lib/client/realtimechannel.ts (1)

436-441: LGTM! Conditional channel attachment implementation.

The implementation correctly:

  1. Checks attachOnSubscribe option before attaching
  2. Includes traceability comment (RTL7g)
  3. Returns appropriate value based on the condition
ably.d.ts (1)

896-902: Verify the impact on existing channel subscriptions.

Let's check the codebase for any potential issues with the new attachOnSubscribe behavior.

✅ Verification successful

Safe addition with backward compatibility maintained

The new attachOnSubscribe option is a safe addition because:

  • It's optional and defaults to true, preserving existing behavior
  • Existing code will continue to work without modification
  • The behavior change only affects code that explicitly opts out
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for existing channel subscriptions that might be affected by the new attachOnSubscribe behavior

# Test 1: Look for channel subscriptions that might rely on implicit attachment
echo "Checking for channel subscriptions..."
rg -A 2 "subscribe\(" --type ts --type js

# Test 2: Look for explicit channel attachments
echo "Checking for explicit channel attachments..."
rg "attach\(\)" --type ts --type js

# Test 3: Look for channel options usage
echo "Checking for channel options usage..."
ast-grep --pattern 'channels.get($_, { $$ })'

Length of output: 58148

src/platform/react-hooks/src/hooks/usePresence.ts (3)

9-9: LGTM! Good refactoring of constants.

Moving the constants to a separate module improves maintainability and reusability across the codebase.


Line range hint 31-34: Verify the impact of skipping presence updates.

The current implementation stores messageOrPresenceObject in a ref to prevent infinite loops, but this means changes to the object won't trigger a presence update. This could lead to stale presence data.

Consider implementing one of these solutions:

  1. Add deep equality comparison (as mentioned in issue Have 'messageOrPresenceObject' parameter passed to usePresence hook as a dependency in its useEffect/useCallback calls #1688)
  2. Document this limitation clearly in the hook's JSDoc
  3. Add a warning when the object changes but presence isn't updated

Would you like me to help implement any of these solutions?


Line range hint 18-24: Verify integration with new attachment behavior.

Given that the PR introduces attachOnSubscribe = false for channels, we should verify that this presence hook correctly handles the new explicit attachment behavior.

✅ Verification successful

Hook correctly handles attachment states

The usePresence hook properly integrates with channel state management through useChannelInstance and includes appropriate state checks before presence operations. It's compatible with the new explicit attachment behavior as it:

  • Respects channel states through INACTIVE_CHANNEL_STATES
  • Delegates channel management to useChannelInstance
  • Implements proper state monitoring
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of attachOnSubscribe in related files
rg -l "attachOnSubscribe" --type ts

# Check if any presence-related tests need to be updated
rg -l "usePresence.*test" --type ts

# Look for potential attachment-related edge cases
ast-grep --pattern 'channel.attach($$$)'

Length of output: 7451


Script:

#!/bin/bash
# Check usePresence implementation and its relationship with attachOnSubscribe
ast-grep --pattern 'function usePresence<T>($$$)'

# Check AblyReactHooks configuration handling
ast-grep --pattern 'attachOnSubscribe.*=.*false'

# Look for presence-related attachment logic in RealtimePresence
rg -A 5 "enter|update|leave.*channel\.attach" src/common/lib/client/realtimepresence.ts

Length of output: 3462


Script:

#!/bin/bash
# Check usePresence hook implementation and channel state handling
rg -A 10 "usePresence.*=.*{" src/platform/react-hooks/src/hooks/usePresence.ts

# Check if usePresence uses ChannelProvider
rg -A 5 "useChannel|ChannelProvider" src/platform/react-hooks/src/hooks/usePresence.ts

# Look for presence state management
rg -A 5 "useState.*Presence|useEffect.*presence" src/platform/react-hooks/src/hooks/usePresence.ts

Length of output: 1537

Comment on lines 59 to 71
const shouldAttachToTheChannel = !skip && !INACTIVE_CONNECTION_STATES.includes(connectionState);

useLayoutEffect(() => {
if (shouldAttachToTheChannel) {
channel.attach();
}

return () => {
if (channel.state !== 'failed') {
channel.detach();
}
};
}, [shouldAttachToTheChannel, channel]);
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 the channel attachment implementation.

The test failure indicates that channel.attach is not a function. This suggests that either:

  1. The channel object is not properly initialized, or
  2. The channel object doesn't have the expected interface.

Verify the channel initialization and interface:

#!/bin/bash
# Search for channel initialization and interface definition
ast-grep --pattern 'interface Channel {
  $$$
  attach(): $_;
  $$$
}'

# Search for channel object creation
ast-grep --pattern 'channel = $_.channels.get($name)'

Copy link
Contributor

@VeskeR VeskeR left a comment

Choose a reason for hiding this comment

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

Like the idea overall. Would like to see hooks tests passing before approving. Left some comments for a discussion

src/common/lib/client/realtimechannel.ts Show resolved Hide resolved
src/common/lib/client/realtimepresence.ts Show resolved Hide resolved
src/platform/react-hooks/src/ChannelProvider.tsx Outdated Show resolved Hide resolved
src/platform/react-hooks/src/ChannelProvider.tsx Outdated Show resolved Hide resolved
@ttypic ttypic force-pushed the ECO-5184/channel-hooks-improvements branch from 1758e16 to ce8e9af Compare January 20, 2025 13:28
@github-actions github-actions bot temporarily deployed to staging/pull/1947/bundle-report January 20, 2025 13:29 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/features January 20, 2025 13:29 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/typedoc January 20, 2025 13:29 Inactive
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)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (2)

27-29: Consider optimizing state synchronization.

The manual state sync check outside useEffect could cause unnecessary re-renders. Consider moving this logic into the useConnectionStateListener callback.

-  if (ably.connection.state !== connectionState) {
-    setConnectionState(ably.connection.state);
-  }

   useConnectionStateListener((stateChange) => {
+    if (ably.connection.state !== stateChange.current) {
       setConnectionState(stateChange.current);
+    }
   }, ablyId);

35-39: Enhance error handling for better debugging.

The current fire-and-forget approach with basic console logging could make debugging difficult in production.

-      channel.attach().catch((reason) => {
-        // we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while
-        // suspending can cause errors that will be automatically resolved
-        console.log(reason);
-      });
+      channel.attach().catch((error: Ably.ErrorInfo) => {
+        // Log structured error information for better debugging
+        console.warn(
+          `Channel attachment failed:`,
+          {
+            code: error.code,
+            statusCode: error.statusCode,
+            message: error.message,
+            cause: error.cause,
+            channelState: channel.state,
+          }
+        );
+      });
src/platform/react-hooks/src/AblyReactHooks.ts (1)

17-26: Add a comment explaining the attachOnSubscribe setting.

The function name change better reflects its purpose. However, it would be helpful to add a comment explaining why attachOnSubscribe is set to false here, as it gives explicit control over channel attachment to the ChannelProvider.

 export function channelOptionsForReactHooks(options?: Ably.ChannelOptions): Ably.ChannelOptions {
+  // Set attachOnSubscribe to false to give explicit control over channel attachment to the ChannelProvider
   return {
     ...options,
     params: {
       ...options?.params,
       agent: `react-hooks/${version}`,
     },
     attachOnSubscribe: false,
   };
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1758e16 and ce8e9af.

📒 Files selected for processing (12)
  • ably.d.ts (1 hunks)
  • src/common/lib/client/realtimechannel.ts (1 hunks)
  • src/common/lib/client/realtimepresence.ts (1 hunks)
  • src/platform/react-hooks/src/AblyReactHooks.ts (1 hunks)
  • src/platform/react-hooks/src/ChannelProvider.tsx (2 hunks)
  • src/platform/react-hooks/src/fakes/ably.ts (3 hunks)
  • src/platform/react-hooks/src/hooks/constants.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannel.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/usePresence.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/usePresenceListener.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/platform/react-hooks/src/ChannelProvider.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/platform/react-hooks/src/hooks/constants.ts
  • src/common/lib/client/realtimepresence.ts
  • src/common/lib/client/realtimechannel.ts
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-npm-package
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-node (16.x)
🔇 Additional comments (6)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (1)

1-15: LGTM! Well-structured hook interface.

The hook parameters and return type are well-defined, providing clear control over channel attachment behavior.

src/platform/react-hooks/src/hooks/usePresenceListener.ts (1)

68-68: LGTM! Proper integration of useChannelAttach.

The hook is correctly integrated with the same parameters used throughout the component.

src/platform/react-hooks/src/hooks/useChannel.ts (1)

86-86: LGTM! Proper integration of useChannelAttach.

The hook is correctly integrated with the same parameters used throughout the component.

src/platform/react-hooks/src/fakes/ably.ts (1)

166-169: LGTM! Test doubles for new attach() methods.

The empty async attach() methods added to the fake implementations align with the new attachOnSubscribe behavior. The implementation is appropriate for test doubles.

Also applies to: 206-209, 359-362

src/platform/react-hooks/src/hooks/usePresence.ts (1)

8-9: LGTM! Improved code organization and attachment handling.

Good improvements:

  • Moving constants to a separate file reduces duplication
  • Using useChannelAttach hook aligns with the new explicit attachment behavior

Also applies to: 49-50

ably.d.ts (1)

896-902: LGTM! Well-documented type definition for attachOnSubscribe.

The new attachOnSubscribe property is:

  • Well-documented with clear description
  • Properly typed as optional boolean
  • Explicitly scoped to realtime client libraries

@VeskeR VeskeR self-requested a review January 20, 2025 13:39
@ttypic ttypic force-pushed the ECO-5184/channel-hooks-improvements branch from ce8e9af to dd948d4 Compare January 21, 2025 11:02
@github-actions github-actions bot temporarily deployed to staging/pull/1947/bundle-report January 21, 2025 11:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/typedoc January 21, 2025 11:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/features January 21, 2025 11:03 Inactive
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)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (2)

18-25: Consider using useCallback for the state change handler.

The state change handler function is recreated on every render. While this might not cause performance issues, using useCallback would follow React best practices.

- useConnectionStateListener((stateChange) => {
-   setConnectionState(stateChange.current);
- }, ablyId);
+ useConnectionStateListener(
+   useCallback((stateChange) => {
+     setConnectionState(stateChange.current);
+   }, []),
+   ablyId
+ );

35-39: Enhance error logging for debugging.

The current error logging is too basic. Consider adding more context about the error and the channel state.

- console.log(reason);
+ console.warn(
+   `Channel attachment failed for channel ${channel.name}:`,
+   reason,
+   `Current channel state: ${channel.state}`
+ );
src/platform/react-hooks/src/hooks/usePresence.ts (1)

Line range hint 71-77: Consider extracting the leave condition logic.

The leave condition checks are complex and could benefit from being extracted into a named function for better readability and maintainability.

+ const canLeavePresence = (connection: Ably.Connection, channel: Ably.RealtimeChannel) => {
+   const canLeaveFromConnectionState = !INACTIVE_CONNECTION_STATES.includes(connection.state);
+   const canLeaveFromChannelState = ['attached', 'attaching'].includes(channel.state);
+   return canLeaveFromChannelState && canLeaveFromConnectionState;
+ };
+
- const canLeaveFromConnectionState = !INACTIVE_CONNECTION_STATES.includes(ably.connection.state);
- const canLeaveFromChannelState = ['attached', 'attaching'].includes(channel.state);
- if (canLeaveFromChannelState && canLeaveFromConnectionState) {
+ if (canLeavePresence(ably.connection, channel)) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ce8e9af and dd948d4.

📒 Files selected for processing (12)
  • ably.d.ts (1 hunks)
  • src/common/lib/client/realtimechannel.ts (1 hunks)
  • src/common/lib/client/realtimepresence.ts (1 hunks)
  • src/platform/react-hooks/src/AblyReactHooks.ts (1 hunks)
  • src/platform/react-hooks/src/ChannelProvider.tsx (2 hunks)
  • src/platform/react-hooks/src/fakes/ably.ts (3 hunks)
  • src/platform/react-hooks/src/hooks/constants.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannel.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/usePresence.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/usePresenceListener.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/platform/react-hooks/src/ChannelProvider.tsx
  • src/platform/react-hooks/src/fakes/ably.ts
  • src/common/lib/client/realtimepresence.ts
  • src/platform/react-hooks/src/AblyReactHooks.ts
  • src/platform/react-hooks/src/hooks/constants.ts
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx
  • src/common/lib/client/realtimechannel.ts
  • ably.d.ts
  • src/platform/react-hooks/src/hooks/useChannel.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-browser (chromium)
🔇 Additional comments (4)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (1)

11-15: LGTM! Clear and well-typed hook signature.

The hook parameters are well-defined and properly typed, allowing for flexible control over channel attachment.

src/platform/react-hooks/src/hooks/usePresenceListener.ts (1)

68-68: LGTM! Proper integration of channel attachment.

The useChannelAttach hook is correctly integrated, ensuring explicit channel attachment control as intended by the PR objectives.

src/platform/react-hooks/src/hooks/usePresence.ts (2)

49-50: LGTM! Improved state management.

The integration of useChannelAttach effectively replaces the previous connection state management, aligning with the PR's goal of explicit attachment control.


52-53: Verify the order of state checks.

The order of checks in shouldNotEnterPresence might affect the behavior. Consider documenting why this specific order was chosen.

✅ Verification successful

The order of state checks is correct and follows a logical hierarchy

The order (connection → channel → skip) follows the natural dependency chain where connection state is a prerequisite for channel operations, preventing unnecessary checks and potential errors. This is consistent with the cleanup logic which applies the same principles, as evidenced by the comments explaining that presence operations require specific connection and channel states.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar patterns of state checks in the codebase
rg -A 2 "INACTIVE_CONNECTION_STATES.includes.*INACTIVE_CHANNEL_STATES.includes"

Length of output: 368


Script:

#!/bin/bash
# Find definitions of the constants
ast-grep --pattern 'const INACTIVE_CONNECTION_STATES = $_'
ast-grep --pattern 'const INACTIVE_CHANNEL_STATES = $_'

# Get broader context of usePresence hook
rg -B 5 -A 10 "shouldNotEnterPresence|INACTIVE_CONNECTION_STATES|INACTIVE_CHANNEL_STATES" src/platform/react-hooks/src/hooks/usePresence.ts

Length of output: 2489

src/platform/react-hooks/src/hooks/useChannelAttach.ts Outdated Show resolved Hide resolved
src/platform/react-hooks/src/hooks/useChannelAttach.ts Outdated Show resolved Hide resolved
if (shouldAttachToTheChannel) {
channel.attach().catch((reason) => {
// we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while
// suspending can cause errors that will be automatically resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

nice, this keeps the behavior with channel attachment same as before, but we now have a better contol over when we should attach to channel, instead of doing it any subscribe/presence.enter call

@VeskeR
Copy link
Contributor

VeskeR commented Jan 21, 2025

I suppose what was the nice thing about the ChannelProvider controlling the attachment process is that it would finally allow us to resolve this issue:#1795 - detaching a channel when the ChannelProvider unmounts.
That said, with the additional control over the attachment lifecycle this PR adds, it sets the groundwork for adding a "detach on unmount" to the ChannelProvider in the future, so lgtm

@ttypic ttypic force-pushed the ECO-5184/channel-hooks-improvements branch from dd948d4 to fd6d136 Compare January 21, 2025 19:21
@github-actions github-actions bot temporarily deployed to staging/pull/1947/bundle-report January 21, 2025 19:22 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/features January 21, 2025 19:22 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/typedoc January 21, 2025 19:22 Inactive
@ttypic
Copy link
Collaborator Author

ttypic commented Jan 21, 2025

Thanks a lot for the review, @VeskeR !

with the additional control over the attachment lifecycle this PR adds, it sets the groundwork for adding a "detach on unmount" to the ChannelProvider in the future, so lgtm

yes, that was the intention!

@ttypic ttypic requested a review from VeskeR January 21, 2025 19:23
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)
src/platform/react-hooks/src/hooks/usePresence.ts (1)

52-53: Well-handled cleanup with proper state checks.

The implementation properly handles presence cleanup by checking both connection and channel states before leaving presence. This prevents exceptions during disconnection or detachment scenarios.

However, consider adding debug logging to track presence leave attempts and their outcomes:

 if (canLeaveFromChannelState && canLeaveFromConnectionState) {
+  console.debug('[Ably Hooks] Leaving presence', { channelState: channel.state, connectionState: ably.connection.state });
   channel.presence.leave();
 }

Also applies to: 65-74

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dd948d4 and fd6d136.

📒 Files selected for processing (12)
  • ably.d.ts (1 hunks)
  • src/common/lib/client/realtimechannel.ts (1 hunks)
  • src/common/lib/client/realtimepresence.ts (1 hunks)
  • src/platform/react-hooks/src/AblyReactHooks.ts (1 hunks)
  • src/platform/react-hooks/src/ChannelProvider.tsx (2 hunks)
  • src/platform/react-hooks/src/fakes/ably.ts (3 hunks)
  • src/platform/react-hooks/src/hooks/constants.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannel.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/usePresence.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/usePresenceListener.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/platform/react-hooks/src/ChannelProvider.tsx
  • src/platform/react-hooks/src/fakes/ably.ts
  • src/common/lib/client/realtimepresence.ts
  • src/platform/react-hooks/src/hooks/constants.ts
  • src/platform/react-hooks/src/AblyReactHooks.ts
  • src/common/lib/client/realtimechannel.ts
  • src/platform/react-hooks/src/hooks/useChannel.ts
  • ably.d.ts
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-browser (chromium)
🔇 Additional comments (2)
src/platform/react-hooks/src/hooks/usePresenceListener.ts (1)

68-68: Consider hook ordering impact.

The useChannelAttach hook is called after the useEffect that sets up presence subscription. While a previous review noted this might not matter, it's worth verifying if the order could impact the subscription behavior, especially during initial mount or reconnection scenarios.

Let's verify the hook dependencies and potential race conditions:

✅ Verification successful

Hook ordering is correct and safe

The current implementation is safe because:

  • useChannelAttach manages channel attachment independently on component mount
  • Effects properly react to connection state changes
  • Test coverage shows correct handling of various states and transitions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of useChannelAttach to understand typical ordering
rg -B 3 -A 3 "useChannelAttach\(" 

# Look for any related issues or discussions
rg -l "race condition.*presence|presence.*race condition"

Length of output: 5665

src/platform/react-hooks/src/hooks/usePresence.ts (1)

49-50: LGTM! Good consolidation of connection state management.

Replacing useConnectionStateListener with useChannelAttach effectively consolidates the connection state management, aligning with the PR's goal of explicit channel attachment control.

Because of implicit `attach()` hooks can produce additional errors e.g. when updating ably client

to avoid this situation, channels created inside `ChannelProvider` now use `attachOnSubscribe = false` flag and attach is happening explicitly.
@ttypic ttypic force-pushed the ECO-5184/channel-hooks-improvements branch from fd6d136 to fdf0fb5 Compare January 22, 2025 10:11
@github-actions github-actions bot temporarily deployed to staging/pull/1947/bundle-report January 22, 2025 10:11 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/typedoc January 22, 2025 10:11 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1947/features January 22, 2025 10:12 Inactive
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 (2)
src/platform/react-hooks/src/hooks/usePresence.ts (2)

49-50: Great architectural improvement!

The switch to useChannelAttach aligns perfectly with the PR objectives by providing explicit control over the attachment lifecycle. This change not only addresses the current issues with implicit attach() hooks but also sets up the foundation for future enhancements like the "detach on unmount" feature.


Line range hint 65-73: Consider consolidating the cleanup conditions.

The cleanup function's conditions could be simplified to improve readability and maintainability.

Consider this refactoring:

-      const canLeaveFromConnectionState = !INACTIVE_CONNECTION_STATES.includes(ably.connection.state);
-      const canLeaveFromChannelState = ['attached', 'attaching'].includes(channel.state);
-      if (canLeaveFromChannelState && canLeaveFromConnectionState) {
+      const isConnectionActive = !INACTIVE_CONNECTION_STATES.includes(ably.connection.state);
+      const isChannelActive = !INACTIVE_CHANNEL_STATES.includes(channel.state);
+      if (isConnectionActive && isChannelActive) {
         channel.presence.leave();
       }

This change:

  1. Uses consistent naming patterns
  2. Reuses the INACTIVE_CHANNEL_STATES constant for consistency
  3. Makes the conditions more intuitive to read
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fd6d136 and fdf0fb5.

📒 Files selected for processing (12)
  • ably.d.ts (1 hunks)
  • src/common/lib/client/realtimechannel.ts (1 hunks)
  • src/common/lib/client/realtimepresence.ts (1 hunks)
  • src/platform/react-hooks/src/AblyReactHooks.ts (1 hunks)
  • src/platform/react-hooks/src/ChannelProvider.tsx (2 hunks)
  • src/platform/react-hooks/src/fakes/ably.ts (3 hunks)
  • src/platform/react-hooks/src/hooks/constants.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannel.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx (1 hunks)
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts (1 hunks)
  • src/platform/react-hooks/src/hooks/usePresence.ts (2 hunks)
  • src/platform/react-hooks/src/hooks/usePresenceListener.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/platform/react-hooks/src/ChannelProvider.tsx
  • src/platform/react-hooks/src/hooks/constants.ts
  • src/platform/react-hooks/src/fakes/ably.ts
  • src/platform/react-hooks/src/hooks/usePresenceListener.ts
  • src/common/lib/client/realtimepresence.ts
  • src/platform/react-hooks/src/AblyReactHooks.ts
  • src/common/lib/client/realtimechannel.ts
  • src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx
  • src/platform/react-hooks/src/hooks/useChannel.ts
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts
  • ably.d.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-browser (chromium)
🔇 Additional comments (1)
src/platform/react-hooks/src/hooks/usePresence.ts (1)

8-9: LGTM! Good refactoring of constants.

Moving the constants to a separate file improves code maintainability and reusability across the codebase.

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)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (1)

28-30: ⚠️ Potential issue

Fix potential race condition in connection state synchronization.

The direct state check and update outside of useEffect could race with the state updates from the listener.

Move the state synchronization into an effect:

-  if (ably.connection.state !== connectionState) {
-    setConnectionState(ably.connection.state);
-  }
+  useEffect(() => {
+    if (ably.connection.state !== connectionState) {
+      setConnectionState(ably.connection.state);
+    }
+  }, [ably.connection.state, connectionState]);
🧹 Nitpick comments (3)
src/platform/react-hooks/src/utils.ts (2)

4-4: Consider adding type safety for the logger access.

The any type could be replaced with a more specific type since the Logger class is exposed on the BaseClient.

-@param ablyClient ably core SDK client, it has any type because we access internal Logger class
+@param ablyClient ably core SDK client with Logger access
interface AblyWithLogger {
  Logger: {
    logAction: (logger: any, level: string, message: string) => void;
    LOG_ERROR: string;
  };
  logger: any;
}

9-9: Consider using LOG_WARN for automatically resolvable errors.

Based on the past review discussion, since these errors are automatically resolved, consider using LOG_WARN instead of LOG_ERROR.

-    ablyClient.Logger.logAction(ablyClient.logger, ablyClient.Logger.LOG_ERROR, `[react-hooks] ${message}`);
+    ablyClient.Logger.logAction(ablyClient.logger, ablyClient.Logger.LOG_WARN, `[react-hooks] ${message}`);
src/platform/react-hooks/src/hooks/useChannelAttach.ts (1)

36-40: Consider enhancing error handling for attach failures.

The current fire-and-forget approach with error logging might miss important error states that could affect the application's behavior.

Consider adding error state tracking:

const [attachError, setAttachError] = useState<Error | null>(null);

// In the effect
channel.attach().catch((reason) => {
  logError(ably, reason.toString());
  setAttachError(reason);
});

// Return error state
return { connectionState, attachError };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fdf0fb5 and daff402.

📒 Files selected for processing (2)
  • src/platform/react-hooks/src/hooks/useChannelAttach.ts (1 hunks)
  • src/platform/react-hooks/src/utils.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: lint
src/platform/react-hooks/src/hooks/useChannelAttach.ts

[warning] 42-42:
React Hook useEffect has a missing dependency: 'ably'. Either include it or remove the dependency array

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-browser (chromium)
🔇 Additional comments (1)
src/platform/react-hooks/src/hooks/useChannelAttach.ts (1)

32-32: LGTM: Clear condition for channel attachment.

The condition effectively prevents attachment during inactive connection states, which aligns with the PR's objective of explicit attachment control.

Comment on lines +34 to +42
useEffect(() => {
if (shouldAttachToTheChannel) {
channel.attach().catch((reason) => {
// we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while
// suspending can cause errors that will be automatically resolved
logError(ably, reason.toString());
});
}
}, [shouldAttachToTheChannel, channel]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing dependency to useEffect.

The effect uses ably from the outer scope but it's not included in the dependencies array.

  useEffect(() => {
    if (shouldAttachToTheChannel) {
      channel.attach().catch((reason) => {
        logError(ably, reason.toString());
      });
    }
-  }, [shouldAttachToTheChannel, channel]);
+  }, [shouldAttachToTheChannel, channel, ably]);
📝 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
useEffect(() => {
if (shouldAttachToTheChannel) {
channel.attach().catch((reason) => {
// we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while
// suspending can cause errors that will be automatically resolved
logError(ably, reason.toString());
});
}
}, [shouldAttachToTheChannel, channel]);
useEffect(() => {
if (shouldAttachToTheChannel) {
channel.attach().catch((reason) => {
// we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while
// suspending can cause errors that will be automatically resolved
logError(ably, reason.toString());
});
}
}, [shouldAttachToTheChannel, channel, ably]);
🧰 Tools
🪛 GitHub Check: lint

[warning] 42-42:
React Hook useEffect has a missing dependency: 'ably'. Either include it or remove the dependency array

@ttypic ttypic merged commit 18ed0f6 into main Jan 22, 2025
11 of 14 checks passed
@ttypic ttypic deleted the ECO-5184/channel-hooks-improvements branch January 22, 2025 15:55
@coderabbitai coderabbitai bot mentioned this pull request Jan 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants