Skip to content

Commit 7c7a751

Browse files
committed
fix: apply LRM,RLM properly for mention direction
1 parent d3c76d5 commit 7c7a751

File tree

5 files changed

+27
-14
lines changed

5 files changed

+27
-14
lines changed

packages/uikit-react-native-foundation/src/components/Text/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React from 'react';
22
import { I18nManager, Text as RNText, TextProps as RNTextProps, StyleSheet, TextStyle } from 'react-native';
33

4+
import { isStartsWithRTL } from '@sendbird/uikit-utils';
5+
46
import useUIKitTheme from '../../theme/useUIKitTheme';
57
import type { TypoName, UIKitTheme } from '../../types';
6-
import { isStartsWithRTL } from './isStartsWithRTL';
78

89
export interface RTLTextAlignSupportProps {
910
/**

packages/uikit-react-native-foundation/src/components/TextInput/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React from 'react';
22
import { I18nManager, TextInput as RNTextInput, StyleSheet, TextInputProps, TextStyle } from 'react-native';
33

4+
import { isStartsWithRTL } from '@sendbird/uikit-utils';
5+
46
import createStyleSheet from '../../styles/createStyleSheet';
57
import useUIKitTheme from '../../theme/useUIKitTheme';
68
import type { UIKitTheme } from '../../types';
79
import { RTLTextAlignSupportProps } from '../Text';
8-
import { isStartsWithRTL } from '../Text/isStartsWithRTL';
910

1011
type Props = {
1112
variant?: keyof UIKitTheme['colors']['ui']['input'];

packages/uikit-react-native/src/libs/MentionManager.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import React from 'react';
2-
import { I18nManager } from 'react-native';
32

43
import { Text, createStyleSheet } from '@sendbird/uikit-react-native-foundation';
54
import type { SendbirdFileMessage, SendbirdUser, SendbirdUserMessage } from '@sendbird/uikit-utils';
6-
import { createMentionTemplateRegex, replaceWithRegex } from '@sendbird/uikit-utils';
5+
import { createMentionTemplateRegex, isEndsWithRTL, replaceWithRegex } from '@sendbird/uikit-utils';
76

87
import type { MentionedUser, Range } from '../types';
98
import type { MentionConfigInterface } from './MentionConfig';
@@ -22,14 +21,17 @@ class MentionManager {
2221
this._templateRegex = createMentionTemplateRegex(this.config.trigger);
2322
}
2423

25-
// Note: When the input starts in LTR and a mentioned user's name is in RTL, it appears as "Hello @{cibarA}."
24+
// Note: When the input starts in LTR and the mentioned user's name is in RTL, it appears as "Hello @{cibarA}."
2625
// If typing continues in RTL, the mention is rendered as: "Hello @{txeTlanoitiddA}{cibarA}."
2726
//
28-
// This appears natural in RTL, but the mention syntax becomes unclear.
29-
// To address this, if RTL is active, we reset subsequent spans using the LRM(Left-To-Right-Mark) Unicode character.
30-
// By applying this trick, the result will be "Hello @{cibarA} {txeTlanoitiddA}," where the mention block remains distinct.
31-
getDirectionOfNextSpan = () => {
32-
return I18nManager.isRTL ? SPAN_DIRECTION.LRM : '';
27+
// Conversely, if the input starts in RTL and the mentioned user's name is in LTR, it appears as "{Eng}@ cibarA."
28+
// If typing continues, it is rendered as: "{Eng}{AdditionalText}@ cibarA."
29+
//
30+
// While this follows the natural text direction, it can make mentions harder to distinguish.
31+
// To address this, we use the RLM or LRM Unicode characters to reset subsequent spans based on the last text string of the user's name.
32+
// By applying this trick, the result will be displayed as "Hello @{cibarA} {txeTlanoitiddA}" or "{AdditionalText} {Eng}@ cibarA," ensuring the mention block remains clearly distinguishable.
33+
getDirectionOfNextSpan = (name: string) => {
34+
return isEndsWithRTL(name) ? SPAN_DIRECTION.LRM : SPAN_DIRECTION.RLM;
3335
};
3436

3537
public rangeHelpers = {
@@ -136,7 +138,7 @@ class MentionManager {
136138
public asMentionedMessageText = (user: SendbirdUser, delimiter = false) => {
137139
const prefix = '';
138140
const content = `${this.config.trigger}${user.nickname}`;
139-
const postfix = this.getDirectionOfNextSpan() + (delimiter ? this.config.delimiter : '');
141+
const postfix = this.getDirectionOfNextSpan(user.nickname) + (delimiter ? this.config.delimiter : '');
140142

141143
return prefix + content + postfix;
142144
};

packages/uikit-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export { default as arrayToMap, arrayToMapWithGetter } from './shared/arrayToMap
33
export * from './shared/regex';
44
export * from './shared/bufferedRequest';
55
export * from './shared/file';
6+
export * from './shared/rtl';
67
export * from './shared';
78

89
export * from './hooks';

packages/uikit-react-native-foundation/src/components/Text/isStartsWithRTL.ts renamed to packages/uikit-utils/src/shared/rtl.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const isStartsWithRTL = (str?: string): boolean => {
1+
const isRTLString = (dir: 'start' | 'end', str?: string) => {
22
if (!str || str.length === 0) {
33
return false;
44
}
@@ -8,15 +8,23 @@ export const isStartsWithRTL = (str?: string): boolean => {
88
return false;
99
}
1010

11-
const firstChar = Array.from(trimmedStr)[0];
12-
const point = firstChar.codePointAt(0);
11+
const char = dir === 'start' ? Array.from(trimmedStr)[0] : Array.from(trimmedStr).pop();
12+
const point = char?.codePointAt(0);
1313
if (point === undefined) {
1414
return false;
1515
}
1616

1717
return isRTLCodePoint(point);
1818
};
1919

20+
export const isStartsWithRTL = (str?: string): boolean => {
21+
return isRTLString('start', str);
22+
};
23+
24+
export const isEndsWithRTL = (str?: string): boolean => {
25+
return isRTLString('end', str);
26+
};
27+
2028
const isRTLCodePoint = (codePoint: number) => {
2129
for (const [start, end] of rtlCodePointRanges) {
2230
if (codePoint >= start && codePoint <= end) {

0 commit comments

Comments
 (0)