refactor(socket.io): deprecate SocketModules.chats.typing in favour of api.chats.toggleTyping

This commit is contained in:
Julian Lam
2023-11-17 14:10:29 -05:00
parent 401e8636bd
commit c1e6be7705
9 changed files with 86 additions and 18 deletions

View File

@@ -202,6 +202,8 @@ paths:
$ref: 'write/chats/roomId/state.yaml'
/chats/{roomId}/watch:
$ref: 'write/chats/roomId/watch.yaml'
/chats/{roomId}/typing:
$ref: 'write/chats/roomId/typing.yaml'
/chats/{roomId}/users:
$ref: 'write/chats/roomId/users.yaml'
/chats/{roomId}/users/{uid}:

View File

@@ -5,6 +5,8 @@ get:
description: >
This operation retrieves a list of pinned messages for a given chat room.
This call will always return a maximum of 50 items, of which the result set can be offset based on the passed-in `start` parameter.
N.B. The calling user must be in the chat room for this call to succeed.
parameters:
- in: path
name: roomId

View File

@@ -0,0 +1,39 @@
put:
tags:
- chats
summary: update typing state in chat room
description: >
This operation updates the typing state in a given chat room, so that other users in the room see that that user is typing.
N.B. The calling user must be in the chat room for this call to succeed.
parameters:
- in: path
name: roomId
schema:
type: number
required: true
description: a valid room id
example: 1
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
typing:
type: boolean
example: true
required:
- typing
responses:
'200':
description: Chat room typing state updated.
content:
application/json:
schema:
type: object
properties:
status:
$ref: ../../../components/schemas/Status.yaml#/Status
response: {}

View File

@@ -391,11 +391,7 @@ define('forum/chats', [
Chats.addTypingHandler = function (parent, roomId) {
const textarea = parent.find('[component="chat/input"]');
function emitTyping(typing) {
socket.emit('modules.chats.typing', {
roomId: roomId,
typing: typing,
username: app.user.username,
});
api.put(`/chats/${roomId}/typing`, { typing }).catch(alerts.error);
}
textarea.on('focus', () => textarea.val() && emitTyping(true));
@@ -744,7 +740,7 @@ define('forum/chats', [
});
socket.on('event:chats.typing', async (data) => {
if (chatModule.isFromBlockedUser(data.uid)) {
if (data.uid === app.user.uid || chatModule.isFromBlockedUser(data.uid)) {
return;
}
chatModule.updateTypingUserList($(`[component="chat/main-wrapper"][data-roomid="${data.roomId}"]`), data);

View File

@@ -245,7 +245,7 @@ define('chat', [
};
module.onUserTyping = function (data) {
if (module.isFromBlockedUser(data.uid)) {
if (data.uid === app.user.uid || module.isFromBlockedUser(data.uid)) {
return;
}
const modal = module.getModal(data.roomId);

View File

@@ -10,7 +10,9 @@ const messaging = require('../messaging');
const notifications = require('../notifications');
const privileges = require('../privileges');
const plugins = require('../plugins');
const utils = require('../utils');
const websockets = require('../socket.io');
const socketHelpers = require('../socket.io/helpers');
const chatsAPI = module.exports;
@@ -207,6 +209,27 @@ chatsAPI.watch = async (caller, { roomId, state }) => {
await messaging.setUserNotificationSetting(caller.uid, roomId, state);
};
chatsAPI.toggleTyping = async (caller, { roomId, typing }) => {
if (!utils.isNumber(roomId) || typeof typing !== 'boolean') {
throw new Error('[[error:invalid-data]]');
}
const [isInRoom, username] = await Promise.all([
messaging.isUserInRoom(caller.uid, roomId),
user.getUserField(caller.uid, 'username'),
]);
if (!isInRoom) {
throw new Error('[[error:no-privileges]]');
}
websockets.in(`chat_room_${roomId}`).emit('event:chats.typing', {
uid: caller.uid,
roomId,
typing,
username,
});
};
chatsAPI.users = async (caller, data) => {
const start = data.hasOwnProperty('start') ? data.start : 0;
const stop = start + 39;

View File

@@ -95,6 +95,13 @@ Chats.watch = async (req, res) => {
helpers.formatApiResponse(200, res);
};
Chats.toggleTyping = async (req, res) => {
const { typing } = req.body;
await api.chats.toggleTyping(req, { typing, ...req.params });
helpers.formatApiResponse(200, res);
};
Chats.users = async (req, res) => {
const { roomId } = req.params;
const start = parseInt(req.query.start, 10) || 0;

View File

@@ -27,6 +27,8 @@ module.exports = function () {
setupApiRoute(router, 'put', '/:roomId/watch', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['value'])], controllers.write.chats.watch);
setupApiRoute(router, 'delete', '/:roomId/watch', [...middlewares, middleware.assert.room], controllers.write.chats.watch);
setupApiRoute(router, 'put', '/:roomId/typing', [...middlewares, middleware.assert.room], controllers.write.chats.toggleTyping);
setupApiRoute(router, 'get', '/:roomId/users', [...middlewares, middleware.assert.room], controllers.write.chats.users);
setupApiRoute(router, 'post', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.invite);
setupApiRoute(router, 'delete', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.kick);

View File

@@ -213,19 +213,16 @@ SocketModules.chats.loadPinnedMessages = async (socket, data) => {
};
SocketModules.chats.typing = async (socket, data) => {
if (!data || !utils.isNumber(data.roomId) || typeof data.typing !== 'boolean') {
sockets.warnDeprecated(socket, 'PUT /api/v3/chats/:roomId/typing');
if (!data) {
throw new Error('[[error:invalid-data]]');
}
const isInRoom = await Messaging.isUserInRoom(socket.uid, data.roomId);
if (!isInRoom) {
throw new Error('[[error:no-privileges]]');
}
socket.to(`chat_room_${data.roomId}`).emit('event:chats.typing', {
uid: socket.uid,
roomId: data.roomId,
typing: data.typing,
username: validator.escape(String(data.username)),
});
// `username` is now inferred from caller uid
delete data.username;
await api.chats.toggleTyping(socket, data);
};