From 7a15cceb282ec06f5f37b637d37af927de90fa79 Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Sat, 26 Dec 2020 12:59:16 +0100 Subject: [PATCH] Rewrite expandable content --- App.tsx | 15 ++- src/components/ParseContent.tsx | 106 +++++++++++------- src/components/Timelines/Timeline.tsx | 8 +- .../Timelines/Timeline/Shared/Content.tsx | 62 +--------- yarn.lock | 34 +++--- 5 files changed, 101 insertions(+), 124 deletions(-) diff --git a/App.tsx b/App.tsx index da6e0493..6fa4a7e6 100644 --- a/App.tsx +++ b/App.tsx @@ -10,14 +10,13 @@ import { persistor, store } from '@root/store' import { resetLocal } from '@root/utils/slices/instancesSlice' import ThemeManager from '@utils/styles/ThemeManager' -// if (__DEV__) { -// const whyDidYouRender = require('@welldone-software/why-did-you-render') -// whyDidYouRender(React, { -// trackAllPureComponents: true, -// trackHooks: true, -// hotReloadBufferMs: 1000 -// }) -// } +if (__DEV__) { + const whyDidYouRender = require('@welldone-software/why-did-you-render') + whyDidYouRender(React, { + trackHooks: true, + hotReloadBufferMs: 1000 + }) +} const queryClient = new QueryClient() diff --git a/src/components/ParseContent.tsx b/src/components/ParseContent.tsx index ed50ab75..3bdeb751 100644 --- a/src/components/ParseContent.tsx +++ b/src/components/ParseContent.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useState } from 'react' +import { LinearGradient } from 'expo-linear-gradient' +import React, { useCallback, useMemo, useState } from 'react' import { Pressable, Text, View } from 'react-native' import HTMLView from 'react-native-htmlview' import { useNavigation } from '@react-navigation/native' @@ -6,8 +7,8 @@ import Emojis from '@components/Timelines/Timeline/Shared/Emojis' import { useTheme } from '@utils/styles/ThemeManager' import { Feather } from '@expo/vector-icons' import { StyleConstants } from '@utils/styles/constants' -import { LinearGradient } from 'expo-linear-gradient' import openLink from '@root/utils/openLink' +import layoutAnimation from '@root/utils/styles/layoutAnimation' // Prevent going to the same hashtag multiple times const renderNode = ({ @@ -96,7 +97,7 @@ const renderNode = ({ } else { if (node.name === 'p') { if (!node.children.length) { - return // bug when the tag is empty + return // bug when the tag is empty } } } @@ -109,6 +110,7 @@ export interface Props { mentions?: Mastodon.Mention[] showFullLink?: boolean numberOfLines?: number + expandHint?: string } const ParseContent: React.FC = ({ @@ -117,7 +119,8 @@ const ParseContent: React.FC = ({ emojis, mentions, showFullLink = false, - numberOfLines = 10 + numberOfLines = 10, + expandHint = '全文' }) => { const navigation = useNavigation() const { theme } = useTheme() @@ -136,55 +139,76 @@ const ParseContent: React.FC = ({ [] ) const textComponent = useCallback(({ children }) => { - return emojis && children ? ( - - ) : ( - {children} - ) + if (children) { + return emojis ? ( + + ) : ( + {children} + ) + } else { + return null + } }, []) const rootComponent = useCallback(({ children }) => { - const { theme } = useTheme() - const [textLoaded, setTextLoaded] = useState(false) - const [totalLines, setTotalLines] = useState() - const [lineHeight, setLineHeight] = useState() - const [shownLines, setShownLines] = useState(numberOfLines) + // layoutAnimation() + const lineHeight = StyleConstants.Font.LineHeight[size] + + const [heightOriginal, setHeightOriginal] = useState() + const [heightTruncated, setHeightTruncated] = useState() + const [allowExpand, setAllowExpand] = useState(false) + const [showAllText, setShowAllText] = useState(false) + + const calNumberOfLines = useMemo(() => { + if (heightOriginal) { + if (!heightTruncated) { + return numberOfLines + } else { + if (allowExpand && !showAllText) { + return numberOfLines + } else { + return undefined + } + } + } else { + return undefined + } + }, [heightOriginal, heightTruncated, allowExpand, showAllText]) return ( - <> + numberOfLines ? shownLines : totalLines - } - style={{ lineHeight: StyleConstants.Font.LineHeight[size] }} - onTextLayout={({ nativeEvent }) => { - if (!textLoaded) { - setTextLoaded(true) - setTotalLines(nativeEvent.lines.length) - setLineHeight(nativeEvent.lines[0].height) + style={{ lineHeight }} + children={children} + numberOfLines={calNumberOfLines} + onLayout={({ nativeEvent }) => { + if (!heightOriginal) { + setHeightOriginal(nativeEvent.layout.height) + } else { + if (!heightTruncated) { + setHeightTruncated(nativeEvent.layout.height) + } else { + if (heightOriginal > heightTruncated) { + setAllowExpand(true) + } + } } }} - > - {children} - - {totalLines && lineHeight && totalLines > shownLines && ( + /> + {allowExpand && ( { - setShownLines(totalLines) - }} - style={{ - marginTop: -lineHeight - }} + onPress={() => setShowAllText(!showAllText)} + style={{ marginTop: showAllText ? 0 : -lineHeight * 1.25 }} > = ({ color: theme.primary }} > - 展开全文 + {`${showAllText ? '折叠' : '展开'}${expandHint}`} )} - + ) }, []) diff --git a/src/components/Timelines/Timeline.tsx b/src/components/Timelines/Timeline.tsx index 184cb05c..8440b6d1 100644 --- a/src/components/Timelines/Timeline.tsx +++ b/src/components/Timelines/Timeline.tsx @@ -55,7 +55,8 @@ const Timeline: React.FC = ({ fetchPreviousPage, isFetchingPreviousPage, hasNextPage, - fetchNextPage + fetchNextPage, + isFetchingNextPage } = useInfiniteQuery(queryKey, timelineFetch, { getPreviousPageParam: firstPage => { return firstPage.toots.length @@ -133,7 +134,10 @@ const Timeline: React.FC = ({ () => , [status] ) - const onEndReached = useCallback(() => !disableRefresh && fetchNextPage(), []) + const onEndReached = useCallback( + () => !disableRefresh && !isFetchingNextPage && fetchNextPage(), + [isFetchingNextPage] + ) const ListFooterComponent = useCallback( () => , [hasNextPage] diff --git a/src/components/Timelines/Timeline/Shared/Content.tsx b/src/components/Timelines/Timeline/Shared/Content.tsx index 9fb27973..91088363 100644 --- a/src/components/Timelines/Timeline/Shared/Content.tsx +++ b/src/components/Timelines/Timeline/Shared/Content.tsx @@ -1,10 +1,7 @@ -import React, { useState } from 'react' -import { LayoutAnimation, Pressable, Text, View } from 'react-native' +import React from 'react' +import { View } from 'react-native' import ParseContent from '@components/ParseContent' -import { useTheme } from '@utils/styles/ThemeManager' import { StyleConstants } from '@utils/styles/constants' -import { LinearGradient } from 'expo-linear-gradient' -import layoutAnimation from '@root/utils/styles/layoutAnimation' export interface Props { status: Mastodon.Status @@ -17,11 +14,6 @@ const TimelineContent: React.FC = ({ numberOfLines, highlighted = false }) => { - layoutAnimation() - const { theme } = useTheme() - const [spoilerCollapsed, setSpoilerCollapsed] = useState(false) - const lineHeight = 28 - return ( <> {status.spoiler_text ? ( @@ -32,58 +24,16 @@ const TimelineContent: React.FC = ({ emojis={status.emojis} numberOfLines={999} /> - + - setSpoilerCollapsed(!spoilerCollapsed)} - style={{ - marginTop: spoilerCollapsed ? -lineHeight : 0 - }} - > - - - {spoilerCollapsed ? '展开' : '收起'}隐藏内容 - - - ) : ( = ({ size={highlighted ? 'L' : 'M'} emojis={status.emojis} mentions={status.mentions} - {...(numberOfLines && { numberOfLines: numberOfLines })} + numberOfLines={numberOfLines} /> )} diff --git a/yarn.lock b/yarn.lock index cba12e31..22191206 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3400,25 +3400,25 @@ gl-react-blurhash@^1.0.0: blurhash "^1.1.3" gl-react-expo@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gl-react-expo/-/gl-react-expo-4.0.1.tgz#7512b65fd7e27d84f3405e737f8b334aed6b69fc" - integrity sha512-L4bwpqEolXmdc1YH8okN4qkfOW13/iLvevhJYgf2mHaLCRwhW1WDUJ18EEip8lUxaUmH9NmT9SC5DwMRiP0YSg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/gl-react-expo/-/gl-react-expo-4.1.0.tgz#0f3b13fae7fe490bc72fcf59c894605293da642d" + integrity sha512-3Q4LOBGYsNNqpLkwUsUlRrFCNLUqjVsVPKE3c6pLABfkNxyBacbDK3mg3T8XOD7jp+Vsp/Ic9McjICMsYHANJw== dependencies: invariant "^2.2.4" webgltexture-loader-expo "1.0.0" gl-react@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gl-react/-/gl-react-4.0.1.tgz#6b747084237dc2209eb3ed05162bb5941c5a6e94" - integrity sha512-/+wbFHVeh21wOS6g3v7r/zAdXoYMqn7xCB0CwLwMxBUwUsnV0jcvGS+8HDoup9Yd20xGt77p2c3gmrt2XtEEgg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/gl-react/-/gl-react-4.1.0.tgz#b7623555a4c3ba92a1ccbbb122c28c46231cd863" + integrity sha512-hTvxQHN2wxLfbA4c6mRcMdXWjA0/8UY2Sxn5eNre0DOrcauBj/+vMeAMbL2+zwE1BfVYnraE4cvKJTIzFIeBLw== dependencies: gl-shader "^4.2.1" invariant "^2.2.4" - ndarray "^1.0.18" + ndarray "^1.0.19" prop-types "^15.7.2" - typedarray-pool "^1.1.0" + typedarray-pool "^1.2.0" webgltexture-loader "1.0.0" - webgltexture-loader-ndarray "1.0.0" + webgltexture-loader-ndarray "1.1.0" gl-shader@^4.2.1: version "4.2.1" @@ -4811,7 +4811,7 @@ ndarray-ops@^1.2.2: dependencies: cwise-compiler "^1.0.0" -ndarray@^1.0.18: +ndarray@^1.0.19: version "1.0.19" resolved "https://registry.yarnpkg.com/ndarray/-/ndarray-1.0.19.tgz#6785b5f5dfa58b83e31ae5b2a058cfd1ab3f694e" integrity sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ== @@ -6365,7 +6365,7 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -typedarray-pool@^1.1.0: +typedarray-pool@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/typedarray-pool/-/typedarray-pool-1.2.0.tgz#e7e90720144ba02b9ed660438af6f3aacfe33ac3" integrity sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ== @@ -6613,14 +6613,14 @@ webgltexture-loader-expo@1.0.0: dependencies: webgltexture-loader "^1.0.0" -webgltexture-loader-ndarray@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/webgltexture-loader-ndarray/-/webgltexture-loader-ndarray-1.0.0.tgz#844181c4cbe0cbc066750d66412c11d9205ad79a" - integrity sha512-ocEStCCkihBu+PyMr14ke2vv08c5wVObP5mH9AuPICcBvaS8zfxvMpoGc9dIxOxBC1reI/lzmpYxpYdoY/Ke8g== +webgltexture-loader-ndarray@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webgltexture-loader-ndarray/-/webgltexture-loader-ndarray-1.1.0.tgz#20869150e79dfb6190cbd6bedafacda262e29a5e" + integrity sha512-t9Q4x5do5feHjMOJLCg8UdQVx1eInnWXvgAqaL9NJJSL5pzI59Ynx3ZZSw3QqGxj15iRbYij7vK8BCIDyp5EZA== dependencies: - ndarray "^1.0.18" + ndarray "^1.0.19" ndarray-ops "^1.2.2" - typedarray-pool "^1.1.0" + typedarray-pool "^1.2.0" webgltexture-loader "^1.0.0" webgltexture-loader@1.0.0, webgltexture-loader@^1.0.0: