Skip to content

Commit 582cf74

Browse files
committed
withReadOnly
1 parent 1e9b076 commit 582cf74

File tree

5 files changed

+95
-36
lines changed

5 files changed

+95
-36
lines changed

src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import { useBroadcastStore } from '@/stores';
66
import { useProviderStore } from '../stores/useProviderStore';
77
import { Base64 } from '../types';
88

9-
export const useCollaboration = (room?: string, initialContent?: Base64) => {
9+
export const useCollaboration = (
10+
room?: string,
11+
initialContent?: Base64,
12+
canEdit?: boolean,
13+
) => {
1014
const collaborationUrl = useCollaborationUrl(room);
1115
const { setBroadcastProvider } = useBroadcastStore();
1216
const { provider, createProvider, destroyProvider } = useProviderStore();
@@ -15,11 +19,16 @@ export const useCollaboration = (room?: string, initialContent?: Base64) => {
1519
* Initialize the provider
1620
*/
1721
useEffect(() => {
18-
if (!room || !collaborationUrl || provider) {
22+
if (!room || !collaborationUrl || provider || canEdit === undefined) {
1923
return;
2024
}
2125

22-
const newProvider = createProvider(collaborationUrl, room, initialContent);
26+
const newProvider = createProvider(
27+
collaborationUrl,
28+
room,
29+
canEdit,
30+
initialContent,
31+
);
2332
setBroadcastProvider(newProvider);
2433
}, [
2534
provider,
@@ -28,11 +37,13 @@ export const useCollaboration = (room?: string, initialContent?: Base64) => {
2837
initialContent,
2938
createProvider,
3039
setBroadcastProvider,
40+
canEdit,
3141
]);
3242

3343
useEffect(() => {
3444
return () => {
3545
if (room) {
46+
console.log('Destroy Provider');
3647
destroyProvider();
3748
}
3849
};

src/frontend/apps/impress/src/features/docs/doc-management/libs/CollaborationProvider.ts

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export const isHocuspocusProviderConfigurationUrl = (
2727
return 'url' in data;
2828
};
2929

30+
type CollaborationProviderConfiguration = HocuspocusProviderConfiguration & {
31+
canEdit: boolean;
32+
};
33+
3034
export class CollaborationProvider extends HocuspocusProvider {
3135
private websocketFailureCount = 0;
3236
private websocketMaxFailureCount = 2;
@@ -37,7 +41,7 @@ export class CollaborationProvider extends HocuspocusProvider {
3741
// Server-Sent Events
3842
private sse: EventSource | null = null;
3943

40-
public constructor(configuration: HocuspocusProviderConfiguration) {
44+
public constructor(configuration: CollaborationProviderConfiguration) {
4145
const withWS = isFirefox();
4246

4347
let url = '';
@@ -50,7 +54,10 @@ export class CollaborationProvider extends HocuspocusProvider {
5054

5155
this.url = url;
5256

53-
this.on('outgoingMessage', this.onPollOutgoingMessage.bind(this));
57+
if (configuration.canEdit) {
58+
this.on('outgoingMessage', this.onPollOutgoingMessage.bind(this));
59+
}
60+
5461
this.configuration.websocketProvider.on(
5562
'connect',
5663
this.onWebsocketConnect.bind(this),
@@ -92,7 +99,6 @@ export class CollaborationProvider extends HocuspocusProvider {
9299
this.isLongPollingStarted = true;
93100
void this.pollSync();
94101
this.initCollaborationSSE();
95-
//void this.longPollAwareness();
96102
}
97103
}
98104
}
@@ -116,14 +122,18 @@ export class CollaborationProvider extends HocuspocusProvider {
116122

117123
//console.log('outgoingMessage', message.description);
118124

119-
const { updated } = await pollOutgoingMessageRequest({
120-
pollUrl: this.toPollUrl('message'),
121-
message64: Buffer.from(message.toUint8Array()).toString('base64'),
122-
});
125+
try {
126+
const { updated } = await pollOutgoingMessageRequest({
127+
pollUrl: this.toPollUrl('message'),
128+
message64: Buffer.from(message.toUint8Array()).toString('base64'),
129+
});
123130

124-
if (!updated) {
125-
console.error('Message not updated');
126-
await this.pollSync();
131+
if (!updated) {
132+
console.error('Message not updated');
133+
await this.pollSync();
134+
}
135+
} catch (error: unknown) {
136+
console.error('Polling message failed:', error);
127137
}
128138
}
129139

@@ -134,52 +144,60 @@ export class CollaborationProvider extends HocuspocusProvider {
134144

135145
console.log('initCollaborationSSE');
136146

137-
const eventSource = new EventSource(this.toPollUrl('message'), {
147+
this.sse = new EventSource(this.toPollUrl('message'), {
138148
withCredentials: true,
139149
});
140150

151+
//eventSource.close();
152+
141153
// 1. onmessage handles messages sent with `data:` lines
142-
eventSource.onmessage = async (event) => {
143-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument
154+
this.sse.onmessage = (event) => {
144155
const { updatedDoc64, stateFingerprint, awareness64 } = JSON.parse(
156+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
145157
event.data,
146158
) as {
147159
updatedDoc64: string;
148160
stateFingerprint: string;
149161
awareness64: string;
150162
};
151163
console.log('Received SSE event:', event.data);
164+
console.log('CLineId:', this.document.clientID);
165+
166+
const localStateFingerprint = this.getStateFingerprint(this.document);
167+
console.log('EQUAL BEF', localStateFingerprint === stateFingerprint);
152168

153169
if (awareness64) {
154170
const awareness = Buffer.from(awareness64, 'base64');
155171

156172
this.onMessage({
157173
data: awareness,
158174
} as MessageEvent);
175+
176+
console.log('EQUAL AWA', localStateFingerprint === stateFingerprint);
177+
// if (localStateFingerprint !== stateFingerprint) {
178+
// await this.pollSync();
179+
// }
159180
}
160181

161182
if (updatedDoc64) {
162183
const uint8Array = Buffer.from(updatedDoc64, 'base64');
163184
Y.applyUpdate(this.document, uint8Array);
164185

165-
const localStateFingerprint = this.getStateFingerprint(this.document);
166-
console.log('stateFingerprint', stateFingerprint);
167-
console.log('localStateFingerprint', localStateFingerprint);
168186
console.log('EQUAL', localStateFingerprint === stateFingerprint);
169187

170-
if (localStateFingerprint !== stateFingerprint) {
171-
await this.pollSync();
172-
}
188+
// if (localStateFingerprint !== stateFingerprint) {
189+
// await this.pollSync();
190+
// }
173191
}
174192
};
175193

176194
// 2. onopen is triggered when the connection is first established
177-
eventSource.onopen = () => {
195+
this.sse.onopen = () => {
178196
console.log('SSE connection opened.');
179197
};
180198

181199
// 3. onerror is triggered if there's a connection issue
182-
eventSource.onerror = (err) => {
200+
this.sse.onerror = (err) => {
183201
console.error('SSE error:', err);
184202
// Depending on the error, the browser may or may not automatically reconnect
185203
};
@@ -190,9 +208,9 @@ export class CollaborationProvider extends HocuspocusProvider {
190208
public onMessage(event: MessageEvent) {
191209
super.onMessage(event);
192210

193-
console.log('onMessage', event);
194-
console.log('isSynced', this.isSynced);
195-
console.log('unsyncedChanges', this.unsyncedChanges);
211+
// console.log('onMessage', event);
212+
// console.log('isSynced', this.isSynced);
213+
// console.log('unsyncedChanges', this.unsyncedChanges);
196214

197215
// if (this.hasUnsyncedChanges) {
198216
// this.unsyncedChanges = 0;
@@ -216,7 +234,6 @@ export class CollaborationProvider extends HocuspocusProvider {
216234
if (syncDoc64) {
217235
const uint8Array = Buffer.from(syncDoc64, 'base64');
218236
Y.applyUpdate(this.document, uint8Array);
219-
this.synced = true;
220237
}
221238
} catch (error) {
222239
console.error('Polling sync failed:', error);

src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface UseCollaborationStore {
1010
createProvider: (
1111
providerUrl: string,
1212
storeId: string,
13+
canEdit: boolean,
1314
initialDoc?: Base64,
1415
) => HocuspocusProvider;
1516
destroyProvider: () => void;
@@ -22,7 +23,7 @@ const defaultValues = {
2223

2324
export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
2425
...defaultValues,
25-
createProvider: (wsUrl, storeId, initialDoc) => {
26+
createProvider: (wsUrl, storeId, canEdit, initialDoc) => {
2627
const doc = new Y.Doc({
2728
guid: storeId,
2829
});
@@ -35,6 +36,7 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
3536
url: wsUrl,
3637
name: storeId,
3738
document: doc,
39+
canEdit,
3840
});
3941

4042
set({

src/frontend/apps/impress/src/pages/docs/[id]/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const DocPage = ({ id }: DocProps) => {
6464
const { addTask } = useBroadcastStore();
6565
const queryClient = useQueryClient();
6666
const { replace } = useRouter();
67-
useCollaboration(doc?.id, doc?.content);
67+
useCollaboration(doc?.id, doc?.content, doc?.abilities.partial_update);
6868

6969
useEffect(() => {
7070
if (doc?.title) {

src/frontend/servers/y-provider/src/libs/PollSync.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ import crypto from 'crypto';
33

44
import {
55
AwarenessUpdate,
6+
Debugger,
67
Document,
78
Hocuspocus,
9+
IncomingMessage,
10+
MessageReceiver,
11+
MessageType,
812
OutgoingMessage,
913
} from '@hocuspocus/server';
1014
import { Request, Response } from 'express';
1115
import { v4 as uuid } from 'uuid';
16+
import { readSyncMessage, readUpdate } from 'y-protocols/sync.js';
1217
import * as Y from 'yjs';
1318

1419
import { base64ToYDoc, logger, toBase64 } from '@/utils';
@@ -109,26 +114,47 @@ export class PollSync<T> {
109114
* Send messages to other clients
110115
*/
111116
public sendMessages(message64: string) {
112-
console.log('Polling Updated 1234');
113117
const hpDoc = this.getHpDocument();
118+
const messageBuffer = Buffer.from(message64, 'base64');
119+
const message = new IncomingMessage(messageBuffer);
120+
const room = message.readVarString();
114121

115-
hpDoc.getConnections().forEach((connection) => {
116-
connection.handleMessage(Buffer.from(message64, 'base64'));
117-
});
122+
logger('Polling sendMessages 1010', hpDoc.name);
123+
124+
if (hpDoc.name !== room) {
125+
logger('Send messages problem, room different', room, hpDoc.name);
126+
return;
127+
}
128+
129+
// We write the sync to the current doc - it will propagate to others by itself
130+
const type = message.readVarUint() as MessageType;
131+
if (type === MessageType.Sync) {
132+
message.writeVarUint(MessageType.Sync);
133+
readSyncMessage(message.decoder, message.encoder, hpDoc, null);
134+
} else {
135+
hpDoc.getConnections().forEach((connection) => {
136+
connection.handleMessage(messageBuffer);
137+
});
138+
}
139+
140+
// hpDoc.getConnections().forEach((connection) => {
141+
// connection.handleMessage(messageBuffer);
142+
// });
118143

119144
logger('Polling Updated YDoc', hpDoc.name);
120145
}
121146

122147
public listenMessages(res: Response) {
123148
const hpDoc = this.getHpDocument();
149+
hpDoc.addDirectConnection();
124150

125151
const updateMessagesFn = (
126152
update: Uint8Array,
127153
_origin: string,
128154
updatedDoc: Y.Doc,
129155
_transaction: Y.Transaction,
130156
) => {
131-
console.log('Doc Update V2');
157+
console.log('Doc Update V2', _transaction.doc.clientID);
132158

133159
res.write(
134160
`data: ${JSON.stringify({
@@ -166,12 +192,14 @@ export class PollSync<T> {
166192
this.room,
167193
).createAwarenessUpdateMessage(hpDoc.awareness, changedClients);
168194

169-
console.log('Awareness Update', awarenessMessage);
195+
//console.log('Awareness Update', awarenessMessage);
170196

197+
console.log('Awareness Update V2');
171198
res.write(
172199
`data: ${JSON.stringify({
173200
time: new Date(),
174201
awareness64: toBase64(awarenessMessage.toUint8Array()),
202+
stateFingerprint: this.getStateFingerprint(hpDoc),
175203
})}\n\n`,
176204
);
177205
};
@@ -188,6 +216,7 @@ export class PollSync<T> {
188216
hpDoc.off('update', updateMessagesFn);
189217
hpDoc.off('destroy', destroyFn);
190218
hpDoc.awareness.off('update', updateAwarenessFn);
219+
hpDoc.removeDirectConnection();
191220
});
192221
}
193222

0 commit comments

Comments
 (0)