Skip to content

feat(react): return history function from useChannel hook#2177

Merged
owenpearson merged 2 commits intomainfrom
use-channel-history
Mar 12, 2026
Merged

feat(react): return history function from useChannel hook#2177
owenpearson merged 2 commits intomainfrom
use-channel-history

Conversation

@owenpearson
Copy link
Copy Markdown
Member

@owenpearson owenpearson commented Mar 10, 2026

  • adds history function returned from useChannel as specified by AITDR-004

Summary by CodeRabbit

  • New Features

    • The useChannel hook now exposes a history method, letting components fetch paginated channel message history.
  • Tests

    • Added comprehensive tests for history behavior, including returned items, parameter forwarding, referential stability across re-renders, and support for derived channels.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

Walkthrough

Adds a history API surface to the useChannel hook: mock Ably connections expose a history method, the hook returns a history function that delegates to channel.history, and test coverage is added to validate behavior and stability.

Changes

Cohort / File(s) Summary
Mock Ably Implementation
src/platform/react-hooks/src/fakes/ably.ts
Added public async history(_params?: any) to ClientSingleChannelConnection and ClientSingleDerivedChannelConnection. Returns an object with items (from publishedMessages.map(...messageEnvelope)), hasNext, isLast, first/current resolving to .history(), and next resolving null.
useChannel Hook Implementation
src/platform/react-hooks/src/hooks/useChannel.ts
Extended ChannelResult to include history: Ably.RealtimeChannel['history']. Implemented history via useCallback to delegate to channel.history(params) and exposed it in the hook return value.
Tests
src/platform/react-hooks/src/hooks/useChannel.test.tsx
Added tests covering presence and callability of history, returned paginated shape (items array), retrieval of published messages (multiple publishes yield multiple items), param forwarding to channel.history, referential stability across rerenders, and behavior with derived channels.

Sequence Diagram(s)

sequenceDiagram
  participant Consumer
  participant useChannelHook as "useChannel Hook"
  participant Channel as "Ably Channel"
  participant AblyMock as "Ably Mock Connection"

  Consumer->>useChannelHook: call history(params)
  useChannelHook->>Channel: channel.history(params)
  Channel->>AblyMock: delegate history call
  AblyMock-->>Channel: return { items, hasNext, isLast, first, current, next }
  Channel-->>useChannelHook: return paginated result
  useChannelHook-->>Consumer: resolved history result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped to the code with a curious nose,
History now peeks where the old message goes.
Mocks hum along, tests clap in a row,
The hook brings the past where the present can show. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: adding a history function return from the useChannel hook. It is specific, concise, and clearly reflects the primary modification across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch use-channel-history
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@github-actions github-actions bot temporarily deployed to staging/pull/2177/bundle-report March 10, 2026 16:32 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2177/typedoc March 10, 2026 16:32 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2177/features March 10, 2026 16:36 Inactive
@owenpearson owenpearson requested review from VeskeR March 10, 2026 17:06
@owenpearson owenpearson marked this pull request as ready for review March 10, 2026 17:06
Copy link
Copy Markdown

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

🧹 Nitpick comments (3)
src/platform/react-hooks/src/hooks/useChannel.test.tsx (1)

155-172: Consider wrapping the async history() call in act() for consistency.

The history() call on line 170 is an async operation that may trigger state updates but isn't wrapped in act(). While this may work in practice since it's just reading data from the mock, wrapping async operations in act() is a React Testing Library best practice to ensure all updates are processed.

