-
Notifications
You must be signed in to change notification settings - Fork 1
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-4943] feat: basic sending and receiving messages in the Chat #16
Conversation
WalkthroughThe changes primarily involve refactoring and enhancing the chat functionality in the Ably library. Key updates include the introduction of a Changes
Assessment against linked issues
Possibly related PRs
Poem
Recent review detailsConfiguration used: CodeRabbit UI Files selected for processing (13)
Files skipped from review due to trivial changes (1)
Files skipped from review as they are similar to previous changes (12)
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? TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
It's proven to be easier to manage to implement when subscription return object that you can use to unsubscribe
14aecaf
to
54ecb93
Compare
98158ac
to
4d3c92c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post
Actionable comments posted: 1
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (13)
- chat-android/src/main/java/com/ably/chat/ChatApi.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/ChatClient.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/JsonUtils.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/Messages.kt (4 hunks)
- chat-android/src/main/java/com/ably/chat/Room.kt (2 hunks)
- chat-android/src/main/java/com/ably/chat/RoomOptions.kt (2 hunks)
- chat-android/src/main/java/com/ably/chat/Rooms.kt (3 hunks)
- chat-android/src/main/java/com/ably/chat/Utils.kt (1 hunks)
- chat-android/src/test/java/com/ably/chat/ChatApiTest.kt (5 hunks)
- chat-android/src/test/java/com/ably/chat/MessagesTest.kt (1 hunks)
- chat-android/src/test/java/com/ably/chat/TestUtils.kt (1 hunks)
- chat-android/src/test/java/io/ably/lib/realtime/RealtimeUtils.kt (1 hunks)
- example/src/main/java/com/ably/chat/example/MainActivity.kt (5 hunks)
Files skipped from review due to trivial changes (1)
- chat-android/src/test/java/com/ably/chat/ChatApiTest.kt
Additional comments not posted (46)
chat-android/src/test/java/io/ably/lib/realtime/RealtimeUtils.kt (2)
12-17
: LGTM!The
buildChannelStateChange
function provides a convenient way to createChannelStateChange
objects for testing purposes. The function parameters cover all the necessary fields to construct aChannelStateChange
object.Note: This function can be removed if the visibility of the
ChannelStateChange
constructor is increased in the future.
23-28
: LGTM!The
buildRealtimeChannel
function provides a convenient way to create realtimeChannel
objects for testing purposes. The function initializes the client with a dummy API key and sets the auto-connect option to false, which is appropriate for testing scenarios.The function allows for easy retrieval of a channel by name, facilitating the interaction with Ably's real-time messaging capabilities.
chat-android/src/main/java/com/ably/chat/ChatClient.kt (1)
40-41
: LGTM!The change to provide a default value for the
clientOptions
parameter in theChatClient
function signature is a good enhancement. It improves the usability of the function by making theclientOptions
argument optional, allowing for simpler instantiation when default options suffice. The change is backward compatible and does not introduce any breaking changes.chat-android/src/test/java/com/ably/chat/TestUtils.kt (4)
8-14
: LGTM!The function correctly mocks the
AsyncHttpPaginatedResponse
object using the MockK library. The implementation is accurate and serves its purpose for testing.
16-25
: LGTM!The function correctly mocks the API response for retrieving messages from a chat room. It intercepts the GET request and invokes the callback with a mocked response, which is suitable for testing purposes.
27-38
: LGTM!The function correctly mocks the API response for sending a message to a chat room. It intercepts the POST request and returns a mocked response, which is suitable for testing purposes.
40-51
: LGTM!The function correctly mocks the API response for retrieving occupancy information of a chat room. It intercepts the GET request to the occupancy endpoint and returns a mocked response, which is suitable for testing purposes.
chat-android/src/main/java/com/ably/chat/RoomOptions.kt (2)
24-24
: LGTM!The change in the
reactions
property initialization and the corresponding change in theRoomReactionsOptions
class declaration to an object declaration are consistent. TreatingRoomReactionsOptions
as a singleton object is a valid approach.
30-30
: LGTM!The change in the
occupancy
property initialization and the corresponding change in theOccupancyOptions
class declaration to an object declaration are consistent. TreatingOccupancyOptions
as a singleton object is a valid approach.chat-android/src/main/java/com/ably/chat/Rooms.kt (4)
27-27
: LGTM!Providing a default value for the
options
parameter enhances the flexibility of theget
function. It allows the method to be called without explicitly passingRoomOptions
, making it easier to retrieve a room without needing to specify options every time.
50-50
: LGTM!Changing the type of the
roomIdToRoom
map fromMutableMap<String, Room>
toMutableMap<String, DefaultRoom>
indicates a more specific implementation of theRoom
interface. This change aligns with the principle of using the most specific type possible and may provide additional functionality or optimizations specific to the default implementation.
75-75
: LGTM!Removing the room from the
roomIdToRoom
map in therelease
method ensures that the room is no longer accessible through theRooms
interface. This change is part of the proper resource management and cleanup process when a room is released.
76-76
: LGTM!Calling the
release
function of theroom
when it is removed from theroomIdToRoom
map ensures proper resource management. It invokes the release logic on the room being removed, which may help prevent memory leaks or other resource-related issues. This change ensures that the room is properly cleaned up when it is no longer needed.chat-android/src/main/java/com/ably/chat/Utils.kt (2)
42-45
: LGTM!The change handles potential null values for
options.params
more gracefully by using the Elvis operator to provide a default empty map. This is a good practice to avoid null pointer exceptions.
48-103
: Excellent work!The new
DeferredValue<T>
class is a well-designed and thread-safe implementation for deferring the evaluation of a value. The class is thoroughly documented, making its purpose and usage clear.Key highlights:
- The class is marked as
internal
, which is appropriate since it's not intended for public use.- The implementation is thread-safe, using a lock object to ensure thread safety during value assignment and observer notifications.
- The
completeWith
method ensures that the value can only be set once, throwing anIllegalStateException
if called again.- The
await
method allows a coroutine to wait until the value is completed, resuming with the completed value once available.Overall, this is a solid addition to the codebase.
chat-android/src/main/java/com/ably/chat/JsonUtils.kt (9)
11-12
: LGTM!The function correctly converts a nullable
JsonElement
to anHttpCore.RequestBody
using the appropriate utility function.
14-16
: LGTM!The function correctly converts a map of string key-value pairs into a
JsonObject
using theaddProperty
function.
18-20
: LGTM!The function correctly converts a
JsonElement
into a map of strings, ensuring that only JSON primitives are included.
22-29
: LGTM!The function correctly ensures that a
JsonElement
is aJsonObject
and throws anAblyException
with an appropriate error message when the expected JSON structure is not met.
31-42
: LGTM!The function correctly retrieves a string value from a
JsonElement
for a given member name, ensuring that the value is aJsonPrimitive
and throwing anAblyException
with an appropriate error message when the expected JSON structure is not met.
44-54
: LGTM!The function correctly retrieves a long value from a
JsonElement
for a given member name, ensuring that the value is a valid long and throwing anAblyException
with an appropriate error message when the expected JSON structure is not met or the value is not a valid long.
56-66
: LGTM!The function correctly retrieves an int value from a
JsonElement
for a given member name, ensuring that the value is a valid int and throwing anAblyException
with an appropriate error message when the expected JSON structure is not met or the value is not a valid int.
68-79
: LGTM!The function correctly retrieves a
JsonPrimitive
value from aJsonElement
for a given member name, ensuring that the value is aJsonPrimitive
and throwing anAblyException
with an appropriate error message when the expected JSON structure is not met.
81-84
: LGTM!The function correctly retrieves a
JsonElement
value from aJsonElement
for a given member name, ensuring that the field exists and throwing anAblyException
with an appropriate error message when the required field is missing.chat-android/src/main/java/com/ably/chat/Room.kt (3)
93-95
: LGTM!The introduction of a private backing property
_messages
is a good practice for encapsulation. The initialization of_messages
with the required dependencies is correct.
99-100
: LGTM!Overriding the
messages
property to return the private_messages
property is a good practice for encapsulation. This change is consistent with the introduction of the private_messages
property.
135-138
: LGTM!The introduction of the
release()
function is a good practice for resource management. Calling therelease()
function on the private_messages
property is correct. This change is consistent with the list of alterations provided in the AI-generated summary.chat-android/src/main/java/com/ably/chat/ChatApi.kt (2)
17-17
: LGTM!Changing the class visibility to internal is a good practice to encapsulate the implementation details and restrict the usage of the class to the same module.
24-30
: LGTM!The addition of the
fromSerial
parameter provides more granular control over message retrieval, which enhances the flexibility of the message-fetching logic. The changes to theparams
variable construction based on thefromSerial
parameter are implemented correctly.example/src/main/java/com/ably/chat/example/MainActivity.kt (5)
17-17
: LGTM!The import statement is necessary and correctly added.
24-24
: LGTM!The import statement is necessary and correctly added.
Line range hint
35-42
: LGTM!The import statements are necessary and correctly added.
49-58
: LGTM!The
realtimeClient
andchatClient
are correctly created with the necessary configurations.
Line range hint
75-134
: LGTM!The changes in the
Chat
composable function are well-implemented and align with the transition fromChatApi
toChatClient
. The usage ofDisposableEffect
for managing the subscription lifecycle and retrieving previous messages is correct. The updates to theLazyColumn
improve the user experience by automatically scrolling to the latest message. The message sending functionality is correctly updated to useroom.messages.send
.Overall, the changes enhance the chat functionality and provide a better structure for message handling.
chat-android/src/test/java/com/ably/chat/MessagesTest.kt (5)
55-86
: LGTM!The test function correctly verifies the ability to send a message and ensures that the response matches the expected structure. The mocking of the API response and the assertion of the sent message against the expected
Message
object are implemented correctly.
88-140
: LGTM!The test function correctly verifies the ability to subscribe to incoming messages and handle the received messages. The mocking of the subscription, the simulation of an incoming message, and the assertion of the received message event against the expected
MessageEvent
object are implemented correctly.
142-153
: LGTM!The test function correctly verifies that accessing message history when not subscribed throws an
AblyException
with the expected error code. The subscription and immediate cancellation, followed by the assertion of the exception, are implemented correctly.
155-168
: LGTM!The test function correctly verifies that each subscription maintains its own channel serial. The setting of the
channelSerial
property, the creation of multiple subscriptions, and the assertions of thefromSerialProvider
for each subscription are implemented correctly.
170-188
: LGTM!The test function correctly verifies that the subscription updates the channel serial after a reattach with
resume = false
. The setting of thechannelSerial
property, the creation of the subscription, the simulation of the channel state change, and the assertion of the updatedfromSerialProvider
are implemented correctly.chat-android/src/main/java/com/ably/chat/Messages.kt (7)
182-183
: LGTM!The change to the
getPreviousMessages
method signature in theMessagesSubscription
interface improves usability by allowing callers to specify message retrieval criteria directly. The default parameter values ensure backward compatibility.
186-205
: Looks good!The
DefaultMessagesSubscription
class correctly implements the updatedMessagesSubscription
interface. The use offromSerialProvider
to associate deferred channel serial values with the current channel's state helps manage message continuity and prevent duplication, aligning with the PR objective of improving message handling.
213-231
: Great improvements!The changes to the
DefaultMessages
class enhance the messaging functionality by providing better management of listeners and handling of channel state changes. The use ofupdateChannelSerialsAfterDiscontinuity
ensures that listeners are updated correctly after any discontinuities, improving the robustness of message handling and aligning with the PR objectives.
234-272
: Nicely done!The updates to the
subscribe
method in theDefaultMessages
class improve the robustness of message handling by parsing the message data and emitting it to listeners. The use ofassociateWithCurrentChannelSerial
helps manage message continuity and prevent duplication, aligning with the PR objective of enhancing the chat functionality.
283-285
: Good addition!The
release
method is a useful addition for cleaning up resources when theDefaultMessages
instance is no longer needed.
303-308
: Helpful utility method!The
requireChannelSerial
method is a helpful utility for retrieving the channel serial or throwing an exception if it's not available, improving code readability and error handling.
341-352
: Nice utility types for parsing message data!The addition of the
PubSubMessageData
data class andparsePubSubMessageData
function improves code organization and readability by encapsulating the parsing logic for message data from the Pub/Sub channel. TheparsePubSubMessageData
function handles invalid data by throwing an exception, ensuring that only valid message data is processed. These changes align with the PR objective of enhancing the chat functionality and improving message handling.
Comments failed to post (1)
chat-android/src/main/java/com/ably/chat/Messages.kt (1)
293-301: > [!TIP]
Codebase Verification
Based on the analysis of the provided code snippets and the potential race condition, we can now generate a final response.
Race condition in
associateWithCurrentChannelSerial
confirmed, impactinggetPreviousMessages
The race condition mentioned in the review comment is indeed present and can potentially lead to duplicated messages in
getPreviousMessages
calls. Here's a summary of the issue:
- The
associateWithCurrentChannelSerial
method sets the channel serial when the channel is attached or becomes attached.- The
getPreviousMessages
method relies on thefromSerialProvider
, which is set byassociateWithCurrentChannelSerial
.- If the channel state changes between checking the state and setting the serial, it could lead to inconsistencies.
To address this issue, consider the following recommendations:
- Implement a synchronization mechanism to ensure atomic operations when setting and reading the channel serial.
- Use a thread-safe data structure or a concurrent primitive to store and update the channel serial.
- Consider implementing a retry mechanism in
getPreviousMessages
to handle cases where the channel serial might be inconsistent.- Add logging or telemetry to track occurrences of this race condition in production.
These changes will help improve the reliability of the messaging system and reduce the likelihood of duplicated messages.
Analysis chain
Potential race condition in
associateWithCurrentChannelSerial
.The
associateWithCurrentChannelSerial
method helps manage message continuity, but the comment indicates a potential race condition that could lead to duplicated messages ingetPreviousMessages
calls.Please verify the impact of this race condition on message duplication and consider addressing it to ensure the reliability of the messaging system. You can use the following script to search for instances of
getPreviousMessages
and analyze how the race condition might affect its behavior:
To gather more information about the implementation and usage of
getPreviousMessages
and its relation to the potential race condition, let's examine the Messages.kt file more closely.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Search for `getPreviousMessages` usage and analyze the impact of the race condition. # Test: Search for `getPreviousMessages` usage. rg --type kotlin $'getPreviousMessages'Length of output: 758
Script:
#!/bin/bash # Description: Examine the implementation of getPreviousMessages and related methods in Messages.kt # Search for the getPreviousMessages implementation and surrounding context rg --type kotlin -A 20 -B 10 'suspend fun getPreviousMessages' chat-android/src/main/java/com/ably/chat/Messages.kt # Search for usage of associateWithCurrentChannelSerial rg --type kotlin 'associateWithCurrentChannelSerial' chat-android/src/main/java/com/ably/chat/Messages.kt # Search for usage of channel serials in message retrieval rg --type kotlin 'channelSerial' chat-android/src/main/java/com/ably/chat/Messages.ktLength of output: 2466
4d3c92c
to
8d136b2
Compare
.gitignore
Outdated
@@ -96,6 +96,7 @@ publish.properties | |||
/.idea/jarRepositories.xml | |||
/.idea/misc.xml | |||
/.idea/shelf | |||
/.idea/uiDesigner.xml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just ignore all of .idea
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, IDE specific settings should be ignored .idea/**
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we shouldn't ignore all files in .idea
folder. It contains dictionary and codestyles settings that are useful if you use jetbrains IDE (Android Studio or IDEA). It's a good practice to add this settings for kotlin projects. But I obviously copied really bad gitignore pattern from some kotlin library template, didn't put much thought to it. I'll remove changes to gitignore in this PR and add separate PR that introduce updates to gitignore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
channelStateListener = ChannelStateListener { | ||
if (!it.resumed) updateChannelSerialsAfterDiscontinuity() | ||
} | ||
channel.on(ChannelState.attached, channelStateListener) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need to handle ChannelEvent.update
here - which is RTL2g in the core feature spec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, nice catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
val pubSubMessage = it ?: throw AblyException.fromErrorInfo( | ||
ErrorInfo("Got empty pubsub channel message", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest), | ||
) | ||
val data = parsePubSubMessageData(pubSubMessage.data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible optimisation we can do here - rather than parsing data for every subscription, can have an "internal" subscriber that's put on the channel which then translates once and emits to all "actual" subscribers.
In JS we did this by registering the "internal" subscriber with the realtime channel, then registering user listeners with an EventEmitter
instance on this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at code further down, won't this cause duplicate messages on multiple subscriptions?
As multiple instances of messageListener
will call emitMessage
which iterates all listeners?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you right, I'll add tests :) I was thinking to use internal eventbus, but decided to do this later, because we need to decide on implementation details. I'll create an issue for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emitMessage(chatMessage) | ||
} | ||
|
||
channel.subscribe(MessageEventType.Created.eventName, messageListener) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would cause an implicit channel attach which will break room lifecycle - subscribing should cause no side-effects.
I assume there's an ably-java ticket that needs doing first? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course, ably/ably-java#1027, I think until it release we can leave it as is. I'll add separate issue for this, too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val data = parsePubSubMessageData(pubSubMessage.data) | ||
val chatMessage = Message( | ||
roomId = roomId, | ||
createdAt = pubSubMessage.timestamp, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In JS we've tended towards native date types for createdAt
- not sure what's better for Kotlin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have strong opinion right now. Kotlin standard library doesn't have date type out of the box, and I still believe that one day we can provide Kotlin multiplatform support for all our products :) I'll think about this, look how easy to use timestamps in example app. Also want to research how libraries with kotlin multiplatform support like apollo-graphql deals with this. For now I suggest keeping date as Long
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdyt about https://github.com/Kotlin/kotlinx-datetime. It's a multiplatform datetime library
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I’m not mistaken, it’s not stable yet, so I don’t think we should depend on it right now. We can decide this later—it’s not an architectural design decision that will cause issues for us later, and it will be easy to refactor whenever we want.
9b4a5e8
to
60813de
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good. More tests can be added in the future.
( Not sure if we support sandbox integration tests yet )
Also, need to update example app with clean code as per comment #16 (comment)
17cdb51
to
e6ead2d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (13)
- chat-android/src/main/java/com/ably/chat/ChatApi.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/ChatClient.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/JsonUtils.kt (1 hunks)
- chat-android/src/main/java/com/ably/chat/Messages.kt (4 hunks)
- chat-android/src/main/java/com/ably/chat/Room.kt (2 hunks)
- chat-android/src/main/java/com/ably/chat/RoomOptions.kt (2 hunks)
- chat-android/src/main/java/com/ably/chat/Rooms.kt (3 hunks)
- chat-android/src/main/java/com/ably/chat/Utils.kt (1 hunks)
- chat-android/src/test/java/com/ably/chat/ChatApiTest.kt (5 hunks)
- chat-android/src/test/java/com/ably/chat/MessagesTest.kt (1 hunks)
- chat-android/src/test/java/com/ably/chat/TestUtils.kt (1 hunks)
- chat-android/src/test/java/io/ably/lib/realtime/RealtimeUtils.kt (1 hunks)
- example/src/main/java/com/ably/chat/example/MainActivity.kt (6 hunks)
Files skipped from review due to trivial changes (1)
- chat-android/src/test/java/com/ably/chat/ChatApiTest.kt
Files skipped from review as they are similar to previous changes (11)
- chat-android/src/main/java/com/ably/chat/ChatApi.kt
- chat-android/src/main/java/com/ably/chat/ChatClient.kt
- chat-android/src/main/java/com/ably/chat/JsonUtils.kt
- chat-android/src/main/java/com/ably/chat/Messages.kt
- chat-android/src/main/java/com/ably/chat/Room.kt
- chat-android/src/main/java/com/ably/chat/RoomOptions.kt
- chat-android/src/main/java/com/ably/chat/Rooms.kt
- chat-android/src/main/java/com/ably/chat/Utils.kt
- chat-android/src/test/java/com/ably/chat/MessagesTest.kt
- chat-android/src/test/java/com/ably/chat/TestUtils.kt
- chat-android/src/test/java/io/ably/lib/realtime/RealtimeUtils.kt
Additional comments not posted (1)
example/src/main/java/com/ably/chat/example/MainActivity.kt (1)
190-219
: Great use of@Preview
annotations for UI componentsAdding
@Preview
composables forMessageBubble
andChatInputField
enhances the development experience by allowing real-time previews of these UI components. This addresses previous concerns about the lack of local previews and facilitates UI adjustments without the need for repeated app runs.
DisposableEffect(Unit) { | ||
val subscription = room.messages.subscribe { | ||
messages += it.message | ||
coroutineScope.launch { | ||
messages = chatApi.getMessages(roomId, QueryOptions(orderBy = OldestFirst)).items | ||
listState.animateScrollToItem(messages.size - 1) | ||
} | ||
}) { | ||
Text("Load") | ||
} | ||
|
||
coroutineScope.launch { | ||
messages = subscription.getPreviousMessages().items.reversed() | ||
if (messages.isNotEmpty()) listState.animateScrollToItem(messages.size - 1) | ||
} | ||
|
||
onDispose { | ||
subscription.unsubscribe() | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace DisposableEffect
with LaunchedEffect
for coroutine management
Using DisposableEffect
in this context may lead to coroutines that outlive the composable's lifecycle, potentially causing memory leaks. Instead, consider using LaunchedEffect
, which automatically cancels coroutines when the composable leaves the composition. This ensures that resources are properly managed.
Apply this diff to refactor the code:
-DisposableEffect(Unit) {
- val subscription = room.messages.subscribe {
- messages += it.message
- coroutineScope.launch {
- listState.animateScrollToItem(messages.size - 1)
- }
- }
-
- coroutineScope.launch {
- messages = subscription.getPreviousMessages().items.reversed()
- if (messages.isNotEmpty()) listState.animateScrollToItem(messages.size - 1)
- }
-
- onDispose {
- subscription.unsubscribe()
- }
-}
+LaunchedEffect(Unit) {
+ val subscription = room.messages.subscribe {
+ messages += it.message
+ listState.animateScrollToItem(messages.size - 1)
+ }
+
+ messages = subscription.getPreviousMessages().items.reversed()
+ if (messages.isNotEmpty()) listState.animateScrollToItem(messages.size - 1)
+
+ awaitDispose {
+ subscription.unsubscribe()
+ }
+}
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.
DisposableEffect(Unit) { | |
val subscription = room.messages.subscribe { | |
messages += it.message | |
coroutineScope.launch { | |
messages = chatApi.getMessages(roomId, QueryOptions(orderBy = OldestFirst)).items | |
listState.animateScrollToItem(messages.size - 1) | |
} | |
}) { | |
Text("Load") | |
} | |
coroutineScope.launch { | |
messages = subscription.getPreviousMessages().items.reversed() | |
if (messages.isNotEmpty()) listState.animateScrollToItem(messages.size - 1) | |
} | |
onDispose { | |
subscription.unsubscribe() | |
} | |
} | |
LaunchedEffect(Unit) { | |
val subscription = room.messages.subscribe { | |
messages += it.message | |
listState.animateScrollToItem(messages.size - 1) | |
} | |
messages = subscription.getPreviousMessages().items.reversed() | |
if (messages.isNotEmpty()) listState.animateScrollToItem(messages.size - 1) | |
awaitDispose { | |
subscription.unsubscribe() | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no method awaitDispose
in the CoroutineScope
. coroutineScope
in the code is produced by rememberCoroutineScope
function, so it can't outlive the composable's lifecycle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ttypic, thank you for the clarification. You're absolutely right—awaitDispose
isn't applicable here, and since coroutineScope
from rememberCoroutineScope
is tied to the composable's lifecycle, it won't outlive the composable. Using DisposableEffect
as you've implemented is appropriate. I appreciate you pointing that out.
(。•́︿•̀。)
Learnings added
Learnt from: ttypic
PR: ably-labs/ably-chat-kotlin#16
File: example/src/main/java/com/ably/chat/example/MainActivity.kt:86-103
Timestamp: 2024-09-17T15:07:06.194Z
Learning: When using `rememberCoroutineScope`, the `CoroutineScope` is tied to the composable's lifecycle, so replacing `DisposableEffect` with `LaunchedEffect` is unnecessary.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make the face you made again 🙉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah it was for you @coderabbitai
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing @sacOO7!
(。•́︿•̀。)
Learnings used
Learnt from: ttypic
PR: ably-labs/ably-chat-kotlin#16
File: example/src/main/java/com/ably/chat/example/MainActivity.kt:86-103
Timestamp: 2024-09-17T15:07:06.349Z
Learning: When using `rememberCoroutineScope`, the `CoroutineScope` is tied to the composable's lifecycle, so replacing `DisposableEffect` with `LaunchedEffect` is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you describe what it exactly represents @coderabbitai
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sacOO7, the face (。•́︿•̀。) represents a look of sadness or disappointment. It conveys feelings of being sorry or empathetic in response to the situation.
Learnings used
Learnt from: ttypic
PR: ably-labs/ably-chat-kotlin#16
File: example/src/main/java/com/ably/chat/example/MainActivity.kt:86-103
Timestamp: 2024-09-17T15:07:06.349Z
Learning: When using `rememberCoroutineScope`, the `CoroutineScope` is tied to the composable's lifecycle, so replacing `DisposableEffect` with `LaunchedEffect` is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add to the learnings to make happy face when you feel empathetic next time
…th coroutine cancellation
5ef6411
to
f5b2078
Compare
In this commit, we introduce the basic implementation for sending and receiving messages. Corner cases and potential race conditions are not the focus at this stage and will be addressed later, once Room lifecycle management is implemented and a unified test suite is set up.
e6ead2d
to
14b1cfc
Compare
Resolves #2
In this PR, we introduce the basic implementation for sending and receiving messages. Corner cases and potential race conditions are not the focus at this stage and will be addressed later, once Room lifecycle management is implemented and a unified test suite is set up.
Summary by CodeRabbit
Release Notes
New Features
ChatClient
for message handling.Improvements
Refactor