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.
@@ -590,6 +590,12 @@ function isOwnUserBaseProperty(property) {
590
590
  };
591
591
  return ownUserBaseProperties[property];
592
592
  }
593
+ var channelHasReadEvents = (channel) => {
594
+ const ownCapabilities = channel?.data?.own_capabilities;
595
+ return !(Array.isArray(ownCapabilities) && !ownCapabilities.includes("read-events"));
596
+ };
597
+ var channelTracksReadLocally = (channel) => !channelHasReadEvents(channel) && !!channel?.getClient().options.isLocalUnreadCountEnabled;
598
+ var userHasReadReceipts = (client) => client.user?.privacy_settings?.read_receipts?.enabled ?? true;
593
599
  function addFileToFormData(uri, name, contentType) {
594
600
  const data = new import_form_data.default();
595
601
  if (isReadableStream(uri) || isBuffer(uri) || isFileWebAPI(uri) || isBlobWebAPI(uri)) {
@@ -8751,6 +8757,7 @@ var MessageDeliveryReporter = class _MessageDeliveryReporter {
8751
8757
  * @param options
8752
8758
  */
8753
8759
  this.markRead = async (collection, options) => {
8760
+ if (!userHasReadReceipts(this.client)) return null;
8754
8761
  let result = null;
8755
8762
  if (isChannel(collection)) {
8756
8763
  result = await collection.markAsReadRequest(options);
@@ -10095,6 +10102,33 @@ var Channel = class {
10095
10102
  ...data
10096
10103
  });
10097
10104
  }
10105
+ /**
10106
+ * markReadLocally - Resets this user's unread count locally, without any backend call. Intended for
10107
+ * channels that have read events disabled (e.g. livestreams) when the client is created with the
10108
+ * `isLocalUnreadCountEnabled` option. Dispatches a dedicated, client-only `message.read_locally` event
10109
+ * that runs through the same `_handleChannelEvent` read logic as a real `message.read` (minus the
10110
+ * delivery-report network sync), so the read-state update lives in one place. When offline support
10111
+ * is enabled, the offline DB persists the reset for read-events-disabled channels, so the local
10112
+ * count stays consistent across app restarts.
10113
+ *
10114
+ * @return {Event | undefined} The dispatched `message.read_locally` event, or `undefined` if there is no connected user.
10115
+ */
10116
+ markReadLocally() {
10117
+ const client = this.getClient();
10118
+ if (!client.userID) return;
10119
+ const event = {
10120
+ channel_id: this.id,
10121
+ channel_type: this.type,
10122
+ cid: this.cid,
10123
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
10124
+ last_read_message_id: this.lastMessage()?.id,
10125
+ team: this.data?.team,
10126
+ type: "message.read_locally",
10127
+ user: client.user
10128
+ };
10129
+ client.dispatchEvent(event);
10130
+ return event;
10131
+ }
10098
10132
  /**
10099
10133
  * clean - Cleans the channel state and fires stop typing if needed
10100
10134
  */
@@ -10250,7 +10284,7 @@ var Channel = class {
10250
10284
  if (message.user?.id === this.getClient().userID) return false;
10251
10285
  if (message.user?.id && this.getClient().userMuteStatus(message.user.id))
10252
10286
  return false;
10253
- if (Array.isArray(this.data?.own_capabilities) && !this.data?.own_capabilities.includes("read-events")) {
10287
+ if (!this.getClient().options.isLocalUnreadCountEnabled && !channelHasReadEvents(this)) {
10254
10288
  return false;
10255
10289
  }
10256
10290
  if (this.getClient()._muteStatus(this.cid).muted) return false;
@@ -10661,6 +10695,11 @@ var Channel = class {
10661
10695
  delete channelState.typing[event.user.id];
10662
10696
  }
10663
10697
  break;
10698
+ // `message.read_locally` is the client-only event dispatched by `markReadLocally()` when read
10699
+ // events are disabled (e.g. livestreams with `isLocalUnreadCountEnabled`). It reuses the exact
10700
+ // `message.read` state logic so the read-state update lives in one place — only the
10701
+ // delivery-report network sync below is skipped for it.
10702
+ case "message.read_locally":
10664
10703
  case "message.read":
10665
10704
  if (event.user?.id && event.created_at) {
10666
10705
  const previousReadState = channelState.read[event.user.id];
@@ -10681,7 +10720,9 @@ var Channel = class {
10681
10720
  const isOwnEvent = event.user?.id === client.user?.id;
10682
10721
  if (isOwnEvent) {
10683
10722
  channelState.unreadCount = 0;
10684
- client.syncDeliveredCandidates([this]);
10723
+ if (event.type === "message.read") {
10724
+ client.syncDeliveredCandidates([this]);
10725
+ }
10685
10726
  }
10686
10727
  }
10687
10728
  break;
@@ -12566,6 +12607,7 @@ var DEFAULT_CONNECTION_RECOVERY_THROTTLE_DURATION = 1e3;
12566
12607
  var MAX_QUERY_THREADS_LIMIT = 25;
12567
12608
  var THREAD_MANAGER_INITIAL_STATE = {
12568
12609
  active: false,
12610
+ wasActivatedAtLeastOnce: false,
12569
12611
  isThreadOrderStale: false,
12570
12612
  threads: [],
12571
12613
  unreadThreadCount: 0,
@@ -12588,7 +12630,7 @@ var ThreadManager = class extends WithSubscriptions {
12588
12630
  this.state.next(THREAD_MANAGER_INITIAL_STATE);
12589
12631
  };
12590
12632
  this.activate = () => {
12591
- this.state.partialNext({ active: true });
12633
+ this.state.partialNext({ active: true, wasActivatedAtLeastOnce: true });
12592
12634
  };
12593
12635
  this.deactivate = () => {
12594
12636
  this.state.partialNext({ active: false });
@@ -12668,8 +12710,8 @@ var ThreadManager = class extends WithSubscriptions {
12668
12710
  }).unsubscribe;
12669
12711
  const throttledHandleConnectionRecovered = throttle(
12670
12712
  () => {
12671
- const { lastConnectionDropAt } = this.state.getLatestValue();
12672
- if (!lastConnectionDropAt) return;
12713
+ const { lastConnectionDropAt, wasActivatedAtLeastOnce } = this.state.getLatestValue();
12714
+ if (!lastConnectionDropAt || !wasActivatedAtLeastOnce) return;
12673
12715
  this.reload({ force: true });
12674
12716
  },
12675
12717
  DEFAULT_CONNECTION_RECOVERY_THROTTLE_DURATION,
@@ -14801,7 +14843,7 @@ var StreamChat = class _StreamChat {
14801
14843
  return this.userAgent;
14802
14844
  }
14803
14845
  if (!this.cachedUserAgent) {
14804
- const version = "9.49.0";
14846
+ const version = "9.50.1";
14805
14847
  const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
14806
14848
  const { name: appName, version: appVersion } = this.appIdentifier ?? {};
14807
14849
  const { os, model: deviceModel } = this.deviceIdentifier ?? {};
@@ -14859,6 +14901,7 @@ var StreamChat = class _StreamChat {
14859
14901
  warmUp: false,
14860
14902
  recoverStateOnReconnect: true,
14861
14903
  disableCache: false,
14904
+ isLocalUnreadCountEnabled: false,
14862
14905
  wsUrlParams: new URLSearchParams({}),
14863
14906
  ...inputOptions
14864
14907
  };
@@ -18212,6 +18255,7 @@ var EVENT_MAP = {
18212
18255
  "ai_indicator.stop": true,
18213
18256
  "ai_indicator.clear": true,
18214
18257
  // local events
18258
+ "message.read_locally": true,
18215
18259
  "channels.queried": true,
18216
18260
  "offline_reactions.queried": true,
18217
18261
  "connection.changed": true,
@@ -18595,7 +18639,8 @@ var AbstractOfflineDB = class {
18595
18639
  if (cid && client.user && client.user.id !== user?.id) {
18596
18640
  const userId = client.user.id;
18597
18641
  const channel = client.activeChannels[cid];
18598
- if (channel) {
18642
+ const tracksReadLocally = channelTracksReadLocally(channel);
18643
+ if (channel && (channelHasReadEvents(channel) || tracksReadLocally)) {
18599
18644
  const ownReads = channel.state.read[userId];
18600
18645
  const unreadCount = channel.countUnread();
18601
18646
  const upsertReadsQueries = await this.upsertReads({
@@ -18603,8 +18648,8 @@ var AbstractOfflineDB = class {
18603
18648
  execute: false,
18604
18649
  reads: [
18605
18650
  {
18606
- last_read: ownReads.last_read.toISOString(),
18607
- last_read_message_id: ownReads.last_read_message_id,
18651
+ last_read: (ownReads?.last_read ?? /* @__PURE__ */ new Date(0)).toISOString(),
18652
+ last_read_message_id: ownReads?.last_read_message_id,
18608
18653
  unread_messages: unreadCount,
18609
18654
  user: client.user
18610
18655
  }
@@ -18803,27 +18848,30 @@ var AbstractOfflineDB = class {
18803
18848
  truncated_at,
18804
18849
  execute: false
18805
18850
  });
18851
+ let finalQueries = [...truncateQueries];
18806
18852
  const userId = ownUser.id;
18807
18853
  const activeChannel = this.client.activeChannels[cid];
18808
- const ownReads = activeChannel.state.read[userId];
18809
- let unreadCount = 0;
18810
- if (truncated_at) {
18811
- const truncatedAt = new Date(truncated_at);
18812
- unreadCount = activeChannel.countUnread(truncatedAt);
18854
+ const tracksReadLocally = channelTracksReadLocally(activeChannel);
18855
+ if (activeChannel && (channelHasReadEvents(activeChannel) || tracksReadLocally)) {
18856
+ const ownReads = activeChannel.state.read[userId];
18857
+ let unreadCount = 0;
18858
+ if (truncated_at) {
18859
+ unreadCount = activeChannel.countUnread(new Date(truncated_at));
18860
+ }
18861
+ const upsertReadQueries = await this.upsertReads({
18862
+ cid,
18863
+ execute: false,
18864
+ reads: [
18865
+ {
18866
+ last_read: (ownReads?.last_read ?? /* @__PURE__ */ new Date(0)).toString(),
18867
+ last_read_message_id: ownReads?.last_read_message_id,
18868
+ unread_messages: unreadCount,
18869
+ user: ownUser
18870
+ }
18871
+ ]
18872
+ });
18873
+ finalQueries = [...finalQueries, ...upsertReadQueries];
18813
18874
  }
18814
- const upsertReadQueries = await this.upsertReads({
18815
- cid,
18816
- execute: false,
18817
- reads: [
18818
- {
18819
- last_read: ownReads.last_read.toString(),
18820
- last_read_message_id: ownReads.last_read_message_id,
18821
- unread_messages: unreadCount,
18822
- user: ownUser
18823
- }
18824
- ]
18825
- });
18826
- const finalQueries = [...truncateQueries, ...upsertReadQueries];
18827
18875
  if (execute) {
18828
18876
  await this.executeSqlBatch(finalQueries);
18829
18877
  }
@@ -18924,6 +18972,13 @@ var AbstractOfflineDB = class {
18924
18972
  if (type === "message.read" || type === "notification.mark_read") {
18925
18973
  return this.handleRead({ event, unreadMessages: 0, execute });
18926
18974
  }
18975
+ if (type === "message.read_locally") {
18976
+ const localChannel = event.cid ? this.client.activeChannels[event.cid] : void 0;
18977
+ if (channelTracksReadLocally(localChannel)) {
18978
+ return this.handleRead({ event, unreadMessages: 0, execute });
18979
+ }
18980
+ return [];
18981
+ }
18927
18982
  if (type === "notification.mark_unread") {
18928
18983
  return this.handleRead({ event, execute });
18929
18984
  }