102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
import { DebugLogger } from '@affine/debug';
|
|
import {
|
|
applyAwarenessUpdate,
|
|
type Awareness,
|
|
encodeAwarenessUpdate,
|
|
removeAwarenessStates,
|
|
} from 'y-protocols/awareness';
|
|
|
|
import type { AwarenessProvider } from '../../engine/awareness';
|
|
import { getIoManager } from '../../utils/affine-io';
|
|
import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64';
|
|
|
|
const logger = new DebugLogger('affine:awareness:socketio');
|
|
|
|
type AwarenessChanges = Record<'added' | 'updated' | 'removed', number[]>;
|
|
|
|
export function createCloudAwarenessProvider(
|
|
workspaceId: string,
|
|
awareness: Awareness
|
|
): AwarenessProvider {
|
|
const socket = getIoManager().socket('/');
|
|
|
|
const awarenessBroadcast = ({
|
|
workspaceId: wsId,
|
|
awarenessUpdate,
|
|
}: {
|
|
workspaceId: string;
|
|
awarenessUpdate: string;
|
|
}) => {
|
|
if (wsId !== workspaceId) {
|
|
return;
|
|
}
|
|
applyAwarenessUpdate(
|
|
awareness,
|
|
base64ToUint8Array(awarenessUpdate),
|
|
'remote'
|
|
);
|
|
};
|
|
|
|
const awarenessUpdate = (changes: AwarenessChanges, origin: unknown) => {
|
|
if (origin === 'remote') {
|
|
return;
|
|
}
|
|
|
|
const changedClients = Object.values(changes).reduce((res, cur) => [
|
|
...res,
|
|
...cur,
|
|
]);
|
|
|
|
const update = encodeAwarenessUpdate(awareness, changedClients);
|
|
uint8ArrayToBase64(update)
|
|
.then(encodedUpdate => {
|
|
socket.emit('awareness-update', {
|
|
workspaceId: workspaceId,
|
|
awarenessUpdate: encodedUpdate,
|
|
});
|
|
})
|
|
.catch(err => logger.error(err));
|
|
};
|
|
|
|
const newClientAwarenessInitHandler = () => {
|
|
const awarenessUpdate = encodeAwarenessUpdate(awareness, [
|
|
awareness.clientID,
|
|
]);
|
|
uint8ArrayToBase64(awarenessUpdate)
|
|
.then(encodedAwarenessUpdate => {
|
|
socket.emit('awareness-update', {
|
|
guid: workspaceId,
|
|
awarenessUpdate: encodedAwarenessUpdate,
|
|
});
|
|
})
|
|
.catch(err => logger.error(err));
|
|
};
|
|
|
|
const windowBeforeUnloadHandler = () => {
|
|
removeAwarenessStates(awareness, [awareness.clientID], 'window unload');
|
|
};
|
|
|
|
return {
|
|
connect: () => {
|
|
socket.on('server-awareness-broadcast', awarenessBroadcast);
|
|
socket.on('new-client-awareness-init', newClientAwarenessInitHandler);
|
|
awareness.on('update', awarenessUpdate);
|
|
|
|
window.addEventListener('beforeunload', windowBeforeUnloadHandler);
|
|
|
|
socket.connect();
|
|
|
|
socket.emit('client-handshake-awareness', workspaceId);
|
|
socket.emit('awareness-init', workspaceId);
|
|
},
|
|
disconnect: () => {
|
|
awareness.off('update', awarenessUpdate);
|
|
socket.emit('client-leave-awareness', workspaceId);
|
|
socket.off('server-awareness-broadcast', awarenessBroadcast);
|
|
socket.off('new-client-awareness-init', newClientAwarenessInitHandler);
|
|
window.removeEventListener('unload', windowBeforeUnloadHandler);
|
|
socket.disconnect();
|
|
},
|
|
};
|
|
}
|