diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js
index 017b74c60d..8604fde31f 100644
--- a/src/components/chatInput/index.js
+++ b/src/components/chatInput/index.js
@@ -26,6 +26,8 @@ import {
StyledMentionSuggestion,
SuggestionsWrapper,
MentionUsername,
+ MentionContent,
+ MentionName,
} from './style';
import sendMessage from 'shared/graphql/mutations/message/sendMessage';
import sendDirectMessage from 'shared/graphql/mutations/message/sendDirectMessage';
@@ -38,8 +40,11 @@ import { ESC, BACKSPACE, DELETE } from 'src/helpers/keycodes';
const MentionSuggestion = ({ entry, search, focused }) => (
-
- {entry.username}
+
+
+ {entry.name}
+ @{entry.username}
+
);
@@ -292,21 +297,40 @@ const ChatInput = (props: Props) => {
);
};
+ const sortSuggestions = (a, b, queryString) => {
+ const aUsernameIndex = a.username.indexOf(queryString || '');
+ const bUsernameIndex = b.username.indexOf(queryString || '');
+ const aNameIndex = a.filterName.indexOf(queryString || '');
+ const bNameIndex = b.filterName.indexOf(queryString || '');
+ if (aNameIndex === 0) return -1;
+ if (aUsernameIndex === 0) return -1;
+ if (aNameIndex === 0) return -1;
+ if (aUsernameIndex === 0) return -1;
+ return aNameIndex - bNameIndex || aUsernameIndex - bUsernameIndex;
+ };
+
const searchUsers = async (queryString, callback) => {
const filteredParticipants = props.participants
? props.participants
.filter(Boolean)
- .filter(
- participant => participant.username.indexOf(queryString || '') > -1
- )
- .sort(
- (a, b) =>
- a.username.indexOf(queryString || '') -
- b.username.indexOf(queryString || '')
- )
+ .filter(participant => {
+ return (
+ participant.username &&
+ (participant.username.indexOf(queryString || '') > -1 ||
+ participant.filterName.indexOf(queryString || '') > -1)
+ );
+ })
+ .sort((a, b) => {
+ return sortSuggestions(a, b, queryString);
+ })
+ .slice(0, 8)
: [];
+
callback(filteredParticipants);
- if (!queryString || queryString.length === 0) return;
+
+ if (!queryString || queryString.length === 0)
+ return callback(filteredParticipants);
+
const {
data: { search },
} = await props.client.query({
@@ -316,10 +340,16 @@ const ChatInput = (props: Props) => {
type: 'USERS',
},
});
- if (!search || !search.searchResultsConnection) return;
+
+ if (!search || !search.searchResultsConnection) {
+ if (filteredParticipants && filteredParticipants.length > 0)
+ return filteredParticipants;
+ return;
+ }
let searchUsers = search.searchResultsConnection.edges
.filter(Boolean)
+ .filter(edge => edge.node.username)
.map(edge => {
const user = edge.node;
return {
@@ -327,12 +357,15 @@ const ChatInput = (props: Props) => {
id: user.username,
display: user.username,
username: user.username,
+ filterName: user.name.toLowerCase(),
};
});
+
// Prepend the filtered participants in case a user is tabbing down right now
const fullResults = [...filteredParticipants, ...searchUsers];
const uniqueResults = [];
const done = [];
+
fullResults.forEach(item => {
if (done.indexOf(item.username) === -1) {
uniqueResults.push(item);
@@ -340,7 +373,7 @@ const ChatInput = (props: Props) => {
}
});
- callback(uniqueResults);
+ return callback(uniqueResults.slice(0, 8));
};
const networkDisabled =
@@ -414,6 +447,7 @@ const ChatInput = (props: Props) => {
(props.focused ? theme.brand.default : theme.text.default)};
+ padding: 8px 12px;
align-items: center;
background: ${props => (props.focused ? theme.brand.wash : theme.bg.default)};
min-width: 156px;
+ line-height: 1.3;
+ border-bottom: 1px solid ${theme.bg.border};
+`;
+
+export const MentionContent = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+export const MentionName = styled.span`
+ margin-left: 12px;
+ width: calc(184px - 62px);
+ ${Truncate};
+ font-size: 14px;
+ font-weight: 500;
+ color: ${props => (props.focused ? theme.brand.default : theme.text.default)};
`;
export const MentionUsername = styled.span`
- margin-left: 8px;
- width: calc(156px - 62px);
+ margin-left: 12px;
+ font-size: 13px;
+ font-weight: 400;
+ width: calc(184px - 62px);
${Truncate};
+ color: ${props => (props.focused ? theme.brand.default : theme.text.alt)};
`;
diff --git a/src/views/thread/components/messages.js b/src/views/thread/components/messages.js
index 06cef1ebd7..07f3d18c5b 100644
--- a/src/views/thread/components/messages.js
+++ b/src/views/thread/components/messages.js
@@ -125,6 +125,14 @@ class MessagesWithData extends React.Component {
componentDidMount() {
this.subscribe();
+ if (
+ this.props.data &&
+ this.props.data.thread &&
+ this.props.onMessagesLoaded
+ ) {
+ this.props.onMessagesLoaded(this.props.data.thread);
+ }
+
if (this.shouldForceScrollToBottom()) {
return setTimeout(() => this.props.forceScrollToBottom());
}
diff --git a/src/views/thread/container.js b/src/views/thread/container.js
index 0b39bb7021..77eee92908 100644
--- a/src/views/thread/container.js
+++ b/src/views/thread/container.js
@@ -401,14 +401,26 @@ class ThreadContainer extends React.Component {
updateThreadParticipants = thread => {
const { messageConnection, author } = thread;
- if (!messageConnection || messageConnection.edges.length === 0) return;
+
+ if (!messageConnection || messageConnection.edges.length === 0)
+ return this.setState({
+ participants: [
+ { ...author.user, filterName: author.user.name.toLowerCase() },
+ ],
+ });
+
const participants = messageConnection.edges
.map(edge => edge.node)
.map(node => node.author.user);
const participantsWithAuthor = [...participants, author.user]
.filter((user, index, array) => array.indexOf(user) === index)
- .map(user => ({ ...user, id: user.username, display: user.username }));
+ .map(user => ({
+ ...user,
+ id: user.username,
+ display: user.username,
+ filterName: user.name.toLowerCase(),
+ }));
return this.setState({ participants: participantsWithAuthor });
};