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-4947] Spec complete for typing indicators #141

Merged
merged 1 commit into from
Nov 22, 2024
Merged

[ECO-4947] Spec complete for typing indicators #141

merged 1 commit into from
Nov 22, 2024

Conversation

umair-ably
Copy link
Collaborator

@umair-ably umair-ably commented Nov 21, 2024

Spec complete for typing indicators

typing.indicators.mov

add unit tests as part of this issue

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced chat functionality with typing notifications.
    • Introduced a dedicated class for managing typing indicators.
    • Improved connection status monitoring and error handling.
  • Bug Fixes

    • Resolved issues with typing indicator delays and connection status updates.
  • Documentation

    • Updated comments and documentation for clarity on typing features and options.
  • Tests

    • Expanded integration tests to cover typing indicators and their behaviors.

Copy link

coderabbitai bot commented Nov 21, 2024

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

The pull request introduces significant enhancements to the chat functionality within the AblyChat application. Key modifications include the implementation of typing indicators, improved connection status handling, and the introduction of a new DefaultTyping class to manage typing events. The ContentView struct is updated to support these features, along with asynchronous handling of connection status and errors in the DefaultConnection class. Additionally, the Room protocol and its implementations are adjusted to incorporate typing capabilities, ensuring a more responsive chat experience.

Changes

File Path Change Summary
Example/AblyChatExample/ContentView.swift Added typing parameter to room(), updated showTypings() and printConnectionStatusChange(), defined startTyping().
Sources/AblyChat/DefaultConnection.swift Updated status and error properties to be async, enhanced onStatusChange method, removed TimerManager.
Sources/AblyChat/DefaultTyping.swift Introduced DefaultTyping class with methods for managing typing events and user presence.
Sources/AblyChat/Room.swift Added _typing property in DefaultRoom, updated typing property to throw error if not enabled.
Sources/AblyChat/RoomFeature.swift Updated .typing case to return specific channel name suffix for typing indicators.
Sources/AblyChat/RoomOptions.swift Changed timeout default value in TypingOptions from 10 to 5 seconds.
Sources/AblyChat/TimerManager.swift Introduced TimerManager actor for managing timer tasks in a concurrent environment.
Tests/AblyChatTests/IntegrationTests.swift Updated tests to include typing options and verify typing event handling.

Assessment against linked issues

Objective Addressed Explanation
Implement typing indicators (ECO-4947)

Possibly related issues

Possibly related PRs

Suggested reviewers

  • lawrence-forooghian

🐰 In the chat, we hop and play,
Typing indicators here to stay!
With every message, our hearts align,
Connection status, oh so fine!
Let's send our thoughts, both near and far,
In this chat, we're all a star! 🌟


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

@umair-ably
Copy link
Collaborator Author

@coderabbitai pause

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: 9

🧹 Outside diff range and nitpick comments (7)
Sources/AblyChat/TimerManager.swift (2)

3-4: Add documentation comments for the actor.

Consider adding documentation comments to explain the purpose and usage of this actor, especially since it's part of the public API for typing indicators.

