diff --git a/app/containers/MessageComposer/MessageComposer.test.tsx b/app/containers/MessageComposer/MessageComposer.test.tsx index 644decabae8..cd63fddf52d 100644 --- a/app/containers/MessageComposer/MessageComposer.test.tsx +++ b/app/containers/MessageComposer/MessageComposer.test.tsx @@ -439,6 +439,70 @@ describe('MessageComposer', () => { expect(onSendMessage).toHaveBeenCalledWith('@john', undefined); }); + test('does not show @all or @here in autocomplete when user does not have permissions', async () => { + mockedStore.dispatch(setPermissions({ 'mention-all': [], 'mention-here': [] })); + const onSendMessage = jest.fn(); + render(); + + await fireEvent(screen.getByTestId('message-composer-input'), 'focus'); + await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@'); + await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', { + nativeEvent: { selection: { start: 1, end: 1 } } + }); + jest.advanceTimersByTime(500); + + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).not.toBeOnTheScreen()); + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).not.toBeOnTheScreen()); + }); + + test('shows only @all when user has mention-all permission', async () => { + mockedStore.dispatch(setPermissions({ 'mention-all': ['user'], 'mention-here': [] })); + const onSendMessage = jest.fn(); + render(); + + await fireEvent(screen.getByTestId('message-composer-input'), 'focus'); + await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@'); + await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', { + nativeEvent: { selection: { start: 1, end: 1 } } + }); + jest.advanceTimersByTime(500); + + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).toBeOnTheScreen()); + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).not.toBeOnTheScreen()); + }); + + test('shows only @here when user has mention-here permission', async () => { + mockedStore.dispatch(setPermissions({ 'mention-here': ['user'], 'mention-all': [] })); + const onSendMessage = jest.fn(); + render(); + + await fireEvent(screen.getByTestId('message-composer-input'), 'focus'); + await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@'); + await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', { + nativeEvent: { selection: { start: 1, end: 1 } } + }); + jest.advanceTimersByTime(500); + + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).toBeOnTheScreen()); + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).not.toBeOnTheScreen()); + }); + + test('shows both @all and @here when user has both permissions', async () => { + mockedStore.dispatch(setPermissions({ 'mention-all': ['user'], 'mention-here': ['user'] })); + const onSendMessage = jest.fn(); + render(); + + await fireEvent(screen.getByTestId('message-composer-input'), 'focus'); + await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@'); + await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', { + nativeEvent: { selection: { start: 1, end: 1 } } + }); + jest.advanceTimersByTime(500); + + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-all')).toBeOnTheScreen()); + await waitFor(() => expect(screen.queryByTestId('autocomplete-item-here')).toBeOnTheScreen()); + }); + test('select # room inserts channel and sends, autocomplete hides', async () => { const onSendMessage = jest.fn(); (search as unknown as jest.Mock).mockImplementationOnce(() => [{ rid: 'r1', name: 'general', t: 'c' }]); diff --git a/app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap b/app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap index 8dd0f6b4152..ca392c9b2f3 100644 --- a/app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap +++ b/app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap @@ -82,7 +82,7 @@ exports[`MessageComposer Audio tap record 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={259} + handlerTag={289} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -177,7 +177,7 @@ exports[`MessageComposer Audio tap record 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={260} + handlerTag={290} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -299,7 +299,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={246} + handlerTag={276} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -415,7 +415,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={247} + handlerTag={277} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -587,7 +587,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={248} + handlerTag={278} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -744,7 +744,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={249} + handlerTag={279} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -860,7 +860,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={250} + handlerTag={280} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1033,7 +1033,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={251} + handlerTag={281} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1214,7 +1214,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={252} + handlerTag={282} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1371,7 +1371,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={253} + handlerTag={283} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1487,7 +1487,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={254} + handlerTag={284} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1660,7 +1660,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={255} + handlerTag={285} handlerType="NativeViewGestureHandler" hitSlop={ { @@ -1841,7 +1841,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = ` borderless={true} collapsable={false} delayLongPress={600} - handlerTag={256} + handlerTag={286} handlerType="NativeViewGestureHandler" hitSlop={ { diff --git a/app/containers/MessageComposer/hooks/useAutocomplete.ts b/app/containers/MessageComposer/hooks/useAutocomplete.ts index 94ab23f63cc..906fe8d631e 100644 --- a/app/containers/MessageComposer/hooks/useAutocomplete.ts +++ b/app/containers/MessageComposer/hooks/useAutocomplete.ts @@ -16,6 +16,7 @@ import { getCommandPreview, getListCannedResponse } from '../../../lib/services/ import log from '../../../lib/methods/helpers/log'; import I18n from '../../../i18n'; import { NO_CANNED_RESPONSES } from '../constants'; +import { usePermissions } from '../../../lib/hooks/usePermissions'; const MENTIONS_COUNT_TO_DISPLAY = 4; @@ -52,6 +53,8 @@ export const useAutocomplete = ({ updateAutocompleteVisible?: (updatedAutocompleteVisible: boolean) => void; }): TAutocompleteItem[] => { const [items, setItems] = useState([]); + const [mentionAll, mentionHere] = usePermissions(['mention-all', 'mention-here']); + useEffect(() => { const getAutocomplete = async () => { try { @@ -87,7 +90,7 @@ export const useAutocomplete = ({ type })) as IAutocompleteUserRoom[]; if (type === '@') { - if ('all'.includes(text.toLocaleLowerCase())) { + if (mentionAll && 'all'.includes(text.toLocaleLowerCase())) { parsedRes.push({ id: 'all', title: 'all', @@ -96,7 +99,7 @@ export const useAutocomplete = ({ t: 'd' }); } - if ('here'.includes(text.toLocaleLowerCase())) { + if (mentionHere && 'here'.includes(text.toLocaleLowerCase())) { parsedRes.push({ id: 'here', title: 'here', diff --git a/app/lib/methods/getPermissions.ts b/app/lib/methods/getPermissions.ts index 8bf2fd21db0..02082089ad1 100644 --- a/app/lib/methods/getPermissions.ts +++ b/app/lib/methods/getPermissions.ts @@ -66,7 +66,9 @@ export const SUPPORTED_PERMISSIONS = [ 'create-team-channel', 'create-team-group', 'delete-team-channel', - 'delete-team-group' + 'delete-team-group', + 'mention-all', + 'mention-here' ] as const; export async function setPermissions(): Promise {