feat: made htmlDirection part of the compositionBox state for easier referencing

user mentions and emoji quick results now support RTL
pull/2796/head
William Grant 2 years ago
parent 435bdb8f8a
commit 1f1bb702c3

@ -57,7 +57,7 @@ import {
getSelectedConversationKey, getSelectedConversationKey,
} from '../../../state/selectors/selectedConversation'; } from '../../../state/selectors/selectedConversation';
import { SettingsKey } from '../../../data/settings-key'; import { SettingsKey } from '../../../data/settings-key';
import { getHTMLDirection } from '../../../util/i18n'; import { getHTMLDirection, HTMLDirection } from '../../../util/i18n';
export interface ReplyingToMessageProps { export interface ReplyingToMessageProps {
convoId: string; convoId: string;
@ -114,28 +114,31 @@ interface State {
ignoredLink?: string; // set the ignored url when users closed the link preview ignoredLink?: string; // set the ignored url when users closed the link preview
stagedLinkPreview?: StagedLinkPreviewData; stagedLinkPreview?: StagedLinkPreviewData;
showCaptionEditor?: AttachmentType; showCaptionEditor?: AttachmentType;
htmlDirection?: HTMLDirection;
} }
const sendMessageStyle = { const sendMessageStyle = (dir: HTMLDirection) => {
control: { return {
wordBreak: 'break-all', control: {
}, wordBreak: 'break-all',
input: { },
overflow: 'auto', input: {
maxHeight: '50vh', overflow: 'auto',
wordBreak: 'break-word', maxHeight: '50vh',
padding: '0px', wordBreak: 'break-word',
margin: '0px', padding: '0px',
}, margin: '0px',
highlighter: { },
boxSizing: 'border-box', highlighter: {
overflow: 'hidden', boxSizing: 'border-box',
maxHeight: '50vh', overflow: 'hidden',
}, maxHeight: '50vh',
flexGrow: 1, },
minHeight: '24px', flexGrow: 1,
width: '100%', minHeight: '24px',
...styleForCompositionBoxSuggestions, width: '100%',
...styleForCompositionBoxSuggestions(dir),
};
}; };
const getDefaultState = (newConvoId?: string) => { const getDefaultState = (newConvoId?: string) => {
@ -146,6 +149,7 @@ const getDefaultState = (newConvoId?: string) => {
ignoredLink: undefined, ignoredLink: undefined,
stagedLinkPreview: undefined, stagedLinkPreview: undefined,
showCaptionEditor: undefined, showCaptionEditor: undefined,
htmlDirection: getHTMLDirection(),
}; };
}; };
@ -206,7 +210,7 @@ const getSelectionBasedOnMentions = (draft: string, index: number) => {
return Number.MAX_SAFE_INTEGER; return Number.MAX_SAFE_INTEGER;
}; };
const StyledEmojiPanelContainer = styled.div<{ dir: string }>` const StyledEmojiPanelContainer = styled.div<{ dir?: HTMLDirection }>`
${StyledEmojiPanel} { ${StyledEmojiPanel} {
position: absolute; position: absolute;
bottom: 68px; bottom: 68px;
@ -214,7 +218,8 @@ const StyledEmojiPanelContainer = styled.div<{ dir: string }>`
} }
`; `;
const StyledSendMessageInput = styled.div<{ dir: string }>` const StyledSendMessageInput = styled.div<{ dir?: HTMLDirection }>`
position: relative;
cursor: text; cursor: text;
display: flex; display: flex;
align-items: center; align-items: center;
@ -233,7 +238,7 @@ const StyledSendMessageInput = styled.div<{ dir: string }>`
textarea { textarea {
font-family: var(--font-default); font-family: var(--font-default);
min-height: calc(var(--composition-container-height) / 3); min-height: calc(var(--composition-container-height) / 3);
max-height: 3 * var(--composition-container-height); max-height: calc(3 * var(--composition-container-height));
margin-right: var(--margins-md); margin-right: var(--margins-md);
color: var(--text-color-primary); color: var(--text-color-primary);
@ -411,11 +416,9 @@ class CompositionBoxInner extends React.Component<Props, State> {
const { showEmojiPanel } = this.state; const { showEmojiPanel } = this.state;
const { typingEnabled } = this.props; const { typingEnabled } = this.props;
const htmlDirection = getHTMLDirection();
return ( return (
<Flex <Flex
dir={htmlDirection} dir={this.state.htmlDirection}
container={true} container={true}
flexDirection={'row'} flexDirection={'row'}
alignItems={'center'} alignItems={'center'}
@ -436,14 +439,14 @@ class CompositionBoxInner extends React.Component<Props, State> {
<StyledSendMessageInput <StyledSendMessageInput
role="main" role="main"
dir={htmlDirection} dir={this.state.htmlDirection}
onClick={this.focusCompositionBox} // used to focus on the textarea when clicking in its container onClick={this.focusCompositionBox} // used to focus on the textarea when clicking in its container
ref={el => { ref={el => {
this.container = el; this.container = el;
}} }}
data-testid="message-input" data-testid="message-input"
> >
{this.renderTextArea(htmlDirection)} {this.renderTextArea()}
</StyledSendMessageInput> </StyledSendMessageInput>
{typingEnabled && ( {typingEnabled && (
@ -452,7 +455,7 @@ class CompositionBoxInner extends React.Component<Props, State> {
<SendMessageButton onClick={this.onSendMessage} /> <SendMessageButton onClick={this.onSendMessage} />
{typingEnabled && showEmojiPanel && ( {typingEnabled && showEmojiPanel && (
<StyledEmojiPanelContainer role="button" dir={htmlDirection}> <StyledEmojiPanelContainer role="button" dir={this.state.htmlDirection}>
<SessionEmojiPanel <SessionEmojiPanel
ref={this.emojiPanel} ref={this.emojiPanel}
show={showEmojiPanel} show={showEmojiPanel}
@ -465,9 +468,9 @@ class CompositionBoxInner extends React.Component<Props, State> {
); );
} }
private renderTextArea(dir: string) { private renderTextArea() {
const { i18n } = window; const { i18n } = window;
const { draft } = this.state; const { draft, htmlDirection } = this.state;
if (!this.props.selectedConversation) { if (!this.props.selectedConversation) {
return null; return null;
@ -494,6 +497,8 @@ class CompositionBoxInner extends React.Component<Props, State> {
const { typingEnabled } = this.props; const { typingEnabled } = this.props;
const neverMatchingRegex = /($a)/; const neverMatchingRegex = /($a)/;
const style = sendMessageStyle(htmlDirection || 'ltr');
return ( return (
<MentionsInput <MentionsInput
value={draft} value={draft}
@ -502,12 +507,12 @@ class CompositionBoxInner extends React.Component<Props, State> {
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}
placeholder={messagePlaceHolder} placeholder={messagePlaceHolder}
spellCheck={true} spellCheck={true}
dir={dir} dir={htmlDirection}
inputRef={this.textarea} inputRef={this.textarea}
disabled={!typingEnabled} disabled={!typingEnabled}
rows={1} rows={1}
data-testid="message-input-text-area" data-testid="message-input-text-area"
style={sendMessageStyle} style={style}
suggestionsPortalHost={this.container as any} suggestionsPortalHost={this.container as any}
forceSuggestionsAboveCursor={true} // force mentions to be rendered on top of the cursor, this is working with a fork of react-mentions for now forceSuggestionsAboveCursor={true} // force mentions to be rendered on top of the cursor, this is working with a fork of react-mentions for now
> >
@ -517,7 +522,9 @@ class CompositionBoxInner extends React.Component<Props, State> {
markup="@ᅭ__id__ᅲ__display__ᅭ" // ᅭ = \uFFD2 is one of the forbidden char for a display name (check displayNameRegex) markup="@ᅭ__id__ᅲ__display__ᅭ" // ᅭ = \uFFD2 is one of the forbidden char for a display name (check displayNameRegex)
trigger="@" trigger="@"
// this is only for the composition box visible content. The real stuff on the backend box is the @markup // this is only for the composition box visible content. The real stuff on the backend box is the @markup
displayTransform={(_id, display) => (dir === 'rtl' ? `${display}@` : `@${display}`)} displayTransform={(_id, display) =>
htmlDirection === 'rtl' ? `${display}@` : `@${display}`
}
data={this.fetchUsersForGroup} data={this.fetchUsersForGroup}
renderSuggestion={renderUserMentionRow} renderSuggestion={renderUserMentionRow}
/> />

@ -8,6 +8,7 @@ import { searchSync } from '../../../util/emoji.js';
const EmojiQuickResult = styled.span` const EmojiQuickResult = styled.span`
display: flex; display: flex;
align-items: center; align-items: center;
min-width: 250px;
width: 100%; width: 100%;
padding-inline-end: 20px; padding-inline-end: 20px;
padding-inline-start: 10px; padding-inline-start: 10px;

@ -1,28 +1,40 @@
import React from 'react'; import React from 'react';
import { SuggestionDataItem } from 'react-mentions'; import { SuggestionDataItem } from 'react-mentions';
import { MemberListItem } from '../../MemberListItem'; import { MemberListItem } from '../../MemberListItem';
import { HTMLDirection } from '../../../util/i18n';
export const styleForCompositionBoxSuggestions = { const listRTLStyle = { position: 'absolute', bottom: '0px', right: '100%' };
suggestions: {
list: { export const styleForCompositionBoxSuggestions = (dir: HTMLDirection) => {
fontSize: 14, const styles = {
boxShadow: 'var(--suggestions-shadow)', suggestions: {
backgroundColor: 'var(--suggestions-background-color)', list: {
color: 'var(--suggestions-text-color)', fontSize: 14,
}, boxShadow: 'var(--suggestions-shadow)',
item: { backgroundColor: 'var(--suggestions-background-color)',
height: '100%', color: 'var(--suggestions-text-color)',
paddingTop: '5px', dir,
paddingBottom: '5px', },
backgroundColor: 'var(--suggestions-background-color)', item: {
color: 'var(--suggestions-text-color)', height: '100%',
transition: '0.25s', paddingTop: '5px',
paddingBottom: '5px',
'&focused': { backgroundColor: 'var(--suggestions-background-color)',
backgroundColor: 'var(--suggestions-background-hover-color)', color: 'var(--suggestions-text-color)',
transition: '0.25s',
'&focused': {
backgroundColor: 'var(--suggestions-background-hover-color)',
},
}, },
}, },
}, };
if (dir === 'rtl') {
styles.suggestions.list = { ...styles.suggestions.list, ...listRTLStyle };
}
return styles;
}; };
export const renderUserMentionRow = (suggestion: SuggestionDataItem) => { export const renderUserMentionRow = (suggestion: SuggestionDataItem) => {

Loading…
Cancel
Save