Skip to content

Conversation

ttypic
Copy link
Collaborator

@ttypic ttypic commented Oct 15, 2025

Replaced current() function with current property in Occupancy and Typing APIs

Summary by CodeRabbit

  • Refactor
    • Replaced method-based access for occupancy and typing state with a read-only property.
    • Standardized naming of occupancy/typing accessors across platforms.
    • Removed public exposure of the channel from the typing API.
    • Public API changes may require small updates in existing integrations and tests.

@ttypic ttypic requested a review from AndyTWF October 15, 2025 22:13
Copy link

coderabbitai bot commented Oct 15, 2025

Walkthrough

Public API signatures changed: Occupancy.current() and Typing.current() were converted to read-only properties (exposed as getCurrent on JVM/Android), Typing.getChannel()/channel exposure removed (DefaultTyping.channel made internal), and call sites/tests updated accordingly.

Changes

Cohort / File(s) Summary
API manifests
chat/api/android/chat.api, chat/api/jvm/chat.api
Renamed current()getCurrent() for Occupancy and Typing in platform API files; removed Typing.getChannel() from public API.
Occupancy implementation
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt
Replaced fun current(): OccupancyData? with val current: OccupancyData?; DefaultOccupancy now overrides the property getter, retaining enablement check and logging.
Typing interface & impl
chat/src/commonMain/kotlin/com/ably/chat/Typing.kt
Replaced fun current(): Set<String> / public channel with val current: Set<String>; made DefaultTyping.channel internal; updated emit and uses to reference the property.
Tests updated
chat/src/commonTest/kotlin/com/ably/chat/TypingTest.kt, chat/src/commonTest/kotlin/com/ably/chat/integration/TypingIntegrationTest.kt
Replaced all typing.current() call sites with property access typing.current and adjusted assertions accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant TypingAPI as Typing (interface)
  participant DefaultTyping as DefaultTyping
  participant OccupancyAPI as Occupancy (interface)
  participant DefaultOccupancy as DefaultOccupancy

  Client->>TypingAPI: access current
  Note right of TypingAPI: property access → getCurrent()
  TypingAPI->>DefaultTyping: getCurrent()
  DefaultTyping-->>Client: Set<String>

  Client->>OccupancyAPI: access current
  Note right of OccupancyAPI: property access → getCurrent()
  OccupancyAPI->>DefaultOccupancy: getCurrent()
  alt occupancy events enabled
    DefaultOccupancy-->>Client: OccupancyData?
  else events disabled
    DefaultOccupancy-->>Client: throws (events disabled)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • AndyTWF
  • sacOO7

Poem

A twitch, a hop, a getter found,
Current now is property-bound.
Typing whispers, channels hide—
Small refactors, tidy stride.
Rabbit cheers: code aligned with sound. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request removes the public getChannel() API and changes channel visibility in Typing, which falls outside the stated objective of refactoring only the current() function to a current property and was not mentioned in the PR description. Please remove or isolate the removal of the getChannel() API from this refactor or update the PR description and objectives to include this change if it is intentional.
Linked Issues Check ❓ Inconclusive The linked issue ECO-5434 lacks specific coding requirements or acceptance criteria to verify whether the changes fully satisfy its objectives, so it is unclear if all linked-issue goals have been met. Please ensure the pull request addresses all coding-related requirements and acceptance criteria defined in ECO-5434 or provide more detail on the issue’s objectives to confirm compliance.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and concisely summarizes the main change of replacing the current() functions with current properties in both the Occupancy and Typing APIs and matches the body of the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ECO-5434/current-to-prop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ttypic ttypic force-pushed the ECO-5434/current-to-prop branch from 1884c06 to d0be280 Compare October 15, 2025 22:14
@github-actions github-actions bot temporarily deployed to staging/pull/177/dokka October 15, 2025 22:17 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)
chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (2)

211-213: Nit: log message still references a function

Update to reflect property access.

- logger.trace("DefaultTyping.current()")
+ logger.trace("DefaultTyping.current")

349-351: Avoid recomputing current for each listener

current creates a new Set; compute once per emit.

-        listeners.forEach {
-            it.invoke(DefaultTypingEvent(current, typingEventChange))
-        }
+        val snapshot = current
+        listeners.forEach {
+            it.invoke(DefaultTypingEvent(snapshot, typingEventChange))
+        }
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)

166-176: Getter logic looks good; minor logging nit

Event-enabled guard and returning the last value is correct. Update the log message to match property access.

