mentions in chat

This commit is contained in:
Abron
2025-03-06 12:48:21 +03:30
parent 12b246b299
commit 20a9573440
2 changed files with 52 additions and 29 deletions

View File

@@ -255,12 +255,13 @@ export function useAutoComplete(
": ", ": ",
); );
} else if (state.type === "user") { } else if (state.type === "user") {
const selectedUser = state.matches[state.selected];
content.splice( content.splice(
index, index,
search.length + 1, search.length + 1,
"<@", "@",
state.matches[state.selected]._id, selectedUser.username,
"> ", " ",
); );
} else { } else {
content.splice( content.splice(
@@ -460,8 +461,7 @@ export default function AutoComplete({
size={20} size={20}
/> />
)} )}
<span style={{ paddingLeft: "4px" }}>{`:${ <span style={{ paddingLeft: "4px" }}>{`:${match instanceof CustomEmoji
match instanceof CustomEmoji
? match.name ? match.name
: match : match
}:`}</span> }:`}</span>

View File

@@ -13,7 +13,7 @@ import { IconButton, Picker } from "@revoltchat/ui";
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
import { debounce } from "../../../lib/debounce"; import { debounce } from "../../../lib/debounce";
import { defer } from "../../../lib/defer"; import { defer, chainedDefer } from "../../../lib/defer";
import { internalEmit, internalSubscribe } from "../../../lib/eventEmitter"; import { internalEmit, internalSubscribe } from "../../../lib/eventEmitter";
import { useTranslation } from "../../../lib/i18n"; import { useTranslation } from "../../../lib/i18n";
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
@@ -325,10 +325,29 @@ export default observer(({ channel }: Props) => {
if (uploadState.type === "uploading" || uploadState.type === "sending") if (uploadState.type === "uploading" || uploadState.type === "sending")
return; return;
const content = state.draft.get(channel._id)?.content?.trim() ?? ""; let content = state.draft.get(channel._id)?.content?.trim() ?? "";
if (uploadState.type !== "none") return sendFile(content); if (uploadState.type !== "none") return sendFile(content);
if (content.length === 0) return; if (content.length === 0) return;
// Convert @username mentions to <@USER_ID> format
const mentionRegex = /@([a-zA-Z0-9_]+)/g;
const mentionMatches = content.match(mentionRegex);
if (mentionMatches) {
for (const mention of mentionMatches) {
const username = mention.substring(1); // Remove the @ symbol
// Find the user with this username
const user = Array.from(client.users.values()).find(
(u) => u.username.toLowerCase() === username.toLowerCase()
);
if (user) {
// Replace @username with <@USER_ID>
content = content.replace(mention, `<@${user._id}>`);
}
}
}
internalEmit("NewMessages", "hide"); internalEmit("NewMessages", "hide");
stopTyping(); stopTyping();
setMessage(); setMessage();
@@ -366,7 +385,7 @@ export default observer(({ channel }: Props) => {
content: newContent.substr(0, 2000), content: newContent.substr(0, 2000),
}) })
.then(() => .then(() =>
defer(() => chainedDefer(() =>
renderer.jumpToBottom( renderer.jumpToBottom(
SMOOTH_SCROLL_ON_RECEIVE, SMOOTH_SCROLL_ON_RECEIVE,
), ),
@@ -388,7 +407,8 @@ export default observer(({ channel }: Props) => {
replies, replies,
}); });
defer(() => renderer.jumpToBottom(SMOOTH_SCROLL_ON_RECEIVE)); // Use chainedDefer for more reliable scrolling
chainedDefer(() => renderer.jumpToBottom(SMOOTH_SCROLL_ON_RECEIVE));
try { try {
await channel.sendMessage({ await channel.sendMessage({
@@ -396,6 +416,9 @@ export default observer(({ channel }: Props) => {
nonce, nonce,
replies, replies,
}); });
// Add another scroll to bottom after the message is sent
chainedDefer(() => renderer.jumpToBottom(SMOOTH_SCROLL_ON_RECEIVE));
} catch (error) { } catch (error) {
state.queue.fail(nonce, takeError(error)); state.queue.fail(nonce, takeError(error));
} }