From f5798d73de6fdb148f7f7737de5a131e8ff11e56 Mon Sep 17 00:00:00 2001
From: Rohit <40559587+Rohit3523@users.noreply.github.com>
Date: Thu, 10 Sep 2026 19:17:45 +0530
Subject: [PATCH] fix: allow channel, user and emoji autocomplete after slash
command
---
.../MessageComposer/MessageComposer.test.tsx | 29 +++++++++++++++++
.../components/ComposerInput.tsx | 32 +++++++++----------
2 files changed, 45 insertions(+), 16 deletions(-)
diff --git a/app/containers/MessageComposer/MessageComposer.test.tsx b/app/containers/MessageComposer/MessageComposer.test.tsx
index 8a15b8a2472..5cc4eb5dc0d 100644
--- a/app/containers/MessageComposer/MessageComposer.test.tsx
+++ b/app/containers/MessageComposer/MessageComposer.test.tsx
@@ -572,6 +572,35 @@ describe('MessageComposer', () => {
expect(onSendMessage).toHaveBeenCalledWith('#general', false);
});
+ test('typing # after a slash command opens channel autocomplete', async () => {
+ const onSendMessage = jest.fn();
+ (searchRemote as unknown as jest.Mock).mockImplementationOnce(() => [{ rid: 'r1', name: 'general', t: 'c' }]);
+ render();
+
+ await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
+ await fireEvent.changeText(screen.getByTestId('message-composer-input'), '/hello #');
+ await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
+ nativeEvent: { selection: { start: 8, end: 8 } }
+ });
+ await advanceComposerTimers();
+
+ await waitFor(() => expect(screen.getByTestId('autocomplete-item-general')).toBeOnTheScreen());
+ });
+
+ test('typing @ after a slash command opens user autocomplete', async () => {
+ const onSendMessage = jest.fn();
+ render();
+
+ await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
+ await fireEvent.changeText(screen.getByTestId('message-composer-input'), '/hello @');
+ await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
+ nativeEvent: { selection: { start: 8, end: 8 } }
+ });
+ await advanceComposerTimers();
+
+ await waitFor(() => expect(screen.getByTestId('autocomplete-item-John')).toBeOnTheScreen());
+ });
+
test('select : emoji inserts emoji and sends, autocomplete hides', async () => {
const onSendMessage = jest.fn();
render();
diff --git a/app/containers/MessageComposer/components/ComposerInput.tsx b/app/containers/MessageComposer/components/ComposerInput.tsx
index a0547bab0a6..33da53c9e6e 100644
--- a/app/containers/MessageComposer/components/ComposerInput.tsx
+++ b/app/containers/MessageComposer/components/ComposerInput.tsx
@@ -325,6 +325,22 @@ export const ComposerInput = memo(
stopAutocomplete();
return;
}
+ if (lastWord.match(/^#/)) {
+ setAutocompleteParams({ text: autocompleteText, type: '#' });
+ return;
+ }
+ if (lastWord.match(/^@/)) {
+ setAutocompleteParams({ text: autocompleteText, type: '@' });
+ return;
+ }
+ if (lastWord.match(/^:/)) {
+ setAutocompleteParams({ text: autocompleteText, type: ':' });
+ return;
+ }
+ if (lastWord.match(/^!/) && room?.t === 'l') {
+ setAutocompleteParams({ text: autocompleteText, type: '!' });
+ return;
+ }
if (!sharing && text.match(/^\//)) {
const commandParameter = text.match(/^\/([a-z0-9._-]+) (.+)/im);
if (commandParameter) {
@@ -344,22 +360,6 @@ export const ComposerInput = memo(
setAutocompleteParams({ text: autocompleteText, type: '/' });
return;
}
- if (lastWord.match(/^#/)) {
- setAutocompleteParams({ text: autocompleteText, type: '#' });
- return;
- }
- if (lastWord.match(/^@/)) {
- setAutocompleteParams({ text: autocompleteText, type: '@' });
- return;
- }
- if (lastWord.match(/^:/)) {
- setAutocompleteParams({ text: autocompleteText, type: ':' });
- return;
- }
- if (lastWord.match(/^!/) && room?.t === 'l') {
- setAutocompleteParams({ text: autocompleteText, type: '!' });
- return;
- }
stopAutocomplete();
}, textInputDebounceTime);