- logger.trace("Occupancy.current()")
+ logger.trace("Occupancy.current")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b5b7a51 and d0be280.

📒 Files selected for processing (4)
  • chat/api/android/chat.api (2 hunks)
  • chat/api/jvm/chat.api (2 hunks)
  • chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (2 hunks)
  • chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-28T11:10:20.947Z
Learnt from: sacOO7
PR: ably/ably-chat-kotlin#66
File: chat-android/src/main/java/com/ably/chat/Typing.kt:178-183
Timestamp: 2024-11-28T11:10:20.947Z
Learning: In `chat-android/src/main/java/com/ably/chat/Typing.kt`, within the `DefaultTyping` class, the `stop()` method must execute within `typingScope` (a `CoroutineScope` with parallelism set to 1) to avoid race conditions when setting and cancelling `typingJob`.

Applied to files:

  • chat/api/jvm/chat.api
  • chat/src/commonMain/kotlin/com/ably/chat/Typing.kt
  • chat/api/android/chat.api
📚 Learning: 2024-11-28T11:11:20.423Z
Learnt from: sacOO7
PR: ably/ably-chat-kotlin#66
File: chat-android/src/main/java/com/ably/chat/Occupancy.kt:145-145
Timestamp: 2024-11-28T11:11:20.423Z
Learning: In `chat-android/src/main/java/com/ably/chat/Occupancy.kt`, within the `DefaultOccupancy` class, when methods use `room.chatApi`, which utilizes the REST API, there's no need to call `room.ensureAttached()` before performing the operation.

Applied to files:

  • chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt
🧬 Code graph analysis (1)
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)
chat/src/commonMain/kotlin/com/ably/chat/Exceptions.kt (1)
  • clientError (110-110)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: test (Android Debug tests, testDebugUnitTest)
  • GitHub Check: test (Android Release tests, testReleaseUnitTest)
  • GitHub Check: coverage
  • GitHub Check: check
🔇 Additional comments (5)
chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (2)

36-39: API change LGTM

Exposing current as a read-only property is consistent with the PR objective. Call sites become clearer.


165-166: Good: channel is now internal

Making Channel internal matches the removal of Typing.getChannel from public APIs and reduces surface area.

chat/api/jvm/chat.api (1)

346-346: API surface change acknowledged

Occupancy.getCurrent and Typing.getCurrent additions look consistent with the move to properties.

  • Confirm no lingering usages of Typing.getChannel remain in the codebase or samples.
  • Verify nullability annotations emitted for Occupancy.getCurrent match Kotlin’s property type (current: OccupancyData?) to avoid surprises for Java callers.

Also applies to: 578-578

chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)

29-35: Property-based current added — check Java nullability expectations

current is nullable, which is sensible before the first event. Ensure the emitted nullability in platform APIs reflects this for Java callers.

  • If Java should never see null, consider making current non-null and documenting that it throws until first event (or return a default). Otherwise, keep nullable but ensure @nullable is visible in API dumps/docs.
chat/api/android/chat.api (1)

346-346: Android API surface aligned

Occupancy.getCurrent and Typing.getCurrent align with JVM and common changes.

  • Double-check that nullability metadata for Occupancy.getCurrent matches Kotlin’s nullable property.
  • Validate there are no remaining references to Typing.getChannel in Android-facing code or docs.

Also applies to: 577-579

