From 859bc53cbbd9a076f2b79ac72736a6ea69693b7d Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Tue, 10 May 2022 11:06:04 +0530 Subject: [PATCH] All team users autocomplete new (#3016) * WIP * Made search debounced * Lint fiX * Fixed tests * Calledn un-debounced version on first load --- .../components/content/textElement.test.tsx | 11 ++++ webapp/src/components/contentBlock.test.tsx | 9 ++++ webapp/src/components/markdownEditor.test.tsx | 12 +++++ .../markdownEditorInput.tsx | 53 ++++++++++++++++--- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/webapp/src/components/content/textElement.test.tsx b/webapp/src/components/content/textElement.test.tsx index 8075792a2..179ad1edc 100644 --- a/webapp/src/components/content/textElement.test.tsx +++ b/webapp/src/components/content/textElement.test.tsx @@ -15,6 +15,8 @@ import {mockDOM, wrapDNDIntl, mockStateStore} from '../../testUtils' import {Utils} from '../../utils' +import {TestBlockFactory} from "../../test/testBlockFactory" + import TextElement from './textElement' jest.mock('../../utils') @@ -41,6 +43,9 @@ describe('components/content/TextElement', () => { mockDOM() }) + const board1 = TestBlockFactory.createBoard() + board1.id = 'board-id-1' + const state = { users: { boardUsers: { @@ -51,6 +56,12 @@ describe('components/content/TextElement', () => { 5: {username: 'g'}, }, }, + boards: { + current: 'board-id-1', + boards: { + [board1.id]: board1, + } + } } const store = mockStateStore([], state) diff --git a/webapp/src/components/contentBlock.test.tsx b/webapp/src/components/contentBlock.test.tsx index 6bb8af1fe..bf05633f1 100644 --- a/webapp/src/components/contentBlock.test.tsx +++ b/webapp/src/components/contentBlock.test.tsx @@ -65,6 +65,9 @@ describe('components/contentBlock', () => { addBlock: jest.fn(), }) + const board1 = TestBlockFactory.createBoard() + board1.id = 'board-id-1' + const state = { users: { boardUsers: { @@ -75,6 +78,12 @@ describe('components/contentBlock', () => { 5: {username: 'g'}, }, }, + boards: { + current: 'board-id-1', + boards: { + [board1.id]: board1, + } + } } const store = mockStateStore([], state) diff --git a/webapp/src/components/markdownEditor.test.tsx b/webapp/src/components/markdownEditor.test.tsx index 24870d7d1..e1541f97d 100644 --- a/webapp/src/components/markdownEditor.test.tsx +++ b/webapp/src/components/markdownEditor.test.tsx @@ -7,6 +7,8 @@ import {Provider as ReduxProvider} from 'react-redux' import {mockDOM, wrapDNDIntl, mockStateStore} from '../testUtils' +import {TestBlockFactory} from "../test/testBlockFactory" + import {MarkdownEditor} from './markdownEditor' jest.mock('../utils') @@ -16,6 +18,10 @@ jest.mock('draft-js/lib/generateRandomKey', () => () => '123') describe('components/markdownEditor', () => { beforeAll(mockDOM) beforeEach(jest.clearAllMocks) + + const board1 = TestBlockFactory.createBoard() + board1.id = 'board-id-1' + const state = { users: { boardUsers: { @@ -26,6 +32,12 @@ describe('components/markdownEditor', () => { 5: {username: 'g'}, }, }, + boards: { + current: 'board-id-1', + boards: { + [board1.id]: board1, + } + } } const store = mockStateStore([], state) test('should match snapshot', async () => { diff --git a/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx b/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx index 6bfee2d55..02f299a1a 100644 --- a/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx +++ b/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx @@ -3,10 +3,7 @@ import Editor from '@draft-js-plugins/editor' import createEmojiPlugin from '@draft-js-plugins/emoji' import '@draft-js-plugins/emoji/lib/plugin.css' -import createMentionPlugin, { - defaultSuggestionsFilter, - MentionData, -} from '@draft-js-plugins/mention' +import createMentionPlugin from '@draft-js-plugins/mention' import '@draft-js-plugins/mention/lib/plugin.css' import {ContentState, DraftHandleValue, EditorState, getDefaultKeyBinding} from 'draft-js' import React, { @@ -15,6 +12,8 @@ import React, { useState, } from 'react' +import {debounce} from "lodash" + import {useAppSelector} from '../../store/hooks' import {IUser} from '../../user' import {getBoardUsersList} from '../../store/users' @@ -22,10 +21,20 @@ import createLiveMarkdownPlugin from '../live-markdown-plugin/liveMarkdownPlugin import './markdownEditorInput.scss' +import {BoardTypeOpen} from "../../blocks/board" +import {getCurrentBoard} from "../../store/boards" +import octoClient from "../../octoClient" + import Entry from './entryComponent/entryComponent' const imageURLForUser = (window as any).Components?.imageURLForUser +type MentionUser = { + name: string + avatar: string + is_bot: boolean +} + type Props = { onChange?: (text: string) => void onFocus?: () => void @@ -38,9 +47,38 @@ type Props = { const MarkdownEditorInput = (props: Props): ReactElement => { const {onChange, onFocus, onBlur, initialText, id, isEditing} = props const boardUsers = useAppSelector(getBoardUsersList) - const mentions: MentionData[] = useMemo(() => boardUsers.map((user) => ({name: user.username, avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`, is_bot: user.is_bot})), [boardUsers]) + const board = useAppSelector(getCurrentBoard) const ref = useRef(null) + const [suggestions, setSuggestions] = useState>([]) + + const loadSuggestions = async (term: string) => { + let users: Array + + if (board && board.type === BoardTypeOpen) { + users = await octoClient.searchTeamUsers(term) + } else { + users = boardUsers + } + + const mentions = users.map( + (user) => ({ + name: user.username, + avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`, + is_bot: user.is_bot} + )) + setSuggestions(mentions) + } + + const debouncedLoadSuggestion = useMemo(() => debounce(loadSuggestions, 200), []) + + useEffect(() => { + // Get the ball rolling. Searching for empty string + // returns first 10 users in alphabetical order. + loadSuggestions('') + }, []) + + const generateEditorState = (text?: string) => { const state = EditorState.createWithContent(ContentState.createFromText(text || '')) return EditorState.moveSelectionToEnd(state) @@ -67,7 +105,6 @@ const MarkdownEditorInput = (props: Props): ReactElement => { const [isMentionPopoverOpen, setIsMentionPopoverOpen] = useState(false) const [isEmojiPopoverOpen, setIsEmojiPopoverOpen] = useState(false) - const [suggestions, setSuggestions] = useState(mentions) const {MentionSuggestions, plugins, EmojiSuggestions} = useMemo(() => { const mentionPlugin = createMentionPlugin({mentionPrefix: '@'}) @@ -144,8 +181,8 @@ const MarkdownEditorInput = (props: Props): ReactElement => { }, []) const onSearchChange = useCallback(({value}: { value: string }) => { - setSuggestions(defaultSuggestionsFilter(value, mentions)) - }, [mentions]) + debouncedLoadSuggestion(value) + }, [suggestions]) let className = 'MarkdownEditorInput' if (!isEditing) {