@pluv/client 0.6.4 → 0.6.5

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/client@0.6.4 build /home/runner/work/pluv/pluv/packages/client
2
+ > @pluv/client@0.6.5 build /home/runner/work/pluv/pluv/packages/client
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,10 +8,10 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- CJS dist/index.js 34.11 KB
12
- CJS ⚡️ Build success in 106ms
13
- ESM dist/index.mjs 32.97 KB
14
- ESM ⚡️ Build success in 107ms
11
+ ESM dist/index.mjs 34.41 KB
12
+ ESM ⚡️ Build success in 95ms
13
+ CJS dist/index.js 35.55 KB
14
+ CJS ⚡️ Build success in 96ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 3699ms
17
- DTS dist/index.d.ts 11.50 KB
16
+ DTS ⚡️ Build success in 3764ms
17
+ DTS dist/index.d.ts 11.68 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @pluv/client
2
2
 
3
+ ## 0.6.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 7ad4967: fixed user's presence not updating locally while offline
8
+ - 4535687: fix local yjs doc not updating while disconnected
9
+ - @pluv/crdt-yjs@0.3.6
10
+ - @pluv/types@0.1.6
11
+
3
12
  ## 0.6.4
4
13
 
5
14
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -60,6 +60,7 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
60
60
  abstract other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>): () => void;
61
61
  abstract storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void): () => void;
62
62
  abstract subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
63
+ abstract updateMyPresence(presence: Partial<TPresence>): void;
63
64
  }
64
65
 
65
66
  interface CrdtManagerOptions<TStorage extends Record<string, AbstractType<any>> = {}> {
@@ -133,6 +134,7 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
133
134
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
134
135
  storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void) => (() => void);
135
136
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
137
+ updateMyPresence: (presence: Partial<TPresence>) => void;
136
138
  private _attachWindowListeners;
137
139
  private _attachWsListeners;
138
140
  private _clearInterval;
@@ -188,10 +190,10 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
188
190
  private _rooms;
189
191
  constructor(options: PluvClientOptions<TIO>);
190
192
  createRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}>(room: string, options?: PluvRoomOptions<TIO, TPresence, TStorage>) => PluvRoom<TIO, TPresence, TStorage>;
191
- enter: (room: string | PluvRoom<TIO, JsonObject, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
193
+ enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
192
194
  getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
193
195
  getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
194
- leave: (room: string | PluvRoom<TIO, JsonObject, any>) => void;
196
+ leave: (room: string | PluvRoom<TIO, any, any>) => void;
195
197
  private _logDebug;
196
198
  }
197
199
 
@@ -225,6 +227,7 @@ declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {},
225
227
  other: (connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO>) => (() => void);
226
228
  storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void) => (() => void);
227
229
  subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
230
+ updateMyPresence: (presence: Partial<TPresence>) => void;
228
231
  private _observeCrdt;
229
232
  }
230
233
 
package/dist/index.js CHANGED
@@ -189,13 +189,20 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
189
189
  // src/UsersManager.ts
