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,13 +287,66 @@ 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;
}} }}
> >
{this.renderTextArea()}
</div>
{typingEnabled && (
<SessionIconButton
iconType={SessionIconType.Emoji}
iconSize={SessionIconSize.Large}
onClick={this.toggleEmojiPanel}
/>
)}
<div className="send-message-button">
<SessionIconButton
iconType={SessionIconType.Send}
iconSize={SessionIconSize.Large}
iconRotation={90}
onClick={this.onSendMessage}
/>
</div>
{typingEnabled && (
<div
ref={ref => (this.emojiPanel = ref)}
onKeyDown={this.onKeyDown}
role="button"
>
{showEmojiPanel && (
<SessionEmojiPanel
onEmojiClicked={this.onEmojiClick}
show={showEmojiPanel}
/>
)}
</div>
)}
</>
);
}
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 <MentionsInput
value={this.state.message} value={message}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
placeholder={messagePlaceHolder} placeholder={messagePlaceHolder}
@ -308,16 +355,15 @@ export class SessionCompositionBox extends React.Component<Props, State> {
disabled={!typingEnabled} disabled={!typingEnabled}
maxLength={Constants.CONVERSATION.MAX_MESSAGE_BODY_LENGTH} maxLength={Constants.CONVERSATION.MAX_MESSAGE_BODY_LENGTH}
rows={1} rows={1}
// maxRows={3}
style={sendMessageStyle} style={sendMessageStyle}
suggestionsPortalHost={this.container} suggestionsPortalHost={this.container}
allowSuggestionsAboveCursor={true} allowSuggestionsAboveCursor={true} // if only one suggestion, it might be rendered below
> >
<Mention <Mention
appendSpaceOnAdd={true} appendSpaceOnAdd={true}
markup="@(__id__)" // @__id__ does not work, see cleanMentions() markup="@(__id__)" // @__id__ does not work, see cleanMentions()
trigger="@" trigger="@"
displayTransform={id => `@${id}`} displayTransform={id => `@(${id})`}
data={this.fetchUsersForGroup} data={this.fetchUsersForGroup}
renderSuggestion={( renderSuggestion={(
suggestion, suggestion,
@ -347,41 +393,9 @@ export class SessionCompositionBox extends React.Component<Props, State> {
)} )}
/> />
</MentionsInput> </MentionsInput>
</div>
{typingEnabled && (
<SessionIconButton
iconType={SessionIconType.Emoji}
iconSize={SessionIconSize.Large}
onClick={this.toggleEmojiPanel}
/>
)}
<div className="send-message-button">
<SessionIconButton
iconType={SessionIconType.Send}
iconSize={SessionIconSize.Large}
iconRotation={90}
onClick={this.onSendMessage}
/>
</div>
{typingEnabled && (
<div
ref={ref => (this.emojiPanel = ref)}
onKeyDown={this.onKeyDown}
role="button"
>
{showEmojiPanel && (
<SessionEmojiPanel
onEmojiClicked={this.onEmojiClick}
show={showEmojiPanel}
/>
)}
</div>
)}
</>
); );
} }
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