Skip to content

Commit 7354b35

Browse files
linspwjanthurau
authored andcommitted
fix: hocuspocus provider auto connect and disconnect behavior (ueberdosis#663)
* fix: hocuspocus provider auto connect and disconnect behavior * docs: subdocuments * docs: add documentation for preseveConnection * fix: change name of property
1 parent eb758fd commit 7354b35

File tree

8 files changed

+110
-32
lines changed

8 files changed

+110
-32
lines changed

docs/guides/multi-subdocuments.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ const server = Server.configure({
9393
// Get a Y-Doc for the 'default' field …
9494
const defaultField = TiptapTransformer.toYdoc(
9595
generateSampleProsemirrorJson("What is love?"),
96-
"default"[(Document, Paragraph, Text)]
96+
"default",
97+
[(Document, Paragraph, Text)]
9798
);
9899
// … and merge it into the given document
99100
data.document.merge(defaultField);
@@ -102,7 +103,8 @@ const server = Server.configure({
102103
// Get a Y-Doc for the 'secondary' field …
103104
const secondaryField = TiptapTransformer.toYdoc(
104105
generateSampleProsemirrorJson("Baby don't hurt me…"),
105-
"secondary"[(Document, Paragraph, Text)]
106+
"secondary",
107+
[(Document, Paragraph, Text)]
106108
);
107109
// … and merge it into the given document
108110
data.document.merge(secondaryField);

docs/provider/configuration.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ tableOfContents: true
1414
| `document` | The actual Y.js document. Optional, by default a new document is created and be access through provider.document. | `new Y.Doc()` |
1515
| `token` | An authentication token that will be passed to the server (works with strings, functions and Promises). | `''` |
1616
| `awareness` | Awareness object, by default attached to the passed Y.js document. | `new Awareness()` |
17-
| `connect` | Whether to connect to the server after intialization. | `true` |
17+
| `connect` | Whether to connect to the server after initialization. | `true` |
18+
| `preserveConnection` | Whether to preserve the websocket connection after closing the provider. | `true` |
1819
| `broadcast` | By default changes are synced between browser tabs through broadcasting. | `true` |
1920
| `forceSyncInterval` | Ask the server every x ms for updates. | `false` |
2021
| `delay` | The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially. | `1000` |

packages/provider/src/HocuspocusProvider.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ export type HocuspocusProviderConfiguration =
4343
)
4444

4545
export interface CompleteHocuspocusProviderConfiguration {
46-
/**
47-
* The identifier/name of your document
48-
*/
46+
/**
47+
* The identifier/name of your document
48+
*/
4949
name: string,
5050
/**
5151
* The actual Y.js document
@@ -96,6 +96,16 @@ export interface CompleteHocuspocusProviderConfiguration {
9696
* Don’t output any warnings.
9797
*/
9898
quiet: boolean,
99+
100+
/**
101+
* Pass `false` to start the connection manually.
102+
*/
103+
connect: boolean,
104+
105+
/**
106+
* Pass `false` to close the connection manually.
107+
*/
108+
preserveConnection: boolean,
99109
}
100110

101111
export class HocuspocusProvider extends EventEmitter {
@@ -124,6 +134,8 @@ export class HocuspocusProvider extends EventEmitter {
124134
onAwarenessChange: () => null,
125135
onStateless: () => null,
126136
quiet: false,
137+
connect: true,
138+
preserveConnection: true,
127139
}
128140

129141
subscribedToBroadcastChannel = false
@@ -242,6 +254,7 @@ export class HocuspocusProvider extends EventEmitter {
242254

243255
this.configuration.websocketProvider = new HocuspocusProviderWebsocket({
244256
url: websocketProviderConfig.url,
257+
connect: websocketProviderConfig.connect,
245258
parameters: websocketProviderConfig.parameters,
246259
})
247260
}
@@ -349,6 +362,9 @@ export class HocuspocusProvider extends EventEmitter {
349362
disconnect() {
350363
this.disconnectBroadcastChannel()
351364
this.configuration.websocketProvider.detach(this)
365+
if (!this.configuration.preserveConnection) {
366+
this.configuration.websocketProvider.disconnect()
367+
}
352368
}
353369

354370
async onOpen(event: Event) {

packages/provider/src/HocuspocusProviderWebsocket.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
211211
}
212212

213213
attach(provider: HocuspocusProvider) {
214-
if (this.status === WebSocketStatus.Disconnected) {
214+
if (this.status === WebSocketStatus.Disconnected && this.shouldConnect) {
215215
this.connect()
216216
}
217217

playground/frontend/src/App.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,26 @@
6262
</div>
6363
</div>
6464
</template>
65+
66+
<style>
67+
.special-button {
68+
border-radius: 8px;
69+
background-color: #155EFA;
70+
color: #FFFFFF;
71+
padding: 8px 16px;
72+
}
73+
74+
pre {
75+
background: #0d0d0d;
76+
color: #fff;
77+
padding: 0.75rem 1rem;
78+
border-radius: 0.5rem;
79+
}
80+
81+
code {
82+
color: inherit;
83+
padding: 0;
84+
background: none;
85+
font-size: 0.8rem;
86+
}
87+
</style>

playground/frontend/src/pages/Awareness.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
:socket="provider.configuration.websocketProvider"
1111
/>
1212

13-
<h2>
13+
<h2 class="mb-2">
1414
Users
1515
</h2>
1616

@@ -19,7 +19,7 @@
1919
v-for="state in states"
2020
:key="state.clientId"
2121
>
22-
<span :style="`background-color: ${state.user.color}; width: 1rem; height: 1rem; margin-right: 0.5rem; display: inline-block;`" />
22+
<span :style="`background-color: ${state.user?.color}; width: 1rem; height: 1rem; margin-right: 0.5rem; display: inline-block;`" />
2323
#{{ state.clientId }} {{ state.user.name }} ({{ state.user.clientX }}, {{ state.user.clientY }})
2424
</li>
2525
</ul>
@@ -29,9 +29,9 @@
2929
:key="state.clientId"
3030
:style="`
3131
position: absolute;
32-
background-color: ${state.user.color};
33-
top: ${state.user.clientY}px;
34-
left: ${state.user.clientX}px;
32+
background-color: ${state.user?.color};
33+
top: ${state.user?.clientY}px;
34+
left: ${state.user?.clientX}px;
3535
width: 12px;
3636
height: 12px;
3737
margin-left: -6px;

playground/frontend/src/pages/Index.vue

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
:socket="socket"
1111
/>
1212

13-
<h2>
13+
<h2 class="mb-2">
1414
Editor
1515
</h2>
1616
<div v-if="editor">
@@ -20,15 +20,22 @@
2020
/>
2121
</div>
2222

23-
<button @click="provider.setAwarenessField('date', Date.now())">provider.setAwarenessField('date', Date.now())</button>
23+
<button
24+
class="special-button"
25+
@click="provider.setAwarenessField('date', Date.now())"
26+
>
27+
Update Date
28+
</button>
29+
30+
<pre><code>{{ $states1 }}</code></pre>
2431

2532
<StatusBar
2633
v-if="anotherProvider"
2734
:provider="anotherProvider"
2835
:socket="socket"
2936
/>
3037

31-
<h2>
38+
<h2 class="mb-2">
3239
Another Editor
3340
</h2>
3441
<div v-if="anotherEditor">
@@ -38,11 +45,19 @@
3845
/>
3946
</div>
4047

41-
<button @click="anotherProvider.setAwarenessField('date', Date.now())">anotherProvider.setAwarenessField('date', Date.now())</button>
48+
<button
49+
class="special-button"
50+
@click="anotherProvider.setAwarenessField('date', Date.now())"
51+
>
52+
Update Date
53+
</button>
54+
55+
<pre><code>{{ $states2 }}</code></pre>
4256
</div>
4357
</template>
4458

4559
<script setup lang="ts">
60+
import { ref } from 'vue'
4661
import { Editor, EditorContent } from '@tiptap/vue-3'
4762
import StarterKit from '@tiptap/starter-kit'
4863
import Collaboration from '@tiptap/extension-collaboration'
@@ -56,16 +71,25 @@ const socket = new HocuspocusProviderWebsocket({
5671
url: 'ws://127.0.0.1:1234',
5772
})
5873
74+
const $states1 = ref({})
75+
const $states2 = ref({})
76+
5977
const provider = new HocuspocusProvider({
6078
websocketProvider: socket,
6179
name: 'hocuspocus-demo',
6280
broadcast: false,
81+
onAwarenessUpdate: ({ states }) => {
82+
$states1.value = states
83+
},
6384
})
6485
6586
const anotherProvider = new HocuspocusProvider({
6687
websocketProvider: socket,
6788
name: 'hocuspocus-demo2',
6889
broadcast: false,
90+
onAwarenessUpdate: ({ states }) => {
91+
$states2.value = states
92+
},
6993
})
7094
7195
const editor = new Editor({
@@ -91,9 +115,10 @@ const anotherEditor = new Editor({
91115
}),
92116
],
93117
})
118+
94119
</script>
95120

96-
<style>
121+
<style >
97122
.ProseMirror {
98123
border: 1px solid grey;
99124
padding: 1rem;

playground/frontend/src/pages/Rooms.vue

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
<td class="p-3 border border-gray-300">{{ room.states }}</td>
1616
<td class="p-3 border border-gray-300 text-center">
1717
<button
18-
v-if="room.status !== 'connected'"
18+
v-if="room.status === 'disconnected'"
1919
@click="room.connect()"
2020
class="border-2 border-black bg-black text-white px-4 py-2 rounded"
2121
>
2222
connect
2323
</button>
24+
2425
<button
25-
v-if="room.status !== 'disconnected'"
26+
v-else-if="room.status === 'connected'"
2627
@click="room.disconnect()"
2728
class="border-2 border-black px-4 py-2 rounded"
2829
>
@@ -34,43 +35,53 @@
3435
</div>
3536
</template>
3637

37-
<script>
38+
<script lang="ts">
3839
import * as Y from 'yjs'
39-
// import { WebsocketProvider } from 'y-websocket'
40-
import { HocuspocusProvider } from '@hocuspocus/provider'
40+
import { ref } from 'vue'
41+
import { HocuspocusProvider, StatesArray } from '@hocuspocus/provider'
42+
// eslint-disable-next-line import/no-extraneous-dependencies
43+
import { awarenessStatesToArray } from '@hocuspocus/common'
4144
4245
class Room {
4346
doc = new Y.Doc()
4447
4548
name = ''
4649
47-
status = 'disconnected'
50+
status = ref('disconnected')
51+
52+
numberOfUsers = ref(0)
4853
49-
states = []
54+
states: StatesArray = []
5055
51-
constructor(name) {
56+
provider: HocuspocusProvider
57+
58+
constructor(name: string) {
5259
this.name = name
5360
// this.provider = new WebsocketProvider('ws://localhost:1234', this.name, this.doc)
5461
this.provider = new HocuspocusProvider({
5562
url: 'ws://localhost:1234',
5663
document: this.doc,
5764
name: this.name,
65+
broadcast: false,
66+
connect: false,
67+
preserveConnection: false,
5868
onStatus: ({ status }) => {
59-
this.status = status
69+
this.status.value = status
6070
},
6171
onAwarenessUpdate: ({ states }) => {
6272
this.states = states
73+
this.numberOfUsers.value = awarenessStatesToArray(this.provider.awareness.getStates()).filter((state => 'user' in state)).length
74+
},
75+
onDisconnect: () => {
76+
this.states = []
77+
this.numberOfUsers.value = 0
6378
},
6479
})
6580
66-
this.provider.setAwarenessField('user', { name: `Jon @ ${this.name}` })
67-
}
68-
69-
get numberOfUsers() {
70-
return this.provider.awareness.getStates().size
7181
}
7282
7383
connect() {
84+
this.provider.setAwarenessField('user', { name: `Jon @ ${this.name}` })
7485
this.provider.connect()
7586
}
7687
@@ -86,7 +97,7 @@ class Room {
8697
export default {
8798
data() {
8899
return {
89-
rooms: [],
100+
rooms: [] as Room[],
90101
}
91102
},
92103

0 commit comments

Comments
 (0)