diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/Link.tsx | 77 | ||||
-rw-r--r-- | src/components/RichText.tsx | 23 | ||||
-rw-r--r-- | src/components/Typography.tsx | 38 | ||||
-rw-r--r-- | src/view/screens/Storybook/Typography.tsx | 5 |
4 files changed, 74 insertions, 69 deletions
diff --git a/src/components/Link.tsx b/src/components/Link.tsx index afd30b5ee..85c13270a 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,9 +1,5 @@ import React from 'react' -import { - GestureResponderEvent, - Linking, - TouchableWithoutFeedback, -} from 'react-native' +import {GestureResponderEvent, Linking} from 'react-native' import { useLinkProps, useNavigation, @@ -23,7 +19,7 @@ import { } from '#/lib/strings/url-helpers' import {useModalControls} from '#/state/modals' import {router} from '#/routes' -import {Text} from '#/components/Typography' +import {Text, TextProps} from '#/components/Typography' /** * Only available within a `Link`, since that inherits from `Button`. @@ -217,7 +213,7 @@ export function Link({ } export type InlineLinkProps = React.PropsWithChildren< - BaseLinkProps & TextStyleProp + BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'> > export function InlineLink({ @@ -228,6 +224,7 @@ export function InlineLink({ style, onPress: outerOnPress, download, + selectable, ...rest }: InlineLinkProps) { const t = useTheme() @@ -253,43 +250,41 @@ export function InlineLink({ const flattenedStyle = flatten(style) return ( - <TouchableWithoutFeedback - accessibilityRole="button" + <Text + selectable={selectable} + label={href} + {...rest} + style={[ + {color: t.palette.primary_500}, + (hovered || focused || pressed) && { + outline: 0, + textDecorationLine: 'underline', + textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, + }, + flattenedStyle, + ]} + role="link" onPress={download ? undefined : onPress} onPressIn={onPressIn} onPressOut={onPressOut} onFocus={onFocus} - onBlur={onBlur}> - <Text - label={href} - {...rest} - style={[ - {color: t.palette.primary_500}, - (hovered || focused || pressed) && { - outline: 0, - textDecorationLine: 'underline', - textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, - }, - flattenedStyle, - ]} - role="link" - onMouseEnter={onHoverIn} - onMouseLeave={onHoverOut} - accessibilityRole="link" - href={href} - {...web({ - hrefAttrs: { - target: download ? undefined : isExternal ? 'blank' : undefined, - rel: isExternal ? 'noopener noreferrer' : undefined, - download, - }, - dataSet: { - // default to no underline, apply this ourselves - noUnderline: '1', - }, - })}> - {children} - </Text> - </TouchableWithoutFeedback> + onBlur={onBlur} + onMouseEnter={onHoverIn} + onMouseLeave={onHoverOut} + accessibilityRole="link" + href={href} + {...web({ + hrefAttrs: { + target: download ? undefined : isExternal ? 'blank' : undefined, + rel: isExternal ? 'noopener noreferrer' : undefined, + download, + }, + dataSet: { + // default to no underline, apply this ourselves + noUnderline: '1', + }, + })}> + {children} + </Text> ) } diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 068ee99e0..8aeef9ea1 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -3,7 +3,7 @@ import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' import {atoms as a, TextStyleProp} from '#/alf' import {InlineLink} from '#/components/Link' -import {Text} from '#/components/Typography' +import {Text, TextProps} from '#/components/Typography' import {toShortUrl} from 'lib/strings/url-helpers' import {getAgent} from '#/state/session' @@ -16,13 +16,15 @@ export function RichText({ numberOfLines, disableLinks, resolveFacets = false, -}: TextStyleProp & { - value: RichTextAPI | string - testID?: string - numberOfLines?: number - disableLinks?: boolean - resolveFacets?: boolean -}) { + selectable, +}: TextStyleProp & + Pick<TextProps, 'selectable'> & { + value: RichTextAPI | string + testID?: string + numberOfLines?: number + disableLinks?: boolean + resolveFacets?: boolean + }) { const detected = React.useRef(false) const [richText, setRichText] = React.useState<RichTextAPI>(() => value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), @@ -50,6 +52,7 @@ export function RichText({ if (text.length <= 5 && /^\p{Extended_Pictographic}+$/u.test(text)) { return ( <Text + selectable={selectable} testID={testID} style={[ { @@ -65,6 +68,7 @@ export function RichText({ } return ( <Text + selectable={selectable} testID={testID} style={styles} numberOfLines={numberOfLines} @@ -88,6 +92,7 @@ export function RichText({ ) { els.push( <InlineLink + selectable={selectable} key={key} to={`/profile/${mention.did}`} style={[...styles, {pointerEvents: 'auto'}]} @@ -102,6 +107,7 @@ export function RichText({ } else { els.push( <InlineLink + selectable={selectable} key={key} to={link.uri} style={[...styles, {pointerEvents: 'auto'}]} @@ -120,6 +126,7 @@ export function RichText({ return ( <Text + selectable={selectable} testID={testID} style={styles} numberOfLines={numberOfLines} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index b34f51018..c9ab7a8a1 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,7 +1,16 @@ import React from 'react' -import {Text as RNText, TextStyle, TextProps} from 'react-native' +import {Text as RNText, TextStyle, TextProps as RNTextProps} from 'react-native' +import {UITextView} from 'react-native-ui-text-view' import {useTheme, atoms, web, flatten} from '#/alf' +import {isIOS} from '#/platform/detection' + +export type TextProps = RNTextProps & { + /** + * Lets the user select text, to use the native copy and paste functionality. + */ + selectable?: boolean +} /** * Util to calculate lineHeight from a text size atom and a leading atom @@ -44,27 +53,24 @@ function normalizeTextStyles(styles: TextStyle[]) { /** * Our main text component. Use this most of the time. */ -export function Text({style, ...rest}: TextProps) { +export function Text({style, selectable, ...rest}: TextProps) { const t = useTheme() const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)]) - return <RNText style={s} {...rest} /> + return selectable && isIOS ? ( + <UITextView style={s} {...rest} /> + ) : ( + <RNText selectable={selectable} style={s} {...rest} /> + ) } export function createHeadingElement({level}: {level: number}) { return function HeadingElement({style, ...rest}: TextProps) { - const t = useTheme() const attr = web({ role: 'heading', 'aria-level': level, }) || {} - return ( - <RNText - {...attr} - {...rest} - style={normalizeTextStyles([t.atoms.text, flatten(style)])} - /> - ) + return <Text {...attr} {...rest} style={style} /> } } @@ -78,21 +84,15 @@ export const H4 = createHeadingElement({level: 4}) export const H5 = createHeadingElement({level: 5}) export const H6 = createHeadingElement({level: 6}) export function P({style, ...rest}: TextProps) { - const t = useTheme() const attr = web({ role: 'paragraph', }) || {} return ( - <RNText + <Text {...attr} {...rest} - style={normalizeTextStyles([ - atoms.text_md, - atoms.leading_normal, - t.atoms.text, - flatten(style), - ])} + style={[atoms.text_md, atoms.leading_normal, flatten(style)]} /> ) } diff --git a/src/view/screens/Storybook/Typography.tsx b/src/view/screens/Storybook/Typography.tsx index 5d3a96f4d..8ee4270b2 100644 --- a/src/view/screens/Storybook/Typography.tsx +++ b/src/view/screens/Storybook/Typography.tsx @@ -8,7 +8,9 @@ import {RichText} from '#/components/RichText' export function Typography() { return ( <View style={[a.gap_md]}> - <Text style={[a.text_5xl]}>atoms.text_5xl</Text> + <Text selectable style={[a.text_5xl]}> + atoms.text_5xl + </Text> <Text style={[a.text_4xl]}>atoms.text_4xl</Text> <Text style={[a.text_3xl]}>atoms.text_3xl</Text> <Text style={[a.text_2xl]}>atoms.text_2xl</Text> @@ -24,6 +26,7 @@ export function Typography() { value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`} /> <RichText + selectable resolveFacets value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`} style={[a.text_xl]} |