Skip to content

Commit 3e845ad

Browse files
committed
feature: support streaming document updates
1 parent 2414278 commit 3e845ad

File tree

3 files changed

+98
-32
lines changed

3 files changed

+98
-32
lines changed

src/routes/event-handlers/joinRoom.handler.ts

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,90 @@ import { ClientToServerEvents, IoClientEvent, ServerToClientEvents } from '../so
33
import type { DefaultEventsMap, Socket } from 'socket.io';
44
import prisma from '../../prisma';
55
import StudentGroup from '../../models/StudentGroup';
6+
import onStreamUpdate from './streamUpdate.handler';
7+
import DocumentRoot from '../../models/DocumentRoot';
8+
import { highestAccess, RWAccess } from '../../helpers/accessPolicy';
9+
type SocketType = Socket<ClientToServerEvents, ServerToClientEvents, DefaultEventsMap, any>;
610

7-
const onJoinRoom: (
8-
user: User,
9-
socket: Socket<ClientToServerEvents, ServerToClientEvents, DefaultEventsMap, any>
10-
) => ClientToServerEvents[IoClientEvent.JOIN_ROOM] =
11+
const isDocumentRoot = (roomId: string) => {
12+
return prisma.documentRoot.findFirst({ where: { id: roomId } });
13+
};
14+
15+
const findDocumentRoot = (user: User, roomId: string) => {
16+
return DocumentRoot.getPermissions(user, roomId).then((res) => {
17+
if (!res) {
18+
return false;
19+
} else {
20+
const access = new Set([
21+
...res.groupPermissions.map((p) => p.access),
22+
...res.userPermissions.map((p) => p.access)
23+
]);
24+
const current = highestAccess(access);
25+
return RWAccess.has(current);
26+
}
27+
});
28+
};
29+
30+
const findStudentGroup = (userId: string, roomId: string) => {
31+
return prisma.studentGroup.findFirst({
32+
where: {
33+
users: {
34+
some: {
35+
AND: [
36+
{
37+
userId: userId,
38+
isAdmin: true
39+
},
40+
{
41+
userId: roomId
42+
}
43+
]
44+
}
45+
}
46+
}
47+
});
48+
};
49+
50+
const joinRoom = (socket: SocketType, roomId: string, joinStreamGroup: boolean) => {
51+
socket.join(roomId);
52+
if (joinStreamGroup) {
53+
socket.on(IoClientEvent.STREAM_UPDATE, onStreamUpdate(roomId, socket));
54+
}
55+
};
56+
57+
const onJoinRoom: (user: User, socket: SocketType) => ClientToServerEvents[IoClientEvent.JOIN_ROOM] =
1158
(user, socket) => (roomId: string, callback: (joined: boolean) => void) => {
1259
if (user.role === Role.ADMIN) {
13-
socket.join(roomId);
14-
return callback(true);
60+
return isDocumentRoot(roomId)
61+
.then((docRoot) => {
62+
joinRoom(socket, roomId, !!docRoot);
63+
callback(true);
64+
})
65+
.catch(() => {
66+
callback(false);
67+
});
1568
}
1669
StudentGroup.findModel(user, roomId).then((group) => {
1770
if (group) {
1871
socket.join(roomId);
1972
callback(true);
2073
} else {
2174
if (user.role === Role.TEACHER) {
22-
prisma.studentGroup
23-
.findFirst({
24-
where: {
25-
users: {
26-
some: {
27-
AND: [
28-
{
29-
userId: user.id,
30-
isAdmin: true
31-
},
32-
{
33-
userId: roomId
34-
}
35-
]
36-
}
37-
}
38-
}
39-
})
40-
.then((userRoom) => {
41-
if (userRoom) {
42-
socket.join(roomId);
43-
callback(true);
44-
} else {
45-
callback(false);
46-
}
47-
});
75+
findStudentGroup(user.id, roomId).then((userRoom) => {
76+
if (userRoom) {
77+
joinRoom(socket, roomId, false);
78+
callback(true);
79+
} else {
80+
findDocumentRoot(user, roomId)
81+
.then((canJoin) => {
82+
joinRoom(socket, roomId, canJoin);
83+
callback(true);
84+
})
85+
.catch(() => {
86+
callback(false);
87+
});
88+
}
89+
});
4890
}
4991
}
5092
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ClientToServerEvents, IoClientEvent, IoEvent, ServerToClientEvents } from '../socketEventTypes';
2+
import type { DefaultEventsMap, Socket } from 'socket.io';
3+
4+
const onStreamUpdate: (
5+
roomId: string,
6+
socket: Socket<ClientToServerEvents, ServerToClientEvents, DefaultEventsMap, any>
7+
) => ClientToServerEvents[IoClientEvent.STREAM_UPDATE] = (roomId, socket) => (payload) => {
8+
if (roomId !== payload.roomId) {
9+
return;
10+
}
11+
socket.to(payload.roomId).emit(IoEvent.CHANGED_DOCUMENT, {
12+
data: payload.data,
13+
id: payload.id,
14+
updatedAt: payload.updatedAt
15+
});
16+
};
17+
18+
export default onStreamUpdate;

src/routes/socketEventTypes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export interface ChangedDocument {
5252
updatedAt: Date;
5353
}
5454

55+
export interface StreamedDynamicDocument extends ChangedDocument {
56+
roomId: string;
57+
}
58+
5559
export interface ConnectedClients {
5660
rooms: [string, number][];
5761
type: 'full' | 'update';
@@ -98,6 +102,7 @@ export type Notification =
98102
export enum IoClientEvent {
99103
JOIN_ROOM = 'JOIN_ROOM',
100104
LEAVE_ROOM = 'LEAVE_ROOM',
105+
STREAM_UPDATE = 'STREAM_UPDATE',
101106
ACTION = 'ACTION'
102107
}
103108

@@ -120,4 +125,5 @@ export interface ClientToServerEvents {
120125
[IoClientEvent.JOIN_ROOM]: (roomId: string, callback: (joined: boolean) => void) => void;
121126
[IoClientEvent.LEAVE_ROOM]: (roomId: string, callback: (left: boolean) => void) => void;
122127
[IoClientEvent.ACTION]: (action: Action, callback: (ok: boolean) => void) => void;
128+
[IoClientEvent.STREAM_UPDATE]: (payload: StreamedDynamicDocument) => void;
123129
}

0 commit comments

Comments
 (0)