diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 6d7e50e480..4edd9f88ee 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -19,7 +19,7 @@ import {Text, TextProps} from '#/components/Typography' const WORD_WRAP = {wordWrap: 1} export type RichTextProps = TextStyleProp & - Pick & { + Pick & { value: RichTextAPI | string testID?: string numberOfLines?: number @@ -43,6 +43,8 @@ export function RichText({ onLinkPress, interactiveStyle, emojiMultiplier = 1.85, + onLayout, + onTextLayout, }: RichTextProps) { const richText = React.useMemo( () => @@ -70,6 +72,8 @@ export function RichText({ selectable={selectable} testID={testID} style={[plainStyles, {fontSize}]} + onLayout={onLayout} + onTextLayout={onTextLayout} // @ts-ignore web only -prf dataSet={WORD_WRAP}> {text} @@ -83,6 +87,8 @@ export function RichText({ testID={testID} style={plainStyles} numberOfLines={numberOfLines} + onLayout={onLayout} + onTextLayout={onTextLayout} // @ts-ignore web only -prf dataSet={WORD_WRAP}> {text} @@ -163,6 +169,8 @@ export function RichText({ testID={testID} style={plainStyles} numberOfLines={numberOfLines} + onLayout={onLayout} + onTextLayout={onTextLayout} // @ts-ignore web only -prf dataSet={WORD_WRAP}> {els} diff --git a/src/screens/Feeds/VibeScreen.tsx b/src/screens/Feeds/VibeScreen.tsx index d318afce3f..27a35c1edc 100644 --- a/src/screens/Feeds/VibeScreen.tsx +++ b/src/screens/Feeds/VibeScreen.tsx @@ -1,6 +1,8 @@ import {useCallback, useEffect, useMemo, useState} from 'react' import { + LayoutAnimation, ListRenderItem, + ScrollView, useWindowDimensions, View, ViewToken, @@ -19,7 +21,8 @@ import { AtUri, RichText as RichTextAPI, } from '@atproto/api' -import {Trans} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import { useFocusEffect, useIsFocused, @@ -27,7 +30,7 @@ import { } from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {VIBES_FEED_URI} from '#/lib/constants' +import {HITSLOP_20, VIBES_FEED_URI} from '#/lib/constants' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' import {sanitizeDisplayName} from '#/lib/strings/display-names' @@ -40,7 +43,7 @@ import {List} from '#/view/com/util/List' import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, ThemeProvider, useTheme} from '#/alf' -import {Button} from '#/components/Button' +import {Button, ButtonText} from '#/components/Button' import * as Layout from '#/components/Layout' import {ListFooter} from '#/components/Lists' import {RichText} from '#/components/RichText' @@ -272,6 +275,8 @@ function VibeOverlay({ const insets = useSafeAreaInsets() const t = useTheme() const navigation = useNavigation() + const [expanded, setExpanded] = useState(false) + const pushToProfile = useNonReactiveCallback(() => { navigation.navigate('Profile', {name: post.author.did}) }) @@ -288,7 +293,7 @@ function VibeOverlay({ const dragLeftGesture = Gesture.Pan() .activeOffsetX([0, 10]) .failOffsetX([-10, 0]) - .failOffsetY([-10, 10]) + .failOffsetY([-5, 5]) .maxPointers(1) .onEnd(evt => { 'worklet' @@ -317,12 +322,16 @@ function VibeOverlay({ - + {sanitizeDisplayName( post.author.displayName || post.author.handle, )} @@ -334,12 +343,14 @@ function VibeOverlay({ - + {record?.text?.trim() && ( + + )} {postShadow !== POST_TOMBSTONE && record && ( ) } + +function ExpandableRichTextView({ + value, + authorHandle, + expanded, + setExpanded, +}: { + value: RichTextAPI + authorHandle?: string + expanded: boolean + setExpanded: React.Dispatch> +}) { + const {height: screenHeight} = useWindowDimensions() + const [constrained, setConstrained] = useState(false) + const [contentHeight, setContentHeight] = useState(0) + const {_} = useLingui() + + return ( + { + LayoutAnimation.configureNext({ + duration: 500, + update: {type: 'spring', springDamping: 0.6}, + }) + setContentHeight(h) + }} + style={{height: Math.min(contentHeight, screenHeight * 0.5)}} + contentContainerStyle={[ + a.gap_xs, + expanded ? [a.align_start] : a.flex_row, + ]}> + { + if (!constrained && evt.nativeEvent.lines.length > 1) { + setConstrained(true) + } + }} + /> + {constrained && ( + + )} + + ) +}