Changeset View
Changeset View
Standalone View
Standalone View
web/chat/chat-input-bar.react.js
Show All 9 Lines | import { | ||||
newThreadActionTypes, | newThreadActionTypes, | ||||
} from 'lib/actions/thread-actions'; | } from 'lib/actions/thread-actions'; | ||||
import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; | import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; | ||||
import { | import { | ||||
userStoreSearchIndex, | userStoreSearchIndex, | ||||
relativeMemberInfoSelectorForMembersOfThread, | relativeMemberInfoSelectorForMembersOfThread, | ||||
} from 'lib/selectors/user-selectors'; | } from 'lib/selectors/user-selectors'; | ||||
import { localIDPrefix, trimMessage } from 'lib/shared/message-utils'; | import { localIDPrefix, trimMessage } from 'lib/shared/message-utils'; | ||||
import SearchIndex from 'lib/shared/search-index'; | |||||
import { | import { | ||||
threadHasPermission, | threadHasPermission, | ||||
viewerIsMember, | viewerIsMember, | ||||
threadFrozenDueToViewerBlock, | threadFrozenDueToViewerBlock, | ||||
threadActualMembers, | threadActualMembers, | ||||
checkIfDefaultMembersAreVoiced, | checkIfDefaultMembersAreVoiced, | ||||
} from 'lib/shared/thread-utils'; | } from 'lib/shared/thread-utils'; | ||||
import { getTypeaheadUserSuggestions } from 'lib/shared/typeahead-utils'; | |||||
import type { CalendarQuery } from 'lib/types/entry-types'; | import type { CalendarQuery } from 'lib/types/entry-types'; | ||||
import type { LoadingStatus } from 'lib/types/loading-types'; | import type { LoadingStatus } from 'lib/types/loading-types'; | ||||
import { messageTypes } from 'lib/types/message-types'; | import { messageTypes } from 'lib/types/message-types'; | ||||
import { | import { | ||||
type ThreadInfo, | type ThreadInfo, | ||||
threadPermissions, | threadPermissions, | ||||
type ClientThreadJoinRequest, | type ClientThreadJoinRequest, | ||||
type ThreadJoinPayload, | type ThreadJoinPayload, | ||||
Show All 34 Lines | type Props = { | ||||
+calendarQuery: () => CalendarQuery, | +calendarQuery: () => CalendarQuery, | ||||
+nextLocalID: number, | +nextLocalID: number, | ||||
+isThreadActive: boolean, | +isThreadActive: boolean, | ||||
+userInfos: UserInfos, | +userInfos: UserInfos, | ||||
// Redux dispatch functions | // Redux dispatch functions | ||||
+dispatchActionPromise: DispatchActionPromise, | +dispatchActionPromise: DispatchActionPromise, | ||||
// async functions that hit server APIs | // async functions that hit server APIs | ||||
+joinThread: (request: ClientThreadJoinRequest) => Promise<ThreadJoinPayload>, | +joinThread: (request: ClientThreadJoinRequest) => Promise<ThreadJoinPayload>, | ||||
+userSearchIndex: SearchIndex, | |||||
+threadMembers: $ReadOnlyArray<RelativeMemberInfo>, | |||||
+typeaheadMatchedStrings: ?TypeaheadMatchedStrings, | +typeaheadMatchedStrings: ?TypeaheadMatchedStrings, | ||||
+suggestedUsers: $ReadOnlyArray<RelativeMemberInfo>, | |||||
}; | }; | ||||
export type TypeaheadMatchedStrings = { | export type TypeaheadMatchedStrings = { | ||||
+entireText: string, | +entireText: string, | ||||
+textBeforeAtSymbol: string, | +textBeforeAtSymbol: string, | ||||
+usernamePrefix: string, | +usernamePrefix: string, | ||||
}; | }; | ||||
class ChatInputBar extends React.PureComponent<Props> { | class ChatInputBar extends React.PureComponent<Props> { | ||||
textarea: ?HTMLTextAreaElement; | textarea: ?HTMLTextAreaElement; | ||||
▲ Show 20 Lines • Show All 239 Lines • ▼ Show 20 Lines | ) { | ||||
content = ( | content = ( | ||||
<span className={css.explanation}> | <span className={css.explanation}> | ||||
You don't have permission to send messages. | You don't have permission to send messages. | ||||
</span> | </span> | ||||
); | ); | ||||
} | } | ||||
let typeaheadTooltip; | let typeaheadTooltip; | ||||
if (this.props.typeaheadMatchedStrings && this.textarea) { | if ( | ||||
this.props.suggestedUsers.length > 0 && | |||||
this.props.typeaheadMatchedStrings && | |||||
this.textarea | |||||
) { | |||||
typeaheadTooltip = ( | typeaheadTooltip = ( | ||||
<TypeaheadTooltip | <TypeaheadTooltip | ||||
inputState={this.props.inputState} | inputState={this.props.inputState} | ||||
textarea={this.textarea} | textarea={this.textarea} | ||||
userSearchIndex={this.props.userSearchIndex} | |||||
threadMembers={this.props.threadMembers} | |||||
viewerID={this.props.viewerID} | |||||
matchedStrings={this.props.typeaheadMatchedStrings} | matchedStrings={this.props.typeaheadMatchedStrings} | ||||
suggestedUsers={this.props.suggestedUsers} | |||||
/> | /> | ||||
); | ); | ||||
} | } | ||||
return ( | return ( | ||||
<div className={css.inputBar}> | <div className={css.inputBar}> | ||||
{joinButton} | {joinButton} | ||||
{previews} | {previews} | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | const typeaheadRegexMatches = React.useMemo( | ||||
], | ], | ||||
); | ); | ||||
const typeaheadMatchedStrings: ?TypeaheadMatchedStrings = React.useMemo( | const typeaheadMatchedStrings: ?TypeaheadMatchedStrings = React.useMemo( | ||||
() => | () => | ||||
typeaheadRegexMatches !== null | typeaheadRegexMatches !== null | ||||
? { | ? { | ||||
entireText: typeaheadRegexMatches[0], | entireText: typeaheadRegexMatches[0], | ||||
textBeforeAtSymbol: typeaheadRegexMatches[1], | textBeforeAtSymbol: | ||||
usernamePrefix: typeaheadRegexMatches[2], | typeaheadRegexMatches.groups?.textPrefix ?? '', | ||||
usernamePrefix: typeaheadRegexMatches.groups?.username ?? '', | |||||
} | } | ||||
: null, | : null, | ||||
[typeaheadRegexMatches], | [typeaheadRegexMatches], | ||||
); | ); | ||||
const suggestedUsers: $ReadOnlyArray<RelativeMemberInfo> = React.useMemo(() => { | |||||
if (!typeaheadMatchedStrings) { | |||||
return []; | |||||
} | |||||
return getTypeaheadUserSuggestions( | |||||
userSearchIndex, | |||||
threadMembers, | |||||
viewerID, | |||||
typeaheadMatchedStrings.usernamePrefix, | |||||
); | |||||
}, [userSearchIndex, threadMembers, viewerID, typeaheadMatchedStrings]); | |||||
return ( | return ( | ||||
<ChatInputBar | <ChatInputBar | ||||
{...props} | {...props} | ||||
viewerID={viewerID} | viewerID={viewerID} | ||||
joinThreadLoadingStatus={joinThreadLoadingStatus} | joinThreadLoadingStatus={joinThreadLoadingStatus} | ||||
threadCreationInProgress={threadCreationInProgress} | threadCreationInProgress={threadCreationInProgress} | ||||
calendarQuery={calendarQuery} | calendarQuery={calendarQuery} | ||||
nextLocalID={nextLocalID} | nextLocalID={nextLocalID} | ||||
isThreadActive={isThreadActive} | isThreadActive={isThreadActive} | ||||
userInfos={userInfos} | userInfos={userInfos} | ||||
dispatchActionPromise={dispatchActionPromise} | dispatchActionPromise={dispatchActionPromise} | ||||
joinThread={callJoinThread} | joinThread={callJoinThread} | ||||
userSearchIndex={userSearchIndex} | |||||
threadMembers={threadMembers} | |||||
typeaheadMatchedStrings={typeaheadMatchedStrings} | typeaheadMatchedStrings={typeaheadMatchedStrings} | ||||
suggestedUsers={suggestedUsers} | |||||
/> | /> | ||||
); | ); | ||||
}, | }, | ||||
); | ); | ||||
export default ConnectedChatInputBar; | export default ConnectedChatInputBar; |