From dc36afcfba518fd46d61a593af793a226f69c609 Mon Sep 17 00:00:00 2001 From: Hossein Date: Wed, 8 Dec 2021 10:04:19 -0500 Subject: [PATCH] GH-1878: Display Bot Badge in Auto Complete (#1906) --- server/model/user.go | 4 ++ .../mattermostauthlayer.go | 6 ++- .../entryComponent/entryComponent.scss | 5 +++ .../entryComponent/entryComponent.tsx | 39 +++++++++++++++++++ .../markdownEditorInput.tsx | 12 +++++- webapp/src/components/workspace.test.tsx | 2 +- webapp/src/user.tsx | 5 ++- 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 webapp/src/components/markdownEditorInput/entryComponent/entryComponent.scss create mode 100644 webapp/src/components/markdownEditorInput/entryComponent/entryComponent.tsx diff --git a/server/model/user.go b/server/model/user.go index 352e48c6a..2db42b200 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -47,6 +47,10 @@ type User struct { // Deleted time, set to indicate user is deleted // required: true DeleteAt int64 `json:"delete_at"` + + // If the user is a bot or not + // required: true + IsBot bool `json:"is_bot"` } type Session struct { diff --git a/server/services/store/mattermostauthlayer/mattermostauthlayer.go b/server/services/store/mattermostauthlayer/mattermostauthlayer.go index 347fefb7c..40b276ad3 100644 --- a/server/services/store/mattermostauthlayer/mattermostauthlayer.go +++ b/server/services/store/mattermostauthlayer/mattermostauthlayer.go @@ -284,10 +284,11 @@ func (s *MattermostAuthLayer) getQueryBuilder() sq.StatementBuilderType { func (s *MattermostAuthLayer) GetUsersByWorkspace(workspaceID string) ([]*model.User, error) { query := s.getQueryBuilder(). Select("id", "username", "email", "password", "MFASecret as mfa_secret", "AuthService as auth_service", "COALESCE(AuthData, '') as auth_data", - "props", "CreateAt as create_at", "UpdateAt as update_at", "DeleteAt as delete_at"). + "props", "Users.CreateAt as create_at", "Users.UpdateAt as update_at", "Users.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot"). From("Users"). Join("ChannelMembers ON ChannelMembers.UserID = Users.ID"). - Where(sq.Eq{"deleteAt": 0}). + LeftJoin("Bots b ON ( b.UserId = Users.ID )"). + Where(sq.Eq{"Users.deleteAt": 0}). Where(sq.Eq{"ChannelMembers.ChannelId": workspaceID}) rows, err := query.Query() @@ -323,6 +324,7 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro &user.CreateAt, &user.UpdateAt, &user.DeleteAt, + &user.IsBot, ) if err != nil { return nil, err diff --git a/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.scss b/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.scss new file mode 100644 index 000000000..53cf9e0d0 --- /dev/null +++ b/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.scss @@ -0,0 +1,5 @@ +.EntryComponent { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.tsx b/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.tsx new file mode 100644 index 000000000..c6083fafc --- /dev/null +++ b/webapp/src/components/markdownEditorInput/entryComponent/entryComponent.tsx @@ -0,0 +1,39 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React, {ReactElement} from 'react' +import {EntryComponentProps} from '@draft-js-plugins/mention/lib/MentionSuggestions/Entry/Entry' +import './entryComponent.scss' + +const BotBadge = (window as any).Components?.BotBadge + +const Entry = (props: EntryComponentProps): ReactElement => { + const { + mention, + theme, + ...parentProps + } = props + + return ( +
+
+ +
+ {mention.name} + {BotBadge && + + } +
+
+
+ ) +} + +export default Entry diff --git a/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx b/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx index 2520604eb..0769b2bd2 100644 --- a/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx +++ b/webapp/src/components/markdownEditorInput/markdownEditorInput.tsx @@ -24,6 +24,8 @@ import {getWorkspaceUsersList} from '../../store/users' import {useAppSelector} from '../../store/hooks' import {IUser} from '../../user' +import Entry from './entryComponent/entryComponent' + const imageURLForUser = (window as any).Components?.imageURLForUser type Props = { @@ -38,7 +40,14 @@ type Props = { const MarkdownEditorInput = (props: Props): ReactElement => { const {onChange, onFocus, onBlur, initialText, id, isEditing} = props const workspaceUsers = useAppSelector(getWorkspaceUsersList) - const mentions: MentionData[] = useMemo(() => workspaceUsers.map((user) => ({name: user.username, avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`})), [workspaceUsers]) + const mentions: MentionData[] = useMemo(() => + workspaceUsers.map((user) => + ({ + name: user.username, + avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`, + isBot: user.is_bot, + })) + , [workspaceUsers]) const ref = useRef(null) const [editorState, setEditorState] = useState(() => { const state = EditorState.createWithContent(ContentState.createFromText(initialText || '')) @@ -160,6 +169,7 @@ const MarkdownEditorInput = (props: Props): ReactElement => { onOpenChange={onMentionPopoverOpenChange} suggestions={suggestions} onSearchChange={onSearchChange} + entryComponent={Entry} /> { const originalModule = jest.requireActual('react-router-dom') diff --git a/webapp/src/user.tsx b/webapp/src/user.tsx index 88191a337..f2ab3be11 100644 --- a/webapp/src/user.tsx +++ b/webapp/src/user.tsx @@ -7,8 +7,9 @@ interface IUser { email: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any props: Record, - createAt: number, - updateAt: number, + create_at: number, + update_at: number, + is_bot: boolean, } interface UserWorkspace {