make mentions work in react when emoji inserted inside at a rand pos

pull/1381/head
Audric Ackermann 5 years ago
parent b5af8eb215
commit c9e81454fb
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -12,7 +12,6 @@
min-width: 20px; min-width: 20px;
} }
.existing-member { .existing-member {
color: green; color: green;
} }
@ -42,7 +41,6 @@
} }
} }
.invisible { .invisible {
visibility: hidden; visibility: hidden;
} }
@ -69,8 +67,6 @@
} }
} }
.mention-profile-name { .mention-profile-name {
color: rgb(194, 244, 255); color: rgb(194, 244, 255);
background-color: rgb(66, 121, 150); background-color: rgb(66, 121, 150);

@ -28,7 +28,13 @@ export class MemberItem extends React.Component<MemberItemProps> {
} }
public render() { public render() {
const {authorProfileName: name, authorPhoneNumber: pubkey, selected, existingMember, checkmarked} = this.props.member; const {
authorProfileName: name,
authorPhoneNumber: pubkey,
selected,
existingMember,
checkmarked,
} = this.props.member;
const shortPubkey = window.shortenPubkey(pubkey); const shortPubkey = window.shortenPubkey(pubkey);
let markType: 'none' | 'kicked' | 'added' | 'existing' = 'none'; let markType: 'none' | 'kicked' | 'added' | 'existing' = 'none';
@ -80,7 +86,6 @@ export class MemberItem extends React.Component<MemberItemProps> {
</div> </div>
); );
} }
private handleClick() { private handleClick() {
this.props.onClicked(this.props.member); this.props.onClicked(this.props.member);
} }

@ -15,6 +15,7 @@ const SessionToastContainerPrivate = () => {
draggable={true} draggable={true}
pauseOnHover={true} pauseOnHover={true}
transition={Slide} transition={Slide}
limit={5}
/> />
); );
}; };

