From 9cd836db1e5af05194ca9028e45ee458894b7a91 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:25:56 -0800 Subject: [PATCH 01/12] Only show 10 suggestions, make sure suggestions have usernames --- src/components/chatInput/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index 017b74c60d..d0ab85b935 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -296,8 +296,11 @@ const ChatInput = (props: Props) => { const filteredParticipants = props.participants ? props.participants .filter(Boolean) + .slice(0, 10) .filter( - participant => participant.username.indexOf(queryString || '') > -1 + participant => + participant.username && + participant.username.indexOf(queryString || '') > -1 ) .sort( (a, b) => @@ -320,6 +323,8 @@ const ChatInput = (props: Props) => { let searchUsers = search.searchResultsConnection.edges .filter(Boolean) + .filter(edge => edge.node.username) + .slice(0, 10) .map(edge => { const user = edge.node; return { From 0e7edae429eb3bb882bbffc4d8a6e28e342f11b3 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:26:08 -0800 Subject: [PATCH 02/12] Pass participants to chatinput for SSR views --- src/views/thread/components/messages.js | 8 ++++++++ 1 file changed, 8 insertions(+) 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()); } From 3d40e1afede9233963eb510e42b2be9533bc4a1c Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:43:34 -0800 Subject: [PATCH 03/12] Darker shadow for contrast --- src/components/chatInput/style.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/chatInput/style.js b/src/components/chatInput/style.js index 597ddf5e06..4d2806f542 100644 --- a/src/components/chatInput/style.js +++ b/src/components/chatInput/style.js @@ -102,10 +102,10 @@ const MentionsInputStyle = { suggestions: { list: { backgroundColor: theme.bg.default, - boxShadow: '1px 0 8px rgba(0,0,0,0.08)', + boxShadow: '1px 0 12px rgba(0,0,0,0.12)', borderRadius: '4px', overflow: 'hidden', - bottom: '24px', + bottom: '28px', position: 'absolute', }, }, From da09c27f110dfe3863be1a109c39a898ff7f2d65 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:46:06 -0800 Subject: [PATCH 04/12] Return original participants if user removes search query --- src/components/chatInput/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index d0ab85b935..635cea4ecd 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -309,7 +309,8 @@ const ChatInput = (props: Props) => { ) : []; callback(filteredParticipants); - if (!queryString || queryString.length === 0) return; + if (!queryString || queryString.length === 0) + return callback(filteredParticipants); const { data: { search }, } = await props.client.query({ From 4e8500341d5a5d06df41e5a2227577100c55e516 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:49:16 -0800 Subject: [PATCH 05/12] If switched thread has no messages, remove participant suggestions --- src/views/thread/container.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/thread/container.js b/src/views/thread/container.js index 0b39bb7021..0ef87f7f1d 100644 --- a/src/views/thread/container.js +++ b/src/views/thread/container.js @@ -401,7 +401,8 @@ 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: [] }); const participants = messageConnection.edges .map(edge => edge.node) .map(node => node.author.user); From 8a16e198c1e538de2c8611f93ed148513dec2059 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 13:59:15 -0800 Subject: [PATCH 06/12] Ensure author is a suggested participant --- src/views/thread/container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/thread/container.js b/src/views/thread/container.js index 0ef87f7f1d..5cd2b70793 100644 --- a/src/views/thread/container.js +++ b/src/views/thread/container.js @@ -402,7 +402,7 @@ class ThreadContainer extends React.Component { updateThreadParticipants = thread => { const { messageConnection, author } = thread; if (!messageConnection || messageConnection.edges.length === 0) - return this.setState({ participants: [] }); + return this.setState({ participants: [author.user] }); const participants = messageConnection.edges .map(edge => edge.node) .map(node => node.author.user); From 284d86cef633cfd20a30a1ca6b43f2f3e01d26ae Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 14:06:44 -0800 Subject: [PATCH 07/12] Better styling, fewer suggestion results --- src/components/chatInput/index.js | 13 +++++++++---- src/components/chatInput/style.js | 29 +++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index 635cea4ecd..347e4f22d0 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} + ); @@ -296,7 +301,7 @@ const ChatInput = (props: Props) => { const filteredParticipants = props.participants ? props.participants .filter(Boolean) - .slice(0, 10) + .slice(0, 8) .filter( participant => participant.username && @@ -325,7 +330,7 @@ const ChatInput = (props: Props) => { let searchUsers = search.searchResultsConnection.edges .filter(Boolean) .filter(edge => edge.node.username) - .slice(0, 10) + .slice(0, 8) .map(edge => { const user = edge.node; return { diff --git a/src/components/chatInput/style.js b/src/components/chatInput/style.js index 4d2806f542..dec730c803 100644 --- a/src/components/chatInput/style.js +++ b/src/components/chatInput/style.js @@ -349,17 +349,34 @@ export const MarkdownHint = styled.div` export const StyledMentionSuggestion = styled.div` display: flex; - padding: 4px 8px; - font-size: 14px; - font-weight: 500; - color: ${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.tertiary}; `; From 9cf4048bfd6dc737d7dbc1e99567c866a62a8a98 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 15:18:00 -0800 Subject: [PATCH 08/12] Improves suggestion sort to factor in name --- src/components/chatInput/index.js | 43 ++++++++++++++++++++++--------- src/views/thread/container.js | 13 ++++++++-- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index 347e4f22d0..99824aa222 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -297,25 +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.name.indexOf(queryString || ''); + const bNameIndex = b.name.indexOf(queryString || ''); + if (aNameIndex === 0) return -1; + if (aUsernameIndex === 0) return -1; + if (aNameIndex === 0) return -1; + if (aUsernameIndex === 0) return -1; + return 1; + }; + const searchUsers = async (queryString, callback) => { const filteredParticipants = props.participants ? props.participants .filter(Boolean) - .slice(0, 8) - .filter( - participant => + .filter(participant => { + return ( participant.username && - participant.username.indexOf(queryString || '') > -1 - ) - .sort( - (a, b) => - a.username.indexOf(queryString || '') - - b.username.indexOf(queryString || '') - ) + (participant.username.indexOf(queryString || '') > -1 || + participant.name.indexOf(queryString || '') > -1) + ); + }) + .sort((a, b) => { + return sortSuggestions(a, b, queryString); + }) + .slice(0, 8) : []; + callback(filteredParticipants); + if (!queryString || queryString.length === 0) return callback(filteredParticipants); + const { data: { search }, } = await props.client.query({ @@ -325,12 +340,12 @@ const ChatInput = (props: Props) => { type: 'USERS', }, }); + if (!search || !search.searchResultsConnection) return; let searchUsers = search.searchResultsConnection.edges .filter(Boolean) .filter(edge => edge.node.username) - .slice(0, 8) .map(edge => { const user = edge.node; return { @@ -338,12 +353,15 @@ const ChatInput = (props: Props) => { id: user.username, display: user.username, username: user.username, + name: 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); @@ -351,7 +369,7 @@ const ChatInput = (props: Props) => { } }); - callback(uniqueResults); + return callback(uniqueResults.slice(0, 8)); }; const networkDisabled = @@ -425,6 +443,7 @@ const ChatInput = (props: Props) => { { updateThreadParticipants = thread => { const { messageConnection, author } = thread; + if (!messageConnection || messageConnection.edges.length === 0) - return this.setState({ participants: [author.user] }); + return this.setState({ + participants: [{ ...author.user, name: 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, + name: user.name.toLowerCase(), + })); return this.setState({ participants: participantsWithAuthor }); }; From 5a7c88125b779454f14e117f216004fe58ee216f Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 15:21:18 -0800 Subject: [PATCH 09/12] Fix capitalization for rendering and filtering --- src/components/chatInput/index.js | 6 +++--- src/views/thread/container.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index 99824aa222..e3923e0b83 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -300,8 +300,8 @@ const ChatInput = (props: Props) => { const sortSuggestions = (a, b, queryString) => { const aUsernameIndex = a.username.indexOf(queryString || ''); const bUsernameIndex = b.username.indexOf(queryString || ''); - const aNameIndex = a.name.indexOf(queryString || ''); - const bNameIndex = b.name.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; @@ -353,7 +353,7 @@ const ChatInput = (props: Props) => { id: user.username, display: user.username, username: user.username, - name: user.name.toLowerCase(), + filterName: user.name.toLowerCase(), }; }); diff --git a/src/views/thread/container.js b/src/views/thread/container.js index e0f60a0f4c..23d8f78be1 100644 --- a/src/views/thread/container.js +++ b/src/views/thread/container.js @@ -404,7 +404,7 @@ class ThreadContainer extends React.Component { if (!messageConnection || messageConnection.edges.length === 0) return this.setState({ - participants: [{ ...author.user, name: user.name.toLowerCase() }], + participants: [{ ...author.user, filterName: user.name.toLowerCase() }], }); const participants = messageConnection.edges @@ -417,7 +417,7 @@ class ThreadContainer extends React.Component { ...user, id: user.username, display: user.username, - name: user.name.toLowerCase(), + filterName: user.name.toLowerCase(), })); return this.setState({ participants: participantsWithAuthor }); From e27b025dd1855d08fac0b24069f43950d58e400f Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 15:25:34 -0800 Subject: [PATCH 10/12] Fix flow --- src/components/chatInput/style.js | 3 +-- src/views/thread/container.js | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/chatInput/style.js b/src/components/chatInput/style.js index dec730c803..45f64a036d 100644 --- a/src/components/chatInput/style.js +++ b/src/components/chatInput/style.js @@ -377,6 +377,5 @@ export const MentionUsername = styled.span` font-weight: 400; width: calc(184px - 62px); ${Truncate}; - color: ${props => - props.focused ? theme.brand.default : theme.text.tertiary}; + color: ${props => (props.focused ? theme.brand.default : theme.text.alt)}; `; diff --git a/src/views/thread/container.js b/src/views/thread/container.js index 23d8f78be1..77eee92908 100644 --- a/src/views/thread/container.js +++ b/src/views/thread/container.js @@ -404,7 +404,9 @@ class ThreadContainer extends React.Component { if (!messageConnection || messageConnection.edges.length === 0) return this.setState({ - participants: [{ ...author.user, filterName: user.name.toLowerCase() }], + participants: [ + { ...author.user, filterName: author.user.name.toLowerCase() }, + ], }); const participants = messageConnection.edges From 38989e3899b5cdd5641d571cbcf6ddd81b086e68 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 15:33:56 -0800 Subject: [PATCH 11/12] Ensure that local results will appear even if no remote results exist --- src/components/chatInput/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index e3923e0b83..f527f65e1b 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -317,7 +317,7 @@ const ChatInput = (props: Props) => { return ( participant.username && (participant.username.indexOf(queryString || '') > -1 || - participant.name.indexOf(queryString || '') > -1) + participant.filterName.indexOf(queryString || '') > -1) ); }) .sort((a, b) => { @@ -341,7 +341,11 @@ const ChatInput = (props: Props) => { }, }); - if (!search || !search.searchResultsConnection) return; + if (!search || !search.searchResultsConnection) { + if (filteredParticipants && filteredParticipants.length > 0) + return filteredParticipants; + return; + } let searchUsers = search.searchResultsConnection.edges .filter(Boolean) From b3bbb40a774747f78026607abae7b40cdb9c5f6f Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 25 Jan 2019 15:42:19 -0800 Subject: [PATCH 12/12] Fix the default sort if search query isn't at the 0 index --- src/components/chatInput/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index f527f65e1b..8604fde31f 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -306,7 +306,7 @@ const ChatInput = (props: Props) => { if (aUsernameIndex === 0) return -1; if (aNameIndex === 0) return -1; if (aUsernameIndex === 0) return -1; - return 1; + return aNameIndex - bNameIndex || aUsernameIndex - bUsernameIndex; }; const searchUsers = async (queryString, callback) => {