♻️ Suggested improvement
-    const paginatedResult = await result.current.history();
+    let paginatedResult;
+    await act(async () => {
+      paginatedResult = await result.current.history();
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/react-hooks/src/hooks/useChannel.test.tsx` around lines 155 -
172, The test calls result.current.history() which is an async operation that
can trigger state updates but isn't wrapped in act(); update the test so the
history() call is wrapped inside an await act(async () => { ... }) block
(similar to how the otherClient.publish calls are wrapped) to ensure React
processes all updates — modify the it('history returns previously published
messages'...) test to call await act(async () => { const paginatedResult = await
result.current.history(); expect(paginatedResult.items.length).toBe(2); });
while keeping the existing renderHook wrapper (AblyProvider/ChannelProvider) and
otherClient.publish steps intact.
src/platform/react-hooks/src/fakes/ably.ts (2)

218-227: Duplicate history implementation - consider extracting to a shared helper.

This history method is identical to the one in ClientSingleChannelConnection (lines 167-176). Consider extracting the pagination result creation into a shared helper function to reduce duplication.

♻️ Suggested refactor to reduce duplication
// Add a helper function at file level
function createPaginatedHistoryResult(channel: Channel) {
  const items = channel.publishedMessages.map((m: any) => m.messageEnvelope);
  const result = {
    items,
    hasNext: () => false,
    isLast: () => true,
    first: () => Promise.resolve(createPaginatedHistoryResult(channel)),
    current: () => Promise.resolve(createPaginatedHistoryResult(channel)),
    next: () => Promise.resolve(null),
  };
  return result;
}

// Then in both classes:
public async history(_params?: any) {
  return createPaginatedHistoryResult(this.channel);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/react-hooks/src/fakes/ably.ts` around lines 218 - 227, Both
classes implement an identical async history method; extract the
pagination-result construction into a shared helper (e.g.,
createPaginatedHistoryResult) to avoid duplication. Implement a file-level
function createPaginatedHistoryResult(channel) that builds the object using
channel.publishedMessages.map(m => m.messageEnvelope) and the
hasNext/isLast/first/current/next behaviors, then replace the history
implementations in both ClientSingleChannelConnection and the other class to
simply return createPaginatedHistoryResult(this.channel). Ensure the helper
returns the same Promise-returning first/current references to itself and next
resolves to null.

167-176: Mock messageEnvelope shape doesn't match Ably.InboundMessage type.

The history() method returns items mapped from messageEnvelope which only contains { data: message } (see line 322-324). However, Ably.RealtimeChannel['history'] returns PaginatedResult<InboundMessage>, and InboundMessage requires id, timestamp, action, version, and annotations as non-optional fields.

While this works for the current tests (which only access .data.text), it may cause type mismatches or test failures if future tests rely on other message properties.

♻️ Consider enriching the messageEnvelope structure

In the Channel.publish method around line 322:

      const messageEnvelope = {
        data: message,
+       id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+       timestamp: Date.now(),
+       action: 1, // MESSAGE_CREATE
+       version: '1.0',
+       annotations: {},
      };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/react-hooks/src/fakes/ably.ts` around lines 167 - 176, The mock
history() returns items from channel.publishedMessages.map(m =>
m.messageEnvelope) but messageEnvelope is too minimal; update Channel.publish
(where publishedMessages are pushed) to construct messageEnvelope objects that
match Ably.InboundMessage by including id, timestamp, action, version,
annotations (and existing data) so history() returns
PaginatedResult<InboundMessage>-shaped items; ensure the shape is applied to
channel.publishedMessages and referenced by history(), and keep values sensible
(e.g., generate unique id, ISO timestamp, numeric action/version, and empty
annotations) to satisfy typings and future tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/platform/react-hooks/src/fakes/ably.ts`:
- Around line 218-227: Both classes implement an identical async history method;
extract the pagination-result construction into a shared helper (e.g.,
createPaginatedHistoryResult) to avoid duplication. Implement a file-level
function createPaginatedHistoryResult(channel) that builds the object using
channel.publishedMessages.map(m => m.messageEnvelope) and the
hasNext/isLast/first/current/next behaviors, then replace the history
implementations in both ClientSingleChannelConnection and the other class to
simply return createPaginatedHistoryResult(this.channel). Ensure the helper
returns the same Promise-returning first/current references to itself and next
resolves to null.
- Around line 167-176: The mock history() returns items from
channel.publishedMessages.map(m => m.messageEnvelope) but messageEnvelope is too
minimal; update Channel.publish (where publishedMessages are pushed) to
construct messageEnvelope objects that match Ably.InboundMessage by including
id, timestamp, action, version, annotations (and existing data) so history()
returns PaginatedResult<InboundMessage>-shaped items; ensure the shape is
applied to channel.publishedMessages and referenced by history(), and keep
values sensible (e.g., generate unique id, ISO timestamp, numeric
action/version, and empty annotations) to satisfy typings and future tests.

In `@src/platform/react-hooks/src/hooks/useChannel.test.tsx`:
- Around line 155-172: The test calls result.current.history() which is an async
operation that can trigger state updates but isn't wrapped in act(); update the
test so the history() call is wrapped inside an await act(async () => { ... })
block (similar to how the otherClient.publish calls are wrapped) to ensure React
processes all updates — modify the it('history returns previously published
messages'...) test to call await act(async () => { const paginatedResult = await
result.current.history(); expect(paginatedResult.items.length).toBe(2); });
while keeping the existing renderHook wrapper (AblyProvider/ChannelProvider) and
otherClient.publish steps intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8168125-8218-4345-9593-4ef65135047c

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec7737 and 02bda7b.

📒 Files selected for processing (3)
  • src/platform/react-hooks/src/fakes/ably.ts
  • src/platform/react-hooks/src/hooks/useChannel.test.tsx
  • src/platform/react-hooks/src/hooks/useChannel.ts

Copy link
Copy Markdown
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.

LGTM, I'd only suggest adding at least a test for history with derived channels ('useChannel with deriveOptions') for peace of mind that nothing funny is going on there

@github-actions github-actions bot temporarily deployed to staging/pull/2177/features March 12, 2026 17:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2177/bundle-report March 12, 2026 17:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2177/typedoc March 12, 2026 17:49 Inactive
Copy link
Copy Markdown

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

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

167-176: Consider extracting duplicated history implementation.

The history() method implementation is identical in both ClientSingleChannelConnection and ClientSingleDerivedChannelConnection. While this is acceptable for a mock, you could reduce duplication by extracting a shared helper or using inheritance.

♻️ Optional: Extract shared helper
// Add helper function at module level
function createPaginatedResult(messages: any[]) {
  const result = {
    items: messages.map((m: any) => m.messageEnvelope),
    hasNext: () => false,
    isLast: () => true,
    first: () => Promise.resolve(result),
    current: () => Promise.resolve(result),
    next: () => Promise.resolve(null),
  };
  return result;
}

// Then in both classes:
public async history(_params?: any) {
  return createPaginatedResult(this.channel.publishedMessages);
}

Also applies to: 218-227

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/react-hooks/src/fakes/ably.ts` around lines 167 - 176, The
history() implementation is duplicated in ClientSingleChannelConnection and
ClientSingleDerivedChannelConnection; extract a shared helper (e.g.,
createPaginatedResult) at module scope that accepts messages
(this.channel.publishedMessages) and returns the paginated object used by
history(), then have both classes' history() methods return that helper result
(or alternatively move history() into a common base class both clients extend)
so the mapping to messageEnvelope and the promise callbacks are defined once.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/platform/react-hooks/src/fakes/ably.ts`:
- Around line 167-176: The history() implementation is duplicated in
ClientSingleChannelConnection and ClientSingleDerivedChannelConnection; extract
a shared helper (e.g., createPaginatedResult) at module scope that accepts
messages (this.channel.publishedMessages) and returns the paginated object used
by history(), then have both classes' history() methods return that helper
result (or alternatively move history() into a common base class both clients
extend) so the mapping to messageEnvelope and the promise callbacks are defined
once.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1392cd03-ae9a-4da2-a437-88a28a6e23d8

📥 Commits

Reviewing files that changed from the base of the PR and between 02bda7b and bc15069.

📒 Files selected for processing (3)
  • src/platform/react-hooks/src/fakes/ably.ts
  • src/platform/react-hooks/src/hooks/useChannel.test.tsx
  • src/platform/react-hooks/src/hooks/useChannel.ts

@owenpearson owenpearson merged commit 53b46ae into main Mar 12, 2026
12 of 14 checks passed
@owenpearson owenpearson deleted the use-channel-history branch March 12, 2026 18:19
@VeskeR VeskeR mentioned this pull request Mar 13, 2026
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