stream-chat 9.49.0 → 9.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.browser.js +82 -27
- package/dist/cjs/index.browser.js.map +2 -2
- package/dist/cjs/index.node.js +82 -27
- package/dist/cjs/index.node.js.map +2 -2
- package/dist/esm/index.mjs +82 -27
- package/dist/esm/index.mjs.map +2 -2
- package/dist/types/channel.d.ts +12 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/thread_manager.d.ts +7 -0
- package/dist/types/types.d.ts +6 -0
- package/dist/types/utils.d.ts +13 -0
- package/package.json +1 -1
- package/src/channel.ts +45 -4
- package/src/client.ts +1 -0
- package/src/events.ts +1 -0
- package/src/messageDelivery/MessageDeliveryReporter.ts +3 -1
- package/src/offline-support/offline_support_api.ts +53 -24
- package/src/thread_manager.ts +11 -3
- package/src/types.ts +6 -0
- package/src/utils.ts +22 -0
package/dist/types/channel.d.ts
CHANGED
|
@@ -503,6 +503,18 @@ export declare class Channel {
|
|
|
503
503
|
* @return {APIResponse} An API response
|
|
504
504
|
*/
|
|
505
505
|
markUnread(data: MarkUnreadOptions): Promise<APIResponse | null>;
|
|
506
|
+
/**
|
|
507
|
+
* markReadLocally - Resets this user's unread count locally, without any backend call. Intended for
|
|
508
|
+
* channels that have read events disabled (e.g. livestreams) when the client is created with the
|
|
509
|
+
* `isLocalUnreadCountEnabled` option. Dispatches a dedicated, client-only `message.read_locally` event
|
|
510
|
+
* that runs through the same `_handleChannelEvent` read logic as a real `message.read` (minus the
|
|
511
|
+
* delivery-report network sync), so the read-state update lives in one place. When offline support
|
|
512
|
+
* is enabled, the offline DB persists the reset for read-events-disabled channels, so the local
|
|
513
|
+
* count stays consistent across app restarts.
|
|
514
|
+
*
|
|
515
|
+
* @return {Event | undefined} The dispatched `message.read_locally` event, or `undefined` if there is no connected user.
|
|
516
|
+
*/
|
|
517
|
+
markReadLocally(): Event | undefined;
|
|
506
518
|
/**
|
|
507
519
|
* clean - Cleans the channel state and fires stop typing if needed
|
|
508
520
|
*/
|
package/dist/types/events.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export declare const EVENT_MAP: {
|
|
|
60
60
|
'ai_indicator.update': boolean;
|
|
61
61
|
'ai_indicator.stop': boolean;
|
|
62
62
|
'ai_indicator.clear': boolean;
|
|
63
|
+
'message.read_locally': boolean;
|
|
63
64
|
'channels.queried': boolean;
|
|
64
65
|
'offline_reactions.queried': boolean;
|
|
65
66
|
'connection.changed': boolean;
|
|
@@ -5,6 +5,7 @@ import type { QueryThreadsOptions } from './types';
|
|
|
5
5
|
import { WithSubscriptions } from './utils/WithSubscriptions';
|
|
6
6
|
export declare const THREAD_MANAGER_INITIAL_STATE: {
|
|
7
7
|
active: boolean;
|
|
8
|
+
wasActivatedAtLeastOnce: boolean;
|
|
8
9
|
isThreadOrderStale: boolean;
|
|
9
10
|
threads: never[];
|
|
10
11
|
unreadThreadCount: number;
|
|
@@ -19,6 +20,12 @@ export declare const THREAD_MANAGER_INITIAL_STATE: {
|
|
|
19
20
|
};
|
|
20
21
|
export type ThreadManagerState = {
|
|
21
22
|
active: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the thread manager has been activated at least once in the current
|
|
25
|
+
* session (i.e. `activate()` was called). Used to avoid requerying threads
|
|
26
|
+
* on connection recovery for consumers that never actually activate the manager.
|
|
27
|
+
*/
|
|
28
|
+
wasActivatedAtLeastOnce: boolean;
|
|
22
29
|
isThreadOrderStale: boolean;
|
|
23
30
|
lastConnectionDropAt: Date | null;
|
|
24
31
|
pagination: ThreadManagerPagination;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1371,6 +1371,12 @@ export type StreamChatOptions = AxiosRequestConfig & {
|
|
|
1371
1371
|
*/
|
|
1372
1372
|
disableCache?: boolean;
|
|
1373
1373
|
enableInsights?: boolean;
|
|
1374
|
+
/**
|
|
1375
|
+
* When true, maintains a client-local unread count on channels that have read events disabled
|
|
1376
|
+
* (e.g. livestreams). The count increments on incoming messages and is reset via
|
|
1377
|
+
* `channel.markReadLocally()`. It is never sent to the backend, but is persisted to the offline DB.
|
|
1378
|
+
*/
|
|
1379
|
+
isLocalUnreadCountEnabled?: boolean;
|
|
1374
1380
|
/** experimental feature, please contact support if you want this feature enabled for you */
|
|
1375
1381
|
enableWSFallback?: boolean;
|
|
1376
1382
|
logger?: Logger;
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -20,6 +20,19 @@ export declare const chatCodes: {
|
|
|
20
20
|
};
|
|
21
21
|
export declare function isOwnUser(user?: OwnUserResponse | UserResponse): user is OwnUserResponse;
|
|
22
22
|
export declare function isOwnUserBaseProperty(property: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* channelHasReadEvents - Whether read events are enabled for the current user on a channel.
|
|
25
|
+
*/
|
|
26
|
+
export declare const channelHasReadEvents: (channel?: Channel) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* channelTracksReadLocally - Whether a channel maintains a client local unread count.
|
|
29
|
+
*/
|
|
30
|
+
export declare const channelTracksReadLocally: (channel?: Channel) => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* userHasReadReceipts - Whether the current user allows read receipts, per their privacy settings.
|
|
33
|
+
* Read receipts are treated as enabled unless the user has explicitly disabled them.
|
|
34
|
+
*/
|
|
35
|
+
export declare const userHasReadReceipts: (client: StreamChat) => boolean;
|
|
23
36
|
export declare function addFileToFormData(uri: string | NodeJS.ReadableStream | Buffer | File, name?: string, contentType?: string): FormData;
|
|
24
37
|
export declare function normalizeQuerySort<T extends Record<string, AscDesc | undefined>>(sort: T | T[]): {
|
|
25
38
|
direction: AscDesc;
|
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CooldownTimer } from './CooldownTimer';
|
|
|
4
4
|
import { MessageComposer } from './messageComposer';
|
|
5
5
|
import { MessageReceiptsTracker } from './messageDelivery';
|
|
6
6
|
import {
|
|
7
|
+
channelHasReadEvents,
|
|
7
8
|
generateChannelTempCid,
|
|
8
9
|
logChatPromiseExecution,
|
|
9
10
|
messageSetPagination,
|
|
@@ -1247,6 +1248,36 @@ export class Channel {
|
|
|
1247
1248
|
});
|
|
1248
1249
|
}
|
|
1249
1250
|
|
|
1251
|
+
/**
|
|
1252
|
+
* markReadLocally - Resets this user's unread count locally, without any backend call. Intended for
|
|
1253
|
+
* channels that have read events disabled (e.g. livestreams) when the client is created with the
|
|
1254
|
+
* `isLocalUnreadCountEnabled` option. Dispatches a dedicated, client-only `message.read_locally` event
|
|
1255
|
+
* that runs through the same `_handleChannelEvent` read logic as a real `message.read` (minus the
|
|
1256
|
+
* delivery-report network sync), so the read-state update lives in one place. When offline support
|
|
1257
|
+
* is enabled, the offline DB persists the reset for read-events-disabled channels, so the local
|
|
1258
|
+
* count stays consistent across app restarts.
|
|
1259
|
+
*
|
|
1260
|
+
* @return {Event | undefined} The dispatched `message.read_locally` event, or `undefined` if there is no connected user.
|
|
1261
|
+
*/
|
|
1262
|
+
markReadLocally() {
|
|
1263
|
+
const client = this.getClient();
|
|
1264
|
+
if (!client.userID) return;
|
|
1265
|
+
|
|
1266
|
+
const event: Event = {
|
|
1267
|
+
channel_id: this.id,
|
|
1268
|
+
channel_type: this.type,
|
|
1269
|
+
cid: this.cid,
|
|
1270
|
+
created_at: new Date().toISOString(),
|
|
1271
|
+
last_read_message_id: this.lastMessage()?.id,
|
|
1272
|
+
team: this.data?.team,
|
|
1273
|
+
type: 'message.read_locally',
|
|
1274
|
+
user: client.user,
|
|
1275
|
+
};
|
|
1276
|
+
client.dispatchEvent(event);
|
|
1277
|
+
|
|
1278
|
+
return event;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1250
1281
|
/**
|
|
1251
1282
|
* clean - Cleans the channel state and fires stop typing if needed
|
|
1252
1283
|
*/
|
|
@@ -1429,10 +1460,11 @@ export class Channel {
|
|
|
1429
1460
|
if (message.user?.id && this.getClient().userMuteStatus(message.user.id))
|
|
1430
1461
|
return false;
|
|
1431
1462
|
|
|
1432
|
-
// Return false if channel doesn't allow read events
|
|
1463
|
+
// Return false if channel doesn't allow read events, unless the client opted into a local
|
|
1464
|
+
// unread count (e.g. livestreams where read events are disabled). See `isLocalUnreadCountEnabled`.
|
|
1433
1465
|
if (
|
|
1434
|
-
|
|
1435
|
-
!this
|
|
1466
|
+
!this.getClient().options.isLocalUnreadCountEnabled &&
|
|
1467
|
+
!channelHasReadEvents(this)
|
|
1436
1468
|
) {
|
|
1437
1469
|
return false;
|
|
1438
1470
|
}
|
|
@@ -1965,6 +1997,11 @@ export class Channel {
|
|
|
1965
1997
|
delete channelState.typing[event.user.id];
|
|
1966
1998
|
}
|
|
1967
1999
|
break;
|
|
2000
|
+
// `message.read_locally` is the client-only event dispatched by `markReadLocally()` when read
|
|
2001
|
+
// events are disabled (e.g. livestreams with `isLocalUnreadCountEnabled`). It reuses the exact
|
|
2002
|
+
// `message.read` state logic so the read-state update lives in one place — only the
|
|
2003
|
+
// delivery-report network sync below is skipped for it.
|
|
2004
|
+
case 'message.read_locally':
|
|
1968
2005
|
case 'message.read':
|
|
1969
2006
|
if (event.user?.id && event.created_at) {
|
|
1970
2007
|
const previousReadState = channelState.read[event.user.id];
|
|
@@ -1987,7 +2024,11 @@ export class Channel {
|
|
|
1987
2024
|
|
|
1988
2025
|
if (isOwnEvent) {
|
|
1989
2026
|
channelState.unreadCount = 0;
|
|
1990
|
-
|
|
2027
|
+
// Delivery reporting buffers a `markChannelsDelivered` network request; the local read
|
|
2028
|
+
// must not hit the backend, so only sync for the real server `message.read`.
|
|
2029
|
+
if (event.type === 'message.read') {
|
|
2030
|
+
client.syncDeliveredCandidates([this]);
|
|
2031
|
+
}
|
|
1991
2032
|
}
|
|
1992
2033
|
}
|
|
1993
2034
|
break;
|
package/src/client.ts
CHANGED
package/src/events.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
MarkReadOptions,
|
|
11
11
|
} from '../types';
|
|
12
12
|
import { type APIErrorResponse } from '../types';
|
|
13
|
-
import { throttle } from '../utils';
|
|
13
|
+
import { throttle, userHasReadReceipts } from '../utils';
|
|
14
14
|
import { isAPIError, isErrorRetryable } from '../errors';
|
|
15
15
|
|
|
16
16
|
const MAX_DELIVERED_MESSAGE_COUNT_IN_PAYLOAD = 100 as const;
|
|
@@ -279,6 +279,8 @@ export class MessageDeliveryReporter {
|
|
|
279
279
|
* @param options
|
|
280
280
|
*/
|
|
281
281
|
public markRead = async (collection: Channel | Thread, options?: MarkReadOptions) => {
|
|
282
|
+
if (!userHasReadReceipts(this.client)) return null;
|
|
283
|
+
|
|
282
284
|
let result: EventAPIResponse | null = null;
|
|
283
285
|
if (isChannel(collection)) {
|
|
284
286
|
result = await collection.markAsReadRequest(options);
|
|
@@ -18,7 +18,12 @@ import type { StreamChat } from '../client';
|
|
|
18
18
|
import type { AxiosError } from 'axios';
|
|
19
19
|
import { OfflineDBSyncManager } from './offline_sync_manager';
|
|
20
20
|
import { StateStore } from '../store';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
channelHasReadEvents,
|
|
23
|
+
channelTracksReadLocally,
|
|
24
|
+
localMessageToNewMessagePayload,
|
|
25
|
+
runDetached,
|
|
26
|
+
} from '../utils';
|
|
22
27
|
import { isMessageUpdateReplayable } from './util';
|
|
23
28
|
|
|
24
29
|
/**
|
|
@@ -609,7 +614,13 @@ export abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
609
614
|
if (cid && client.user && client.user.id !== user?.id) {
|
|
610
615
|
const userId = client.user.id;
|
|
611
616
|
const channel = client.activeChannels[cid];
|
|
612
|
-
|
|
617
|
+
// Persist the current user's read state for channels that track reads server-side. When the
|
|
618
|
+
// client opted into a local unread count, also persist it for read-events-disabled channels
|
|
619
|
+
// (e.g. livestreams) so the client-local count survives a cold start. The server never sends
|
|
620
|
+
// a read for those, so state.read[userId] may be absent here; fall back accordingly and rely
|
|
621
|
+
// on countUnread() (the aggregate the local count maintains) for the value.
|
|
622
|
+
const tracksReadLocally = channelTracksReadLocally(channel);
|
|
623
|
+
if (channel && (channelHasReadEvents(channel) || tracksReadLocally)) {
|
|
613
624
|
const ownReads = channel.state.read[userId];
|
|
614
625
|
const unreadCount = channel.countUnread();
|
|
615
626
|
const upsertReadsQueries = await this.upsertReads({
|
|
@@ -617,8 +628,8 @@ export abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
617
628
|
execute: false,
|
|
618
629
|
reads: [
|
|
619
630
|
{
|
|
620
|
-
last_read: ownReads
|
|
621
|
-
last_read_message_id: ownReads
|
|
631
|
+
last_read: (ownReads?.last_read ?? new Date(0)).toISOString() as string,
|
|
632
|
+
last_read_message_id: ownReads?.last_read_message_id,
|
|
622
633
|
unread_messages: unreadCount,
|
|
623
634
|
user: client.user,
|
|
624
635
|
},
|
|
@@ -867,32 +878,38 @@ export abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
867
878
|
execute: false,
|
|
868
879
|
});
|
|
869
880
|
|
|
881
|
+
let finalQueries = [...truncateQueries];
|
|
882
|
+
|
|
883
|
+
// Persist read state for channels that track reads server-side. When the client opted into a
|
|
884
|
+
// local unread count, also persist it for read-events-disabled channels (e.g. livestreams) so
|
|
885
|
+
// the client-local count survives a cold start. state.read[userId] may be absent for those, so
|
|
886
|
+
// fall back accordingly.
|
|
870
887
|
const userId = ownUser.id;
|
|
871
888
|
const activeChannel = this.client.activeChannels[cid];
|
|
872
|
-
const
|
|
889
|
+
const tracksReadLocally = channelTracksReadLocally(activeChannel);
|
|
890
|
+
if (activeChannel && (channelHasReadEvents(activeChannel) || tracksReadLocally)) {
|
|
891
|
+
const ownReads = activeChannel.state.read[userId];
|
|
873
892
|
|
|
874
|
-
|
|
893
|
+
let unreadCount = 0;
|
|
894
|
+
if (truncated_at) {
|
|
895
|
+
unreadCount = activeChannel.countUnread(new Date(truncated_at));
|
|
896
|
+
}
|
|
875
897
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
898
|
+
const upsertReadQueries = await this.upsertReads({
|
|
899
|
+
cid,
|
|
900
|
+
execute: false,
|
|
901
|
+
reads: [
|
|
902
|
+
{
|
|
903
|
+
last_read: (ownReads?.last_read ?? new Date(0)).toString() as string,
|
|
904
|
+
last_read_message_id: ownReads?.last_read_message_id,
|
|
905
|
+
unread_messages: unreadCount,
|
|
906
|
+
user: ownUser,
|
|
907
|
+
},
|
|
908
|
+
],
|
|
909
|
+
});
|
|
910
|
+
finalQueries = [...finalQueries, ...upsertReadQueries];
|
|
879
911
|
}
|
|
880
912
|
|
|
881
|
-
const upsertReadQueries = await this.upsertReads({
|
|
882
|
-
cid,
|
|
883
|
-
execute: false,
|
|
884
|
-
reads: [
|
|
885
|
-
{
|
|
886
|
-
last_read: ownReads.last_read.toString() as string,
|
|
887
|
-
last_read_message_id: ownReads.last_read_message_id,
|
|
888
|
-
unread_messages: unreadCount,
|
|
889
|
-
user: ownUser,
|
|
890
|
-
},
|
|
891
|
-
],
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
const finalQueries = [...truncateQueries, ...upsertReadQueries];
|
|
895
|
-
|
|
896
913
|
if (execute) {
|
|
897
914
|
await this.executeSqlBatch(finalQueries);
|
|
898
915
|
}
|
|
@@ -1021,6 +1038,18 @@ export abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
1021
1038
|
return this.handleRead({ event, unreadMessages: 0, execute });
|
|
1022
1039
|
}
|
|
1023
1040
|
|
|
1041
|
+
// `message.read_locally` is the client-only reset dispatched by `Channel.markReadLocally()`. Only
|
|
1042
|
+
// persist it for read-events-disabled channels with the local unread count opted in (the only
|
|
1043
|
+
// situation it is ever dispatched), so it can never touch channels that track reads server-side.
|
|
1044
|
+
if (type === 'message.read_locally') {
|
|
1045
|
+
const localChannel = event.cid ? this.client.activeChannels[event.cid] : undefined;
|
|
1046
|
+
// channelTracksReadLocally returns false when localChannel is undefined, so no extra guard.
|
|
1047
|
+
if (channelTracksReadLocally(localChannel)) {
|
|
1048
|
+
return this.handleRead({ event, unreadMessages: 0, execute });
|
|
1049
|
+
}
|
|
1050
|
+
return [];
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1024
1053
|
if (type === 'notification.mark_unread') {
|
|
1025
1054
|
return this.handleRead({ event, execute });
|
|
1026
1055
|
}
|
package/src/thread_manager.ts
CHANGED
|
@@ -10,6 +10,7 @@ const DEFAULT_CONNECTION_RECOVERY_THROTTLE_DURATION = 1000;
|
|
|
10
10
|
const MAX_QUERY_THREADS_LIMIT = 25;
|
|
11
11
|
export const THREAD_MANAGER_INITIAL_STATE = {
|
|
12
12
|
active: false,
|
|
13
|
+
wasActivatedAtLeastOnce: false,
|
|
13
14
|
isThreadOrderStale: false,
|
|
14
15
|
threads: [],
|
|
15
16
|
unreadThreadCount: 0,
|
|
@@ -25,6 +26,12 @@ export const THREAD_MANAGER_INITIAL_STATE = {
|
|
|
25
26
|
|
|
26
27
|
export type ThreadManagerState = {
|
|
27
28
|
active: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the thread manager has been activated at least once in the current
|
|
31
|
+
* session (i.e. `activate()` was called). Used to avoid requerying threads
|
|
32
|
+
* on connection recovery for consumers that never actually activate the manager.
|
|
33
|
+
*/
|
|
34
|
+
wasActivatedAtLeastOnce: boolean;
|
|
28
35
|
isThreadOrderStale: boolean;
|
|
29
36
|
lastConnectionDropAt: Date | null;
|
|
30
37
|
pagination: ThreadManagerPagination;
|
|
@@ -90,7 +97,7 @@ export class ThreadManager extends WithSubscriptions {
|
|
|
90
97
|
};
|
|
91
98
|
|
|
92
99
|
public activate = () => {
|
|
93
|
-
this.state.partialNext({ active: true });
|
|
100
|
+
this.state.partialNext({ active: true, wasActivatedAtLeastOnce: true });
|
|
94
101
|
};
|
|
95
102
|
|
|
96
103
|
public deactivate = () => {
|
|
@@ -197,8 +204,9 @@ export class ThreadManager extends WithSubscriptions {
|
|
|
197
204
|
|
|
198
205
|
const throttledHandleConnectionRecovered = throttle(
|
|
199
206
|
() => {
|
|
200
|
-
const { lastConnectionDropAt } =
|
|
201
|
-
|
|
207
|
+
const { lastConnectionDropAt, wasActivatedAtLeastOnce } =
|
|
208
|
+
this.state.getLatestValue();
|
|
209
|
+
if (!lastConnectionDropAt || !wasActivatedAtLeastOnce) return;
|
|
202
210
|
this.reload({ force: true });
|
|
203
211
|
},
|
|
204
212
|
DEFAULT_CONNECTION_RECOVERY_THROTTLE_DURATION,
|
package/src/types.ts
CHANGED
|
@@ -1591,6 +1591,12 @@ export type StreamChatOptions = AxiosRequestConfig & {
|
|
|
1591
1591
|
*/
|
|
1592
1592
|
disableCache?: boolean;
|
|
1593
1593
|
enableInsights?: boolean;
|
|
1594
|
+
/**
|
|
1595
|
+
* When true, maintains a client-local unread count on channels that have read events disabled
|
|
1596
|
+
* (e.g. livestreams). The count increments on incoming messages and is reset via
|
|
1597
|
+
* `channel.markReadLocally()`. It is never sent to the backend, but is persisted to the offline DB.
|
|
1598
|
+
*/
|
|
1599
|
+
isLocalUnreadCountEnabled?: boolean;
|
|
1594
1600
|
/** experimental feature, please contact support if you want this feature enabled for you */
|
|
1595
1601
|
enableWSFallback?: boolean;
|
|
1596
1602
|
logger?: Logger;
|
package/src/utils.ts
CHANGED
|
@@ -110,6 +110,28 @@ export function isOwnUserBaseProperty(property: string) {
|
|
|
110
110
|
return ownUserBaseProperties[property as keyof OwnUserBase];
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* channelHasReadEvents - Whether read events are enabled for the current user on a channel.
|
|
115
|
+
*/
|
|
116
|
+
export const channelHasReadEvents = (channel?: Channel) => {
|
|
117
|
+
const ownCapabilities = channel?.data?.own_capabilities;
|
|
118
|
+
return !(Array.isArray(ownCapabilities) && !ownCapabilities.includes('read-events'));
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* channelTracksReadLocally - Whether a channel maintains a client local unread count.
|
|
123
|
+
*/
|
|
124
|
+
export const channelTracksReadLocally = (channel?: Channel) =>
|
|
125
|
+
!channelHasReadEvents(channel) &&
|
|
126
|
+
!!channel?.getClient().options.isLocalUnreadCountEnabled;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* userHasReadReceipts - Whether the current user allows read receipts, per their privacy settings.
|
|
130
|
+
* Read receipts are treated as enabled unless the user has explicitly disabled them.
|
|
131
|
+
*/
|
|
132
|
+
export const userHasReadReceipts = (client: StreamChat) =>
|
|
133
|
+
client.user?.privacy_settings?.read_receipts?.enabled ?? true;
|
|
134
|
+
|
|
113
135
|
export function addFileToFormData(
|
|
114
136
|
uri: string | NodeJS.ReadableStream | Buffer | File,
|
|
115
137
|
name?: string,
|