You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This change adds the ability for clients to broadcast information about
"Presence" - the notion of a client's position or state in a particular
document. This might be represent a cursor in a text document, or a
highlighted field in a more complex JSON document, or any other
transient, current information about a client that shouldn't necessarily
be stored in the document's chain of ops.
The main complication that this feature solves is the issue of keeping
presence correctly associated with the version of a `Doc` it was created
at. For example, in a "naive" implementation of presence, presence
information can arrive ahead of or behind ops, which - in a text-based
example - can cause the cursor to "jitter" around the change. Using the
ShareDB implementation will ensure that the presence is correctly
transformed against any ops, and will ensure that presence information
is always consistent with the version of the document. We also locally
transform existing presence, which should help to keep (static) remote
presence correctly positioned, independent of latency.
In order to facilitate this, the feature must be used with an OT type
that supports presence. The only requirement for supporting presence is
the support of a `transformPresence` method:
```javascript
type.transformPresence(presence, op, isOwnOperation): presence;
```
* `presence` _Object_: the presence data being transformed. The type
will define this shape to be whatever is appropriate for the type.
* `op` _Op_: the operation against which to transform the presence
* `isOwnOperation`: _boolean_: whether the presence and the op have the
same "owner". This information can be useful for some types to break
ties when transforming a presence, for example as used in
[`rich-text`][1]
This work is based on the [work][2] by @gkubisa and @curran, but with
the following aims:
- avoid modifying the existing `Doc` class as much as possible, and
instead use lifecycle hooks
- keep presence separate as its own conceptual entity
- break the presence subscriptions apart from `Doc` subscriptions
(although in practice, the two are obviously tightly coupled)
- allow multiple presences on a single `Doc` on the same `Connection`
[1]: https://github.com/quilljs/delta#tranformposition
[2]: #288
Get a [`Presence`](#class-sharedbpresence) instance that can be used to subscribe to presence information to other clients, and create instances of local presence.
Get a special [`Presence`](#class-sharedbpresence) instance that can be used to subscribe to presence information to other clients, and create instances of local presence. This is tied to a `Doc`, and all presence will be automatically transformed against ops to keep presence current. Note that the `Doc` must be of a type that supports presence.
315
+
316
+
*`collection`_(String)_
317
+
Document collection
318
+
*`id`_(String)_
319
+
Document ID
320
+
306
321
### Class: `ShareDB.Doc`
307
322
308
323
`doc.type`_(String_)
@@ -349,6 +364,9 @@ The document was deleted. Document contents before deletion are passed in as an
349
364
`doc.on('error', function(err) {...})`
350
365
There was an error fetching the document or applying an operation.
A remote client has sent presence information. `id` is an ID provided by the remote client, and `presence` is the presence data, whose structure will depend on document's OT type.
369
+
352
370
`doc.removeListener(eventName, listener)`
353
371
Removes any listener you added with `doc.on`. `eventName` should be one of `'load'`, `'create'`, `'before op'`, `'op'`, `'del'`, or `'error'`. `listener` should be the function you passed in as the second argument to `on`. Note that both `on` and `removeListener` are inherited from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).
354
372
@@ -379,6 +397,12 @@ Invokes the given callback function after
379
397
380
398
Note that `whenNothingPending` does NOT wait for pending `model.query()` calls.
381
399
400
+
`doc.subscribeToPresence([function(err) {...}])`
401
+
Subscribes to presence updates sent by other clients, emitting `presence` events (see above).
Unsubscribe from presence updates sent by other clients, and stop receiving `presence` events (see above).
405
+
382
406
### Class: `ShareDB.Query`
383
407
384
408
`query.ready`_(Boolean)_
@@ -629,6 +653,67 @@ var connectionInfo = getUserPermissions();
629
653
var connection =backend.connect(null, connectionInfo);
630
654
```
631
655
656
+
### Class: `ShareDB.Presence`
657
+
658
+
Representation of the presence data associated with a given channel, or - in the case of `getDocPresence` - presence data associated with a `Doc` instance.
659
+
660
+
#### `subscribe`
661
+
662
+
```javascript
663
+
presence.subscribe(callback);
664
+
```
665
+
666
+
Subscribe to presence updates from other clients. Note that presence can be submitted without subscribing, but remote clients will not be able to re-request presence if not subscribed.
667
+
668
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
669
+
670
+
#### `unsubscribe`
671
+
672
+
```javascript
673
+
presence.unsubscribe(callback);
674
+
```
675
+
676
+
Unsubscribe from presence updates from remote clients.
677
+
678
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
679
+
680
+
#### `create`
681
+
682
+
```javascript
683
+
presence.create(presenceId): LocalPresence;
684
+
```
685
+
686
+
Create an instance of [`LocalPresence`](#class-sharedblocalpresence), which can be used to represent local presence. Many or none such local presences may exist on a `Presence` instance.
687
+
688
+
*`presenceId`_string_: a unique ID representing the local presence
689
+
690
+
#### `destroy`
691
+
692
+
```javascript
693
+
presence.destroy(callback);
694
+
```
695
+
696
+
Updates all remote clients with a `null` presence. Then unsubscribes and destroys the local instance of the presence by de-registering all the `Doc` hooks it listens to, and removes it from the `Connection` cache, so that it can be garbage-collected. This should be called when you are done with a presence, and no longer need to use it to fire updates.
697
+
698
+
This method is automatically called when calling `doc.destroy`.
699
+
700
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
701
+
702
+
### Class: `ShareDB.LocalPresence`
703
+
704
+
`LocalPresence` represents the presence of the local client in a given `Doc`. For example, this might be the position of a caret in a text document; which field has been highlighted in a complex JSON object; etc. Multiple presences may exist per `Doc` even on the same client.
705
+
706
+
#### `submit`
707
+
708
+
```javascript
709
+
localPresence.submit(presence, callback);
710
+
```
711
+
712
+
Update the local representation of presence, and broadcast that presence to any other document presence subscribers.
713
+
714
+
*`presence`_Object_: the presence object to broadcast. The structure of this will depend on the OT type
715
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
716
+
632
717
### Logging
633
718
634
719
By default, ShareDB logs to `console`. This can be overridden if you wish to silence logs, or to log to your own logging driver or alert service.
0 commit comments