Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions ai-docs/ai-migration-v9-to-v10.md
Original file line number Diff line number Diff line change
Expand Up @@ -1068,8 +1068,10 @@ params object rather than the prop's positional arguments:
- v9 prop: `doUpdateMessageRequest(channelId, localMessage, options)`
- v10 handler: `updateMessageRequest({ localMessage, options })` → `{ message }`

If you need the old `{ id, message }` request shape inside your handler, derive it with
`localMessageToNewMessagePayload(localMessage)` — that is what the SDK's adapter used to do.
If your handler calls the client itself, derive the request with
`localMessageToNewMessagePayload(localMessage)` — that is what the SDK's adapter used to do — and
pass the message id as its own argument:
`client.updateMessage({ id: localMessage.id }, { message: localMessageToNewMessagePayload(localMessage), ...options })`.

`doSendMessageRequest` is removed too — see §13.1 for its `sendMessageRequest` replacement.

Expand Down Expand Up @@ -1142,8 +1144,20 @@ Highlights that hit integrator code:
`Role`→`RoleName`, `Logger`→`Sink`, `*SortBase`→`*Sort`, `TranslationLanguages`→`TranslationLanguage`.
- **`Event` is a discriminated union** — narrow with `EventPayload<'the.type'>`, or drop the `: Event`
annotation so `client.on('x', cb)` narrows automatically.
- **Method signatures collapsed to single objects** — `channel.sendReaction({ id, reaction, ... })`,
`deleteReaction({ id, type })`, `sendMessage({ message, ... })`, `queryChannels(request)`;
- **Method signatures collapsed to request objects, with path parameters split out** — a method whose
endpoint has URL path parameters takes them as a separate first argument,
`method(pathParams, request?)`: `channel.sendReaction({ id }, { reaction, ... })`,
`client.deleteMessage({ id }, { hard })`, `client.queryReactions({ id }, { filter, sort, limit })`;
one whose arguments are all path parameters takes just those (`deleteReaction({ id, type })`),
but keeps an unused request slot so request options stay third — pass `undefined` there:
`channel.deleteReaction({ id, type }, undefined, { signal })`,
`client.getMessage({ id }, undefined, { signal })`. Methods that take no arguments of their own
(`client.getAppSettings`, `channel.pin` / `unpin` / `archive` / `unarchive`,
`channel.disableSlowMode`, `channel.stopAIResponse`) likewise take request options second:
`channel.pin(undefined, { signal })`. Calls that pass no request options are unchanged.
Methods without path parameters take a single request object (`sendMessage({ message, ... })`,
`queryChannels(request)`). A body field left in the path-params argument through a spread is not a
compile error — the client throws at runtime instead;
`client.uploadImage({ file: { uri, name, type } })` for RN image upload — the `file` field takes
a browser `File`/`Blob` or an RN `{ uri, name, type }` descriptor, so the MIME type still has to
be explicit, it just lives on the descriptor now; the same shape applies to `client.uploadFile`
Expand Down
2 changes: 1 addition & 1 deletion examples/ExpoMessaging/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"react-native-teleport": "^1.1.12",
"react-native-web": "^0.21.2",
"react-native-worklets": "0.11.1",
"stream-chat": "^10.0.0-rc.17",
"stream-chat": "^10.0.0-rc.18",
"stream-chat-expo": "workspace:^",
"stream-chat-react-native-core": "workspace:^"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/SampleApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"react-native-teleport": "^1.1.12",
"react-native-video": "^6.19.2",
"react-native-worklets": "^0.12.1",
"stream-chat": "^10.0.0-rc.17",
"stream-chat": "^10.0.0-rc.18",
"stream-chat-react-native": "workspace:^",
"stream-chat-react-native-core": "workspace:^"
},
Expand Down
16 changes: 8 additions & 8 deletions examples/SampleApp/src/components/Reminders/ReminderItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,21 @@ export const ReminderItem = (
const buttons: AlertButton[] = client.reminders.scheduledOffsetsMs.map((offsetMs) => ({
text: t('duration.remindMe', { milliseconds: offsetMs }),
onPress: async () => {
await client.reminders.upsertReminder({
message_id: item.message_id,
remind_at: new Date(Date.now() + offsetMs),
});
await client.reminders.upsertReminder(
{ message_id: item.message_id },
{ remind_at: new Date(Date.now() + offsetMs) },
);
},
style: 'default',
}));

buttons.push({
text: 'Clear Due Date',
onPress: async () => {
await client.reminders.upsertReminder({
message_id: item.message_id,
remind_at: undefined,
});
await client.reminders.upsertReminder(
{ message_id: item.message_id },
{ remind_at: undefined },
);
},
style: 'default',
});
Expand Down
15 changes: 3 additions & 12 deletions examples/SampleApp/src/utils/DraftsManager.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { StateStore } from '@stream-io/state-store';
import { DraftResponse, QueryDraftsRequest, SortParamRequest, StreamChat } from 'stream-chat';
import { DraftResponse, QueryDraftsRequest, StreamChat } from 'stream-chat';

import { WithSubscriptions } from './WithSubscription';

export type QueryDraftOptions = {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unrelated to the generator change, just noticed that we have a generated type for this one

limit?: number;
next?: string;
prev?: string;
filter?: QueryDraftsRequest['filter'];
sort?: SortParamRequest[];
user_id?: string;
};

const MAX_QUERY_DRAFTS_LIMIT = 25;

export const DRAFT_MANAGER_INITIAL_STATE = {
Expand Down Expand Up @@ -237,15 +228,15 @@ export class DraftsManager extends WithSubscriptions {
}
};

public queryDrafts = async (options: QueryDraftOptions = {}) => {
public queryDrafts = async (options: QueryDraftsRequest = {}) => {
const response = await this.client.queryDrafts({
limit: MAX_QUERY_DRAFTS_LIMIT,
...options,
});
return response;
};

public loadNextPage = async (options: QueryDraftOptions = {}) => {
public loadNextPage = async (options: QueryDraftsRequest = {}) => {
const { pagination } = this.state.getLatestValue();

if (pagination.isLoadingNext || !pagination.nextCursor) {
Expand Down
10 changes: 5 additions & 5 deletions examples/SampleApp/src/utils/messageActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function channelMessageActions({
// if (reminder) {
// await chatClient.reminders.deleteReminder(reminder.id);
// } else {
// await chatClient.reminders.createReminder({ messageId: params.message.id });
// await chatClient.reminders.createReminder({ message_id: params.message.id });
// }
// dismissOverlay();
// } catch (error) {
Expand Down Expand Up @@ -75,10 +75,10 @@ export function channelMessageActions({
// text: t('duration.remindMe', { milliseconds: offsetMs }),
// onPress: () => {
// chatClient.reminders
// .upsertReminder({
// messageId: params.message.id,
// remind_at: new Date(Date.now() + offsetMs),
// })
// .upsertReminder(
// { message_id: params.message.id },
// { remind_at: new Date(Date.now() + offsetMs) },
// )
// .catch((_error) => {
// console.error('Error creating reminder:', _error);
// });
Expand Down
2 changes: 1 addition & 1 deletion package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"path": "0.12.7",
"react-native-markdown-package": "1.8.2",
"react-native-url-polyfill": "^2.0.0",
"stream-chat": "^10.0.0-rc.17",
"stream-chat": "^10.0.0-rc.18",
"use-sync-external-store": "^1.7.0"
},
"peerDependencies": {
Expand Down
33 changes: 20 additions & 13 deletions package/src/__tests__/offline-support/optimistic-update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ export const OptimisticUpdates = () => {
const pendingTaskType = pendingTasksRows?.[0]?.type;
const pendingTaskPayload = JSON.parse((pendingTasksRows?.[0]?.payload as string) || '{}');
expect(pendingTaskType).toBe('delete-message');
expect(pendingTaskPayload[0].id).toBe(message.id);
// The payload is `client.deleteMessage`'s argument list: the id alone is the first argument.
expect(pendingTaskPayload[0]).toEqual({ id: message.id });
});
});

Expand Down Expand Up @@ -406,7 +407,9 @@ export const OptimisticUpdates = () => {
const pendingTaskType = pendingTasksRows?.[0]?.type;
const pendingTaskPayload = JSON.parse((pendingTasksRows?.[0]?.payload as string) || '{}');
expect(pendingTaskType).toBe('send-reaction');
expect(pendingTaskPayload[0].id).toBe(targetMessage.id);
// `[{ id }, { reaction, ... }]` - the reaction travels in the request, not with the id.
expect(pendingTaskPayload[0]).toEqual({ id: targetMessage.id });
expect(pendingTaskPayload[1].reaction.type).toBe(reaction.type);
});
});

Expand Down Expand Up @@ -589,11 +592,12 @@ export const OptimisticUpdates = () => {
localMessage: LocalMessage;
options?: unknown;
}) => {
// The LLC hands over a `localMessage`; the prop received the `updateMessage` request
// shape `{ id, message }`. Rebuilt so the queued pending-task payload is unchanged.
const updatedMessage = {
id: localMessage.id,
// The LLC hands over a `localMessage`; the queued pending-task payload is the
// `client.updateMessage` argument list, `[{ id }, { message, ...options }]`.
const pathParams = { id: localMessage.id };
const request = {
message: localMessageToNewMessagePayload(localMessage),
...(options as object | undefined),
};
const editedMessage = {
...message,
Expand All @@ -605,7 +609,7 @@ export const OptimisticUpdates = () => {
channelId: channel.id,
channelType: channel.type,
messageId: message.id,
payload: [updatedMessage, options],
payload: [pathParams, request],
type: 'update-message',
});
// A complete offline update handler persists the optimistic edit to the DB (so it
Expand All @@ -623,12 +627,7 @@ export const OptimisticUpdates = () => {

render(
<Chat client={chatClient} enableOfflineSupport>
<Channel
channel={channel}
// v10 invokes doUpdateMessageRequest with the `updateMessage` request shape
// `{ id, message }`, not a flat LocalMessage. Echo a
// server-shaped response reflecting the edit; the LLC's success path re-ingests it.
>
<Channel channel={channel}>
<CallbackEffectWithContext
callback={async ({ editMessage }) => {
await flushMountEffects();
Expand Down Expand Up @@ -1228,6 +1227,14 @@ export const OptimisticUpdates = () => {
expect(deleteMessageSpy).toHaveBeenCalled();
expect(sendReactionSpy).toHaveBeenCalled();
});
// Replayed from the SQLite row: the split argument list survives the JSON round trip, with
// the (empty, soft-delete) options in the request slot rather than merged into the id.
expect(deleteMessageSpy.mock.calls[0][0]).toEqual({ id: message.id });
expect(deleteMessageSpy.mock.calls[0][1]).toEqual({});
expect(sendReactionSpy.mock.calls[0][0]).toEqual({ id: message.id });
expect(sendReactionSpy.mock.calls[0][1]).toEqual(
expect.objectContaining({ reaction: expect.objectContaining({ type: reaction.type }) }),
);
});

// This is a separate test so CallbackEffectWithContext does not need to be modified in order
Expand Down
11 changes: 4 additions & 7 deletions package/src/components/MessageMenu/hooks/useFetchReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,10 @@ export const useFetchReactions = ({
return;
}
try {
const response = await client.queryReactions({
filter: reactionType ? { type: reactionType } : undefined,
id: messageId,
limit,
next,
sort,
});
const response = await client.queryReactions(
{ id: messageId },
{ filter: reactionType ? { type: reactionType } : undefined, limit, next, sort },
);
if (response) {
setNext(response.next);

Expand Down
14 changes: 7 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6610,7 +6610,7 @@ __metadata:
react-native-teleport: "npm:^1.1.12"
react-native-web: "npm:^0.21.2"
react-native-worklets: "npm:0.11.1"
stream-chat: "npm:^10.0.0-rc.17"
stream-chat: "npm:^10.0.0-rc.18"
stream-chat-expo: "workspace:^"
stream-chat-react-native-core: "workspace:^"
typescript: "npm:6.0.3"
Expand Down Expand Up @@ -17608,7 +17608,7 @@ __metadata:
react-native-teleport: "npm:^1.1.12"
react-native-video: "npm:^6.19.2"
react-native-worklets: "npm:^0.12.1"
stream-chat: "npm:^10.0.0-rc.17"
stream-chat: "npm:^10.0.0-rc.18"
stream-chat-react-native: "workspace:^"
stream-chat-react-native-core: "workspace:^"
typescript: "npm:6.0.3"
Expand Down Expand Up @@ -18352,7 +18352,7 @@ __metadata:
react-native-worklets: "npm:^0.12.1"
react-test-renderer: "npm:19.2.3"
rimraf: "npm:^6.0.1"
stream-chat: "npm:^10.0.0-rc.17"
stream-chat: "npm:^10.0.0-rc.18"
typescript: "npm:6.0.3"
use-sync-external-store: "npm:^1.7.0"
uuid: "npm:^11.1.0"
Expand Down Expand Up @@ -18426,9 +18426,9 @@ __metadata:
languageName: unknown
linkType: soft

"stream-chat@npm:^10.0.0-rc.17":
version: 10.0.0-rc.17
resolution: "stream-chat@npm:10.0.0-rc.17"
"stream-chat@npm:^10.0.0-rc.18":
version: 10.0.0-rc.18
resolution: "stream-chat@npm:10.0.0-rc.18"
dependencies:
"@stream-io/logger": "npm:^2.0.0"
"@stream-io/state-store": "npm:^1.1.6"
Expand All @@ -18439,7 +18439,7 @@ __metadata:
built: true
husky:
built: true
checksum: 10c0/f4721fb4db398ff465bf58a918aa4cd4a46085cc57ce305019c65fdda5b710dfaa14af9870bf060894ab35053fe4697e6ca29276323a625bf8b445c9ca274b77
checksum: 10c0/0b4629e8c73f6967994d93a3c2fbc1f809aecfca3d26bc4ea4634c2f78780568f055d4af7a0e3c9c7a710def5aeba04741c4a5f1bf60afa522b6a4b133facffa
languageName: node
linkType: hard

Expand Down
Loading