From 78dba4ae1aa4dc2c4c7d2f07b1aed750928208d0 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Fri, 16 Dec 2022 00:21:53 +0100 Subject: [PATCH] Pilot fix for #558 --- fastlane/metadata/en-US/release_notes.txt | 1 + fastlane/metadata/zh-Hans/release_notes.txt | 1 + src/components/Timeline/Default.tsx | 9 +- src/components/Timeline/Shared/Avatar.tsx | 8 +- src/components/Timeline/Shared/Context.tsx | 1 + src/screens/Tabs/Shared/Toot.tsx | 115 ++++++++++++++++++-- 6 files changed, 119 insertions(+), 16 deletions(-) diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 6ea61e2c..cf45f9e4 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -6,5 +6,6 @@ Enjoy toooting! This version includes following improvements and fixes: - Allow hiding boosts and replies in home timeline - Support toot in RTL languages - Added notification for admins +- Pilot conversation hierarchy - Fix whole word filter matching - Fix tablet cannot delete toot drafts \ No newline at end of file diff --git a/fastlane/metadata/zh-Hans/release_notes.txt b/fastlane/metadata/zh-Hans/release_notes.txt index 4b29b0a7..95a80d31 100644 --- a/fastlane/metadata/zh-Hans/release_notes.txt +++ b/fastlane/metadata/zh-Hans/release_notes.txt @@ -6,5 +6,6 @@ toooting愉快!此版本包括以下改进和修复: - 关注列表可隐藏转嘟和回复 - 新增管理员推送通知 - 支持嘟文右到左文字 +- 测试显示对话层级 - 修复过滤整词功能 - 修复平板不能删除草稿 \ No newline at end of file diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 446d138b..cb3a8f0d 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -34,6 +34,7 @@ export interface Props { highlighted?: boolean disableDetails?: boolean disableOnPress?: boolean + isConversation?: boolean } // When the poll is long @@ -43,7 +44,8 @@ const TimelineDefault: React.FC = ({ rootQueryKey, highlighted = false, disableDetails = false, - disableOnPress = false + disableOnPress = false, + isConversation = false }) => { const { colors } = useTheme() const navigation = useNavigation>() @@ -74,7 +76,7 @@ const TimelineDefault: React.FC = ({ ? StyleConstants.Spacing.Global.PagePadding / 1.5 : StyleConstants.Spacing.Global.PagePadding, backgroundColor: colors.backgroundDefault, - paddingBottom: disableDetails ? StyleConstants.Spacing.Global.PagePadding / 1.5 : 0, + paddingBottom: disableDetails ? StyleConstants.Spacing.Global.PagePadding / 1.5 : 0 } const main = () => ( <> @@ -140,7 +142,8 @@ const TimelineDefault: React.FC = ({ highlighted, inThread: queryKey?.[1].page === 'Toot', disableDetails, - disableOnPress + disableOnPress, + isConversation }} > {disableOnPress ? ( diff --git a/src/components/Timeline/Shared/Avatar.tsx b/src/components/Timeline/Shared/Avatar.tsx index d04cc92b..385b83af 100644 --- a/src/components/Timeline/Shared/Avatar.tsx +++ b/src/components/Timeline/Shared/Avatar.tsx @@ -12,7 +12,8 @@ export interface Props { } const TimelineAvatar: React.FC = ({ account }) => { - const { status, highlighted, disableDetails, disableOnPress } = useContext(StatusContext) + const { status, highlighted, disableDetails, disableOnPress, isConversation } = + useContext(StatusContext) const actualAccount = account || status?.account if (!actualAccount) return null @@ -34,7 +35,7 @@ const TimelineAvatar: React.FC = ({ account }) => { } uri={{ original: actualAccount.avatar, static: actualAccount.avatar_static }} dimension={ - disableDetails + disableDetails || isConversation ? { width: StyleConstants.Avatar.XS, height: StyleConstants.Avatar.XS @@ -47,7 +48,8 @@ const TimelineAvatar: React.FC = ({ account }) => { style={{ borderRadius: StyleConstants.Avatar.M, overflow: 'hidden', - marginRight: StyleConstants.Spacing.S + marginRight: StyleConstants.Spacing.S, + marginLeft: isConversation ? StyleConstants.Avatar.M - StyleConstants.Avatar.XS : undefined }} /> ) diff --git a/src/components/Timeline/Shared/Context.tsx b/src/components/Timeline/Shared/Context.tsx index 7a4db11e..f00668d1 100644 --- a/src/components/Timeline/Shared/Context.tsx +++ b/src/components/Timeline/Shared/Context.tsx @@ -19,6 +19,7 @@ type ContextType = { inThread?: boolean disableDetails?: boolean disableOnPress?: boolean + isConversation?: boolean } const StatusContext = createContext({} as ContextType) diff --git a/src/screens/Tabs/Shared/Toot.tsx b/src/screens/Tabs/Shared/Toot.tsx index 6421a350..91a93f1f 100644 --- a/src/screens/Tabs/Shared/Toot.tsx +++ b/src/screens/Tabs/Shared/Toot.tsx @@ -5,8 +5,10 @@ import { TabSharedStackScreenProps } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { FlatList } from 'react-native' +import { FlatList, View } from 'react-native' import { InfiniteQueryObserver, useQueryClient } from '@tanstack/react-query' +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' const TabSharedToot: React.FC> = ({ navigation, @@ -14,6 +16,7 @@ const TabSharedToot: React.FC> = ({ params: { toot, rootQueryKey } } }) => { + const { colors } = useTheme() const { t } = useTranslation('screenTabs') useEffect(() => { @@ -34,6 +37,10 @@ const TabSharedToot: React.FC> = ({ queryKey, enabled: false }) + + const replyLevels = useRef<{ id: string; level: number }[]>([]) + const data = useRef() + const highlightIndex = useRef(0) useEffect(() => { return observer.subscribe(result => { if (result.isSuccess) { @@ -46,6 +53,24 @@ const TabSharedToot: React.FC> = ({ navigation.goBack() return } + data.current = flattenData + highlightIndex.current = flattenData.findIndex(({ id }) => id === toot.id) + + for (const [index, status] of flattenData.entries()) { + if (status.id === toot.id) continue + if (status.in_reply_to_id === toot.id) continue + + if (!replyLevels.current.find(reply => reply.id === status.in_reply_to_id)) { + const prevLevel = + replyLevels.current.find(reply => reply.id === flattenData[index - 1].in_reply_to_id) + ?.level || 0 + replyLevels.current.push({ + id: status.in_reply_to_id, + level: prevLevel + 1 + }) + } + } + setItemsLength(flattenData.length) if (!scrolled.current) { scrolled.current = true @@ -68,7 +93,7 @@ const TabSharedToot: React.FC> = ({ } } }) - }, [scrolled.current]) + }, [scrolled.current, replyLevels.current]) return ( > = ({ queryKey={queryKey} queryOptions={{ staleTime: 0, refetchOnMount: true }} customProps={{ - renderItem: ({ item }) => ( - - ), + renderItem: ({ item, index }) => { + const levels = { + previous: + replyLevels.current.find( + reply => reply.id === data.current?.[index - 1]?.in_reply_to_id + )?.level || 0, + current: + replyLevels.current.find(reply => reply.id === item.in_reply_to_id)?.level || 0, + next: + replyLevels.current.find( + reply => reply.id === data.current?.[index + 1]?.in_reply_to_id + )?.level || 0 + } + + return ( + <> + + {Array.from(Array(levels.current)).map((_, i) => { + if (index < highlightIndex.current) return null + if ( + levels.previous + 1 === levels.current || + (levels.previous && levels.current && levels.previous === levels.current) + ) { + return ( + + ) + } else { + return null + } + })} + {Array.from(Array(levels.next)).map((_, i) => { + if (index < highlightIndex.current) return null + if ( + levels.current + 1 === levels.next || + (levels.current && levels.next && levels.current === levels.next) + ) { + return ( + i + 1 + ? StyleConstants.Spacing.Global.PagePadding + StyleConstants.Avatar.XS + : StyleConstants.Spacing.Global.PagePadding, + left: StyleConstants.Spacing.Global.PagePadding / 2 + 8 * i, + height: 200, + borderLeftColor: colors.border, + borderLeftWidth: 1 + }} + /> + ) + } else { + return null + } + })} + + ) + }, onScrollToIndexFailed: error => { const offset = error.averageItemLength * error.index flRef.current?.scrollToOffset({ offset })