190
190
  var UsersManager = class {
191
191
  constructor(config) {
192
+ /**
193
+ * @description This is only set when the user is connected.
194
+ */
192
195
  this._myself = null;
193
196
  this._others = /* @__PURE__ */ new Map();
194
197
  this._presence = null;
195
198
  const { initialPresence, presence = null } = config;
196
199
  this._initialPresence = initialPresence;
200
+ this._myPresence = initialPresence;
197
201
  this._presence = presence;
198
202
  }
203
+ get myPresence() {
204
+ return this._myPresence;
205
+ }
199
206
  get myself() {
200
207
  return this._myself;
201
208
  }
@@ -212,21 +219,15 @@ var UsersManager = class {
212
219
  patchPresence(connectionId, patch) {
213
220
  var _a, _b;
214
221
  if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId) {
215
- this._myself.presence = Object.assign(
216
- /* @__PURE__ */ Object.create(null),
217
- this._myself.presence,
218
- patch
219
- );
222
+ const presence2 = __spreadValues(__spreadValues({}, this._myPresence), patch);
223
+ this._myself.presence = presence2;
224
+ this._myPresence = presence2;
220
225
  return;
221
226
  }
222
227
  const other = this._others.get(connectionId);
223
228
  if (!other)
224
229
  return;
225
- const presence = Object.assign(
226
- /* @__PURE__ */ Object.create(null),
227
- other.presence,
228
- patch
229
- );
230
+ const presence = __spreadValues(__spreadValues({}, other.presence), patch);
230
231
  (_b = this._presence) == null ? void 0 : _b.parse(presence);
231
232
  this._others.set(connectionId, __spreadProps(__spreadValues({}, other), { presence }));
232
233
  }
@@ -242,11 +243,13 @@ var UsersManager = class {
242
243
  presence: this._initialPresence,
243
244
  user
244
245
  };
246
+ this._myPresence = this._initialPresence;
245
247
  }
246
248
  setPresence(connectionId, presence) {
247
249
  var _a;
248
250
  if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId) {
249
251
  this._myself.presence = presence;
252
+ this._myPresence = presence;
250
253
  return;
251
254
  }
252
255
  const other = this._others.get(connectionId);
@@ -267,6 +270,15 @@ var UsersManager = class {
267
270
  user
268
271
  });
269
272
  }
273
+ /**
274
+ * @description This method need not care about being connected.
275
+ */
276
+ updateMyPresence(presence) {
277
+ this._myPresence = __spreadValues(__spreadValues({}, this._myPresence), presence);
278
+ if (!this._myself)
279
+ return;
280
+ this._myself.presence = this._myPresence;
281
+ }
270
282
  };
271
283
 
272
284
  // src/PluvRoom.ts
