Skip to content

Commit 43bbd50

Browse files
authored
feat: Polls using component. (#2916)
* squash: Moves to use component. * squash: Uses json-message format with types. * squash: Fixes comments and moves validate polls to backend. * squash: Drops roomJid from messages.
1 parent 13aeca6 commit 43bbd50

File tree

8 files changed

+171
-2
lines changed

8 files changed

+171
-2
lines changed

JitsiConference.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import type { ChatRoom, PresenceHandler } from './modules/xmpp/ChatRoom';
5454
import FileSharing from './modules/xmpp/FileSharing';
5555
import type JingleSessionPC from './modules/xmpp/JingleSessionPC';
5656
import { MediaSessionEvents } from './modules/xmpp/MediaSessionEvents';
57+
import Polls from './modules/xmpp/Polls';
5758
import RoomMetadata from './modules/xmpp/RoomMetadata';
5859
import SignalingLayerImpl from './modules/xmpp/SignalingLayerImpl';
5960
import XMPP, {
@@ -4634,6 +4635,15 @@ export default class JitsiConference extends Listenable {
46344635
return this.room?.getMetadataHandler();
46354636
}
46364637

4638+
/**
4639+
* Returns the polls object.
4640+
*
4641+
* @returns {Optional<Polls>} the polls.
4642+
*/
4643+
public getPolls(): Optional<Polls> {
4644+
return this.room?.getPolls();
4645+
}
4646+
46374647
/**
46384648
* Requests short-term credentials from the backend if available.
46394649
* @param {string} service - The service for which to request the credentials.

JitsiConferenceEventManager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,11 @@ export default class JitsiConferenceEventManager {
391391
participantId, txt, ts, displayName, isVisitor, messageId, source, replyToId);
392392
});
393393

394+
this.chatRoomForwarder.forward(XMPPEvents.POLLS_RECEIVE_EVENT,
395+
JitsiConferenceEvents.POLL_RECEIVED);
396+
this.chatRoomForwarder.forward(XMPPEvents.POLLS_ANSWER_EVENT,
397+
JitsiConferenceEvents.POLL_ANSWER_RECEIVED);
398+
394399
chatRoom.addListener(
395400
XMPPEvents.REACTION_RECEIVED,
396401

JitsiConferenceEvents.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ export enum JitsiConferenceEvents {
338338
*/
339339
PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged',
340340

341+
/**
342+
* An answer for a pool was received.
343+
*/
344+
POLL_ANSWER_RECEIVED = 'conference.pollAnswerReceived',
345+
346+
/**
347+
* A new poll was received or a poll is loaded from the history.
348+
*/
349+
POLL_RECEIVED = 'conference.pollReceived',
350+
341351
/**
342352
* New private text message was received.
343353
*/

modules/xmpp/ChatRoom.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import AVModeration from './AVModeration';
2020
import BreakoutRooms from './BreakoutRooms';
2121
import FileSharing from './FileSharing';
2222
import Lobby from './Lobby';
23+
import Polls from './Polls';
2324
import RoomMetadata from './RoomMetadata';
2425
import XmppConnection, { ErrorCallback } from './XmppConnection';
2526
import XMPP, { FEATURE_TRANSCRIBER } from './xmpp';
@@ -232,6 +233,7 @@ export default class ChatRoom extends Listenable {
232233
private avModeration: AVModeration;
233234
private breakoutRooms: BreakoutRooms;
234235
private fileSharing: FileSharing;
236+
private polls: Polls;
235237
private roomMetadata: RoomMetadata;
236238
private lastPresences: Record<string, IPresenceNode[]>;
237239
private phoneNumber: Nullable<string>;
@@ -308,6 +310,7 @@ export default class ChatRoom extends Listenable {
308310
this.avModeration = new AVModeration(this);
309311
this.breakoutRooms = new BreakoutRooms(this);
310312
this.fileSharing = new FileSharing(this);
313+
this.polls = new Polls(this);
311314
this.roomMetadata = new RoomMetadata(this);
312315
this.initPresenceMap(options);
313316
this.lastPresences = {};
@@ -2048,6 +2051,13 @@ export default class ChatRoom extends Listenable {
20482051
return this.roomMetadata;
20492052
}
20502053

2054+
/**
2055+
* @returns {Polls}
2056+
*/
2057+
public getPolls(): Polls {
2058+
return this.polls;
2059+
}
2060+
20512061
/**
20522062
* Returns the phone number for joining the conference.
20532063
*/
@@ -2186,6 +2196,7 @@ export default class ChatRoom extends Listenable {
21862196
this.avModeration.dispose();
21872197
this.breakoutRooms.dispose();
21882198
this.fileSharing.dispose();
2199+
this.polls.dispose();
21892200
this.roomMetadata.dispose();
21902201

21912202
const promises = [];

modules/xmpp/FileSharing.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ export default class FileSharing {
8585
}
8686

8787
/**
88-
* Whether AV moderation is supported on backend.
88+
* Whether file sharing is supported on backend.
8989
*
90-
* @returns {boolean} whether AV moderation is supported on backend.
90+
* @returns {boolean} whether file sharing is supported on backend.
9191
*/
9292
isSupported() {
9393
return Boolean(this._xmpp.fileSharingComponentAddress);

modules/xmpp/Polls.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { XMPPEvents } from '../../service/xmpp/XMPPEvents';
2+
3+
import ChatRoom from './ChatRoom';
4+
import XMPP from './xmpp';
5+
6+
export const COMMAND_ANSWER_POLL = 'answer-poll';
7+
export const COMMAND_NEW_POLL = 'new-poll';
8+
export const COMMAND_OLD_POLLS = 'old-polls';
9+
10+
export default class Polls {
11+
private _mainRoom: ChatRoom;
12+
private _xmpp: XMPP;
13+
14+
/**
15+
* Constructs polls for a room.
16+
*
17+
* @param {ChatRoom} room the main room.
18+
*/
19+
constructor(room: ChatRoom) {
20+
this._mainRoom = room;
21+
this._xmpp = room.xmpp;
22+
23+
this._handleMessages = this._handleMessages.bind(this);
24+
this._mainRoom.xmpp.addListener(XMPPEvents.POLLS_EVENT, this._handleMessages);
25+
}
26+
27+
/**
28+
* Whether polls is supported on backend.
29+
*
30+
* @returns {boolean} whether polls is supported on backend.
31+
*/
32+
isSupported() {
33+
return Boolean(this._xmpp.pollsComponentAddress);
34+
}
35+
36+
/**
37+
* Stops listening for events.
38+
*/
39+
dispose() {
40+
this._mainRoom.xmpp.removeListener(XMPPEvents.POLLS_EVENT, this._handleMessages);
41+
}
42+
43+
/**
44+
* Creates and sends a new poll.
45+
*
46+
* @param pollId
47+
* @param question
48+
* @param answers
49+
*/
50+
createPoll(pollId: string, question: string, answers: Array<{ name: string; }>) {
51+
this._mainRoom.sendPrivateMessage(
52+
this._xmpp.pollsComponentAddress,
53+
JSON.stringify({
54+
answers: answers,
55+
command: COMMAND_NEW_POLL,
56+
pollId,
57+
question,
58+
type: 'polls'
59+
}),
60+
'json-message',
61+
true);
62+
}
63+
64+
/**
65+
* Sends answers for a poll.
66+
*
67+
* @param pollId
68+
* @param answers
69+
*/
70+
answerPoll(pollId: string, answers: Array<boolean>) {
71+
this._mainRoom.sendPrivateMessage(
72+
this._xmpp.pollsComponentAddress,
73+
JSON.stringify({
74+
answers,
75+
command: COMMAND_ANSWER_POLL,
76+
pollId,
77+
type: 'polls'
78+
}),
79+
'json-message',
80+
true);
81+
}
82+
83+
/**
84+
* Handles a message for polls.
85+
*
86+
* @param {object} payload - Arbitrary data.
87+
*/
88+
_handleMessages(payload) {
89+
switch (payload.command) {
90+
case COMMAND_NEW_POLL:
91+
this._mainRoom.eventEmitter.emit(XMPPEvents.POLLS_RECEIVE_EVENT, payload);
92+
93+
break;
94+
case COMMAND_OLD_POLLS: {
95+
payload?.polls?.forEach((poll: any) => {
96+
this._mainRoom.eventEmitter.emit(XMPPEvents.POLLS_RECEIVE_EVENT, {
97+
history: true,
98+
...poll
99+
});
100+
});
101+
break;
102+
}
103+
case COMMAND_ANSWER_POLL: {
104+
this._mainRoom.eventEmitter.emit(XMPPEvents.POLLS_ANSWER_EVENT, payload);
105+
break;
106+
}
107+
}
108+
}
109+
}

modules/xmpp/xmpp.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export default class XMPP extends Listenable {
279279
public breakoutRoomsFeatures: Optional<IBreakoutRoomsFeatures>;
280280
public fileSharingComponentAddress: Optional<string>;
281281
public roomMetadataComponentAddress: Optional<string>;
282+
public pollsComponentAddress: Optional<string>;
282283

283284

284285
/**
@@ -496,6 +497,8 @@ export default class XMPP extends Listenable {
496497
this.eventEmitter.emit(XMPPEvents.ROOM_METADATA_EVENT, parsedJson);
497498
} else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'visitors') {
498499
this.eventEmitter.emit(XMPPEvents.VISITORS_MESSAGE, parsedJson);
500+
} else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'polls') {
501+
this.eventEmitter.emit(XMPPEvents.POLLS_EVENT, parsedJson);
499502
}
500503

501504
return true;
@@ -648,6 +651,11 @@ export default class XMPP extends Listenable {
648651
if (identity.type === 'visitors') {
649652
this._components.push(identity.name);
650653
}
654+
655+
if (identity.type === 'polls') {
656+
this.pollsComponentAddress = identity.name;
657+
this._components.push(this.pollsComponentAddress);
658+
}
651659
});
652660

653661
this._maybeSendDeploymentInfoStat(true);

service/xmpp/XMPPEvents.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,22 @@ export enum XMPPEvents {
276276
* Indicates that phone number changed.
277277
*/
278278
PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged',
279+
280+
/**
281+
* Event fired when we receive an answer for a poll.
282+
*/
283+
POLLS_ANSWER_EVENT = 'xmpp.polls.answer.event',
284+
285+
/**
286+
* Event fired when we receive a message for polls.
287+
*/
288+
POLLS_EVENT = 'xmpp.polls.event',
289+
290+
/**
291+
* Event fired when we received a poll or polls history event (old-polls).
292+
*/
293+
POLLS_RECEIVE_EVENT = 'xmpp.polls.received.event',
294+
279295
PRESENCE_RECEIVED = 'xmpp.presence_received',
280296
PRESENCE_STATUS = 'xmpp.presence_status',
281297

0 commit comments

Comments
 (0)