Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
47dc9f4
fix: remove hand-written getPinnedMessages method
szuperaz Aug 19, 2026
d4ab783
fix: derive OwnUserBase from the generated user shapes
szuperaz Aug 19, 2026
9d590ff
refactor: drop type aliases orphaned by the server-side split
szuperaz Aug 19, 2026
30a34a9
refactor: collapse type aliases that restated generated types
szuperaz Aug 19, 2026
89e000a
refactor: retire the APIResponse envelope and UpdatedMessage
szuperaz Aug 19, 2026
8c344ad
refactor: remove redundant guards and pass-through methods
szuperaz Aug 19, 2026
ac4d548
refactor: reinstate GiphyVersions and MessageLabel
szuperaz Aug 20, 2026
cf53a07
refactor: move moderation onto the generated V2 API
szuperaz Aug 20, 2026
911ab88
fix: supply blocked_user_ids instead of asserting it
szuperaz Aug 20, 2026
a15f0ee
refactor: drop the created_by_device_id TODO from live location updates
szuperaz Aug 20, 2026
6bfc503
refactor: replace the hand-written pagination option types
szuperaz Aug 20, 2026
83d62d8
refactor: remove the v1 permission system and assignRoles
szuperaz Aug 20, 2026
3d58bed
refactor: derive VotingVisibility and drop the phantom PollOptionData
szuperaz Aug 20, 2026
6722cee
refactor: derive ModerationFlagOptions from FlagRequest
szuperaz Aug 20, 2026
90d1906
docs: record the verified entity_creator_id behaviour
szuperaz Aug 20, 2026
49b9fbb
refactor: stop sending an empty entity_creator_id on flags
szuperaz Aug 20, 2026
e963c64
Merge branch 'release-v10' into reduce-hand-written-types
szuperaz Aug 21, 2026
339d539
fix: remove leftover comments
szuperaz Aug 21, 2026
05d2f2f
Merge branch 'release-v10' into reduce-hand-written-types
szuperaz Aug 21, 2026
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
11 changes: 5 additions & 6 deletions src/LiveLocationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
* RULES:
*
* 1. one loc-sharing message per channel per user
* 2. live location is intended to be per device
* but created_by_device_id has currently no checks,
* and user can update the location from another device
* thus making location sharing based on user and channel
* 2. live location is intended to be per device, but `created_by_device_id` is set once
* when the share is created and cannot be reassigned — `UpdateLiveLocationRequest` has no
* device field at all, by design. Any of the user's devices can therefore push coordinate
* updates to the same share, which makes location sharing effectively per user and
* channel rather than per device.
*/