@ -124,7 +124,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
private emojiPanel: any; private emojiPanel: any;
private linkPreviewAbortController?: AbortController; private linkPreviewAbortController?: AbortController;
private container: any; private container: any;
private mentionsData: Array<{ display: string; id: string }>; private readonly mentionsRegex = /@\(05[0-9a-f]{64}\)/g;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
@ -132,7 +132,6 @@ export class SessionCompositionBox extends React.Component<Props, State> {
this.textarea = props.textarea; this.textarea = props.textarea;
this.fileInput = React.createRef(); this.fileInput = React.createRef();
this.mentionsData = [];
// Emojis // Emojis
this.emojiPanel = null; this.emojiPanel = null;
@ -143,9 +142,10 @@ export class SessionCompositionBox extends React.Component<Props, State> {
this.renderRecordingView = this.renderRecordingView.bind(this); this.renderRecordingView = this.renderRecordingView.bind(this);
this.renderCompositionView = this.renderCompositionView.bind(this); this.renderCompositionView = this.renderCompositionView.bind(this);
this.renderTextArea = this.renderTextArea.bind(this);
this.renderQuotedMessage = this.renderQuotedMessage.bind(this); this.renderQuotedMessage = this.renderQuotedMessage.bind(this);
this.renderStagedLinkPreview = this.renderStagedLinkPreview.bind(this);
this.renderStagedLinkPreview = this.renderStagedLinkPreview.bind(this);
this.renderAttachmentsStaged = this.renderAttachmentsStaged.bind(this); this.renderAttachmentsStaged = this.renderAttachmentsStaged.bind(this);
// Recording view functions // Recording view functions
@ -185,7 +185,6 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// reset the state on new conversation key // reset the state on new conversation key
if (prevProps.conversationKey !== this.props.conversationKey) { if (prevProps.conversationKey !== this.props.conversationKey) {
this.setState(getDefaultState()); this.setState(getDefaultState());
this.mentionsData = [];
} }
} }
@ -248,20 +247,15 @@ export class SessionCompositionBox extends React.Component<Props, State> {
); );
} }
private renderCompositionView() { private isTypingEnabled(): boolean {
const { isBlocked, isKickedFromGroup, leftGroup, isPrivate } = this.props; const { isBlocked, isKickedFromGroup, leftGroup, isPrivate } = this.props;
const { showEmojiPanel, message } = this.state;
const typingEnabled = !(isBlocked || isKickedFromGroup || leftGroup); return !(isBlocked || isKickedFromGroup || leftGroup);
const { i18n } = window; }
const messagePlaceHolder = isKickedFromGroup
? i18n('youGotKickedFromGroup') private renderCompositionView() {
: leftGroup const { showEmojiPanel } = this.state;
? i18n('youLeftTheGroup') const typingEnabled = this.isTypingEnabled();
: isBlocked && isPrivate
? i18n('unblockToSend')
: isBlocked && !isPrivate
? i18n('unblockGroupToSend')
: i18n('sendMessage');
return ( return (
<> <>
@ -293,60 +287,12 @@ export class SessionCompositionBox extends React.Component<Props, State> {
<div <div
className="send-message-input" className="send-message-input"
role="main" role="main"
onClick={this.focusCompositionBox} onClick={this.focusCompositionBox} // used to focus on the textarea when clicking in its container
ref={el => { ref={el => {
this.container = el; this.container = el;
}} }}
> >
<MentionsInput {this.renderTextArea()}
value={this.state.message}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
placeholder={messagePlaceHolder}
spellCheck={false}
inputRef={this.textarea}
disabled={!typingEnabled}
maxLength={Constants.CONVERSATION.MAX_MESSAGE_BODY_LENGTH}
rows={1}
// maxRows={3}
style={sendMessageStyle}
suggestionsPortalHost={this.container}
allowSuggestionsAboveCursor={true}
>
<Mention
appendSpaceOnAdd={true}
markup="@(__id__)" // @__id__ does not work, see cleanMentions()
trigger="@"
displayTransform={id => `@${id}`}
data={this.fetchUsersForGroup}
renderSuggestion={(
suggestion,
_search,
_highlightedDisplay,
_index,
focused
) => (
<MemberItem
i18n={window.i18n}
selected={focused}
// tslint:disable-next-line: no-empty
onClicked={() => {}}
existingMember={false}
member={{
id: `${suggestion.id}`,
authorPhoneNumber: `${suggestion.id}`,
selected: false,
authorProfileName: `${suggestion.display}`,
authorName: `${suggestion.display}`,
existingMember: false,
checkmarked: false,
authorAvatarPath: '',
}}
checkmarked={false}
/>
)}
/>
</MentionsInput>
</div> </div>
{typingEnabled && ( {typingEnabled && (
@ -382,6 +328,74 @@ export class SessionCompositionBox extends React.Component<Props, State> {
</> </>
); );
} }
private renderTextArea() {
const { i18n } = window;
const { message } = this.state;
const { isKickedFromGroup, leftGroup, isPrivate, isBlocked } = this.props;
const messagePlaceHolder = isKickedFromGroup
? i18n('youGotKickedFromGroup')
: leftGroup
? i18n('youLeftTheGroup')
: isBlocked && isPrivate
? i18n('unblockToSend')
: isBlocked && !isPrivate
? i18n('unblockGroupToSend')
: i18n('sendMessage');
const typingEnabled = this.isTypingEnabled();
return (
<MentionsInput
value={message}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
placeholder={messagePlaceHolder}
spellCheck={false}
inputRef={this.textarea}
disabled={!typingEnabled}
maxLength={Constants.CONVERSATION.MAX_MESSAGE_BODY_LENGTH}
rows={1}
style={sendMessageStyle}
suggestionsPortalHost={this.container}
allowSuggestionsAboveCursor={true} // if only one suggestion, it might be rendered below
>
<Mention
appendSpaceOnAdd={true}
markup="@(__id__)" // @__id__ does not work, see cleanMentions()
trigger="@"
displayTransform={id => `@(${id})`}
data={this.fetchUsersForGroup}
renderSuggestion={(
suggestion,
_search,
_highlightedDisplay,
_index,
focused
) => (
<MemberItem
i18n={window.i18n}
selected={focused}
// tslint:disable-next-line: no-empty
onClicked={() => {}}
existingMember={false}
member={{
id: `${suggestion.id}`,
authorPhoneNumber: `${suggestion.id}`,
selected: false,
authorProfileName: `${suggestion.display}`,
authorName: `${suggestion.display}`,
existingMember: false,
checkmarked: false,
authorAvatarPath: '',
}}
checkmarked={false}
/>
)}
/>
</MentionsInput>
);
}
private fetchUsersForGroup(query: any, callback: any) { private fetchUsersForGroup(query: any, callback: any) {
if (!query) { if (!query) {
return; return;
@ -451,13 +465,12 @@ export class SessionCompositionBox extends React.Component<Props, State> {
.filter(d => .filter(d =>
d.authorProfileName?.toLowerCase()?.includes(query.toLowerCase()) d.authorProfileName?.toLowerCase()?.includes(query.toLowerCase())
); );
// Transform the users to what react-mentions expects
// Transform the users to what react-mentions expects
const mentionsData = members.map(user => ({ const mentionsData = members.map(user => ({
display: user.authorProfileName, display: user.authorProfileName,
id: user.authorPhoneNumber, id: user.authorPhoneNumber,
})); }));
this.mentionsData = mentionsData;
callback(mentionsData); callback(mentionsData);
} }
@ -654,10 +667,9 @@ export class SessionCompositionBox extends React.Component<Props, State> {
// tslint:disable-next-line: cyclomatic-complexity // tslint:disable-next-line: cyclomatic-complexity
private async onSendMessage() { private async onSendMessage() {
// replace all @(xxx) by @xxx // this is dirty but we have to replace all @(xxx) by @xxx manually here
const cleanMentions = (text: string): string => { const cleanMentions = (text: string): string => {
const mentionRegex = /@\(05[0-9a-f]{64}\)/g; const matches = text.match(this.mentionsRegex);
const matches = text.match(mentionRegex);
let replacedMentions = text; let replacedMentions = text;
(matches || []).forEach(match => { (matches || []).forEach(match => {
const replacedMention = match.substring(2, match.length - 1); const replacedMention = match.substring(2, match.length - 1);
@ -847,13 +859,14 @@ export class SessionCompositionBox extends React.Component<Props, State> {
this.setState({ message }); this.setState({ message });
} }
private onEmojiClick({ colons, native }: { colons: string; native: string }) { private onEmojiClick({ colons }: { colons: string }) {
const messageBox = this.textarea.current; const messageBox = this.textarea.current;
if (!messageBox) { if (!messageBox) {
return; return;
} }
const { message } = this.state; const { message } = this.state;
const currentSelectionStart = Number(messageBox.selectionStart); const currentSelectionStart = Number(messageBox.selectionStart);
const currentSelectionEnd = Number(messageBox.selectionEnd); const currentSelectionEnd = Number(messageBox.selectionEnd);

Loading…
Cancel
Save