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
Copy file name to clipboardExpand all lines: README.md
+118
Original file line number
Diff line number
Diff line change
@@ -158,6 +158,7 @@ Register a new middleware.
158
158
the database.
159
159
*`'receive'`: Received a message from a client
160
160
*`'reply'`: About to send a non-error reply to a client message
161
+
*`'sendPresence'`: About to send presence information to a client
161
162
*`fn`_(Function(context, callback))_
162
163
Call this function at the time specified by `action`.
163
164
*`context` will always have the following properties:
@@ -307,6 +308,20 @@ Get a read-only snapshot of a document at the requested version.
307
308
}
308
309
```
309
310
311
+
`connection.getPresence(channel): Presence;`
312
+
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 [`DocPresence`](#class-sharedbdocpresence) 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.
Representation of the presence data associated with a given channel.
661
+
662
+
#### `subscribe`
663
+
664
+
```javascript
665
+
presence.subscribe(callback):void;
666
+
```
667
+
668
+
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 from you if you are not subscribed.
669
+
670
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
671
+
672
+
#### `unsubscribe`
673
+
674
+
```javascript
675
+
presence.unsubscribe(callback):void;
676
+
```
677
+
678
+
Unsubscribe from presence updates from remote clients.
679
+
680
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
681
+
682
+
#### `on`
683
+
684
+
```javascript
685
+
presence.on('receive', callback):void;
686
+
```
687
+
688
+
An update from a remote presence client has been received.
689
+
690
+
*`callback`_Function_: callback for handling the received presence: `function (presenceId, presenceValue): void;`
691
+
692
+
```javascript
693
+
presence.on('error', callback):void;
694
+
```
695
+
696
+
A presence-related error has occurred.
697
+
698
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
699
+
700
+
#### `create`
701
+
702
+
```javascript
703
+
presence.create(presenceId): LocalPresence;
704
+
```
705
+
706
+
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.
707
+
708
+
*`presenceId`_string_: a unique ID representing the local presence. Remember - depending on use-case - the same client might have multiple presences, so this might not necessarily be a user or client ID.
709
+
710
+
#### `destroy`
711
+
712
+
```javascript
713
+
presence.destroy(callback);
714
+
```
715
+
716
+
Updates all remote clients with a `null` presence, 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.
717
+
718
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
719
+
720
+
### Class: `ShareDB.DocPresence`
721
+
722
+
Specialised case of [`Presence`](#class-sharedbpresence), which is tied to a specific [`Doc`](#class-sharedbdoc). When using presence with an associated `Doc`, any ops applied to the `Doc` will automatically be used to transform associated presence. On destroy, the `DocPresence` will unregister its listeners from the `Doc`.
723
+
724
+
See [`Presence`](#class-sharedbpresence) for available methods.
725
+
726
+
### Class: `ShareDB.LocalPresence`
727
+
728
+
`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.
729
+
730
+
#### `submit`
731
+
732
+
```javascript
733
+
localPresence.submit(presence, callback):void;
734
+
```
735
+
736
+
Update the local representation of presence, and broadcast that presence to any other document presence subscribers.
737
+
738
+
*`presence`_Object_: the presence object to broadcast. The structure of this will depend on the OT type
739
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
740
+
741
+
#### `send`
742
+
743
+
```javascript
744
+
localPresence.send(callback):void;
745
+
```
746
+
747
+
Send presence like `submit`, but without updating the value. Can be useful if local presences expire periodically.
748
+
749
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
750
+
751
+
#### `destroy`
752
+
753
+
```javascript
754
+
localPresence.destroy(callback):void;
755
+
```
756
+
757
+
Informs all remote clients that this presence is now `null`, and deletes itself for garbage collection.
758
+
759
+
*`callback`_Function_: a callback with the signature `function (error: Error): void;`
760
+
643
761
### Logging
644
762
645
763
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