mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 10:31:50 +08:00
refactor: new provider (#4900)
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import {
|
||||
applyAwarenessUpdate,
|
||||
type Awareness,
|
||||
encodeAwarenessUpdate,
|
||||
removeAwarenessStates,
|
||||
} from 'y-protocols/awareness';
|
||||
|
||||
import { getIoManager } from '../../utils/affine-io';
|
||||
import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64';
|
||||
import type { AwarenessProvider } from '..';
|
||||
|
||||
const logger = new DebugLogger('affine:awareness:socketio');
|
||||
|
||||
export type AwarenessChanges = Record<
|
||||
'added' | 'updated' | 'removed',
|
||||
number[]
|
||||
>;
|
||||
|
||||
export function createAffineAwarenessProvider(
|
||||
workspaceId: string,
|
||||
awareness: Awareness
|
||||
): AwarenessProvider {
|
||||
const socket = getIoManager().socket('/');
|
||||
|
||||
const awarenessBroadcast = ({
|
||||
workspaceId,
|
||||
awarenessUpdate,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
awarenessUpdate: string;
|
||||
}) => {
|
||||
if (workspaceId !== 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.emit('awareness-init', workspaceId);
|
||||
socket.connect();
|
||||
},
|
||||
disconnect: () => {
|
||||
awareness.off('update', awarenessUpdate);
|
||||
socket.off('server-awareness-broadcast', awarenessBroadcast);
|
||||
socket.off('new-client-awareness-init', newClientAwarenessInitHandler);
|
||||
window.removeEventListener('unload', windowBeforeUnloadHandler);
|
||||
socket.disconnect();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { Awareness } from 'y-protocols/awareness.js';
|
||||
import {
|
||||
applyAwarenessUpdate,
|
||||
encodeAwarenessUpdate,
|
||||
} from 'y-protocols/awareness.js';
|
||||
|
||||
import type { AwarenessProvider } from '..';
|
||||
import type { AwarenessChanges } from '../affine';
|
||||
|
||||
type ChannelMessage =
|
||||
| { type: 'connect' }
|
||||
| { type: 'update'; update: Uint8Array };
|
||||
|
||||
export function createBroadcastChannelAwarenessProvider(
|
||||
workspaceId: string,
|
||||
awareness: Awareness
|
||||
): AwarenessProvider {
|
||||
const channel = new BroadcastChannel('awareness:' + workspaceId);
|
||||
|
||||
function handleAwarenessUpdate(changes: AwarenessChanges, origin: unknown) {
|
||||
if (origin === 'remote') {
|
||||
return;
|
||||
}
|
||||
|
||||
const changedClients = Object.values(changes).reduce((res, cur) => [
|
||||
...res,
|
||||
...cur,
|
||||
]);
|
||||
|
||||
const update = encodeAwarenessUpdate(awareness, changedClients);
|
||||
channel.postMessage({
|
||||
type: 'update',
|
||||
update: update,
|
||||
} satisfies ChannelMessage);
|
||||
}
|
||||
|
||||
function handleChannelMessage(event: MessageEvent<ChannelMessage>) {
|
||||
if (event.data.type === 'update') {
|
||||
const update = event.data.update;
|
||||
applyAwarenessUpdate(awareness, update, 'remote');
|
||||
}
|
||||
if (event.data.type === 'connect') {
|
||||
channel.postMessage({
|
||||
type: 'update',
|
||||
update: encodeAwarenessUpdate(awareness, [awareness.clientID]),
|
||||
} satisfies ChannelMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect() {
|
||||
channel.postMessage({
|
||||
type: 'connect',
|
||||
} satisfies ChannelMessage);
|
||||
awareness.on('update', handleAwarenessUpdate);
|
||||
channel.addEventListener('message', handleChannelMessage);
|
||||
},
|
||||
disconnect() {
|
||||
awareness.off('update', handleAwarenessUpdate);
|
||||
channel.removeEventListener('message', handleChannelMessage);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface AwarenessProvider {
|
||||
connect(): void;
|
||||
disconnect(): void;
|
||||
}
|
||||
|
||||
export * from './affine';
|
||||
export * from './broadcast-channel';
|
||||
Reference in New Issue
Block a user