import { withCancellation } from './utils/concurrency';
Expand Down Expand Up @@ -190,8 +191,6 @@ export class LiveLocationManager extends WithSubscriptions {
if (location.latitude === latitude && location.longitude === longitude)
continue;
const promise = this.client.updateLiveLocation({
// TODO: this is missing from the OAPI spec
// created_by_device_id: location.created_by_device_id,
message_id: messageId,
latitude,
longitude,
Expand Down
180 changes: 21 additions & 159 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ import type { StreamChat } from './client';
import { chatLoggerSystem } from './logger';
import type {
AIState,
APIResponse,
BanUserOptions,
ChannelData,
ChannelGetOrCreateRequest,
ChannelInput,
ChannelMemberResponse,
ChannelResponse,
ChannelStateResponseFields,
Expand All @@ -34,18 +33,14 @@ import type {
EventPayload,
EventType,
FileUploadInput,
GetRepliesAPIResponse,
LocalMessage,
MarkReadRequest,
MarkReadResponse,
MessagePaginationOptions,
MessagePaginationParams,
MessageRequest,
MessageResponse,
MessageSetType,
PinnedMessagePaginationOptions,
PinnedMessagesSort,
QueryMembersPayload,
ReactionAPIResponse,
ReactionRequest,
SendMessageOptions,
SendReactionRequest,
Expand All @@ -58,7 +53,6 @@ import type {
UpdateMessageOptions,
UserResponse,
} from './types';
import type { RoleName } from './permissions';
import { StateStore } from './store';
import type {
ChannelMemberRequest as Gen_ChannelMemberRequest,
Expand Down Expand Up @@ -146,7 +140,7 @@ export type ChannelInstanceConfig = {
export class Channel extends ChannelApi {
_client: StreamChat;
data: Partial<ChannelResponse> | undefined;
_data: ChannelData;
_data: ChannelInput;
cid: string;
/** */
listeners: Map<EventType, Set<EventHandler>>;
Expand Down Expand Up @@ -194,7 +188,7 @@ export class Channel extends ChannelApi {
client: StreamChat,
type: string,
id: string | undefined,
data: ChannelData,
data: ChannelInput,
) {
const validTypeRe = /^[\w_-]+$/;
const validIDRe = /^[\w!_-]+$/;
Expand Down Expand Up @@ -559,18 +553,6 @@ export class Channel extends ChannelApi {
return await super.sendEvent(request, requestOptions);
}

/**
* Queries messages.
*
* @param ...args - `[request, requestOptions]`. The optional `request.payload` accepts
* MongoDB-style filters and additional options such as `user_id`. `requestOptions` carries
* per-request options such as an abort `signal` and is never serialized into the request.
* @returns The search messages response.
*/
async search(...args: Parameters<ChatApi['search']>) {
return await this.getClient().search(...args);
}

/**
* Queries members.
*
Expand Down Expand Up @@ -632,7 +614,7 @@ export class Channel extends ChannelApi {
if (offlineDb) {
// The optimistic reaction row is written by the local-update layer
// (`applyReactionLocally`); here we only queue the request for replay.
return await offlineDb.queueTask<ReactionAPIResponse>({
return await offlineDb.queueTask<Awaited<ReturnType<ChatApi['sendReaction']>>>({
task: {
channelId: this.id as string,
channelType: this.type,
Expand Down Expand Up @@ -664,7 +646,7 @@ export class Channel extends ChannelApi {
if (offlineDb) {
// The optimistic reaction-row removal is handled by the local-update layer
// (`applyReactionLocally`); here we only queue the request for replay.
return await offlineDb.queueTask<ReactionAPIResponse>({
return await offlineDb.queueTask<Awaited<ReturnType<ChatApi['deleteReaction']>>>({
task: {
channelId: this.id as string,
channelType: this.type,
Expand Down Expand Up @@ -933,28 +915,6 @@ export class Channel extends ChannelApi {
);
}

/**
* Sets member roles in a channel.
*
* @param roles - List of role assignments.
* @param message - Message object for channel members notification (optional).
* @param options - Configuration to control the behavior while updating (optional, defaults to `{}`).
* @param requestOptions - Per-request options such as an abort `signal`. Never serialized
* into the request (optional).
* @returns The server response.
*/
async assignRoles(
roles: { channel_role: RoleName; user_id: string }[],
message?: MessageRequest,
options: ChannelUpdateOptions = {},
requestOptions?: StreamRequestOptions,
) {
return await this.update(
{ assign_roles: roles, message, ...options },
requestOptions,
);
}

/**
* Invite members to the channel.
*
Expand Down Expand Up @@ -1152,9 +1112,6 @@ export class Channel extends ChannelApi {
requestOptions?: StreamRequestOptions,
) {
this._checkInitialized();
if (!messageId) {
throw Error(`Message ID is missing`);
}
return this.getClient().runMessageAction(
{
id: messageId,
Expand Down Expand Up @@ -1506,59 +1463,6 @@ export class Channel extends ChannelApi {
return response;
}

/**
* List the message replies for a parent message.
*
* The recommended way of working with threads is to use the `Thread` class.
*
* @param ...args - `[request, requestOptions]`. `request` holds the parent message ID, pagination
* params, and optional sort directions for `created_at`; `requestOptions` carries per-request
* options such as an abort `signal` and is never serialized into the request.
* @returns A response with a list of messages.
*/
async getReplies(...args: Parameters<ChatApi['getReplies']>) {
const data = await this.getClient().getReplies(...args);

// Thread reply state is owned by the Thread object (Thread.messagePaginator); the returned
// replies are consumed there. The channel message list is owned by channel.messagePaginator.
return data;
}

// TODO: find out v2 equivalent
/**
* List pinned messages of the channel.
*
* @param options - Pagination params, e.g. `{ limit: 10, id_lte: 10 }`.
* @param sort - Defines sorting direction of pinned messages (optional, defaults to `[]`).
* @returns A response with a list of messages.
*/
async getPinnedMessages(
options: PinnedMessagePaginationOptions,
sort: PinnedMessagesSort = [],
) {
return await this.getClient().api.get<GetRepliesAPIResponse>(
this._channelURL() + '/pinned_messages',
{
payload: {
...options,
sort,
},
},
);
}

/**
* List the reactions; supports pagination.
*
* @param ...args - `[request, requestOptions]`. `request` holds the target message ID and
* pagination options (`limit`, `offset`); `requestOptions` carries per-request options such as
* an abort `signal` and is never serialized into the request.
* @returns The server response.
*/
getReactions(...args: Parameters<ChatApi['getReactions']>) {
return this.getClient().getReactions(...args);
}

/**
* Retrieves a list of messages by ID.
*
Expand Down Expand Up @@ -1796,7 +1700,7 @@ export class Channel extends ChannelApi {
const isLatestMessageSet =
messageSetToAddToIfDoesNotExist === 'latest' &&
!options?.messages?.id_around &&
!(options?.messages as MessagePaginationOptions | undefined)?.created_at_around;
!(options?.messages as MessagePaginationParams | undefined)?.created_at_around;

this.getClient().polls.hydratePollCache(state.messages, true);
this.getClient().reminders.hydrateState(state.messages);
Expand Down Expand Up @@ -1853,12 +1757,12 @@ export class Channel extends ChannelApi {
* @param options - Ban options.
* @returns The server response.
*/
async banUser(targetUserId: string, options: BanUserOptions) {
async banUser(targetUserId: string, options: Omit<BanUserOptions, 'channel_cid'>) {
this._checkInitialized();
return await this.getClient().banUser(targetUserId, {
return await this.getClient().moderation.ban({
...options,
type: this.type,
id: this.id,
target_user_id: targetUserId,
channel_cid: this.cid,
});
}

Expand Down Expand Up @@ -1905,36 +1809,6 @@ export class Channel extends ChannelApi {
});
}

/**
* Shadow bans a user from a channel.
*
* @param targetUserId - The user to shadow ban.
* @param options - Ban options.
* @returns The server response.
*/
async shadowBan(targetUserId: string, options: BanUserOptions) {
this._checkInitialized();
return await this.getClient().shadowBan(targetUserId, {
...options,
type: this.type,
id: this.id,
});
}

/**
* Removes the shadow ban for a user on a channel.
*
* @param targetUserId - The user to remove the shadow ban for.
* @returns The server response.
*/
async removeShadowBan(targetUserId: string) {
this._checkInitialized();
return await this.getClient().removeShadowBan(targetUserId, {
type: this.type,
id: this.id,
});
}

/**
* Casts or cancels one or more votes on a poll.
*
Expand Down Expand Up @@ -1996,15 +1870,17 @@ export class Channel extends ChannelApi {
try {
const offlineDb = this.getClient().offlineDb;
if (offlineDb) {
return (await offlineDb.queueTask<APIResponse>({
task: {
channelId: this.id as string,
channelType: this.type,
threadId: request?.parent_id,
payload: args,
type: 'delete-draft',
return (await offlineDb.queueTask<Awaited<ReturnType<ChannelApi['deleteDraft']>>>(
{
task: {
channelId: this.id as string,
channelType: this.type,
threadId: request?.parent_id,
payload: args,
type: 'delete-draft',
},
},
})) as Awaited<ReturnType<ChannelApi['deleteDraft']>>;
)) as Awaited<ReturnType<ChannelApi['deleteDraft']>>;
}
} catch (error) {
offlineDbLogger
Expand Down Expand Up @@ -2650,20 +2526,6 @@ export class Channel extends ChannelApi {
);
};

/**
* Returns the channel url.
*
* @returns The channel url.
*/
_channelURL = () => {
if (!this.id) {
throw new Error('channel id is not defined');
}
return `${this.getClient().baseURL}/channels/${encodeURIComponent(
this.type,
)}/${encodeURIComponent(this.id)}`;
};

_checkInitialized() {
if (!this.initialized && !this.offlineMode) {
throw Error(
Expand Down
Loading