@@ -343,6 +355,18 @@ var PluvRoom = class extends AbstractRoom {
343
355
  this.subscribe = (name, callback) => {
344
356
  return this._stateNotifier.subscribe(name, callback);
345
357
  };
358
+ this.updateMyPresence = (presence) => {
359
+ var _a;
360
+ this._usersManager.updateMyPresence(presence);
361
+ const myPresence = this._usersManager.myPresence;
362
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
363
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
364
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
365
+ this.broadcast(
366
+ "$UPDATE_PRESENCE",
367
+ { presence }
368
+ );
369
+ };
346
370
  this._debug = debug;
347
371
  this._endpoints = {
348
372
  authEndpoint,
@@ -430,8 +454,10 @@ var PluvRoom = class extends AbstractRoom {
430
454
  ].some((state) => this._state.connection.state === state);
431
455
  if (!canDisconnect)
432
456
  return;
457
+ this._clearInterval(this._intervals.heartbeat);
433
458
  this._closeWs();
434
459
  (_a = this._crdtManager) == null ? void 0 : _a.destroy();
460
+ this._stateNotifier.subjects["my-presence"].next(null);
435
461
  this._updateState((oldState) => {
436
462
  oldState.authorization.token = null;
437
463
  oldState.connection.state = "Closed" /* Closed */;
@@ -503,7 +529,6 @@ var PluvRoom = class extends AbstractRoom {
503
529
  if (!this._state.webSocket)
504
530
  return;
505
531
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
506
- this._clearInterval(this._intervals.heartbeat);
507
532
  this._clearTimeout(this._timeouts.pong);
508
533
  this._clearTimeout(this._timeouts.reconnect);
509
534
  this._detachWindowListeners();
@@ -517,7 +542,6 @@ var PluvRoom = class extends AbstractRoom {
517
542
  if (canClose)
518
543
  this._state.webSocket.close();
519
544
  this._detachWsListeners();
520
- this._stateNotifier.subjects["my-presence"].next(null);
521
545
  this._stateNotifier.subjects["myself"].next(null);
522
546
  }
523
547
  _detachWindowListeners() {
@@ -592,7 +616,7 @@ var PluvRoom = class extends AbstractRoom {
592
616
  this._otherNotifier.subjects.delete(connectionId);
593
617
  }
594
618
  _handlePresenceUpdatedMessage(message) {
595
- var _a, _b, _c;
619
+ var _a;
596
620
  const { connectionId } = message;
597
621
  if (!connectionId)
598
622
  return;
@@ -603,8 +627,8 @@ var PluvRoom = class extends AbstractRoom {
603
627
  connectionId,
604
628
  data.presence
605
629
  );
606
- const myPresence = (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
607
- const myself = (_c = this._usersManager.myself) != null ? _c : null;
630
+ const myPresence = this._usersManager.myPresence;
631
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
608
632
  if ((myself == null ? void 0 : myself.connectionId) === connectionId) {
609
633
  this._stateNotifier.subjects["my-presence"].next(myPresence);
610
634
  this._stateNotifier.subjects["myself"].next(myself);
@@ -637,7 +661,7 @@ var PluvRoom = class extends AbstractRoom {
637
661
  this._stateNotifier.subjects["others"].next(others);
638
662
  }
639
663
  _handleRegisteredMessage(message) {
640
- var _a, _b, _c;
664
+ var _a;
641
665
  const { connectionId, user } = message;
642
666
  if (!connectionId)
643
667
  return;
@@ -652,8 +676,8 @@ var PluvRoom = class extends AbstractRoom {
652
676
  connectionId,
653
677
  user
654
678
  );
655
- const myPresence = (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
656
- const myself = (_c = this._usersManager.myself) != null ? _c : null;
679
+ const myPresence = this._usersManager.myPresence;
680
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
657
681
  this._stateNotifier.subjects["my-presence"].next(myPresence);
658
682
  this._stateNotifier.subjects["myself"].next(myself);
659
683
  this._sendMessage({ type: "$GET_OTHERS", data: {} });
@@ -758,6 +782,13 @@ var PluvRoom = class extends AbstractRoom {
758
782
  return;
759
783
  if (origin === "$STORAGE_UPDATED")
760
784
  return;
785
+ const sharedTypes = this._crdtManager.doc.getSharedTypes();
786
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
787
+ if (!this._crdtManager)
788
+ return;
789
+ const serialized = sharedType.toJSON();
790
+ this._crdtNotifier.subject(prop).next(serialized);
791
+ });
761
792
  this._sendMessage({
762
793
  type: "$UPDATE_STORAGE",
763
794
  data: { origin, update }
@@ -1069,6 +1100,14 @@ var MockedRoom = class extends AbstractRoom {
1069
1100
  this.subscribe = (name, callback) => {
1070
1101
  return this._stateNotifier.subscribe(name, callback);
1071
1102
  };
1103
+ this.updateMyPresence = (presence) => {
1104
+ var _a;
1105
+ this._usersManager.updateMyPresence(presence);
1106
+ const myPresence = this._usersManager.myPresence;
1107
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
1108
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
1109
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
1110
+ };
1072
1111
  this._events = events;
1073
1112
  this._usersManager = new UsersManager({
1074
1113
  initialPresence,
@@ -1112,14 +1151,11 @@ var MockedRoom = class extends AbstractRoom {
1112
1151
  if (origin === "$STORAGE_UPDATED")
1113
1152
  return;
1114
1153
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
1115
- Object.keys(sharedTypes).forEach((key) => {
1154
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
1116
1155
  if (!this._crdtManager)
1117
1156
  return;
1118
- const sharedType = sharedTypes[key];
1119
- if (!sharedType)
1120
- return;
1121
1157
  const serialized = sharedType.toJSON();
1122
- this._crdtNotifier.subject(key).next(serialized);
1158
+ this._crdtNotifier.subject(prop).next(serialized);
1123
1159
  });
1124
1160
  }
1125
1161
  );
package/dist/index.mjs CHANGED
@@ -164,13 +164,20 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
164
164
  // src/UsersManager.ts
165
165
  var UsersManager = class {
166
166
  constructor(config) {
167
+ /**
168
+ * @description This is only set when the user is connected.
169
+ */
167
170
  this._myself = null;
168
171
  this._others = /* @__PURE__ */ new Map();
169
172
  this._presence = null;
170
173
  const { initialPresence, presence = null } = config;
171
174
  this._initialPresence = initialPresence;
175
+ this._myPresence = initialPresence;
172
176
  this._presence = presence;
173
177
  }
178
+ get myPresence() {
179
+ return this._myPresence;
180
+ }
174
181
  get myself() {
175
182
  return this._myself;
176
183
  }
@@ -187,21 +194,15 @@ var UsersManager = class {
187
194
  patchPresence(connectionId, patch) {
188
195
  var _a, _b;
189
196
  if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId) {
190
- this._myself.presence = Object.assign(
191
- /* @__PURE__ */ Object.create(null),
192
- this._myself.presence,
193
- patch
194
- );
197
+ const presence2 = __spreadValues(__spreadValues({}, this._myPresence), patch);
198
+ this._myself.presence = presence2;
199
+ this._myPresence = presence2;
195
200
  return;
196
201
  }
197
202
  const other = this._others.get(connectionId);
198
203
  if (!other)
199
204
  return;
200
- const presence = Object.assign(
201
- /* @__PURE__ */ Object.create(null),
202
- other.presence,
203
- patch
204
- );
205
+ const presence = __spreadValues(__spreadValues({}, other.presence), patch);
205
206
  (_b = this._presence) == null ? void 0 : _b.parse(presence);
206
207
  this._others.set(connectionId, __spreadProps(__spreadValues({}, other), { presence }));
207
208
  }
@@ -217,11 +218,13 @@ var UsersManager = class {
217
218
  presence: this._initialPresence,
218
219
  user
219
220
  };
221
+ this._myPresence = this._initialPresence;
220
222
  }
221
223
  setPresence(connectionId, presence) {
222
224
  var _a;
223
225
  if (((_a = this._myself) == null ? void 0 : _a.connectionId) === connectionId) {
224
226
  this._myself.presence = presence;
227
+ this._myPresence = presence;
225
228
  return;
226
229
  }
227
230
  const other = this._others.get(connectionId);
@@ -242,6 +245,15 @@ var UsersManager = class {
242
245
  user
243
246
  });
244
247
  }
248
+ /**
249
+ * @description This method need not care about being connected.
250
+ */
251
+ updateMyPresence(presence) {
252
+ this._myPresence = __spreadValues(__spreadValues({}, this._myPresence), presence);
253
+ if (!this._myself)
254
+ return;
255
+ this._myself.presence = this._myPresence;
256
+ }
245
257
  };
246
258
 
247
259
  // src/PluvRoom.ts
@@ -318,6 +330,18 @@ var PluvRoom = class extends AbstractRoom {
318
330
  this.subscribe = (name, callback) => {
319
331
  return this._stateNotifier.subscribe(name, callback);
320
332
  };
333
+ this.updateMyPresence = (presence) => {
334
+ var _a;
335
+ this._usersManager.updateMyPresence(presence);
336
+ const myPresence = this._usersManager.myPresence;
337
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
338
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
339
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
340
+ this.broadcast(
341
+ "$UPDATE_PRESENCE",
342
+ { presence }
343
+ );
344
+ };
321
345
  this._debug = debug;
322
346
  this._endpoints = {
323
347
  authEndpoint,
@@ -405,8 +429,10 @@ var PluvRoom = class extends AbstractRoom {
405
429
  ].some((state) => this._state.connection.state === state);
406
430
  if (!canDisconnect)
407
431
  return;
432
+ this._clearInterval(this._intervals.heartbeat);
408
433
  this._closeWs();
409
434
  (_a = this._crdtManager) == null ? void 0 : _a.destroy();
435
+ this._stateNotifier.subjects["my-presence"].next(null);
410
436
  this._updateState((oldState) => {
411
437
  oldState.authorization.token = null;
412
438
  oldState.connection.state = "Closed" /* Closed */;
@@ -478,7 +504,6 @@ var PluvRoom = class extends AbstractRoom {
478
504
  if (!this._state.webSocket)
479
505
  return;
480
506
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
481
- this._clearInterval(this._intervals.heartbeat);
482
507
  this._clearTimeout(this._timeouts.pong);
483
508
  this._clearTimeout(this._timeouts.reconnect);
484
509
  this._detachWindowListeners();
@@ -492,7 +517,6 @@ var PluvRoom = class extends AbstractRoom {
492
517
  if (canClose)
493
518
  this._state.webSocket.close();
494
519
  this._detachWsListeners();
495
- this._stateNotifier.subjects["my-presence"].next(null);
496
520
  this._stateNotifier.subjects["myself"].next(null);
497
521
  }
498
522
  _detachWindowListeners() {
@@ -567,7 +591,7 @@ var PluvRoom = class extends AbstractRoom {
567
591
  this._otherNotifier.subjects.delete(connectionId);
568
592
  }
569
593
  _handlePresenceUpdatedMessage(message) {
570
- var _a, _b, _c;
594
+ var _a;
571
595
  const { connectionId } = message;
572
596
  if (!connectionId)
573
597
  return;
@@ -578,8 +602,8 @@ var PluvRoom = class extends AbstractRoom {
578
602
  connectionId,
579
603
  data.presence
580
604
  );
581
- const myPresence = (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
582
- const myself = (_c = this._usersManager.myself) != null ? _c : null;
605
+ const myPresence = this._usersManager.myPresence;
606
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
583
607
  if ((myself == null ? void 0 : myself.connectionId) === connectionId) {
584
608
  this._stateNotifier.subjects["my-presence"].next(myPresence);
585
609
  this._stateNotifier.subjects["myself"].next(myself);
@@ -612,7 +636,7 @@ var PluvRoom = class extends AbstractRoom {
612
636
  this._stateNotifier.subjects["others"].next(others);
613
637
  }
614
638
  _handleRegisteredMessage(message) {
615
- var _a, _b, _c;
639
+ var _a;
616
640
  const { connectionId, user } = message;
617
641
  if (!connectionId)
618
642
  return;
@@ -627,8 +651,8 @@ var PluvRoom = class extends AbstractRoom {
627
651
  connectionId,
628
652
  user
629
653
  );
630
- const myPresence = (_b = (_a = this._usersManager.myself) == null ? void 0 : _a.presence) != null ? _b : null;
631
- const myself = (_c = this._usersManager.myself) != null ? _c : null;
654
+ const myPresence = this._usersManager.myPresence;
655
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
632
656
  this._stateNotifier.subjects["my-presence"].next(myPresence);
633
657
  this._stateNotifier.subjects["myself"].next(myself);
634
658
  this._sendMessage({ type: "$GET_OTHERS", data: {} });
@@ -733,6 +757,13 @@ var PluvRoom = class extends AbstractRoom {
733
757
  return;
734
758
  if (origin === "$STORAGE_UPDATED")
735
759
  return;
760
+ const sharedTypes = this._crdtManager.doc.getSharedTypes();
761
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
762
+ if (!this._crdtManager)
763
+ return;
764
+ const serialized = sharedType.toJSON();
765
+ this._crdtNotifier.subject(prop).next(serialized);
766
+ });
736
767
  this._sendMessage({
737
768
  type: "$UPDATE_STORAGE",
738
769
  data: { origin, update }
@@ -1044,6 +1075,14 @@ var MockedRoom = class extends AbstractRoom {
1044
1075
  this.subscribe = (name, callback) => {
1045
1076
  return this._stateNotifier.subscribe(name, callback);
1046
1077
  };
1078
+ this.updateMyPresence = (presence) => {
1079
+ var _a;
1080
+ this._usersManager.updateMyPresence(presence);
1081
+ const myPresence = this._usersManager.myPresence;
1082
+ const myself = (_a = this._usersManager.myself) != null ? _a : null;
1083
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
1084
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
1085
+ };
1047
1086
  this._events = events;
1048
1087
  this._usersManager = new UsersManager({
1049
1088
  initialPresence,
@@ -1087,14 +1126,11 @@ var MockedRoom = class extends AbstractRoom {
1087
1126
  if (origin === "$STORAGE_UPDATED")
1088
1127
  return;
1089
1128
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
1090
- Object.keys(sharedTypes).forEach((key) => {
1129
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
1091
1130
  if (!this._crdtManager)
1092
1131
  return;
1093
- const sharedType = sharedTypes[key];
1094
- if (!sharedType)
1095
- return;
1096
1132
  const serialized = sharedType.toJSON();
1097
- this._crdtNotifier.subject(key).next(serialized);
1133
+ this._crdtNotifier.subject(prop).next(serialized);
1098
1134
  });
1099
1135
  }
1100
1136
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@
28
28
  "tsup": "^6.7.0",
29
29
  "typescript": "^5.0.4",
30
30
  "yjs": "^13.5.52",
31
- "@pluv/tsconfig": "^0.1.5",
31
+ "@pluv/tsconfig": "^0.1.6",
32
32
  "eslint-config-pluv": "^0.1.10"
33
33
  },
34
34
  "scripts": {
@@ -68,4 +68,6 @@ export abstract class AbstractRoom<
68
68
  name: TSubject,
69
69
  callback: SubscriptionCallback<TIO, TPresence, TSubject>
70
70
  ): () => void;
71
+
72
+ public abstract updateMyPresence(presence: Partial<TPresence>): void;
71
73
  }
package/src/MockedRoom.ts CHANGED
@@ -177,6 +177,16 @@ export class MockedRoom<
177
177
  return this._stateNotifier.subscribe(name, callback);
178
178
  };
179
179
 
180
+ public updateMyPresence = (presence: Partial<TPresence>): void => {
181
+ this._usersManager.updateMyPresence(presence);
182
+
183
+ const myPresence = this._usersManager.myPresence;
184
+ const myself = this._usersManager.myself ?? null;
185
+
186
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
187
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
188
+ };
189
+
180
190
  private _observeCrdt(): void {
181
191
  this._subscriptions.observeCrdt?.();
182
192
 
@@ -191,16 +201,12 @@ export class MockedRoom<
191
201
 
192
202
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
193
203
 
194
- Object.keys(sharedTypes).forEach((key) => {
204
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
195
205
  if (!this._crdtManager) return;
196
206
 
197
- const sharedType = sharedTypes[key];
198
-
199
- if (!sharedType) return;
200
-
201
207
  const serialized = sharedType.toJSON();
202
208
 
203
- this._crdtNotifier.subject(key).next(serialized);
209
+ this._crdtNotifier.subject(prop).next(serialized);
204
210
  });
205
211
  }
206
212
  );
package/src/PluvClient.ts CHANGED
@@ -17,7 +17,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
17
17
  private _debug: boolean;
18
18
  private _wsEndpoint: WsEndpoint;
19
19
 
20
- private _rooms = new Map<string, PluvRoom<TIO, JsonObject, any>>();
20
+ private _rooms = new Map<string, PluvRoom<TIO, any, any>>();
21
21
 
22
22
  constructor(options: PluvClientOptions<TIO>) {
23
23
  const { authEndpoint, debug = false, wsEndpoint } = options;
@@ -55,7 +55,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
55
55
  };
56
56
 
57
57
  public enter = async (
58
- room: string | PluvRoom<TIO, JsonObject, any>
58
+ room: string | PluvRoom<TIO, any, any>
59
59
  ): Promise<PluvRoom<TIO, JsonObject, any>> => {
60
60
  const toEnter = typeof room === "string" ? this.getRoom(room) : room;
61
61
 
@@ -89,7 +89,7 @@ export class PluvClient<TIO extends IOLike = IOLike> {
89
89
  return Array.from(this._rooms.values());
90
90
  };
91
91
 
92
- public leave = (room: string | PluvRoom<TIO, JsonObject, any>): void => {
92
+ public leave = (room: string | PluvRoom<TIO, any, any>): void => {
93
93
  const toLeave = typeof room === "string" ? this.getRoom(room) : room;
94
94
 
95
95
  if (!toLeave) return;
package/src/PluvRoom.ts CHANGED
@@ -279,9 +279,13 @@ export class PluvRoom<
279
279
 
280
280
  if (!canDisconnect) return;
281
281
 
282
+ this._clearInterval(this._intervals.heartbeat);
283
+
282
284
  this._closeWs();
283
285
  this._crdtManager?.destroy();
284
286
 
287
+ this._stateNotifier.subjects["my-presence"].next(null);
288
+
285
289
  this._updateState((oldState) => {
286
290
  oldState.authorization.token = null;
287
291
  oldState.connection.state = ConnectionState.Closed;
@@ -352,6 +356,21 @@ export class PluvRoom<
352
356
  return this._stateNotifier.subscribe(name, callback);
353
357
  };
354
358
 
359
+ public updateMyPresence = (presence: Partial<TPresence>): void => {
360
+ this._usersManager.updateMyPresence(presence);
361
+
362
+ const myPresence = this._usersManager.myPresence;
363
+ const myself = this._usersManager.myself ?? null;
364
+
365
+ this._stateNotifier.subjects["my-presence"].next(myPresence);
366
+ !!myself && this._stateNotifier.subjects["myself"].next(myself);
367
+
368
+ this.broadcast(
369
+ "$UPDATE_PRESENCE" as keyof InferIOInput<TIO>,
370
+ { presence } as any
371
+ );
372
+ };
373
+
355
374
  private _attachWindowListeners(): void {
356
375
  if (typeof window === "undefined") return;
357
376
  if (typeof document === "undefined") return;
@@ -416,7 +435,6 @@ export class PluvRoom<
416
435
 
417
436
  this._subscriptions.observeCrdt?.();
418
437
 
419
- this._clearInterval(this._intervals.heartbeat);
420
438
  this._clearTimeout(this._timeouts.pong);
421
439
  this._clearTimeout(this._timeouts.reconnect);
422
440
 
@@ -431,7 +449,6 @@ export class PluvRoom<
431
449
 
432
450
  this._detachWsListeners();
433
451
 
434
- this._stateNotifier.subjects["my-presence"].next(null);
435
452
  this._stateNotifier.subjects["myself"].next(null);
436
453
  }
437
454
 
@@ -537,7 +554,7 @@ export class PluvRoom<
537
554
  data.presence as TPresence
538
555
  );
539
556
 
540
- const myPresence = this._usersManager.myself?.presence ?? null;
557
+ const myPresence = this._usersManager.myPresence;
541
558
  const myself = this._usersManager.myself ?? null;
542
559
 
543
560
  if (myself?.connectionId === connectionId) {
@@ -605,7 +622,7 @@ export class PluvRoom<
605
622
  user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>
606
623
  );
607
624
 
608
- const myPresence = this._usersManager.myself?.presence ?? null;
625
+ const myPresence = this._usersManager.myPresence;
609
626
  const myself = this._usersManager.myself ?? null;
610
627
 
611
628
  this._stateNotifier.subjects["my-presence"].next(myPresence);
@@ -739,6 +756,16 @@ export class PluvRoom<
739
756
 
740
757
  if (origin === "$STORAGE_UPDATED") return;
741
758
 
759
+ const sharedTypes = this._crdtManager.doc.getSharedTypes();
760
+
761
+ Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
762
+ if (!this._crdtManager) return;
763
+
764
+ const serialized = sharedType.toJSON();
765
+
766
+ this._crdtNotifier.subject(prop).next(serialized);
767
+ });
768
+
742
769
  this._sendMessage({
743
770
  type: "$UPDATE_STORAGE",
744
771
  data: { origin, update },
@@ -20,6 +20,14 @@ export class UsersManager<
20
20
  TPresence extends JsonObject = {}
21
21
  > {
22
22
  private _initialPresence: TPresence;
23
+ /**
24
+ * @description This presence can be updated while the user is not
25
+ * connected.
26
+ */
27
+ private _myPresence: TPresence;
28
+ /**
29
+ * @description This is only set when the user is connected.
30
+ */
23
31
  private _myself: UserInfo<TIO, TPresence> | null = null;
24
32
  private _others = new Map<string, UserInfo<TIO, TPresence>>();
25
33
  private _presence: InputZodLike<TPresence> | null = null;
@@ -28,9 +36,14 @@ export class UsersManager<
28
36
  const { initialPresence, presence = null } = config;
29
37
 
30
38
  this._initialPresence = initialPresence as TPresence;
39
+ this._myPresence = initialPresence as TPresence;
31
40
  this._presence = presence;
32
41
  }
33
42
 
43
+ public get myPresence(): TPresence {
44
+ return this._myPresence;
45
+ }
46
+
34
47
  public get myself(): UserInfo<TIO, TPresence> | null {
35
48
  return this._myself;
36
49
  }
@@ -52,11 +65,13 @@ export class UsersManager<
52
65
  patch: Partial<TPresence>
53
66
  ): void {
54
67
  if (this._myself?.connectionId === connectionId) {
55
- this._myself.presence = Object.assign(
56
- Object.create(null),
57
- this._myself.presence,
58
- patch
59
- ) as TPresence;
68
+ const presence = {
69
+ ...this._myPresence,
70
+ ...patch,
71
+ } as TPresence;
72
+
73
+ this._myself.presence = presence;
74
+ this._myPresence = presence;
60
75
 
61
76
  return;
62
77
  }
@@ -65,11 +80,10 @@ export class UsersManager<
65
80
 
66
81
  if (!other) return;
67
82
 
68
- const presence = Object.assign(
69
- Object.create(null),
70
- other.presence,
71
- patch
72
- ) as TPresence;
83
+ const presence = {
84
+ ...other.presence,
85
+ ...patch,
86
+ } as TPresence;
73
87
 
74
88
  this._presence?.parse(presence);
75
89
 
@@ -93,11 +107,14 @@ export class UsersManager<
93
107
  presence: this._initialPresence,
94
108
  user,
95
109
  };
110
+
111
+ this._myPresence = this._initialPresence;
96
112
  }
97
113
 
98
114
  public setPresence(connectionId: string, presence: TPresence): void {
99
115
  if (this._myself?.connectionId === connectionId) {
100
116
  this._myself.presence = presence;
117
+ this._myPresence = presence;
101
118
 
102
119
  return;
103
120
  }
@@ -125,4 +142,15 @@ export class UsersManager<
125
142
  user,
126
143
  });
127
144
  }
145
+
146
+ /**
147
+ * @description This method need not care about being connected.
148
+ */
149
+ public updateMyPresence(presence: Partial<TPresence>): void {
150
+ this._myPresence = { ...this._myPresence, ...presence };
151
+
152
+ if (!this._myself) return;
153
+
154
+ this._myself.presence = this._myPresence;
155
+ }
128
156
  }