Comment on lines 206 to 214
/**
* Spec: CHA-T9
*/
override val current: Set<String>
get() {
logger.trace("DefaultTyping.current()")
return currentlyTypingMembers.toSet()
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Return a stable snapshot to avoid concurrent iteration of a mutable set

current builds a Set by iterating a mutable backing set that’s updated on a different coroutine context. Reads can happen from any thread, risking CME or visibility issues. Prefer a precomputed, thread-safe snapshot.

Consider maintaining a snapshot and returning it:

// Add near currentlyTypingMembers
private val typingSnapshot = kotlinx.coroutines.flow.MutableStateFlow<Set<String>>(emptySet())

// Update snapshot inside processReceivedTypingEvents, right before emit(...)
typingSnapshot.value = currentlyTypingMembers.toSet()

// Getter
override val current: Set<String>
  get() {
    logger.trace("DefaultTyping.current")
    return typingSnapshot.value
  }

This avoids iterating the mutable set on arbitrary threads and computes the copy only on writes. If you’d rather not introduce StateFlow here, at least document the threading expectations for readers. [Based on learnings]


🏁 Script executed:

#!/bin/bash
rg -n "currentlyTypingMembers" -C3 --glob '*.kt'

Length of output: 3626


🏁 Script executed:

#!/bin/bash
# Get the class definition and surrounding context to understand scope/dispatcher
rg -n "class DefaultTyping" -A20 --glob '*.kt' chat/src/commonMain/kotlin/com/ably/chat/Typing.kt

Length of output: 1254


Avoid concurrent iteration over a mutable set
currentlyTypingMembers is a plain MutableSet updated on typingScope but read via current from any coroutine/thread, risking CME or visibility issues. Guard access with a thread-safe snapshot (e.g. a MutableStateFlow<Set<String>> updated on writes) or synchronize reads/writes to eliminate concurrency hazards.

🤖 Prompt for AI Agents
In chat/src/commonMain/kotlin/com/ably/chat/Typing.kt around lines 206 to 214,
currentlyReading from the plain MutableSet currentlyTypingMembers without
synchronization risks concurrent-modification or visibility issues; change the
backing storage to a thread-safe snapshot (e.g. replace MutableSet with a
MutableStateFlow<Set<String>> and update it on all writers, returning
state.value for current) or protect both reads and writes with a shared
lock/Mutex (ensure every mutation and the current getter acquire the lock).
Update all places that mutate currentlyTypingMembers to emit a new Set to the
StateFlow (or perform mutations inside the same lock) so reads return a stable
snapshot and eliminate CME/visibility hazards.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (1)

206-214: Fix concurrent iteration/visibility on currentlyTypingMembers in current getter

The getter iterates a mutable set that’s mutated on typingScope. Readers can run on any thread, risking ConcurrentModificationException and stale reads.

Use a precomputed, thread-safe snapshot updated on writes:

@@
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
@@
-    private var currentlyTypingMembers: MutableSet<String> = mutableSetOf()
+    private var currentlyTypingMembers: MutableSet<String> = mutableSetOf()
+    private val typingSnapshot = MutableStateFlow<Set<String>>(emptySet())
@@
-    override val current: Set<String>
-        get() {
-            logger.trace("DefaultTyping.current")
-            return currentlyTypingMembers.toSet()
-        }
+    override val current: Set<String>
+        get() {
+            logger.trace("DefaultTyping.current")
+            return typingSnapshot.value
+        }

Update writers to refresh the snapshot:

@@
             TypingEventType.Started -> { // CHA-T13b1
                 currentlyTypingMembers.add(clientId)
+                typingSnapshot.value = currentlyTypingMembers.toSet()
                 // CHA-T13b2 - Cancel the current self stopping event and replace with new one
                 typingStartEventPrunerJobs[clientId]?.cancel()
@@
                     // typingEventWaitingTimeout elapsed, so remove given clientId and emit stop event
                     currentlyTypingMembers.remove(clientId)
+                    typingSnapshot.value = currentlyTypingMembers.toSet()
                     typingStartEventPrunerJobs.remove(clientId)
                     emit(TypingEventType.Stopped, clientId)
                 }
@@
             TypingEventType.Stopped -> { // CHA-T13b4
                 val clientIdPresent = currentlyTypingMembers.remove(clientId)
                 typingStartEventPrunerJobs[clientId]?.cancel()
                 typingStartEventPrunerJobs.remove(clientId)
                 if (!clientIdPresent) { // CHA-T13b5
                     return
                 }
+                typingSnapshot.value = currentlyTypingMembers.toSet()
             }
🧹 Nitpick comments (1)
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)

166-175: Avoid cross-thread visibility issues for current (use a StateFlow-backed snapshot)

latestOccupancyData is mutated on Dispatchers.Default and read from arbitrary threads; without a memory barrier, readers may see stale values. Prefer a thread-safe snapshot.

Apply this minimal refactor:

@@
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
@@
-    private var latestOccupancyData: OccupancyData? = null
+    private val occupancySnapshot = MutableStateFlow<OccupancyData?>(null)
@@
-    override val current: OccupancyData?
+    override val current: OccupancyData?
         get() {
             logger.trace("Occupancy.current")
             if (!room.options.occupancy.enableEvents) { // CHA-O7c
                 throw clientError("cannot get current occupancy; occupancy events are not enabled in room options")
             }
             // CHA-O7a
             // CHA-O7b
-            return latestOccupancyData
+            return occupancySnapshot.value
         }
@@
-        latestOccupancyData = occupancyData
+        occupancySnapshot.value = occupancyData
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d0be280 and 24268b9.

