Fix RTL post alignment on native
This commit is contained in:
@@ -326,6 +326,11 @@ export function QuoteEmbed({
|
||||
{richText ? (
|
||||
<RichText
|
||||
value={richText}
|
||||
language={
|
||||
bsky.isType(app.bsky.feed.post, quote.record)
|
||||
? quote.record.langs?.[0]
|
||||
: undefined
|
||||
}
|
||||
style={a.text_md}
|
||||
numberOfLines={20}
|
||||
disableLinks
|
||||
|
||||
@@ -2,8 +2,9 @@ import {useMemo} from 'react'
|
||||
import {type StyleProp, type TextStyle} from 'react-native'
|
||||
import {RichText as RichTextAPI} from '@bsky/sdk/richtext'
|
||||
|
||||
import {isRTL} from '#/lib/strings/bidi'
|
||||
import {toShortUrl} from '#/lib/strings/url-helpers'
|
||||
import {android, atoms as a, flatten, type TextStyleProp} from '#/alf'
|
||||
import {android, atoms as a, flatten, native, type TextStyleProp} from '#/alf'
|
||||
import {isOnlyEmoji} from '#/alf/typography'
|
||||
import {InlineLinkText, type LinkProps} from '#/components/Link'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
@@ -20,6 +21,7 @@ const URL_REGEX =
|
||||
export type RichTextProps = TextStyleProp &
|
||||
Pick<TextProps, 'selectable' | 'onLayout' | 'onTextLayout'> & {
|
||||
value: RichTextAPI | string
|
||||
language?: string
|
||||
testID?: string
|
||||
numberOfLines?: number
|
||||
disableLinks?: boolean
|
||||
@@ -56,6 +58,7 @@ export type RichTextProps = TextStyleProp &
|
||||
export function RichText({
|
||||
testID,
|
||||
value,
|
||||
language,
|
||||
style,
|
||||
numberOfLines,
|
||||
disableLinks,
|
||||
@@ -82,7 +85,10 @@ export function RichText({
|
||||
}
|
||||
}, [value])
|
||||
|
||||
const plainStyles = style
|
||||
const plainStyles = [
|
||||
style,
|
||||
isRTL(language) ? native({textAlign: 'right'}) : null,
|
||||
]
|
||||
const suffixStyles =
|
||||
suffix && suffixOffset
|
||||
? android({paddingBottom: suffixOffset, marginBottom: -suffixOffset})
|
||||
|
||||
@@ -11,7 +11,7 @@ jest.mock('#/env', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
import {forceLTR} from '../bidi'
|
||||
import {forceLTR, isRTL} from '../bidi'
|
||||
|
||||
const LEFT_TO_RIGHT_EMBEDDING = '\u202A'
|
||||
const POP_DIRECTIONAL_FORMATTING = '\u202C'
|
||||
@@ -31,3 +31,21 @@ describe('forceLTR', () => {
|
||||
expect(forceLTR('@alice.bsky.social')).toBe('@alice.bsky.social')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isRTL', () => {
|
||||
it('recognizes right-to-left languages and scripts', () => {
|
||||
expect(isRTL('he')).toBe(true)
|
||||
expect(isRTL('ar')).toBe(true)
|
||||
expect(isRTL('az-Arab')).toBe(true)
|
||||
})
|
||||
|
||||
it('recognizes left-to-right languages and scripts', () => {
|
||||
expect(isRTL('en')).toBe(false)
|
||||
expect(isRTL('az-Latn')).toBe(false)
|
||||
})
|
||||
|
||||
it('handles missing and invalid language tags', () => {
|
||||
expect(isRTL(undefined)).toBe(false)
|
||||
expect(isRTL('not_a_language')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,3 +17,17 @@ export function forceLTR(str: string) {
|
||||
if (IS_WEB) return str
|
||||
return LEFT_TO_RIGHT_EMBEDDING + str + POP_DIRECTIONAL_FORMATTING
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a BCP 47 language tag uses a right-to-left script.
|
||||
*/
|
||||
export function isRTL(language: string | undefined) {
|
||||
if (!language) return false
|
||||
|
||||
try {
|
||||
return new Intl.Locale(language).getTextInfo().direction === 'rtl'
|
||||
} catch (error) {
|
||||
if (error instanceof RangeError) return false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ function MessageInputPostEmbed({
|
||||
enableTags
|
||||
testID="postText"
|
||||
value={rt}
|
||||
language={record.langs?.[0]}
|
||||
style={[a.text_sm, t.atoms.text_contrast_high]}
|
||||
authorHandle={post.author.handle}
|
||||
numberOfLines={3}
|
||||
|
||||
@@ -405,6 +405,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
enableTags
|
||||
selectable
|
||||
value={richText}
|
||||
language={record.langs?.[0]}
|
||||
style={[a.flex_1, a.text_lg]}
|
||||
authorHandle={post.author.handle}
|
||||
shouldProxyLinks={true}
|
||||
|
||||
@@ -327,6 +327,7 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
|
||||
<RichText
|
||||
enableTags
|
||||
value={richText}
|
||||
language={record.langs?.[0]}
|
||||
style={[a.flex_1, a.text_md]}
|
||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
||||
authorHandle={post.author.handle}
|
||||
|
||||
@@ -355,6 +355,7 @@ const ThreadItemTreePostInner = memo(function ThreadItemTreePostInner({
|
||||
<RichText
|
||||
enableTags
|
||||
value={richText}
|
||||
language={record.langs?.[0]}
|
||||
style={[a.flex_1, a.text_md]}
|
||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
||||
authorHandle={post.author.handle}
|
||||
|
||||
@@ -963,6 +963,7 @@ function Overlay({
|
||||
{record?.text?.trim() && (
|
||||
<ExpandableRichTextView
|
||||
value={richText}
|
||||
language={record.langs?.[0]}
|
||||
authorHandle={post.author.handle}
|
||||
/>
|
||||
)}
|
||||
@@ -1020,9 +1021,11 @@ function Overlay({
|
||||
|
||||
function ExpandableRichTextView({
|
||||
value,
|
||||
language,
|
||||
authorHandle,
|
||||
}: {
|
||||
value: RichTextAPI
|
||||
language?: string
|
||||
authorHandle?: string
|
||||
}) {
|
||||
const {height: screenHeight} = useSafeAreaFrame()
|
||||
@@ -1057,6 +1060,7 @@ function ExpandableRichTextView({
|
||||
]}>
|
||||
<RichText
|
||||
value={value}
|
||||
language={language}
|
||||
style={[a.text_sm, a.flex_1, a.leading_relaxed]}
|
||||
authorHandle={authorHandle}
|
||||
enableTags
|
||||
|
||||
@@ -226,6 +226,7 @@ function PostInner({
|
||||
enableTags
|
||||
testID="postText"
|
||||
value={richText}
|
||||
language={record?.langs?.[0]}
|
||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
||||
style={[a.flex_1, a.text_md]}
|
||||
authorHandle={post.author.handle}
|
||||
|
||||
@@ -520,6 +520,7 @@ let PostContent = ({
|
||||
enableTags
|
||||
testID="postText"
|
||||
value={richText}
|
||||
language={record?.langs?.[0]}
|
||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
||||
style={[a.flex_1, a.text_md]}
|
||||
authorHandle={postAuthor.handle}
|
||||
|
||||
Reference in New Issue
Block a user