Skip to content

Commit 941aac5

Browse files
committed
fix: collab
1 parent 36b6177 commit 941aac5

File tree

10 files changed

+72
-20
lines changed

10 files changed

+72
-20
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"react": "18.3.1",
5959
"react-dom": "18.3.1",
6060
"ssf": "https://cdn.sheetjs.com/ssf-0.11.3/ssf-0.11.3.tgz",
61+
"y-indexeddb": "^9.0.12",
6162
"yjs": "13.6.20"
6263
},
6364
"engines": {

rsbuild.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { pluginReact } from '@rsbuild/plugin-react';
33

44
export default defineConfig({
55
plugins: [pluginReact()],
6+
dev: {
7+
hmr: true,
8+
},
69
source: {
710
entry: {
811
index: './src/index.tsx',
@@ -17,6 +20,10 @@ export default defineConfig({
1720
},
1821
},
1922
output: {
23+
sourceMap: {
24+
js: 'source-map',
25+
css: true,
26+
},
2027
assetPrefix: process.env.CI ? '/excel/' : '',
2128
distPath: {
2229
js: '',

src/collaboration/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
import type { Doc } from 'yjs';
22
import { initProvider } from './provider';
33
import * as Y from 'yjs';
4+
import { collaborationLog } from '@/util';
5+
6+
function areUint8ArraysEqual(arr1: Uint8Array, arr2: Uint8Array) {
7+
if (arr1.length !== arr2.length) {
8+
return false;
9+
}
10+
for (let i = 0; i < arr1.length; i++) {
11+
if (arr1[i] !== arr2[i]) {
12+
return false;
13+
}
14+
}
15+
return true;
16+
}
417

518
export async function initCollaboration(doc: Doc) {
619
const provider = initProvider(doc);
720
doc.on('update', (update: Uint8Array) => {
8-
provider.addHistory(doc.guid, update);
21+
const stateVector = Y.encodeStateVector(doc);
22+
const diff = Y.encodeStateAsUpdate(doc, stateVector);
23+
console.log(`areUint8ArraysEqual: ${areUint8ArraysEqual(diff, update)}`);
24+
collaborationLog('doc update', update);
25+
26+
provider.addHistory(doc.guid, diff);
927
});
1028
const result = await provider.retrieveHistory(doc.guid);
1129
if (result.length > 0) {
Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
11
import { CollaborationProvider, HistoryItem } from '@/types';
22
import * as Y from 'yjs';
3-
// import { eventEmitter } from '@/util';
3+
import { collaborationLog, generateUUID } from '@/util';
4+
// import { IndexeddbPersistence } from 'y-indexeddb';
45

56
export class LocalProvider implements CollaborationProvider {
67
private readonly broadcastChannel: BroadcastChannel;
8+
// private readonly db: IndexeddbPersistence;
9+
private readonly peerId = generateUUID();
710
constructor(doc: Y.Doc) {
8-
const docId = doc.guid;
9-
this.broadcastChannel = new BroadcastChannel(docId);
11+
this.broadcastChannel = new BroadcastChannel(doc.guid);
1012
this.broadcastChannel.onmessage = (
11-
event: MessageEvent<{ docId: string; update: Uint8Array }>,
13+
event: MessageEvent<{
14+
docId: string;
15+
update: Uint8Array;
16+
peerId: string;
17+
}>,
1218
) => {
13-
const { update } = event.data;
14-
console.log(docId, event);
15-
Y.applyUpdate(doc, update);
19+
const { update, peerId } = event.data;
20+
collaborationLog('onmessage', event, this.peerId);
21+
if (peerId === this.peerId) {
22+
return;
23+
}
24+
Y.applyUpdate(doc, update, peerId);
1625
// eventEmitter.emit('modelChange', { changeSet: new Set(['rangeMap']) });
1726
};
27+
// this.db = new IndexeddbPersistence('excel', doc);
1828
}
1929
addHistory = async (docId: string, update: Uint8Array) => {
20-
this.broadcastChannel.postMessage({ docId, update });
30+
this.broadcastChannel.postMessage({ docId, update, peerId: this.peerId });
2131
};
22-
retrieveHistory = async (docId: string): Promise<HistoryItem[]> => {
23-
console.log(docId);
32+
retrieveHistory = async (_docId: string): Promise<HistoryItem[]> => {
33+
// await this.db.whenSynced;
34+
// console.log('load db success')
2435
return [];
2536
};
2637
}

src/containers/canvas/util.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ const handleStateChange = (
209209
canRedo: controller.canRedo(),
210210
canUndo: controller.canUndo(),
211211
};
212-
213212
if (changeSet.has('currentSheetId')) {
214213
core.activeUuid = '';
215214
core.currentSheetId = controller.getCurrentSheetId();

src/containers/hooks/useClickOutSide.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ export function useClickOutside(
77
const ref = useRef<HTMLDivElement>(null);
88

99
function handleEvent(event: Event) {
10-
const node = event.target! as Node;
11-
if (ref?.current && (ref.current === node || ref.current.contains(node))) {
10+
const node = event.target as any;
11+
if (
12+
ref?.current &&
13+
(ref.current === node ||
14+
(typeof ref.current.contains === 'function' &&
15+
ref.current.contains(node)))
16+
) {
1217
return;
1318
}
1419
handler();

src/controller/Controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,12 @@ export class Controller implements IController {
257257
}
258258
undo() {
259259
this.model.undo();
260+
this.changeSet.add('undo')
260261
this.emitChange();
261262
}
262263
redo() {
263264
this.model.redo();
265+
this.changeSet.add('redo')
264266
this.emitChange();
265267
}
266268
getColWidth(col: number, sheetId?: string) {

src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const controller = initController(
6363
);
6464
window.controller = controller;
6565
window.doc = doc;
66+
6667
// controller.fromJSON(MOCK_MODEL);
6768
initCollaboration(doc).then(() => {
6869
createRoot(document.getElementById('root')!).render(

src/model/Model.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export class Model implements IModel {
6868
const root = doc.getMap('excel');
6969
this.doc = doc;
7070
root.observeDeep((event) => {
71-
console.log(event)
71+
modelLog('observeDeep', event);
7272
const changeSet = modelToChangeSet(event);
7373
for (const item of this.changeSet.keys()) {
7474
changeSet.add(item);
@@ -81,6 +81,10 @@ export class Model implements IModel {
8181
});
8282
this.root = root as ModelRoot;
8383
this.undoManager = new Y.UndoManager(this.root, { doc });
84+
this.undoManager.on('stack-item-popped', (event) => {
85+
modelLog('stack-item-popped', event);
86+
this.changeSet.add(event.type);
87+
});
8488
this.workbookManager = new Workbook(this);
8589
this.rangeMapManager = new RangeMap(this);
8690
this.drawingsManager = new Drawing(this);
@@ -92,9 +96,6 @@ export class Model implements IModel {
9296
this.filterManager = new FilterManger(this);
9397
}
9498
transaction = (fn: () => boolean, _isNoHistory?: boolean) => {
95-
// if (isNoHistory) {
96-
// this.stopCapturing();
97-
// }
9899
this.doc.transact(fn);
99100
};
100101
getRoot() {
@@ -114,7 +115,12 @@ export class Model implements IModel {
114115
this.undoManager.stopCapturing();
115116
}
116117
async emitChange(changeSet: Set<ChangeEventType>) {
117-
const localChangeList: ChangeEventType[] = ['scroll', 'antLine'];
118+
const localChangeList: ChangeEventType[] = [
119+
'scroll',
120+
'antLine',
121+
'undo',
122+
'redo',
123+
];
118124
for (const item of localChangeList) {
119125
if (changeSet.has(item)) {
120126
this.changeSet.add(item);

src/types/model.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ export type ChangeEventType =
123123
| 'cellValue'
124124
| 'cellStyle'
125125
| 'antLine'
126-
| 'scroll';
126+
| 'scroll'
127+
| 'redo'
128+
| 'undo';
127129
export interface EventType {
128130
changeSet: Set<ChangeEventType>;
129131
}

0 commit comments

Comments
 (0)