From fe859ea905d0f3e003a3b5deb4f8e7cc3993a568 Mon Sep 17 00:00:00 2001 From: Zita Szupera Date: Thu, 20 Aug 2026 08:47:52 -0500 Subject: [PATCH 1/3] fix: adapt to removed stream-chat type aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `stream-chat` collapsed the twelve `*Sort` aliases — all of them exactly `SortParamRequest[]`, twelve names for one type — and dropped several aliases that either restated a generated type or added a restriction the generated model does not have. Sort aliases -> `SortParamRequest[]` (note the brackets: the alias *was* the array): * `ChannelSort` in 8 files, including two public surfaces — `ChannelListEventListenerOptions.sort` (exported via `package/src/index.ts`) and `ChannelList`'s `sort` prop. * `ReactionSort` in 4 files, including `useFetchReactions`' public `sort` param. * `DraftSort` in the SampleApp draft manager. Restated aliases -> the generated type they restated: * `PollResponse_old` -> `PollResponseData` in the three offline-store poll files. Note this is `PollResponseData`, NOT the `PollResponse` that tsc suggests — `PollResponse` is the `{ duration, poll }` envelope, a different type. * `ChannelData` -> `ChannelInput`. * `Pager` -> inlined as `limit` / `next` / `prev` on the SampleApp's own options bag. `LiveLocationPayload` -> `SharedLocation`, which is a real behavioural correction rather than a rename. The old alias was `RequireLiteral`, i.e. it made `end_at` required — an extra restriction the generated model does not carry, because a *static* shared location legitimately has no expiry. `useMessagePreviewText` was written against that false guarantee. It already tested `end_at` for truthiness, but did so through two separate cast expressions, so the narrowing did not carry to the `new Date()` call. Reads the field into a local now, which both narrows correctly and drops the duplicated cast. `CommandVariants` was removed and deliberately not replaced with an equivalent local union. It is genuinely hand-written — eight literals plus `keyof CustomCommandData`, with no generated backing — so keeping a copy here would just relocate the problem. Both uses are better served without it: * `AutoCompleteSuggestionCommandIcon` takes `name: string`. It maps a command name to one of *our* icons and already ends in an `else` fallback for anything unrecognised, so `string` is what it actually accepts. It also branched on `'flag'` and `'imgur'`, which `CommandVariants` never contained. * `AutoCompleteSuggestionItem` no longer needs `name as CommandVariants` — `name` is already a string. Also, while in the area: * `usePollState` drops the `APIResponse &` intersections from `addComment` and `endVote`. The generated response types already carry `duration`, and `APIResponse` is scheduled for removal along with the hand-written `/moderation/*` methods. Doing it now avoids a second edit to this exported type. * `channelMocks` used `type: 'MessageLabel'` for three mocked messages — the *type name* pasted where a value belonged, which is not a valid message type. Now `'regular'`. * `generator/channel.ts` pins `automod` / `automod_behavior` with `as const`. Both are narrow unions now (`Automod`, `AutomodBehavior` lost their `| (string & {})` tails), and a bare object literal widens them to `string`. * `Channel.tsx` JSDoc no longer refers to the removed `UpdatedMessage`. Verified against a local build of the stream-chat branch: * `tsc -p tsconfig.test.json` in `package/`: clean (was 20 errors). * Root `yarn typecheck`: 130 -> 108 errors. All 22 fixed are the ones above; none introduced. The two that appear to be new are the pre-existing `Property 'logger' does not exist on type 'StreamChat'` pair in `DraftsManager.ts`, shifted 3 lines by the reformatted import. * `yarn lint`: clean. * `yarn test:unit`: 38 suites / 289 tests fail both with and without these changes — byte-identical failing-suite lists. That debt predates this work; the repo was never adapted to `10.0.0-rc.4` (the version-bump commit changed only `package.json` and `yarn.lock`). No regressions from this change. Co-Authored-By: Claude Opus 5 --- examples/SampleApp/src/utils/DraftsManager.ts | 9 ++++++--- .../src/__tests__/offline-support/offline-feature.tsx | 4 ++-- .../AutoCompleteSuggestionCommandIcon.tsx | 6 ++---- .../AutoCompleteInput/AutoCompleteSuggestionItem.tsx | 9 ++------- package/src/components/Channel/Channel.tsx | 2 +- package/src/components/ChannelList/ChannelList.tsx | 6 +++--- .../ChannelList/hooks/usePaginatedChannels.ts | 4 ++-- .../components/MessageMenu/MessageUserReactions.tsx | 4 ++-- .../components/MessageMenu/hooks/useFetchReactions.ts | 4 ++-- package/src/components/Poll/hooks/usePollState.ts | 5 ++--- .../hooks/messagePreview/useMessagePreviewText.tsx | 11 +++++------ package/src/mock-builders/api/channelMocks.tsx | 6 +++--- package/src/mock-builders/generator/channel.ts | 4 ++-- .../__tests__/edit-channel-details-store.test.ts | 4 ++-- package/src/store/apis/getChannelsForFilterSort.ts | 4 ++-- package/src/store/apis/getReactionsforFilterSort.ts | 4 ++-- .../apis/queries/selectChannelIdsForFilterSort.ts | 4 ++-- .../store/apis/queries/selectReactionsForMessages.ts | 4 ++-- package/src/store/apis/upsertCidsForQuery.ts | 4 ++-- package/src/store/apis/upsertPoll.ts | 4 ++-- .../src/store/apis/utils/convertFilterSortToQuery.ts | 4 ++-- package/src/store/mappers/mapPollToStorable.ts | 4 ++-- package/src/store/mappers/mapStorableToPoll.ts | 4 ++-- package/src/types/types.ts | 4 ++-- 24 files changed, 56 insertions(+), 62 deletions(-) diff --git a/examples/SampleApp/src/utils/DraftsManager.ts b/examples/SampleApp/src/utils/DraftsManager.ts index b82f2b8dfa..2ec9b68897 100644 --- a/examples/SampleApp/src/utils/DraftsManager.ts +++ b/examples/SampleApp/src/utils/DraftsManager.ts @@ -1,10 +1,13 @@ -import { DraftFilters, DraftResponse, DraftSort, Pager, StateStore, StreamChat } from 'stream-chat'; +import { DraftFilters, DraftResponse, SortParamRequest, StateStore, StreamChat } from 'stream-chat'; import { WithSubscriptions } from './WithSubscription'; -export type QueryDraftOptions = Pager & { +export type QueryDraftOptions = { + limit?: number; + next?: string; + prev?: string; filter?: DraftFilters; - sort?: DraftSort; + sort?: SortParamRequest[]; user_id?: string; }; diff --git a/package/src/__tests__/offline-support/offline-feature.tsx b/package/src/__tests__/offline-support/offline-feature.tsx index 49d657dac8..dbecb04045 100644 --- a/package/src/__tests__/offline-support/offline-feature.tsx +++ b/package/src/__tests__/offline-support/offline-feature.tsx @@ -9,7 +9,7 @@ import type { Channel as ChannelLLC, ChannelFilters, ChannelMemberResponse, - ChannelSort, + SortParamRequest, Event, LocalMessage, MessageResponse, @@ -255,7 +255,7 @@ export const Generic = () => { foo: 'bar', type: 'messaging', } as ChannelFilters; - const sort: ChannelSort = [{ direction: 1, field: 'last_updated' }]; + const sort: SortParamRequest[] = [{ direction: 1, field: 'last_updated' }]; const renderComponent = async () => { const result = render( diff --git a/package/src/components/AutoCompleteInput/AutoCompleteSuggestionCommandIcon.tsx b/package/src/components/AutoCompleteInput/AutoCompleteSuggestionCommandIcon.tsx index e457544782..ec31457963 100644 --- a/package/src/components/AutoCompleteInput/AutoCompleteSuggestionCommandIcon.tsx +++ b/package/src/components/AutoCompleteInput/AutoCompleteSuggestionCommandIcon.tsx @@ -1,12 +1,10 @@ import React from 'react'; import { StyleSheet, View } from 'react-native'; -import { CommandVariants } from 'stream-chat'; - import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext'; import { useTheme } from '../../contexts/themeContext/ThemeContext'; -export const SuggestionCommandIcon = ({ name }: { name: CommandVariants }) => { +export const SuggestionCommandIcon = ({ name }: { name: string }) => { const { theme: { semantics }, } = useTheme(); @@ -31,7 +29,7 @@ export const SuggestionCommandIcon = ({ name }: { name: CommandVariants }) => { } }; -export const AutoCompleteSuggestionCommandIcon = ({ name }: { name: CommandVariants }) => { +export const AutoCompleteSuggestionCommandIcon = ({ name }: { name: string }) => { const { theme: { messageComposer: { diff --git a/package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx b/package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx index 313d32b319..65d8ecdbcb 100644 --- a/package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx +++ b/package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx @@ -1,12 +1,7 @@ import React, { useCallback, useMemo } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; -import type { - CommandSuggestion, - CommandVariants, - MentionSuggestion, - TextComposerSuggestion, -} from 'stream-chat'; +import type { CommandSuggestion, MentionSuggestion, TextComposerSuggestion } from 'stream-chat'; import { AutoCompleteSuggestionCommandIcon } from './AutoCompleteSuggestionCommandIcon'; import { @@ -93,7 +88,7 @@ export const CommandSuggestionItem = (item: CommandSuggestion) => { return ( - {name ? : null} + {name ? : null} & /** * Overrides the Stream default update message request (Advanced usage only) * @param channelId - * @param updatedMessage UpdatedMessage object + * @param updatedMessage The update-message request payload */ doUpdateMessageRequest?: ( channelId: string, diff --git a/package/src/components/ChannelList/ChannelList.tsx b/package/src/components/ChannelList/ChannelList.tsx index 4899e99be7..2e12c3ea04 100644 --- a/package/src/components/ChannelList/ChannelList.tsx +++ b/package/src/components/ChannelList/ChannelList.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import type { FlatList } from 'react-native-gesture-handler'; -import { Channel, ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat'; +import { Channel, ChannelFilters, ChannelOptions, SortParamRequest } from 'stream-chat'; import { ChannelListView } from './ChannelListView'; import { useCreateChannelsContext } from './hooks/useCreateChannelsContext'; @@ -61,7 +61,7 @@ export type ChannelListProps = Partial< * Object containing channel sort parameters * @see See [Channel query documentation](https://getstream.io/chat/docs/query_channels) for a list of available sorting fields * */ - sort?: ChannelSort; + sort?: SortParamRequest[]; /** * A custom request implementation for this list's `ChannelPaginator` (its `doRequest`). Use it to @@ -77,7 +77,7 @@ export type ChannelListProps = Partial< const DEFAULT_FILTERS = {}; const DEFAULT_OPTIONS = {}; -const DEFAULT_SORT: ChannelSort = []; +const DEFAULT_SORT: SortParamRequest[] = []; /** * This component fetches a list of channels, allowing you to select the channel you want to open. diff --git a/package/src/components/ChannelList/hooks/usePaginatedChannels.ts b/package/src/components/ChannelList/hooks/usePaginatedChannels.ts index 9f9b470dcc..f87927ecf0 100644 --- a/package/src/components/ChannelList/hooks/usePaginatedChannels.ts +++ b/package/src/components/ChannelList/hooks/usePaginatedChannels.ts @@ -7,7 +7,7 @@ import { ChannelPaginator, ChannelPaginatorState, ChannelQueryShape, - ChannelSort, + SortParamRequest, PaginatorOptions, } from 'stream-chat'; @@ -31,7 +31,7 @@ export type ChannelListQueryChannelsOverride = PaginatorOptions< type Parameters = { filters: ChannelFilters; options: ChannelOptions; - sort: ChannelSort; + sort: SortParamRequest[]; lockChannelOrder?: boolean; queryChannelsOverride?: ChannelListQueryChannelsOverride; }; diff --git a/package/src/components/MessageMenu/MessageUserReactions.tsx b/package/src/components/MessageMenu/MessageUserReactions.tsx index 56208fe82f..f522a58ed0 100644 --- a/package/src/components/MessageMenu/MessageUserReactions.tsx +++ b/package/src/components/MessageMenu/MessageUserReactions.tsx @@ -4,7 +4,7 @@ import { FlatList } from 'react-native-gesture-handler'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import { ReactionSort } from 'stream-chat'; +import { SortParamRequest } from 'stream-chat'; import { EmojiPickerList } from './EmojiPickerList'; import { useFetchReactions } from './hooks/useFetchReactions'; @@ -51,7 +51,7 @@ export type MessageUserReactionsProps = Partial void; diff --git a/package/src/components/MessageMenu/hooks/useFetchReactions.ts b/package/src/components/MessageMenu/hooks/useFetchReactions.ts index 34d23769b3..3af0d8c0b7 100644 --- a/package/src/components/MessageMenu/hooks/useFetchReactions.ts +++ b/package/src/components/MessageMenu/hooks/useFetchReactions.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { LocalMessage, ReactionResponse, ReactionSort } from 'stream-chat'; +import { LocalMessage, ReactionResponse, SortParamRequest } from 'stream-chat'; import { useChatContext } from '../../../contexts/chatContext/ChatContext'; import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext'; @@ -10,7 +10,7 @@ export type UseFetchReactionParams = { limit?: number; message?: LocalMessage; reactionType?: string; - sort?: ReactionSort; + sort?: SortParamRequest[]; }; const isSameReaction = (left: ReactionResponse, right: ReactionResponse) => diff --git a/package/src/components/Poll/hooks/usePollState.ts b/package/src/components/Poll/hooks/usePollState.ts index 4ebf42b516..dc122f0b11 100644 --- a/package/src/components/Poll/hooks/usePollState.ts +++ b/package/src/components/Poll/hooks/usePollState.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { - APIResponse, PollOptionData, PollOptionResponseData, PollResponse, @@ -37,9 +36,9 @@ export type UsePollStateSelectorReturnType = { }; export type UsePollStateReturnType = UsePollStateSelectorReturnType & { - addComment: (answerText: string) => Promise; + addComment: (answerText: string) => Promise; addOption: (optionText: string) => Promise; - endVote: () => Promise; + endVote: () => Promise; }; const selector = (nextValue: PollState): UsePollStateSelectorReturnType => ({ diff --git a/package/src/hooks/messagePreview/useMessagePreviewText.tsx b/package/src/hooks/messagePreview/useMessagePreviewText.tsx index 9eef9486d4..f2ea5b0ca6 100644 --- a/package/src/hooks/messagePreview/useMessagePreviewText.tsx +++ b/package/src/hooks/messagePreview/useMessagePreviewText.tsx @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { DraftMessage, - LiveLocationPayload, + SharedLocation, LocalMessage, MessageResponse, PollState, @@ -46,11 +46,10 @@ export const useMessagePreviewText = ({ } if (message?.shared_location) { - if ( - // There is a problem with types in Draft Message, and its not able to infer the type of `end_at` correctly, so the `as` is used. - (message?.shared_location as LiveLocationPayload)?.end_at && - new Date((message?.shared_location as LiveLocationPayload)?.end_at) > new Date() - ) { + // Draft messages type `shared_location` loosely, hence the cast. `end_at` is optional + // because a static location has no expiry — only a live one does. + const { end_at: endAt } = message.shared_location as SharedLocation; + if (endAt && new Date(endAt) > new Date()) { return t('messagePreview.liveLocation.label', 'Live Location'); } return t('messagePreview.location.label', 'Location'); diff --git a/package/src/mock-builders/api/channelMocks.tsx b/package/src/mock-builders/api/channelMocks.tsx index 46d16ca26b..611da7ae64 100644 --- a/package/src/mock-builders/api/channelMocks.tsx +++ b/package/src/mock-builders/api/channelMocks.tsx @@ -36,7 +36,7 @@ const CHANNEL_WITH_MESSAGES_TEXT = { deleted_at: new Date('2021-02-12T12:12:35.862Z'), id: 'ljkblk', text: 'jkbkbiubicbi', - type: 'MessageLabel', + type: 'regular', user: mockUser({ id: 'okechukwu' }), }), mockMessage({ @@ -50,7 +50,7 @@ const CHANNEL_WITH_MESSAGES_TEXT = { deleted_at: new Date('2021-02-12T12:12:35.862Z'), id: 'jbkjb', text: 'jkbkbiubicbi', - type: 'MessageLabel', + type: 'regular', user: mockUser({ id: 'okechukwu' }), }), ], @@ -149,7 +149,7 @@ const LATEST_MESSAGE = mockMessage({ deleted_at: new Date('2021-02-12T12:12:35.862Z'), id: 'string', text: 'jkbkbiubicbi', - type: 'MessageLabel', + type: 'regular', user: mockUser({ id: 'okechukwu' }), }); diff --git a/package/src/mock-builders/generator/channel.ts b/package/src/mock-builders/generator/channel.ts index 551e16a89c..c0605e1747 100644 --- a/package/src/mock-builders/generator/channel.ts +++ b/package/src/mock-builders/generator/channel.ts @@ -30,8 +30,8 @@ const defaultCapabilities: ChannelOwnCapability[] = [ ]; const defaultConfig = { - automod: 'disabled', - automod_behavior: 'flag', + automod: 'disabled' as const, + automod_behavior: 'flag' as const, commands: [ { args: '[text]', diff --git a/package/src/state-store/__tests__/edit-channel-details-store.test.ts b/package/src/state-store/__tests__/edit-channel-details-store.test.ts index 930c0f4f10..6d1982eb02 100644 --- a/package/src/state-store/__tests__/edit-channel-details-store.test.ts +++ b/package/src/state-store/__tests__/edit-channel-details-store.test.ts @@ -1,6 +1,6 @@ import { act, renderHook } from '@testing-library/react-native'; -import type { Channel, ChannelData } from 'stream-chat'; +import type { Channel, ChannelInput } from 'stream-chat'; import { generateChannelResponse } from '../../mock-builders/generator/channel'; import { getTestClientWithUser } from '../../mock-builders/mock'; @@ -26,7 +26,7 @@ const createChannel = async (data: { image?: string; name?: string } = {}) => { return client.channel( 'messaging', response.channel.id, - response.channel as unknown as ChannelData, + response.channel as unknown as ChannelInput, ) as Channel; }; diff --git a/package/src/store/apis/getChannelsForFilterSort.ts b/package/src/store/apis/getChannelsForFilterSort.ts index c23c2eb8d7..5d4b37b2f2 100644 --- a/package/src/store/apis/getChannelsForFilterSort.ts +++ b/package/src/store/apis/getChannelsForFilterSort.ts @@ -1,7 +1,7 @@ import type { ChannelFilters, ChannelOptions, - ChannelSort, + SortParamRequest, ChannelStateResponseFields, } from 'stream-chat'; @@ -29,7 +29,7 @@ export const getChannelsForFilterSort = async ({ currentUserId: string; filters?: ChannelFilters; options?: ChannelOptions; - sort?: ChannelSort; + sort?: SortParamRequest[]; }): Promise[] | null> => { if (!filters && !sort && !options?.predefined_filter) { console.warn( diff --git a/package/src/store/apis/getReactionsforFilterSort.ts b/package/src/store/apis/getReactionsforFilterSort.ts index 629ec450a5..51e50a2436 100644 --- a/package/src/store/apis/getReactionsforFilterSort.ts +++ b/package/src/store/apis/getReactionsforFilterSort.ts @@ -1,4 +1,4 @@ -import type { ReactionFilters, ReactionResponse, ReactionSort } from 'stream-chat'; +import type { ReactionFilters, ReactionResponse, SortParamRequest } from 'stream-chat'; import { getReactions } from './getReactions'; import { selectReactionsForMessages } from './queries/selectReactionsForMessages'; @@ -20,7 +20,7 @@ export const getReactionsForFilterSort = async ({ }: { messageId: string; filters?: Pick; - sort?: ReactionSort; + sort?: SortParamRequest[]; limit?: number; }): Promise => { if (!filters && !sort) { diff --git a/package/src/store/apis/queries/selectChannelIdsForFilterSort.ts b/package/src/store/apis/queries/selectChannelIdsForFilterSort.ts index cf62bbe0c0..38eefe9093 100644 --- a/package/src/store/apis/queries/selectChannelIdsForFilterSort.ts +++ b/package/src/store/apis/queries/selectChannelIdsForFilterSort.ts @@ -1,4 +1,4 @@ -import type { ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat'; +import type { ChannelFilters, ChannelOptions, SortParamRequest } from 'stream-chat'; import { createSelectQuery } from '../../sqlite-utils/createSelectQuery'; import { SqliteClient } from '../../SqliteClient'; @@ -22,7 +22,7 @@ export const selectChannelIdsForFilterSort = async ({ }: { filters?: ChannelFilters; options?: ChannelOptions; - sort?: ChannelSort; + sort?: SortParamRequest[]; }): Promise => { const query = convertFilterSortToQuery({ filters, options, sort }); diff --git a/package/src/store/apis/queries/selectReactionsForMessages.ts b/package/src/store/apis/queries/selectReactionsForMessages.ts index 3239dd1d89..aae60585f2 100644 --- a/package/src/store/apis/queries/selectReactionsForMessages.ts +++ b/package/src/store/apis/queries/selectReactionsForMessages.ts @@ -1,4 +1,4 @@ -import type { ReactionFilters, ReactionSort } from 'stream-chat'; +import type { ReactionFilters, SortParamRequest } from 'stream-chat'; import { tables } from '../../schema'; import { SqliteClient } from '../../SqliteClient'; @@ -15,7 +15,7 @@ export const selectReactionsForMessages = async ( messageIds: string[], limit: number | null = 25, filters?: Pick, - sort?: ReactionSort, + sort?: SortParamRequest[], ): Promise[]> => { const questionMarks = Array(messageIds.length).fill('?').join(','); const reactionsColumnNames = Object.keys(tables.reactions.columns) diff --git a/package/src/store/apis/upsertCidsForQuery.ts b/package/src/store/apis/upsertCidsForQuery.ts index 3076d60d47..4a2695379a 100644 --- a/package/src/store/apis/upsertCidsForQuery.ts +++ b/package/src/store/apis/upsertCidsForQuery.ts @@ -1,4 +1,4 @@ -import type { ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat'; +import type { ChannelFilters, ChannelOptions, SortParamRequest } from 'stream-chat'; import { convertFilterSortToQuery } from './utils/convertFilterSortToQuery'; @@ -16,7 +16,7 @@ export const upsertCidsForQuery = async ({ filters?: ChannelFilters; execute?: boolean; options?: ChannelOptions; - sort?: ChannelSort; + sort?: SortParamRequest[]; }) => { // Update the database only if the query is provided. const cidsString = JSON.stringify(cids); diff --git a/package/src/store/apis/upsertPoll.ts b/package/src/store/apis/upsertPoll.ts index 84f9fba657..6d65861d0c 100644 --- a/package/src/store/apis/upsertPoll.ts +++ b/package/src/store/apis/upsertPoll.ts @@ -1,4 +1,4 @@ -import type { PollResponse_old } from 'stream-chat'; +import type { PollResponseData } from 'stream-chat'; import { mapPollToStorable } from '../mappers/mapPollToStorable'; import { createUpsertQuery } from '../sqlite-utils/createUpsertQuery'; @@ -9,7 +9,7 @@ export const upsertPoll = async ({ execute = true, poll, }: { - poll: PollResponse_old; + poll: PollResponseData; execute?: boolean; }) => { const queries: PreparedQueries[] = []; diff --git a/package/src/store/apis/utils/convertFilterSortToQuery.ts b/package/src/store/apis/utils/convertFilterSortToQuery.ts index 3dc11eabfc..09c6b41d76 100644 --- a/package/src/store/apis/utils/convertFilterSortToQuery.ts +++ b/package/src/store/apis/utils/convertFilterSortToQuery.ts @@ -1,4 +1,4 @@ -import type { ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat'; +import type { ChannelFilters, ChannelOptions, SortParamRequest } from 'stream-chat'; type PredefinedFilterCacheKeyOptions = Pick< ChannelOptions, @@ -26,7 +26,7 @@ export const convertFilterSortToQuery = ({ }: { filters?: ChannelFilters; options?: ChannelOptions; - sort?: ChannelSort; + sort?: SortParamRequest[]; }) => { const predefinedFilterOptions = getPredefinedFilterOptions(options); diff --git a/package/src/store/mappers/mapPollToStorable.ts b/package/src/store/mappers/mapPollToStorable.ts index 848c3ddd35..f822fff0c9 100644 --- a/package/src/store/mappers/mapPollToStorable.ts +++ b/package/src/store/mappers/mapPollToStorable.ts @@ -1,10 +1,10 @@ -import type { PollResponse_old } from 'stream-chat'; +import type { PollResponseData } from 'stream-chat'; import { mapDateTimeToStorable } from './mapDateTimeToStorable'; import type { TableRow } from '../types'; -export const mapPollToStorable = (poll: PollResponse_old): TableRow<'poll'> => { +export const mapPollToStorable = (poll: PollResponseData): TableRow<'poll'> => { const { allow_answers, allow_user_suggested_options, diff --git a/package/src/store/mappers/mapStorableToPoll.ts b/package/src/store/mappers/mapStorableToPoll.ts index 4f9bab5768..a6e90b0108 100644 --- a/package/src/store/mappers/mapStorableToPoll.ts +++ b/package/src/store/mappers/mapStorableToPoll.ts @@ -1,8 +1,8 @@ -import type { PollResponse_old } from 'stream-chat'; +import type { PollResponseData } from 'stream-chat'; import type { TableRow } from '../types'; -export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponse_old => { +export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponseData => { const { allow_answers, allow_user_suggested_options, diff --git a/package/src/types/types.ts b/package/src/types/types.ts index 27a4ef5e50..eadacca8c5 100644 --- a/package/src/types/types.ts +++ b/package/src/types/types.ts @@ -1,6 +1,6 @@ import type { ChannelFilters, - ChannelSort, + SortParamRequest, ChannelState, FileReference, LocalAudioAttachment, @@ -87,7 +87,7 @@ export type Reaction = { export type ChannelListEventListenerOptions = { filters?: ChannelFilters; - sort?: ChannelSort; + sort?: SortParamRequest[]; }; export type UnknownType = Record; From d6eb8c89ede688874dcb76c1b3bc29bfedf8e0ab Mon Sep 17 00:00:00 2001 From: Zita Szupera Date: Thu, 20 Aug 2026 12:50:00 -0500 Subject: [PATCH 2/3] fix: call moderation through client.moderation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `stream-chat` removed the hand-written `/moderation/*` methods from `StreamChat` in favour of the generated V2 moderation API, reachable as `client.moderation`. * `client.muteUser(id)` -> `client.moderation.mute({ target_ids: [id] })` * `client.unmuteUser(id)` -> `client.moderation.unmute({ target_ids: [id] })` * `client.banUser(id)` -> `client.moderation.ban({ target_user_id: id })` * `client.flagMessage(id)` -> `client.moderation.flagMessage(id)` V2 mute/unmute take `target_ids` as an array, hence the wrapping. The ban call stays unscoped, matching the app-wide ban it performed before — note this differs from stream-chat-react, which bans through `channel.banUser` and is therefore channel-scoped. Call sites: `useMessageActionHandlers` (mute, unmute, ban, flag), `useChannelActions` and `useUserActions` (mute, unmute). `client.unbanUser` is unchanged — it has no generated equivalent upstream and keeps its v1 implementation. No public API change: none of these responses were read, and no exported type or prop signature moves. Tests: the mock client in `useUserActions.test` exposes `moderation: { mute, unmute }` instead of top-level `muteUser` / `unmuteUser`, and its assertions expect `{ target_ids: [id] }` rather than a bare id. Verified: `tsc --noEmit -p tsconfig.test.json` clean, `yarn lint` clean, and `yarn test:unit` matches its pre-change baseline exactly — 38 suites / 289 tests failing before and after, all pre-existing (this repo was never adapted to stream-chat 10.0.0-rc.4). No regressions. Co-Authored-By: Claude Opus 5 --- .../components/Message/hooks/useMessageActionHandlers.ts | 8 ++++---- .../src/hooks/actions/__tests__/useUserActions.test.tsx | 7 +++---- package/src/hooks/actions/useChannelActions.ts | 4 ++-- package/src/hooks/actions/useUserActions.ts | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/package/src/components/Message/hooks/useMessageActionHandlers.ts b/package/src/components/Message/hooks/useMessageActionHandlers.ts index a11fea726e..f58e56a2ec 100644 --- a/package/src/components/Message/hooks/useMessageActionHandlers.ts +++ b/package/src/components/Message/hooks/useMessageActionHandlers.ts @@ -170,7 +170,7 @@ export const useMessageActionHandlers = ({ try { if (isMuted) { - await client.unmuteUser(message.user.id); + await client.moderation.unmute({ target_ids: [message.user.id] }); addNotification({ message: t('message.userUnmuted.text', '{{ user }} has been unmuted', { user: message.user?.name || message.user?.id, @@ -179,7 +179,7 @@ export const useMessageActionHandlers = ({ origin: { context: { message }, emitter: 'MessageActions' }, }); } else { - await client.muteUser(message.user.id); + await client.moderation.mute({ target_ids: [message.user.id] }); addNotification({ message: t('message.userMuted.text', '{{ user }} has been muted', { user: message.user?.name || message.user?.id, @@ -215,7 +215,7 @@ export const useMessageActionHandlers = ({ if (messageUser.banned) { await client.unbanUser(messageUser.id); } else { - await client.banUser(messageUser.id); + await client.moderation.ban({ target_user_id: messageUser.id }); } }); @@ -274,7 +274,7 @@ export const useMessageActionHandlers = ({ { onPress: async () => { try { - await client.flagMessage(message.id); + await client.moderation.flagMessage(message.id); addNotification({ message: t('message.flagged.text', 'Message has been successfully flagged'), options: { severity: 'success', type: 'api:message:flag:success' }, diff --git a/package/src/hooks/actions/__tests__/useUserActions.test.tsx b/package/src/hooks/actions/__tests__/useUserActions.test.tsx index 2888ccc147..65fc91450b 100644 --- a/package/src/hooks/actions/__tests__/useUserActions.test.tsx +++ b/package/src/hooks/actions/__tests__/useUserActions.test.tsx @@ -32,9 +32,8 @@ describe('useUserActions', () => { ({ client: { blockUser, - muteUser, + moderation: { mute: muteUser, unmute: unmuteUser }, unBlockUser, - unmuteUser, }, }) as unknown as ChatContext.ChatContextValue, ); @@ -75,7 +74,7 @@ describe('useUserActions', () => { await result.current.muteUser({ onSuccess }); - expect(muteUser).toHaveBeenCalledWith('target-user-id'); + expect(muteUser).toHaveBeenCalledWith({ target_ids: ['target-user-id'] }); expect(onSuccess).toHaveBeenCalledTimes(1); expect(addNotification).toHaveBeenCalledWith( expect.objectContaining({ @@ -119,7 +118,7 @@ describe('useUserActions', () => { await result.current.unmuteUser({ onSuccess }); - expect(unmuteUser).toHaveBeenCalledWith('target-user-id'); + expect(unmuteUser).toHaveBeenCalledWith({ target_ids: ['target-user-id'] }); expect(onSuccess).toHaveBeenCalledTimes(1); expect(addNotification).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/package/src/hooks/actions/useChannelActions.ts b/package/src/hooks/actions/useChannelActions.ts index c3093a8c72..3cff198c76 100644 --- a/package/src/hooks/actions/useChannelActions.ts +++ b/package/src/hooks/actions/useChannelActions.ts @@ -296,7 +296,7 @@ export const useChannelActions = (channel: Channel) => { try { if (otherUser?.user?.id) { - await client.muteUser(otherUser.user.id); + await client.moderation.mute({ target_ids: [otherUser.user.id] }); addNotification({ message: t('message.userMuted.text', '{{ user }} has been muted', { user: otherUser.user.name || otherUser.user.id, @@ -329,7 +329,7 @@ export const useChannelActions = (channel: Channel) => { try { if (otherUser?.user?.id) { - await client.unmuteUser(otherUser.user.id); + await client.moderation.unmute({ target_ids: [otherUser.user.id] }); addNotification({ message: t('message.userUnmuted.text', '{{ user }} has been unmuted', { user: otherUser.user.name || otherUser.user.id, diff --git a/package/src/hooks/actions/useUserActions.ts b/package/src/hooks/actions/useUserActions.ts index ee4926e2ef..2aaa8f7ef2 100644 --- a/package/src/hooks/actions/useUserActions.ts +++ b/package/src/hooks/actions/useUserActions.ts @@ -31,7 +31,7 @@ export const useUserActions = (user: UserResponse | undefined): UserActions => { } try { - await client.muteUser(user.id); + await client.moderation.mute({ target_ids: [user.id] }); addNotification({ message: t('message.userMuted.text', '{{ user }} has been muted', { user: user.name || user.id, @@ -59,7 +59,7 @@ export const useUserActions = (user: UserResponse | undefined): UserActions => { } try { - await client.unmuteUser(user.id); + await client.moderation.unmute({ target_ids: [user.id] }); addNotification({ message: t('message.userUnmuted.text', '{{ user }} has been unmuted', { user: user.name || user.id, From 593a555ee90c28588d3ce54b24eb0e5caa5364d3 Mon Sep 17 00:00:00 2001 From: Zita Szupera Date: Thu, 20 Aug 2026 15:05:43 -0500 Subject: [PATCH 3/3] refactor: adopt the derived VotingVisibility and drop the PollOptionData cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `VotingVisibility` is no longer an enum in stream-chat — it is `NonNullable`, so it is not a runtime value any more. Member names matched their string values, so `VotingVisibility.anonymous` becomes `'anonymous'`. The `as VotingVisibility` narrowing in `usePollState` stays: `PollResponseData.voting_visibility` is still typed `string` in the spec. `PollOptionData` is gone; `poll.createOption()` now takes `CreatePollOptionRequest`, which does not require an option `id`. The `as PollOptionData` cast in `usePollState.addOption` existed only to defeat that requirement, so it can go. Co-Authored-By: Claude Opus 5 --- package/src/components/Poll/CreatePollContent.tsx | 4 ++-- package/src/components/Poll/components/PollAnswersList.tsx | 4 ++-- .../components/Poll/components/PollResults/PollVote.tsx | 7 ++----- package/src/components/Poll/hooks/usePollState.ts | 3 +-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/package/src/components/Poll/CreatePollContent.tsx b/package/src/components/Poll/CreatePollContent.tsx index 754508ceeb..4d75268d8d 100644 --- a/package/src/components/Poll/CreatePollContent.tsx +++ b/package/src/components/Poll/CreatePollContent.tsx @@ -4,7 +4,7 @@ import { StyleSheet, Switch, Text, View } from 'react-native'; import { ScrollView } from 'react-native-gesture-handler'; import Animated, { LinearTransition, useSharedValue } from 'react-native-reanimated'; -import { PollComposerState, StateStore, VotingVisibility } from 'stream-chat'; +import { PollComposerState, StateStore } from 'stream-chat'; import { CreatePollOptions, CurrentOptionPositionsCache } from './components'; @@ -120,7 +120,7 @@ export const CreatePollContent = () => { async (value: boolean) => { setIsAnonymousPoll(value); await pollComposer.updateFields({ - voting_visibility: value ? VotingVisibility.anonymous : VotingVisibility.public, + voting_visibility: value ? 'anonymous' : 'public', }); }, [pollComposer], diff --git a/package/src/components/Poll/components/PollAnswersList.tsx b/package/src/components/Poll/components/PollAnswersList.tsx index d107503143..61c7da878d 100644 --- a/package/src/components/Poll/components/PollAnswersList.tsx +++ b/package/src/components/Poll/components/PollAnswersList.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { FlatList, type FlatListProps, StyleSheet, Text, View } from 'react-native'; -import { PollVoteResponseData, VotingVisibility } from 'stream-chat'; +import { PollVoteResponseData } from 'stream-chat'; import { PollButtonProps } from './Button'; import { PollInputDialog } from './PollInputDialog'; @@ -99,7 +99,7 @@ export const PollAnswerListItem = ({ answer }: { answer: PollVoteResponseData }) const isMyAnswer = client.userID === answer.user?.id; const isAnonymous = useMemo( - () => votingVisibility === VotingVisibility.anonymous && !isMyAnswer, + () => votingVisibility === 'anonymous' && !isMyAnswer, [votingVisibility, isMyAnswer], ); diff --git a/package/src/components/Poll/components/PollResults/PollVote.tsx b/package/src/components/Poll/components/PollResults/PollVote.tsx index 96892dd22f..69263643b8 100644 --- a/package/src/components/Poll/components/PollResults/PollVote.tsx +++ b/package/src/components/Poll/components/PollResults/PollVote.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import { PollVoteResponseData as PollVoteClass, VotingVisibility } from 'stream-chat'; +import { PollVoteResponseData as PollVoteClass } from 'stream-chat'; import { useTheme, useTranslationContext } from '../../../../contexts'; import { primitives } from '../../../../theme'; @@ -36,10 +36,7 @@ export const PollVote = ({ vote }: { vote: PollVoteClass }) => { [vote.created_at, t, tDateTimeParser], ); - const isAnonymous = useMemo( - () => votingVisibility === VotingVisibility.anonymous, - [votingVisibility], - ); + const isAnonymous = useMemo(() => votingVisibility === 'anonymous', [votingVisibility]); return ( diff --git a/package/src/components/Poll/hooks/usePollState.ts b/package/src/components/Poll/hooks/usePollState.ts index dc122f0b11..f6e9895c7f 100644 --- a/package/src/components/Poll/hooks/usePollState.ts +++ b/package/src/components/Poll/hooks/usePollState.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { - PollOptionData, PollOptionResponseData, PollResponse, PollState, @@ -83,7 +82,7 @@ export const usePollState = (): UsePollStateReturnType => { const addOption = useCallback( async (optionText: string) => { - await poll.createOption({ text: optionText } as PollOptionData); + await poll.createOption({ text: optionText }); }, [poll], );