+/// An actor that manages timer-based operations in a thread-safe manner.
+/// Used primarily for handling typing indicator timeouts and other time-based events.
 internal final actor TimerManager {
     private var currentTask: Task<Void, Never>?

23-25: Consider checking task status for more accuracy.

The current implementation might return true even if the task has completed but hasn't been cleared yet.

Consider this more accurate implementation:

 internal func hasRunningTask() -> Bool {
-    currentTask != nil
+    if let task = currentTask {
+        return !task.isCancelled
+    }
+    return false
 }
Sources/AblyChat/Room.swift (1)

Line range hint 67-228: Consider extracting feature initialization logic.

The feature initialization pattern is repeated for multiple features (messages, reactions, presence, occupancy, and now typing). Consider extracting this into a factory pattern or builder to:

  • Reduce code duplication
  • Make feature addition more maintainable
  • Centralize feature initialization logic

This would make future feature additions easier and reduce the risk of inconsistencies in initialization patterns.

Tests/AblyChatTests/IntegrationTests.swift (2)

Line range hint 93-177: Consider documenting the rationale for delay durations

The test structure for reactions, occupancy, and presence is well-organized. The 2-second delays are necessary for eventual consistency in occupancy updates. Consider adding a brief comment explaining why these specific delay durations were chosen.

Add a comment explaining the rationale for the 2-second delay duration, for example:

- // It can take a moment for the occupancy to update from the clients connecting above, so we'll wait a 2 seconds here.
+ // Wait 2 seconds for occupancy updates to propagate through the Ably system.
+ // This duration was chosen based on empirical testing and typical network latency considerations.

178-206: Well-structured typing indicator tests, consider additional edge cases

The typing indicator test implementation is solid and covers the basic flow:

  1. Subscription to typing events
  2. Verification of typing start
  3. Verification of automatic timeout

Consider adding these additional test cases:

  1. Multiple users typing simultaneously
  2. Manual stop typing (vs timeout)
  3. Reconnection scenarios

Example test case for multiple users typing:

// Test multiple users typing simultaneously
try await txRoom.typing.start()
try await rxRoom.typing.start()

var multipleTypingEvents: [TypingEvent] = []
for await typingEvent in rxTypingSubscription {
    multipleTypingEvents.append(typingEvent)
    if typingEvent.currentlyTyping.count == 2 { break }
}

#expect(multipleTypingEvents.last?.currentlyTyping.count == 2)
Example/AblyChatExample/ContentView.swift (1)

232-242: Add error handling for typing subscription

While the implementation correctly handles the typing events, it should include error handling for the subscription stream to maintain stability.

 Task {
+    do {
         for await typing in typingSubscription {
             withAnimation {
                 typingInfo = typing.currentlyTyping.isEmpty ?
                     "" :
                     "Typing: \(typing.currentlyTyping.joined(separator: ", "))..."
             }
         }
+    } catch {
+        print("Typing subscription error: \(error)")
+        // Consider implementing retry logic or user notification
+    }
 }
Sources/AblyChat/DefaultTyping.swift (1)

79-101: Simplify Asynchronous Logic in get() Method

In the get() method, you're using withCheckedThrowingContinuation to bridge the callback-based presence.get method to async/await (lines 90-101). While this is acceptable, since we're refactoring other methods, consider using an async alternative if available for consistency and readability.

Proposed Improvement: Use Async Alternative if Available

If the Ably SDK provides an async version of presence.get, you can simplify the method:

     // (CHA-T2) Users may retrieve a list of the currently typing client IDs...
     internal func get() async throws -> Set<String> {
         logger.log(message: "Getting presence", level: .debug)

         // CHA-T2c to CHA-T2f
         do {
             try await featureChannel.waitToBeAbleToPerformPresenceOperations(requestedByFeature: RoomFeature.presence)
         } catch {
             logger.log(message: "Error waiting to perform presence get: \(error)", level: .error)
             throw error
         }

-        return try await withCheckedThrowingContinuation { continuation in
-            channel.presence.get { [processPresenceGet] members, error in
-                do {
-                    let presenceMembers = try processPresenceGet(members, error)
-                    continuation.resume(returning: presenceMembers)
-                } catch {
-                    continuation.resume(throwing: error)
-                    // processPresenceGet will log any errors
-                }
-            }
-        }
+        let members = try await channel.presence.get()
+        let presenceMembers = try processPresenceGet(members, nil)
+        return presenceMembers
     }

Ensure that channel.presence.get() supports async/await. This change improves code readability and maintains consistency with other async methods.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d2e5ddb and 88c8e37.

📒 Files selected for processing (8)
  • Example/AblyChatExample/ContentView.swift (6 hunks)
  • Sources/AblyChat/DefaultConnection.swift (0 hunks)
  • Sources/AblyChat/DefaultTyping.swift (1 hunks)
  • Sources/AblyChat/Room.swift (4 hunks)
  • Sources/AblyChat/RoomFeature.swift (1 hunks)
  • Sources/AblyChat/RoomOptions.swift (1 hunks)
  • Sources/AblyChat/TimerManager.swift (1 hunks)
  • Tests/AblyChatTests/IntegrationTests.swift (5 hunks)
💤 Files with no reviewable changes (1)
  • Sources/AblyChat/DefaultConnection.swift
🔇 Additional comments (13)
Sources/AblyChat/TimerManager.swift (2)

18-21: LGTM!

The cancellation implementation is correct and thread-safe.


1-26: Verify timer cleanup in parent components.

Since this TimerManager is crucial for typing indicators, ensure proper cleanup is implemented in parent components to avoid memory leaks.

✅ Verification successful

Let me check how these components handle cleanup by examining their implementations.


Timer cleanup is properly implemented in parent components

The verification shows that timer cleanup is well-handled in both parent components:

  • In DefaultTyping:

    • Timer is automatically cancelled when stop() is called
    • Uses weak self references to prevent retain cycles
    • Timer tasks are properly cleaned up when typing sessions end
  • In DefaultConnection:

    • Implements proper timer cancellation when connection state changes
    • Uses weak self references in callbacks
    • Cleans up timers when superseding states occur

The implementation follows best practices for memory management and timer cleanup.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for TimerManager usage to verify proper cleanup
rg -A 5 "TimerManager" 

Length of output: 1579


Script:

#!/bin/bash
# Check DefaultTyping.swift implementation for cleanup
cat Sources/AblyChat/DefaultTyping.swift

# Check DefaultConnection.swift implementation for cleanup
cat Sources/AblyChat/DefaultConnection.swift

Length of output: 17776

Sources/AblyChat/RoomOptions.swift (2)

30-30: LGTM! Clear and well-documented specification.

The comment clearly documents the typing timeout configuration option and its default value, following the specification format.


32-34: Implementation matches spec, but note the breaking change.

The timeout reduction from 10s to 5s matches the specification but represents a breaking change that could affect existing implementations.

Let's verify the consistent usage of this timeout value across the codebase:

✅ Verification successful

Breaking change verified: timeout reduction from 10s to 5s is consistently implemented

The codebase scan confirms that:

  • The new 5-second default timeout is correctly implemented in both RoomOptions.swift and Room.swift
  • No legacy references to the old 10-second timeout for typing were found
  • The change aligns with the documented specification comment stating "The default is 5000ms"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any hardcoded timeout values that might need updating
# Look for any numeric values that might be related to typing timeouts
rg -g '!*.{md,txt,json}' '(typing.*?(10|5000|10000|5))|([^a-zA-Z]([5]|10)\s*(seconds|s\b))'

Length of output: 1051


Script:

#!/bin/bash
# Let's check for any test files that might be verifying the timeout behavior
rg -l "test.*typing.*timeout|typing.*timeout.*test" --type-add 'swift:*.swift'

# Also check for any direct references to the old 10-second timeout
rg "10\s*\)" --type swift

Length of output: 1962

Sources/AblyChat/RoomFeature.swift (1)

26-27: LGTM! Implementation aligns with the specification.

The implementation correctly follows the CHA-T1 specification by using the dedicated channel format <roomId>::$chat::$typingIndicators for typing indicators.

Let's verify that the typing channel name is used consistently across the codebase:

✅ Verification successful

Let me gather more information to ensure the typing indicators implementation is complete and consistent.


Implementation is consistent with the specification and used correctly across the codebase

The typing indicators channel name implementation is correctly used throughout the codebase:

  • The channel name format <roomId>::$chat::$typingIndicators is properly defined in RoomFeature.swift
  • The implementation is consistently used in DefaultTyping.swift which handles all typing-related operations
  • Integration tests verify the typing channel functionality
  • The feature is properly integrated into the room lifecycle management
  • Error handling specifically for typing channels is implemented
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the typing channel name usage
# Expected: All typing-related channel names should follow the format <roomId>::$chat::$typingIndicators

# Search for typing channel name usage
rg -l "typingIndicators" 

# Search for potential inconsistent channel name formats
rg "::.*typing" 

Length of output: 382


Script:

#!/bin/bash
# Search for any typing-related code to verify implementation completeness
rg -A 5 -B 5 "typing" --type swift

# Search for channel name construction to verify consistent usage
ast-grep --pattern 'case typing = "typingIndicators"'

Length of output: 38874

Sources/AblyChat/Room.swift (3)

Line range hint 8-10: LGTM! Documentation and property declaration are consistent.

The typing property follows the established pattern for optional features, with clear documentation about the fatal error case.


67-67: LGTM! Property declaration follows established patterns.

The _typing property declaration maintains consistency with other optional feature properties in the class.


190-190: LGTM! Feature channel setup is correct.

The typing feature is properly integrated into the feature channel creation system.

Tests/AblyChatTests/IntegrationTests.swift (3)

66-89: LGTM: Well-structured message testing sequence

The message testing section has been improved with clear sequential numbering and descriptive comments. The test flow is logical and covers important scenarios:

  1. Pre-subscription message sending
  2. Subscription setup
  3. Post-subscription message verification
  4. Historical message retrieval

209-227: LGTM: Thorough cleanup and verification

The cleanup section ensures proper resource management by:

  1. Detaching the room and verifying status
  2. Releasing the room and verifying status
  3. Confirming new room creation post-release

39-39: LGTM: Appropriate test timeout configuration

The 2-second timeout for typing indicators is appropriate for testing purposes, while being shorter than the production default of 5 seconds to keep test execution time reasonable.

Let's verify the default timeout value in the production code:

Also applies to: 48-48

✅ Verification successful

Verified: Test timeout value is appropriately configured

The 2-second timeout in tests is a good choice since the production code (RoomOptions.swift) shows a default timeout of 5 seconds, as evidenced by:

public var timeout: TimeInterval = 5
public init(timeout: TimeInterval = 5)

Using a shorter timeout in tests helps maintain quick test execution while still validating the typing indicator functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the default typing timeout in RoomOptions
rg -A 2 "TypingOptions.*init" Sources/

Length of output: 40


Script:

#!/bin/bash
# Let's try different approaches to find typing-related configurations
# Search for typing-related code in Swift files
rg -t swift "typing|Typing" Sources/

# Also search for any timeout configurations
rg -t swift "timeout.*=.*[0-9]" Sources/

Length of output: 8185

Example/AblyChatExample/ContentView.swift (1)

61-63: LGTM: Room initialization properly configured for typing feature

The typing feature is correctly initialized alongside other room features.

Sources/AblyChat/DefaultTyping.swift (1)

211-223: Efficient Use of EventTracker Actor for Concurrency Control

The EventTracker actor correctly manages concurrent presence events by ensuring thread-safe updates and checks of event IDs. This helps prevent race conditions and ensures that only the latest typing events are processed.

Sources/AblyChat/TimerManager.swift Show resolved Hide resolved
Sources/AblyChat/Room.swift Show resolved Hide resolved
Sources/AblyChat/Room.swift Show resolved Hide resolved
Example/AblyChatExample/ContentView.swift Show resolved Hide resolved
Example/AblyChatExample/ContentView.swift Show resolved Hide resolved
Example/AblyChatExample/ContentView.swift Show resolved Hide resolved
Sources/AblyChat/DefaultTyping.swift Show resolved Hide resolved
Sources/AblyChat/DefaultTyping.swift Show resolved Hide resolved
Sources/AblyChat/DefaultTyping.swift Show resolved Hide resolved
Copy link

coderabbitai bot commented Nov 21, 2024

✅ Actions performed

Reviews paused.

@maratal
Copy link
Collaborator

maratal commented Nov 21, 2024

I would suggest filter out own typings.

Copy link
Collaborator

@maratal maratal left a comment

Choose a reason for hiding this comment

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

Left a question

Sources/AblyChat/TimerManager.swift Show resolved Hide resolved
Copy link
Collaborator

@maratal maratal left a comment

Choose a reason for hiding this comment

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

LGTM

@umair-ably
Copy link
Collaborator Author

I would suggest filter out own typings.

some users might not run it on 2 separate simulators so i think it's still nice to see in the demo app

@umair-ably umair-ably merged commit 307e978 into main Nov 22, 2024
12 checks passed
@umair-ably umair-ably deleted the ECO-4947 branch November 22, 2024 00:23
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