From fe1ca72a3ee852ce647cf79fa5f6419bf6c6baf7 Mon Sep 17 00:00:00 2001 From: Zhiyuan Zheng Date: Mon, 14 Dec 2020 23:44:57 +0100 Subject: [PATCH] Show pinned posts in Account page --- src/components/Timelines/Timeline.tsx | 10 +- src/components/Timelines/Timeline/Default.tsx | 10 +- .../Timelines/Timeline/Shared/Actioned.tsx | 14 +- src/screens/Shared/Account/Information.tsx | 282 +++++++++++------- src/utils/fetches/timelineFetch.ts | 36 +-- 5 files changed, 226 insertions(+), 126 deletions(-) diff --git a/src/components/Timelines/Timeline.tsx b/src/components/Timelines/Timeline.tsx index ae682888..2f339156 100644 --- a/src/components/Timelines/Timeline.tsx +++ b/src/components/Timelines/Timeline.tsx @@ -65,13 +65,14 @@ const Timeline: React.FC = ({ }) const flattenData = data ? data.flatMap(d => [...d?.toots]) : [] const flattenPointer = data ? data.flatMap(d => [d?.pointer]) : [] + const flattenPinnedLength = data ? data.flatMap(d => [d?.pinnedLength]) : [] const flRef = useRef(null) useEffect(() => { if (toot && isSuccess) { setTimeout(() => { flRef.current?.scrollToIndex({ - index: flattenPointer[0], + index: flattenPointer[0]!, viewOffset: 100 }) }, 500) @@ -79,7 +80,7 @@ const Timeline: React.FC = ({ }, [isSuccess]) const flKeyExtrator = useCallback(({ id }) => id, []) - const flRenderItem = useCallback(({ item }) => { + const flRenderItem = useCallback(({ item, index }) => { switch (page) { case 'Conversations': return @@ -90,6 +91,11 @@ const Timeline: React.FC = ({ ) diff --git a/src/components/Timelines/Timeline/Default.tsx b/src/components/Timelines/Timeline/Default.tsx index 81d1b08c..69101365 100644 --- a/src/components/Timelines/Timeline/Default.tsx +++ b/src/components/Timelines/Timeline/Default.tsx @@ -16,6 +16,8 @@ import { StyleConstants } from '@utils/styles/constants' export interface Props { item: Mastodon.Status queryKey: App.QueryKey + index: number + pinnedLength?: number highlighted?: boolean } @@ -23,6 +25,8 @@ export interface Props { const TimelineDefault: React.FC = ({ item, queryKey, + index, + pinnedLength, highlighted = false }) => { const isRemotePublic = queryKey[0] === 'RemotePublic' @@ -72,9 +76,11 @@ const TimelineDefault: React.FC = ({ return ( - {item.reblog && ( + {item.reblog ? ( - )} + ) : pinnedLength && index < pinnedLength ? ( + + ) : null} = ({ account, - action, notification = false }) => { @@ -25,6 +24,17 @@ const TimelineActioned: React.FC = ({ let icon let content switch (action) { + case 'pinned': + icon = ( + + ) + content = `置顶` + break case 'favourite': icon = ( = ({ account }) => { const { theme } = useTheme() const [avatarLoaded, setAvatarLoaded] = useState(false) + const shimmerAvatarRef = createRef() + const shimmerNameRef = createRef() + const shimmerAccountRef = createRef() + const shimmerCreatedRef = createRef() + const shimmerStatTootRef = createRef() + const shimmerStatFolloingRef = createRef() + const shimmerStatFollowerRef = createRef() + useEffect(() => { + const informationAnimated = Animated.stagger(400, [ + Animated.parallel([ + shimmerAvatarRef.current!.getAnimated(), + shimmerNameRef.current!.getAnimated(), + shimmerAccountRef.current!.getAnimated(), + shimmerCreatedRef.current!.getAnimated(), + shimmerStatTootRef.current!.getAnimated(), + shimmerStatFolloingRef.current!.getAnimated(), + shimmerStatFollowerRef.current!.getAnimated() + ]) + ]) + Animated.loop(informationAnimated).start() + }, []) + return ( {/* Moved or not: {account.moved} */} - + = ({ account }) => { /> - - {account?.emojis ? ( - - ) : ( + + + {account?.emojis ? ( + + ) : ( + + {account?.display_name || account?.username} + + )} + + + + + - {account?.display_name || account?.username} + @{account?.acct} - )} - - - - - @{account?.acct} - - {account?.locked && ( - - )} - {account?.bot && ( - - )} - + {account?.locked && ( + + )} + {account?.bot && ( + + )} + + {account?.fields && account.fields.length > 0 && ( @@ -84,15 +127,7 @@ const AccountInformation: React.FC = ({ account }) => { style={[styles.field, { borderBottomColor: theme.border }]} > = ({ account }) => { name='check-circle' size={StyleConstants.Font.Size.M} color={theme.primary} + style={styles.fieldCheck} /> )} - + = ({ account }) => { )} - - - - {t( - 'content.created_at', - { + + + + + {t('content.created_at', { date: new Date(account?.created_at!).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }) - } || null - )} - - + })} + + + - - - {t('content.summary.statuses_count', { - count: account?.statuses_count || 0 - })} - - - {t('content.summary.followers_count', { - count: account?.followers_count || 0 - })} - - - {t('content.summary.following_count', { - count: account?.following_count || 0 - })} - + + + + {t('content.summary.statuses_count', { + count: account?.statuses_count || 0 + })} + + + + + {t('content.summary.followers_count', { + count: account?.followers_count || 0 + })} + + + + + {t('content.summary.following_count', { + count: account?.following_count || 0 + })} + + ) @@ -203,8 +261,7 @@ const styles = StyleSheet.create({ }, account: { flexDirection: 'row', - alignItems: 'center', - marginBottom: StyleConstants.Spacing.L + alignItems: 'center' }, account_types: { marginLeft: StyleConstants.Spacing.S }, fields: { @@ -218,21 +275,40 @@ const styles = StyleSheet.create({ paddingTop: StyleConstants.Spacing.S, paddingBottom: StyleConstants.Spacing.S }, + fieldLeft: { + flexBasis: '30%', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRightWidth: 1, + paddingLeft: StyleConstants.Spacing.S, + paddingRight: StyleConstants.Spacing.S + }, + fieldCheck: { marginLeft: StyleConstants.Spacing.XS }, + fieldRight: { + flexBasis: '70%', + alignItems: 'center', + justifyContent: 'center', + paddingLeft: StyleConstants.Spacing.S, + paddingRight: StyleConstants.Spacing.S + }, note: { marginBottom: StyleConstants.Spacing.L }, created_at: { flexDirection: 'row', - alignItems: 'center', - marginBottom: StyleConstants.Spacing.M + alignItems: 'center' }, created_at_icon: { marginRight: StyleConstants.Spacing.XS }, - summary: { + stats: { flex: 1, flexDirection: 'row', justifyContent: 'space-between' + }, + stat: { + fontSize: StyleConstants.Font.Size.S } }) diff --git a/src/utils/fetches/timelineFetch.ts b/src/utils/fetches/timelineFetch.ts index af297018..301fb67f 100644 --- a/src/utils/fetches/timelineFetch.ts +++ b/src/utils/fetches/timelineFetch.ts @@ -25,7 +25,11 @@ export const timelineFetch = async ( direction: 'prev' | 'next' id: string } -) => { +): Promise<{ + toots: Mastodon.Status[] + pointer?: number + pinnedLength?: number +}> => { let res if (pagination && pagination.id) { @@ -47,7 +51,7 @@ export const timelineFetch = async ( url: 'timelines/home', params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Local': params.local = 'true' @@ -57,7 +61,7 @@ export const timelineFetch = async ( url: 'timelines/public', params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'LocalPublic': res = await client({ @@ -66,7 +70,7 @@ export const timelineFetch = async ( url: 'timelines/public', params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'RemotePublic': res = await client({ @@ -75,7 +79,7 @@ export const timelineFetch = async ( url: 'timelines/public', params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Notifications': res = await client({ @@ -84,7 +88,7 @@ export const timelineFetch = async ( url: 'notifications', params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Account_Default': res = await client({ @@ -95,6 +99,7 @@ export const timelineFetch = async ( pinned: 'true' } }) + const pinnedLength = res.body.length let toots: Mastodon.Status[] = res.body res = await client({ method: 'get', @@ -105,7 +110,7 @@ export const timelineFetch = async ( } }) toots = uniqBy([...toots, ...res.body], 'id') - return Promise.resolve({ toots: toots, pointer: null }) + return Promise.resolve({ toots: toots, pinnedLength }) case 'Account_All': res = await client({ @@ -114,7 +119,7 @@ export const timelineFetch = async ( url: `accounts/${account}/statuses`, params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Account_Media': res = await client({ @@ -125,7 +130,7 @@ export const timelineFetch = async ( only_media: 'true' } }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Hashtag': res = await client({ @@ -134,7 +139,7 @@ export const timelineFetch = async ( url: `timelines/tag/${hashtag}`, params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Conversations': res = await client({ @@ -149,7 +154,7 @@ export const timelineFetch = async ( (b: Mastodon.Conversation) => b.id !== pagination.id ) } - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Bookmarks': res = await client({ @@ -158,7 +163,7 @@ export const timelineFetch = async ( url: `bookmarks`, params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Favourites': res = await client({ @@ -167,7 +172,7 @@ export const timelineFetch = async ( url: `favourites`, params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'List': res = await client({ @@ -176,7 +181,7 @@ export const timelineFetch = async ( url: `timelines/list/${list}`, params }) - return Promise.resolve({ toots: res.body, pointer: null }) + return Promise.resolve({ toots: res.body }) case 'Toot': res = await client({ @@ -188,8 +193,5 @@ export const timelineFetch = async ( toots: [...res.body.ancestors, toot, ...res.body.descendants], pointer: res.body.ancestors.length }) - - default: - console.error('Page is not provided') } }