From f9a62cec4e7f7b43ee32512bdd8d03cacab4f69c Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Sun, 7 Aug 2022 21:57:41 +0300 Subject: [PATCH] making typing indicators per-channel --- frontend/src/components/MessageInput.svelte | 10 +++---- frontend/src/stores.js | 25 +++++++++++------- src/routes/api/v1/channels.ts | 29 +++++++++++++++++++++ src/routes/api/v1/users.ts | 20 -------------- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/frontend/src/components/MessageInput.svelte b/frontend/src/components/MessageInput.svelte index 1115bd8..a97e3a0 100644 --- a/frontend/src/components/MessageInput.svelte +++ b/frontend/src/components/MessageInput.svelte @@ -12,8 +12,8 @@ $: messages = messagesStoreProvider.getStore(channel.id); $: { - const typing = [ ...$typingStore ]; - const ownIndex = typing.findIndex(a => a.id === $userInfoStore.id); + const typing = $typingStore.filter(a => a.channelId === channel.id); + const ownIndex = typing.findIndex(a => a.user.id === $userInfoStore.id); if (ownIndex !== -1) { typing.splice(ownIndex, 1); } @@ -22,7 +22,7 @@ typingList = "?no one?"; typingMessage = "is typing..."; } else if (typing.length === 1) { - typingList = `${typing[0].username}`; + typingList = `${typing[0].user.username}`; typingMessage = "is typing..."; } else if (typing.length > 1) { typingList = ""; @@ -30,9 +30,9 @@ const item = typing[i]; if (i == (typing.length - 1)) { // we are at the end - typingList += `and ${item.username} `; + typingList += `and ${item.user.username} `; } else { - typingList += `${item.username}, `; + typingList += `${item.user.username}, `; } } typingMessage = "are typing..."; diff --git a/frontend/src/stores.js b/frontend/src/stores.js index 7861518..48d41af 100644 --- a/frontend/src/stores.js +++ b/frontend/src/stores.js @@ -326,10 +326,11 @@ class TypingStore extends Store { this.ownTimeout = null; this.ownNeedsUpdate = true; - gateway.subscribe(GatewayPayloadType.TypingStart, ({ user, time }) => { - if (userInfoStore.value && user.id === userInfoStore.value.id) + gateway.subscribe(GatewayPayloadType.TypingStart, ({ user, channel, time }) => { + if (userInfoStore && user.id === userInfoStore.value.id) return; - this.startedTyping(user, time); + + this.startedTyping(user, channel.id, time); }); // assume someone has stopped typing once they send a message @@ -339,7 +340,7 @@ class TypingStore extends Store { } stoppedTyping(id) { - const index = this.value.findIndex(e => e.id === id); + const index = this.value.findIndex(e => e.user.id === id); this.value.splice(index, 1); if (this.timeouts.get(id)) { @@ -356,7 +357,7 @@ class TypingStore extends Store { this.updated(); } - startedTyping(user, time) { + startedTyping(user, channelId, time) { if (this.timeouts.get(user.id)) { clearTimeout(this.timeouts.get(user.id)); } @@ -372,9 +373,15 @@ class TypingStore extends Store { }, time); } - const index = this.value.findIndex(e => e.id === user.id); + const index = this.value.findIndex(e => e.user.id === user.id); if (index === -1) { - this.value.push(user); + this.value.push({ + user, + channelId + }); + this.updated(); + } else if (this.value[index].channelId !== channelId) { // user just switched the channel they're typing in + this.value[index].channelId = channelId; this.updated(); } } @@ -383,10 +390,10 @@ class TypingStore extends Store { if (!userInfoStore.value) return; - this.startedTyping(userInfoStore.value, 6500); + this.startedTyping(userInfoStore.value, selectedChannel.value.id, 6500); if (this.ownNeedsUpdate) { this.ownNeedsUpdate = false; - await request("PUT", apiRoute("users/self/typing"), true, {}); + await request("POST", apiRoute(`channels/${selectedChannel.value.id}/typing`), true, {}); } } } diff --git a/src/routes/api/v1/channels.ts b/src/routes/api/v1/channels.ts index 9a21017..5414ef4 100644 --- a/src/routes/api/v1/channels.ts +++ b/src/routes/api/v1/channels.ts @@ -236,5 +236,34 @@ router.get( } ); +router.post( + "/:id/typing", + authenticateRoute(), + param("id").isNumeric(), + async (req, res) => { + const validationErrors = validationResult(req); + if (!validationErrors.isEmpty()) { + return res.status(400).json({ ...errors.INVALID_DATA, errors: validationErrors.array() }); + } + + const channelId = parseInt(req.params.id); + + dispatch(`channel:${channelId}`, { + t: GatewayPayloadType.TypingStart, + d: { + user: { + id: req.publicUser.id, + username: req.publicUser.username + }, + channel: { + id: channelId + }, + time: 7500 + } + }); + + return res.status(201).send(""); + } +); export default router; \ No newline at end of file diff --git a/src/routes/api/v1/users.ts b/src/routes/api/v1/users.ts index 2b37644..c8642b3 100644 --- a/src/routes/api/v1/users.ts +++ b/src/routes/api/v1/users.ts @@ -109,24 +109,4 @@ router.post( } ); -router.put( - "/self/typing", - authenticateRoute(), - (req, res) => { - // TODO: add a ratelimit to this - dispatch("*", { - t: GatewayPayloadType.TypingStart, - d: { - user: { - id: req.publicUser.id, - username: req.publicUser.username - }, - time: 7500 - } - }); - - return res.status(204).send(""); - } -); - export default router;