Skip to content

Commit 2b8555a

Browse files
authored
Merge branch 'master' into mod210
2 parents 1b74aaf + 8612079 commit 2b8555a

File tree

11 files changed

+329
-97
lines changed

11 files changed

+329
-97
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## [9.25.0](https://github.com/GetStream/stream-chat-js/compare/v9.24.0...v9.25.0) (2025-10-23)
2+
3+
### Bug Fixes
4+
5+
* prevent search source pagination on non-retryable response errors ([#1638](https://github.com/GetStream/stream-chat-js/issues/1638)) ([d306a1a](https://github.com/GetStream/stream-chat-js/commit/d306a1a7dd6fb138d07d0278068d31d0dcf068de))
6+
7+
### Features
8+
9+
* add delivery_events channel configuration parameter ([#1639](https://github.com/GetStream/stream-chat-js/issues/1639)) ([13d295a](https://github.com/GetStream/stream-chat-js/commit/13d295ae251f1c5aa5d97fad9340cce092dfb91c))
10+
11+
## [9.24.0](https://github.com/GetStream/stream-chat-js/compare/v9.23.0...v9.24.0) (2025-10-17)
12+
13+
### Features
14+
15+
* llm config types for upsert config api ([#1640](https://github.com/GetStream/stream-chat-js/issues/1640)) ([e80e42c](https://github.com/GetStream/stream-chat-js/commit/e80e42cbf43a40a79f5c3ba811c4cb9360a2b94f))
16+
117
## [9.23.0](https://github.com/GetStream/stream-chat-js/compare/v9.22.1...v9.23.0) (2025-10-15)
218

319
### Features

src/channel.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,6 +1936,15 @@ export class Channel {
19361936
deliveredAt: event.created_at,
19371937
lastDeliveredMessageId: event.last_delivered_message_id,
19381938
});
1939+
1940+
const client = this.getClient();
1941+
const isOwnEvent = event.user?.id === client.user?.id;
1942+
1943+
// make sure not to report deliveries that were
1944+
// already confirmed from own user from another device
1945+
if (isOwnEvent) {
1946+
client.syncDeliveredCandidates([this]);
1947+
}
19391948
}
19401949
break;
19411950
case 'user.watching.start':

src/channel_state.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,15 @@ export class ChannelState {
250250
delete this.threads[message.id];
251251
}
252252

253-
if (!this.last_message_at) {
254-
this.last_message_at = new Date(message.created_at.getTime());
255-
}
256-
257-
if (message.created_at.getTime() > this.last_message_at.getTime()) {
253+
const shouldSkipLastMessageAtUpdate =
254+
this._channel.getConfig()?.skip_last_msg_update_for_system_msgs &&
255+
message.type === 'system';
256+
257+
if (
258+
!shouldSkipLastMessageAtUpdate &&
259+
(!this.last_message_at ||
260+
message.created_at.getTime() > this.last_message_at.getTime())
261+
) {
258262
this.last_message_at = new Date(message.created_at.getTime());
259263
}
260264
}

src/client.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4749,17 +4749,14 @@ export class StreamChat {
47494749
}
47504750

47514751
/**
4752-
* Send the mark delivered event for this user
4752+
* Mark the channels delivered for the given messages and the user
47534753
*
47544754
* @param {MarkDeliveredOptions} data
47554755
* @return {Promise<EventAPIResponse | void>} Description
47564756
*/
47574757
async markChannelsDelivered(data: MarkDeliveredOptions) {
47584758
if (!data?.latest_delivered_messages?.length) return;
4759-
return await this.post<EventAPIResponse>(
4760-
this.baseURL + '/channels/delivered',
4761-
data ?? {},
4762-
);
4759+
return await this.post<EventAPIResponse>(this.baseURL + '/channels/delivered', data);
47634760
}
47644761

47654762
syncDeliveredCandidates(collections: Channel[]) {

src/messageDelivery/MessageDeliveryReporter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ export class MessageDeliveryReporter {
5454
return this.deliveryReportCandidates.size > 0;
5555
}
5656

57+
private static hasPermissionToReportDeliveryFor(collection: Channel | Thread) {
58+
if (isChannel(collection)) return !!collection.getConfig()?.delivery_events;
59+
if (isThread(collection)) return !!collection.channel.getConfig()?.delivery_events;
60+
}
61+
5762
/**
5863
* Build latest_delivered_messages payload from an arbitrary buffer (deliveryReportCandidates / nextDeliveryReportCandidates)
5964
*/
@@ -142,8 +147,7 @@ export class MessageDeliveryReporter {
142147
* @param collection
143148
*/
144149
private trackDeliveredCandidate(collection: Channel | Thread) {
145-
if (isChannel(collection) && !collection.getConfig()?.read_events) return;
146-
if (isThread(collection) && !collection.channel.getConfig()?.read_events) return;
150+
if (!MessageDeliveryReporter.hasPermissionToReportDeliveryFor(collection)) return;
147151
const candidate = this.getNextDeliveryReportCandidate(collection);
148152
if (!candidate?.key) return;
149153
const buffer = this.markDeliveredRequestInFlight

src/search/BaseSearchSource.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type {
66
SearchSourceState,
77
SearchSourceType,
88
} from './types';
9+
import type { APIError } from '../errors';
10+
import { isAPIError, isErrorRetryable } from '../errors';
911

1012
export type DebounceOptions = {
1113
debounceMs: number;
@@ -237,6 +239,9 @@ export abstract class BaseSearchSource<T>
237239
stateUpdate.items = await this.filterQueryResults(items);
238240
} catch (e) {
239241
stateUpdate.lastQueryError = e as Error;
242+
if (isAPIError(e as Error) && !isErrorRetryable(e as APIError)) {
243+
stateUpdate.hasNext = false;
244+
}
240245
} finally {
241246
this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
242247
}
@@ -285,6 +290,9 @@ export abstract class BaseSearchSourceSync<T>
285290
stateUpdate.items = this.filterQueryResults(items);
286291
} catch (e) {
287292
stateUpdate.lastQueryError = e as Error;
293+
if (isAPIError(e as Error) && !isErrorRetryable(e as APIError)) {
294+
stateUpdate.hasNext = false;
295+
}
288296
} finally {
289297
this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
290298
}

src/types.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export type AppSettingsAPIResponse = APIResponse & {
9595
connect_events?: boolean;
9696
created_at?: string;
9797
custom_events?: boolean;
98+
delivery_events?: boolean;
9899
mark_messages_pending?: boolean;
99100
max_message_length?: number;
100101
message_retention?: string;
@@ -1015,6 +1016,7 @@ export type CreateChannelOptions = {
10151016
connect_events?: boolean;
10161017
connection_id?: string;
10171018
custom_events?: boolean;
1019+
delivery_events?: boolean;
10181020
grants?: Record<string, string[]>;
10191021
mark_messages_pending?: boolean;
10201022
max_message_length?: number;
@@ -1120,6 +1122,7 @@ export type UpdateChannelTypeRequest =
11201122
commands?: CommandVariants[];
11211123
connect_events?: boolean;
11221124
custom_events?: boolean;
1125+
delivery_events?: boolean;
11231126
grants?: Record<string, string[]>;
11241127
mark_messages_pending?: boolean;
11251128
mutes?: boolean;
@@ -1151,6 +1154,7 @@ export type UpdateChannelTypeResponse = {
11511154
connect_events: boolean;
11521155
created_at: string;
11531156
custom_events: boolean;
1157+
delivery_events: boolean;
11541158
duration: string;
11551159
grants: Record<string, string[]>;
11561160
mark_messages_pending: boolean;
@@ -1188,6 +1192,7 @@ export type GetChannelTypeResponse = {
11881192
connect_events: boolean;
11891193
created_at: string;
11901194
custom_events: boolean;
1195+
delivery_events: boolean;
11911196
duration: string;
11921197
grants: Record<string, string[]>;
11931198
mark_messages_pending: boolean;
@@ -2384,6 +2389,7 @@ export type ChannelConfigFields = {
23842389
blocklist_behavior?: ChannelConfigAutomodBehavior;
23852390
connect_events?: boolean;
23862391
custom_events?: boolean;
2392+
delivery_events?: boolean;
23872393
mark_messages_pending?: boolean;
23882394
max_message_length?: number;
23892395
message_retention?: string;
@@ -2397,6 +2403,7 @@ export type ChannelConfigFields = {
23972403
replies?: boolean;
23982404
search?: boolean;
23992405
shared_locations?: boolean;
2406+
skip_last_msg_update_for_system_msgs?: boolean;
24002407
count_messages?: boolean;
24012408
typing_events?: boolean;
24022409
uploads?: boolean;
@@ -3889,6 +3896,7 @@ export type ModerationConfig = {
38893896
automod_semantic_filters_config?: AutomodSemanticFiltersConfig;
38903897
automod_toxicity_config?: AutomodToxicityConfig;
38913898
block_list_config?: BlockListConfig;
3899+
llm_config?: LLMConfig;
38923900
team?: string;
38933901
};
38943902

@@ -4119,6 +4127,8 @@ export type ModerationActionType =
41194127
| 'bounce_flag'
41204128
| 'bounce_remove';
41214129

4130+
export type ModerationSeverity = 'low' | 'medium' | 'high' | 'critical';
4131+
41224132
export type AutomodRule = {
41234133
action: ModerationActionType;
41244134
label: string;
@@ -4136,6 +4146,24 @@ export type BlockListConfig = {
41364146
async?: boolean;
41374147
};
41384148

4149+
export type LLMConfig = {
4150+
rules: LLMRule[];
4151+
severity_descriptions?: Record<ModerationSeverity, string>;
4152+
app_context?: string;
4153+
};
4154+
4155+
export type LLMRule = {
4156+
label: string;
4157+
description: string;
4158+
action: ModerationActionType;
4159+
severity_rules?: LLMSeverityRule[];
4160+
};
4161+
4162+
export type LLMSeverityRule = {
4163+
severity: ModerationSeverity;
4164+
action: ModerationActionType;
4165+
};
4166+
41394167
export type AutomodToxicityConfig = {
41404168
enabled: boolean;
41414169
rules: AutomodRule[];
@@ -4162,7 +4190,7 @@ export type AutomodSemanticFiltersConfig = {
41624190

41634191
export type AITextSeverityRule = {
41644192
action: ModerationActionType;
4165-
severity: 'low' | 'medium' | 'high' | 'critical';
4193+
severity: ModerationSeverity;
41664194
};
41674195

41684196
export type AITextRule = {

0 commit comments

Comments
 (0)