Skip to content

Commit 639ace2

Browse files
authored
feat: show @ALL and @here in mention list based on user permission (#6821)
1 parent 1bd62a2 commit 639ace2

File tree

4 files changed

+85
-16
lines changed

4 files changed

+85
-16
lines changed

app/containers/MessageComposer/MessageComposer.test.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,70 @@ describe('MessageComposer', () => {
439439
expect(onSendMessage).toHaveBeenCalledWith('@john', undefined);
440440
});
441441

442+
test('does not show @all or @here in autocomplete when user does not have permissions', async () => {
443+
mockedStore.dispatch(setPermissions({ 'mention-all': [], 'mention-here': [] }));
444+
const onSendMessage = jest.fn();
445+
render(<Render context={{ onSendMessage }} />);
446+
447+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
448+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
449+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
450+
nativeEvent: { selection: { start: 1, end: 1 } }
451+
});
452+
jest.advanceTimersByTime(500);
453+
454+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).not.toBeOnTheScreen());
455+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).not.toBeOnTheScreen());
456+
});
457+
458+
test('shows only @all when user has mention-all permission', async () => {
459+
mockedStore.dispatch(setPermissions({ 'mention-all': ['user'], 'mention-here': [] }));
460+
const onSendMessage = jest.fn();
461+
render(<Render context={{ onSendMessage }} />);
462+
463+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
464+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
465+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
466+
nativeEvent: { selection: { start: 1, end: 1 } }
467+
});
468+
jest.advanceTimersByTime(500);
469+
470+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).toBeOnTheScreen());
471+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).not.toBeOnTheScreen());
472+
});
473+
474+
test('shows only @here when user has mention-here permission', async () => {
475+
mockedStore.dispatch(setPermissions({ 'mention-here': ['user'], 'mention-all': [] }));
476+
const onSendMessage = jest.fn();
477+
render(<Render context={{ onSendMessage }} />);
478+
479+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
480+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
481+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
482+
nativeEvent: { selection: { start: 1, end: 1 } }
483+
});
484+
jest.advanceTimersByTime(500);
485+
486+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).toBeOnTheScreen());
487+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).not.toBeOnTheScreen());
488+
});
489+
490+
test('shows both @all and @here when user has both permissions', async () => {
491+
mockedStore.dispatch(setPermissions({ 'mention-all': ['user'], 'mention-here': ['user'] }));
492+
const onSendMessage = jest.fn();
493+
render(<Render context={{ onSendMessage }} />);
494+
495+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
496+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
497+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
498+
nativeEvent: { selection: { start: 1, end: 1 } }
499+
});
500+
jest.advanceTimersByTime(500);
501+
502+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).toBeOnTheScreen());
503+
await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).toBeOnTheScreen());
504+
});
505+
442506
test('select # room inserts channel and sends, autocomplete hides', async () => {
443507
const onSendMessage = jest.fn();
444508
(search as unknown as jest.Mock).mockImplementationOnce(() => [{ rid: 'r1', name: 'general', t: 'c' }]);

app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ exports[`MessageComposer Audio tap record 1`] = `
8282
borderless={true}
8383
collapsable={false}
8484
delayLongPress={600}
85-
handlerTag={259}
85+
handlerTag={289}
8686
handlerType="NativeViewGestureHandler"
8787
hitSlop={
8888
{
@@ -177,7 +177,7 @@ exports[`MessageComposer Audio tap record 1`] = `
177177
borderless={true}
178178
collapsable={false}
179179
delayLongPress={600}
180-
handlerTag={260}
180+
handlerTag={290}
181181
handlerType="NativeViewGestureHandler"
182182
hitSlop={
183183
{
@@ -299,7 +299,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
299299
borderless={true}
300300
collapsable={false}
301301
delayLongPress={600}
302-
handlerTag={246}
302+
handlerTag={276}
303303
handlerType="NativeViewGestureHandler"
304304
hitSlop={
305305
{
@@ -415,7 +415,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
415415
borderless={true}
416416
collapsable={false}
417417
delayLongPress={600}
418-
handlerTag={247}
418+
handlerTag={277}
419419
handlerType="NativeViewGestureHandler"
420420
hitSlop={
421421
{
@@ -587,7 +587,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
587587
borderless={true}
588588
collapsable={false}
589589
delayLongPress={600}
590-
handlerTag={248}
590+
handlerTag={278}
591591
handlerType="NativeViewGestureHandler"
592592
hitSlop={
593593
{
@@ -744,7 +744,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
744744
borderless={true}
745745
collapsable={false}
746746
delayLongPress={600}
747-
handlerTag={249}
747+
handlerTag={279}
748748
handlerType="NativeViewGestureHandler"
749749
hitSlop={
750750
{
@@ -860,7 +860,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
860860
borderless={true}
861861
collapsable={false}
862862
delayLongPress={600}
863-
handlerTag={250}
863+
handlerTag={280}
864864
handlerType="NativeViewGestureHandler"
865865
hitSlop={
866866
{
@@ -1033,7 +1033,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
10331033
borderless={true}
10341034
collapsable={false}
10351035
delayLongPress={600}
1036-
handlerTag={251}
1036+
handlerTag={281}
10371037
handlerType="NativeViewGestureHandler"
10381038
hitSlop={
10391039
{
@@ -1214,7 +1214,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
12141214
borderless={true}
12151215
collapsable={false}
12161216
delayLongPress={600}
1217-
handlerTag={252}
1217+
handlerTag={282}
12181218
handlerType="NativeViewGestureHandler"
12191219
hitSlop={
12201220
{
@@ -1371,7 +1371,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
13711371
borderless={true}
13721372
collapsable={false}
13731373
delayLongPress={600}
1374-
handlerTag={253}
1374+
handlerTag={283}
13751375
handlerType="NativeViewGestureHandler"
13761376
hitSlop={
13771377
{
@@ -1487,7 +1487,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
14871487
borderless={true}
14881488
collapsable={false}
14891489
delayLongPress={600}
1490-
handlerTag={254}
1490+
handlerTag={284}
14911491
handlerType="NativeViewGestureHandler"
14921492
hitSlop={
14931493
{
@@ -1660,7 +1660,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
16601660
borderless={true}
16611661
collapsable={false}
16621662
delayLongPress={600}
1663-
handlerTag={255}
1663+
handlerTag={285}
16641664
handlerType="NativeViewGestureHandler"
16651665
hitSlop={
16661666
{
@@ -1841,7 +1841,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
18411841
borderless={true}
18421842
collapsable={false}
18431843
delayLongPress={600}
1844-
handlerTag={256}
1844+
handlerTag={286}
18451845
handlerType="NativeViewGestureHandler"
18461846
hitSlop={
18471847
{

app/containers/MessageComposer/hooks/useAutocomplete.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { getCommandPreview, getListCannedResponse } from '../../../lib/services/
1616
import log from '../../../lib/methods/helpers/log';
1717
import I18n from '../../../i18n';
1818
import { NO_CANNED_RESPONSES } from '../constants';
19+
import { usePermissions } from '../../../lib/hooks/usePermissions';
1920

2021
const MENTIONS_COUNT_TO_DISPLAY = 4;
2122

@@ -52,6 +53,8 @@ export const useAutocomplete = ({
5253
updateAutocompleteVisible?: (updatedAutocompleteVisible: boolean) => void;
5354
}): TAutocompleteItem[] => {
5455
const [items, setItems] = useState<TAutocompleteItem[]>([]);
56+
const [mentionAll, mentionHere] = usePermissions(['mention-all', 'mention-here']);
57+
5558
useEffect(() => {
5659
const getAutocomplete = async () => {
5760
try {
@@ -87,7 +90,7 @@ export const useAutocomplete = ({
8790
type
8891
})) as IAutocompleteUserRoom[];
8992
if (type === '@') {
90-
if ('all'.includes(text.toLocaleLowerCase())) {
93+
if (mentionAll && 'all'.includes(text.toLocaleLowerCase())) {
9194
parsedRes.push({
9295
id: 'all',
9396
title: 'all',
@@ -96,7 +99,7 @@ export const useAutocomplete = ({
9699
t: 'd'
97100
});
98101
}
99-
if ('here'.includes(text.toLocaleLowerCase())) {
102+
if (mentionHere && 'here'.includes(text.toLocaleLowerCase())) {
100103
parsedRes.push({
101104
id: 'here',
102105
title: 'here',

app/lib/methods/getPermissions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ export const SUPPORTED_PERMISSIONS = [
6666
'create-team-channel',
6767
'create-team-group',
6868
'delete-team-channel',
69-
'delete-team-group'
69+
'delete-team-group',
70+
'mention-all',
71+
'mention-here'
7072
] as const;
7173

7274
export async function setPermissions(): Promise<void> {

0 commit comments

Comments
 (0)