📒 Files selected for processing (6)
  • chat/api/android/chat.api (2 hunks)
  • chat/api/jvm/chat.api (2 hunks)
  • chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (2 hunks)
  • chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (5 hunks)
  • chat/src/commonTest/kotlin/com/ably/chat/TypingTest.kt (9 hunks)
  • chat/src/commonTest/kotlin/com/ably/chat/integration/TypingIntegrationTest.kt (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • chat/api/jvm/chat.api
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-28T11:11:20.423Z
Learnt from: sacOO7
PR: ably/ably-chat-kotlin#66
File: chat-android/src/main/java/com/ably/chat/Occupancy.kt:145-145
Timestamp: 2024-11-28T11:11:20.423Z
Learning: In `chat-android/src/main/java/com/ably/chat/Occupancy.kt`, within the `DefaultOccupancy` class, when methods use `room.chatApi`, which utilizes the REST API, there's no need to call `room.ensureAttached()` before performing the operation.

Applied to files:

  • chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt
  • chat/api/android/chat.api
📚 Learning: 2024-11-28T11:10:20.947Z
Learnt from: sacOO7
PR: ably/ably-chat-kotlin#66
File: chat-android/src/main/java/com/ably/chat/Typing.kt:178-183
Timestamp: 2024-11-28T11:10:20.947Z
Learning: In `chat-android/src/main/java/com/ably/chat/Typing.kt`, within the `DefaultTyping` class, the `stop()` method must execute within `typingScope` (a `CoroutineScope` with parallelism set to 1) to avoid race conditions when setting and cancelling `typingJob`.

Applied to files:

  • chat/src/commonTest/kotlin/com/ably/chat/integration/TypingIntegrationTest.kt
  • chat/api/android/chat.api
  • chat/src/commonMain/kotlin/com/ably/chat/Typing.kt
🧬 Code graph analysis (3)
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)
chat/src/commonMain/kotlin/com/ably/chat/Exceptions.kt (1)
  • clientError (110-110)
chat/src/commonTest/kotlin/com/ably/chat/integration/TypingIntegrationTest.kt (1)
chat/src/commonTest/kotlin/com/ably/chat/TestUtils.kt (1)
  • assertWaiter (160-169)
chat/src/commonTest/kotlin/com/ably/chat/TypingTest.kt (1)
chat/src/commonTest/kotlin/com/ably/chat/TestUtils.kt (1)
  • assertWaiter (160-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test (JVM tests, jvmTest)
  • GitHub Check: test (Android Release tests, testReleaseUnitTest)
  • GitHub Check: test (Android Debug tests, testDebugUnitTest)
  • GitHub Check: build
  • GitHub Check: coverage
  • GitHub Check: check
🔇 Additional comments (8)
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt (1)

29-35: API change to a read-only current property looks good

Docs reflect throws behavior; naming and nullability align with prior semantics.

chat/src/commonMain/kotlin/com/ably/chat/Typing.kt (3)

36-39: API switch to current property is consistent and clear

Property form reads naturally at call sites and matches the PR goal.


165-165: Making channel internal is appropriate

Reduces surface area and aligns with encapsulation goals. No API regressions observed in Android stubs.


350-351: Event emission using current property is fine

Constructing the event from the current snapshot keeps listeners consistent with the exposed API.

chat/api/android/chat.api (2)

346-347: Verify nullability for Occupancy.getCurrent() on Android/JVM surface

Kotlin declares Occupancy.current: OccupancyData?. Ensure the generated Android API carries nullability (e.g., @nullable) so Java callers aren’t misled.

If missing, consider adding explicit nullability annotations or a typealias annotated for the Java surface.


578-582: Android stub for Typing.getCurrent() matches the Kotlin property change

Surface aligns with the refactor and removes prior method form.

chat/src/commonTest/kotlin/com/ably/chat/integration/TypingIntegrationTest.kt (1)

38-40: Tests correctly updated to property access

typing.current usage aligns with the new API; assertions remain valid.

Also applies to: 53-55, 97-99, 114-116, 131-133

chat/src/commonTest/kotlin/com/ably/chat/TypingTest.kt (1)

231-231: Unit tests updated to typing.current property — looks good

All call sites now use the property; expectations unchanged.

Also applies to: 243-243, 247-248, 251-252, 321-321, 340-340, 361-361, 378-378, 387-388, 410-410, 421-421

Copy link

Code Coverage

File Coverage [87.97%]
chat/src/commonMain/kotlin/com/ably/chat/Occupancy.kt 78.67%
chat/src/commonMain/kotlin/com/ably/chat/Typing.kt 96.39%
Total Project Coverage 85.54%

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.

1 participant