canvu-react 0.4.56 → 0.4.57

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.
@@ -823,6 +823,7 @@ var DRAFT_STORAGE_PREFIX = "canvu-realtime-draft:";
823
823
  var DOCUMENT_FLUSH_DEBOUNCE_MS = 120;
824
824
  var DRAFT_PERSIST_DEBOUNCE_MS = 420;
825
825
  var CONNECTION_MESSAGE_STATE_UPDATE_INTERVAL_MS = 1e3;
826
+ var PRESENCE_UPDATE_THROTTLE_MS = 80;
826
827
  function requestRuntimeIdleCallback(callback, timeout) {
827
828
  const runtime = globalThis;
828
829
  if (typeof runtime.requestIdleCallback === "function") {
@@ -958,6 +959,10 @@ function nowMs() {
958
959
  function shouldUpdateRealtimeConnectionMessageState(input) {
959
960
  return input.lastStateUpdateAt == null || input.receivedAt - input.lastStateUpdateAt >= CONNECTION_MESSAGE_STATE_UPDATE_INTERVAL_MS;
960
961
  }
962
+ function shouldSendRealtimePresenceUpdate(input) {
963
+ const minIntervalMs = input.minIntervalMs ?? PRESENCE_UPDATE_THROTTLE_MS;
964
+ return input.lastPresenceSentAt == null || input.requestedAt - input.lastPresenceSentAt >= minIntervalMs;
965
+ }
961
966
  function hasDurableDocumentPersistence(snapshot) {
962
967
  return snapshot.persistedRevision == null || snapshot.persistedRevision >= snapshot.revision;
963
968
  }
@@ -1098,6 +1103,7 @@ function useRealtimeSession(options) {
1098
1103
  const documentFlushIdleRef = useRef(null);
1099
1104
  const draftPersistTimerRef = useRef(null);
1100
1105
  const draftPersistIdleRef = useRef(null);
1106
+ const presenceUpdateTimerRef = useRef(null);
1101
1107
  const draftStorageRef = useRef(draftStorage);
1102
1108
  draftStorageRef.current = draftStorage;
1103
1109
  const manualDisconnectRef = useRef(false);
@@ -1122,6 +1128,7 @@ function useRealtimeSession(options) {
1122
1128
  enabled ? "connecting" : "offline"
1123
1129
  );
1124
1130
  const lastConnectionMessageStateUpdateAtRef = useRef(null);
1131
+ const lastPresenceSentAtRef = useRef(null);
1125
1132
  const localDraftRef = useRef(null);
1126
1133
  const conflictRef = useRef(null);
1127
1134
  const onErrorRef = useRef(onError);
@@ -1186,6 +1193,12 @@ function useRealtimeSession(options) {
1186
1193
  cancelRuntimeIdleCallback(draftPersistIdleRef.current);
1187
1194
  draftPersistIdleRef.current = null;
1188
1195
  }, []);
1196
+ const clearPresenceUpdateSchedule = useCallback(() => {
1197
+ if (presenceUpdateTimerRef.current != null) {
1198
+ globalThis.clearTimeout(presenceUpdateTimerRef.current);
1199
+ presenceUpdateTimerRef.current = null;
1200
+ }
1201
+ }, []);
1189
1202
  const updateConnection = useCallback(
1190
1203
  (patch) => {
1191
1204
  setConnection((prev) => {
@@ -1544,7 +1557,8 @@ function useRealtimeSession(options) {
1544
1557
  },
1545
1558
  [applyDocument, roomId, scheduleDocumentFlush, setLocalDraft]
1546
1559
  );
1547
- const sendPresenceUpdate = useCallback(() => {
1560
+ const sendPresenceUpdateNow = useCallback(() => {
1561
+ lastPresenceSentAtRef.current = nowMs();
1548
1562
  sendRaw({
1549
1563
  type: "presence:update",
1550
1564
  roomId,
@@ -1557,6 +1571,24 @@ function useRealtimeSession(options) {
1557
1571
  }
1558
1572
  });
1559
1573
  }, [roomId, sendRaw]);
1574
+ const sendPresenceUpdate = useCallback(() => {
1575
+ const requestedAt = nowMs();
1576
+ if (shouldSendRealtimePresenceUpdate({
1577
+ lastPresenceSentAt: lastPresenceSentAtRef.current,
1578
+ requestedAt
1579
+ })) {
1580
+ clearPresenceUpdateSchedule();
1581
+ sendPresenceUpdateNow();
1582
+ return;
1583
+ }
1584
+ if (presenceUpdateTimerRef.current != null) return;
1585
+ const elapsedMs = requestedAt - (lastPresenceSentAtRef.current ?? requestedAt);
1586
+ const delayMs = Math.max(0, PRESENCE_UPDATE_THROTTLE_MS - elapsedMs);
1587
+ presenceUpdateTimerRef.current = globalThis.setTimeout(() => {
1588
+ presenceUpdateTimerRef.current = null;
1589
+ sendPresenceUpdateNow();
1590
+ }, delayMs);
1591
+ }, [clearPresenceUpdateSchedule, sendPresenceUpdateNow]);
1560
1592
  const scheduleReconnect = useCallback(() => {
1561
1593
  if (!reconnect || manualDisconnectRef.current) return;
1562
1594
  clearReconnectTimer();
@@ -1638,6 +1670,7 @@ function useRealtimeSession(options) {
1638
1670
  if (!roomId) {
1639
1671
  clearDocumentFlushSchedule();
1640
1672
  clearDraftPersistSchedule();
1673
+ clearPresenceUpdateSchedule();
1641
1674
  localDraftRef.current = null;
1642
1675
  setHasLocalOfflineDraft(false);
1643
1676
  setHasPendingDocumentSync(false);
@@ -1680,6 +1713,7 @@ function useRealtimeSession(options) {
1680
1713
  applyDraftSnapshot,
1681
1714
  clearDocumentFlushSchedule,
1682
1715
  clearDraftPersistSchedule,
1716
+ clearPresenceUpdateSchedule,
1683
1717
  roomId,
1684
1718
  setConflictState,
1685
1719
  setLocalDraft
@@ -1692,6 +1726,7 @@ function useRealtimeSession(options) {
1692
1726
  clearHeartbeatTimer();
1693
1727
  clearConnectTimeout();
1694
1728
  clearDocumentFlushSchedule();
1729
+ clearPresenceUpdateSchedule();
1695
1730
  wsRef.current?.close();
1696
1731
  wsRef.current = null;
1697
1732
  queuedDirtyRef.current = localDraftRef.current != null;
@@ -1975,12 +2010,14 @@ function useRealtimeSession(options) {
1975
2010
  clearHeartbeatTimer();
1976
2011
  clearConnectTimeout();
1977
2012
  clearDocumentFlushSchedule();
2013
+ clearPresenceUpdateSchedule();
1978
2014
  socket.close();
1979
2015
  };
1980
2016
  }, [
1981
2017
  clearConnectTimeout,
1982
2018
  clearDocumentFlushSchedule,
1983
2019
  clearHeartbeatTimer,
2020
+ clearPresenceUpdateSchedule,
1984
2021
  clearReconnectTimer,
1985
2022
  connectSequence,
1986
2023
  connectTimeoutMs,
@@ -2008,12 +2045,17 @@ function useRealtimeSession(options) {
2008
2045
  () => () => {
2009
2046
  clearDocumentFlushSchedule();
2010
2047
  clearDraftPersistSchedule();
2048
+ clearPresenceUpdateSchedule();
2011
2049
  if (boardRef.current) {
2012
2050
  boardRef.current.doc.destroy();
2013
2051
  boardRef.current = null;
2014
2052
  }
2015
2053
  },
2016
- [clearDocumentFlushSchedule, clearDraftPersistSchedule]
2054
+ [
2055
+ clearDocumentFlushSchedule,
2056
+ clearDraftPersistSchedule,
2057
+ clearPresenceUpdateSchedule
2058
+ ]
2017
2059
  );
2018
2060
  const flushDocumentSync = useCallback(async () => {
2019
2061
  const board = boardRef.current;
@@ -2065,6 +2107,7 @@ function useRealtimeSession(options) {
2065
2107
  clearReconnectTimer();
2066
2108
  clearHeartbeatTimer();
2067
2109
  clearConnectTimeout();
2110
+ clearPresenceUpdateSchedule();
2068
2111
  sendRaw({
2069
2112
  type: "session:leave",
2070
2113
  roomId,
@@ -2081,6 +2124,7 @@ function useRealtimeSession(options) {
2081
2124
  }, [
2082
2125
  clearConnectTimeout,
2083
2126
  clearHeartbeatTimer,
2127
+ clearPresenceUpdateSchedule,
2084
2128
  clearReconnectTimer,
2085
2129
  collapsePeersToSelf,
2086
2130
  roomId,