1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Use simpler Account page

This commit is contained in:
Zhiyuan Zheng
2021-01-15 01:15:27 +01:00
parent d39bc82909
commit 9f4a4e908c
13 changed files with 105 additions and 247 deletions

View File

@@ -9,7 +9,12 @@ import { useScrollToTop } from '@react-navigation/native'
import { localUpdateNotification } from '@utils/slices/instancesSlice' import { localUpdateNotification } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import React, { useCallback, useEffect, useMemo, useRef } from 'react' import React, { useCallback, useEffect, useMemo, useRef } from 'react'
import { Platform, RefreshControl, StyleSheet } from 'react-native' import {
FlatListProps,
Platform,
RefreshControl,
StyleSheet
} from 'react-native'
import { FlatList } from 'react-native-gesture-handler' import { FlatList } from 'react-native-gesture-handler'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
@@ -24,6 +29,7 @@ export interface Props {
account?: Mastodon.Account['id'] account?: Mastodon.Account['id']
disableRefresh?: boolean disableRefresh?: boolean
disableInfinity?: boolean disableInfinity?: boolean
customProps?: Partial<FlatListProps<any>>
} }
const Timeline: React.FC<Props> = ({ const Timeline: React.FC<Props> = ({
@@ -33,7 +39,8 @@ const Timeline: React.FC<Props> = ({
toot, toot,
account, account,
disableRefresh = false, disableRefresh = false,
disableInfinity = false disableInfinity = false,
customProps
}) => { }) => {
const queryKeyParams = { const queryKeyParams = {
page, page,
@@ -208,7 +215,6 @@ const Timeline: React.FC<Props> = ({
return ( return (
<FlatList <FlatList
bounces={!disableRefresh}
ref={flRef} ref={flRef}
windowSize={11} windowSize={11}
data={flattenData} data={flattenData}
@@ -230,6 +236,7 @@ const Timeline: React.FC<Props> = ({
// minIndexForVisible: 0, // minIndexForVisible: 0,
// autoscrollToTopThreshold: 2 // autoscrollToTopThreshold: 2
// }} // }}
{...customProps}
/> />
) )
} }

View File

@@ -1,19 +1,15 @@
import BottomSheet from '@components/BottomSheet' import BottomSheet from '@components/BottomSheet'
import { HeaderRight } from '@components/Header' import { HeaderRight } from '@components/Header'
import Timeline from '@components/Timelines/Timeline'
import HeaderActionsAccount from '@components/Timelines/Timeline/Shared/HeaderActions/ActionsAccount' import HeaderActionsAccount from '@components/Timelines/Timeline/Shared/HeaderActions/ActionsAccount'
import { useAccountQuery } from '@utils/queryHooks/account' import { useAccountQuery } from '@utils/queryHooks/account'
import { getLocalAccount } from '@utils/slices/instancesSlice' import { getLocalAccount } from '@utils/slices/instancesSlice'
import React, { useEffect, useReducer, useState } from 'react' import React, { useCallback, useEffect, useReducer, useState } from 'react'
import Animated, { import { useSharedValue } from 'react-native-reanimated'
useAnimatedScrollHandler,
useSharedValue
} from 'react-native-reanimated'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import AccountHeader from './Account/Header' import AccountHeader from './Account/Header'
import AccountInformation from './Account/Information' import AccountInformation from './Account/Information'
import AccountNav from './Account/Nav' import AccountNav from './Account/Nav'
import AccountSegmentedControl from './Account/SegmentedControl'
import AccountToots from './Account/Toots'
import AccountContext from './Account/utils/createContext' import AccountContext from './Account/utils/createContext'
import accountInitialState from './Account/utils/initialState' import accountInitialState from './Account/utils/initialState'
import accountReducer from './Account/utils/reducer' import accountReducer from './Account/utils/reducer'
@@ -50,26 +46,29 @@ const ScreenSharedAccount: React.FC<SharedAccountProp> = ({
return updateHeaderRight() return updateHeaderRight()
}, []) }, [])
const onScroll = useAnimatedScrollHandler(event => { const onScroll = useCallback(({ nativeEvent }) => {
scrollY.value = event.contentOffset.y scrollY.value = nativeEvent.contentOffset.y
}) }, [])
return ( return (
<AccountContext.Provider value={{ accountState, accountDispatch }}> <AccountContext.Provider value={{ accountState, accountDispatch }}>
<AccountNav scrollY={scrollY} account={data} /> <AccountNav scrollY={scrollY} account={data} />
{accountState.informationLayout?.height &&
accountState.informationLayout.y ? ( <Timeline
<AccountSegmentedControl scrollY={scrollY} /> page='Account_Default'
) : null} account={account.id}
<Animated.ScrollView disableRefresh
scrollEventThrottle={16} customProps={{
showsVerticalScrollIndicator={false} onScroll,
onScroll={onScroll} scrollEventThrottle: 16,
> ListHeaderComponent: (
<AccountHeader account={data} /> <>
<AccountInformation account={data} /> <AccountHeader account={data} />
<AccountToots id={account.id} /> <AccountInformation account={data} />
</Animated.ScrollView> </>
)
}}
/>
<BottomSheet <BottomSheet
visible={modalVisible} visible={modalVisible}

View File

@@ -43,8 +43,8 @@ const AccountHeader: React.FC<Props> = ({ account, limitHeight = false }) => {
}, [account]) }, [account])
return ( return (
<Animated.View <Animated.Image
// source={{ uri: account?.header }} source={{ uri: account?.header }}
style={[styleHeight, { backgroundColor: theme.disabled }]} style={[styleHeight, { backgroundColor: theme.disabled }]}
/> />
) )

View File

@@ -1,4 +1,5 @@
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { createRef, useCallback, useContext, useEffect } from 'react' import React, { createRef, useCallback, useContext, useEffect } from 'react'
import { Animated, StyleSheet, View } from 'react-native' import { Animated, StyleSheet, View } from 'react-native'
import AccountInformationAccount from './Information/Account' import AccountInformationAccount from './Information/Account'
@@ -21,7 +22,9 @@ const AccountInformation: React.FC<Props> = ({
account, account,
ownAccount = false ownAccount = false
}) => { }) => {
const { theme } = useTheme()
const { accountDispatch } = useContext(AccountContext) const { accountDispatch } = useContext(AccountContext)
const shimmerAvatarRef = createRef<any>() const shimmerAvatarRef = createRef<any>()
const shimmerNameRef = createRef<any>() const shimmerNameRef = createRef<any>()
const shimmerAccountRef = createRef<any>() const shimmerAccountRef = createRef<any>()
@@ -56,7 +59,10 @@ const AccountInformation: React.FC<Props> = ({
) )
return ( return (
<View style={styles.base} onLayout={onLayout}> <View
style={[styles.base, { borderBottomColor: theme.border }]}
onLayout={onLayout}
>
{/* <Text>Moved or not: {account.moved}</Text> */} {/* <Text>Moved or not: {account.moved}</Text> */}
<View style={styles.avatarAndActions}> <View style={styles.avatarAndActions}>
<AccountInformationAvatar ref={shimmerAvatarRef} account={account} /> <AccountInformationAvatar ref={shimmerAvatarRef} account={account} />
@@ -99,7 +105,8 @@ const AccountInformation: React.FC<Props> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
base: { base: {
marginTop: -StyleConstants.Spacing.Global.PagePadding * 3, marginTop: -StyleConstants.Spacing.Global.PagePadding * 3,
padding: StyleConstants.Spacing.Global.PagePadding padding: StyleConstants.Spacing.Global.PagePadding,
borderBottomWidth: StyleSheet.hairlineWidth
}, },
avatarAndActions: { avatarAndActions: {
flexDirection: 'row', flexDirection: 'row',

View File

@@ -23,7 +23,7 @@ const AccountInformationAccount = forwardRef<ShimmerPlaceholder, Props>(
ref={ref} ref={ref}
visible={account?.acct !== undefined} visible={account?.acct !== undefined}
width={StyleConstants.Font.Size.M * 8} width={StyleConstants.Font.Size.M * 8}
height={StyleConstants.Font.Size.M} height={StyleConstants.Font.LineHeight.M}
style={{ marginBottom: StyleConstants.Spacing.L }} style={{ marginBottom: StyleConstants.Spacing.L }}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >

View File

@@ -24,7 +24,7 @@ const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>(
ref={ref} ref={ref}
visible={account?.created_at !== undefined} visible={account?.created_at !== undefined}
width={StyleConstants.Font.Size.S * 8} width={StyleConstants.Font.Size.S * 8}
height={StyleConstants.Font.Size.S} height={StyleConstants.Font.LineHeight.S}
style={{ marginBottom: StyleConstants.Spacing.M }} style={{ marginBottom: StyleConstants.Spacing.M }}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >

View File

@@ -9,47 +9,52 @@ export interface Props {
account: Mastodon.Account account: Mastodon.Account
} }
const AccountInformationFields: React.FC<Props> = ({ account }) => { const AccountInformationFields = React.memo(
const { theme } = useTheme() ({ account }: Props) => {
const { theme } = useTheme()
return ( return (
<View style={[styles.fields, { borderTopColor: theme.border }]}> <View style={[styles.fields, { borderTopColor: theme.border }]}>
{account.fields.map((field, index) => ( {account.fields.map((field, index) => (
<View <View
key={index} key={index}
style={[styles.field, { borderBottomColor: theme.border }]} style={[styles.field, { borderBottomColor: theme.border }]}
> >
<View style={[styles.fieldLeft, { borderRightColor: theme.border }]}> <View
<ParseHTML style={[styles.fieldLeft, { borderRightColor: theme.border }]}
content={field.name} >
size={'M'} <ParseHTML
emojis={account.emojis} content={field.name}
showFullLink size={'M'}
numberOfLines={3} emojis={account.emojis}
/> showFullLink
{field.verified_at ? ( numberOfLines={3}
<Icon
name='CheckCircle'
size={StyleConstants.Font.Size.M}
color={theme.primary}
style={styles.fieldCheck}
/> />
) : null} {field.verified_at ? (
<Icon
name='CheckCircle'
size={StyleConstants.Font.Size.M}
color={theme.primary}
style={styles.fieldCheck}
/>
) : null}
</View>
<View style={styles.fieldRight}>
<ParseHTML
content={field.value}
size={'M'}
emojis={account.emojis}
showFullLink
numberOfLines={3}
/>
</View>
</View> </View>
<View style={styles.fieldRight}> ))}
<ParseHTML </View>
content={field.value} )
size={'M'} },
emojis={account.emojis} () => true
showFullLink )
numberOfLines={3}
/>
</View>
</View>
))}
</View>
)
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
fields: { fields: {

View File

@@ -24,7 +24,7 @@ const AccountInformationName = forwardRef<ShimmerPlaceholder, Props>(
account?.display_name !== undefined || account?.username !== undefined account?.display_name !== undefined || account?.username !== undefined
} }
width={StyleConstants.Font.Size.L * 8} width={StyleConstants.Font.Size.L * 8}
height={StyleConstants.Font.Size.L} height={StyleConstants.Font.LineHeight.L}
style={styles.name} style={styles.name}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >

View File

@@ -7,13 +7,16 @@ export interface Props {
account: Mastodon.Account account: Mastodon.Account
} }
const AccountInformationNotes: React.FC<Props> = ({ account }) => { const AccountInformationNotes = React.memo(
return ( ({ account }: Props) => {
<View style={styles.note}> return (
<ParseHTML content={account.note!} size={'M'} emojis={account.emojis} /> <View style={styles.note}>
</View> <ParseHTML content={account.note!} size={'M'} emojis={account.emojis} />
) </View>
} )
},
() => true
)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
note: { note: {

View File

@@ -40,7 +40,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
ref={ref1} ref={ref1}
visible={account !== undefined} visible={account !== undefined}
width={StyleConstants.Font.Size.S * 5} width={StyleConstants.Font.Size.S * 5}
height={StyleConstants.Font.Size.S} height={StyleConstants.Font.LineHeight.S}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >
<Text style={[styles.stat, { color: theme.primary }]}> <Text style={[styles.stat, { color: theme.primary }]}>
@@ -53,7 +53,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
ref={ref2} ref={ref2}
visible={account !== undefined} visible={account !== undefined}
width={StyleConstants.Font.Size.S * 5} width={StyleConstants.Font.Size.S * 5}
height={StyleConstants.Font.Size.S} height={StyleConstants.Font.LineHeight.S}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >
<Text <Text
@@ -75,7 +75,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
ref={ref3} ref={ref3}
visible={account !== undefined} visible={account !== undefined}
width={StyleConstants.Font.Size.S * 5} width={StyleConstants.Font.Size.S * 5}
height={StyleConstants.Font.Size.S} height={StyleConstants.Font.LineHeight.S}
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]} shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
> >
<Text <Text

View File

@@ -1,93 +0,0 @@
import SegmentedControl from '@react-native-community/segmented-control'
import { StyleConstants } from '@root/utils/styles/constants'
import { useTheme } from '@root/utils/styles/ThemeManager'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet } from 'react-native'
import Animated, {
Extrapolate,
interpolate,
useAnimatedStyle
} from 'react-native-reanimated'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import AccountContext from './utils/createContext'
export interface Props {
scrollY: Animated.SharedValue<number>
}
const AccountSegmentedControl: React.FC<Props> = ({ scrollY }) => {
const { accountState, accountDispatch } = useContext(AccountContext)
const { t } = useTranslation('sharedAccount')
const { mode, theme } = useTheme()
const headerHeight = useSafeAreaInsets().top + 44
const styleTransform = useAnimatedStyle(() => {
return {
transform: [
{
translateY: interpolate(
scrollY.value,
[
0,
(accountState.informationLayout?.y || 0) +
(accountState.informationLayout?.height || 0) -
headerHeight
],
[
0,
-(accountState.informationLayout?.y || 0) -
(accountState.informationLayout?.height || 0) +
headerHeight
],
Extrapolate.CLAMP
)
}
]
}
})
return (
<Animated.View
style={[
styles.base,
styleTransform,
{
top:
(accountState.informationLayout?.y || 0) +
(accountState.informationLayout?.height || 0),
borderTopColor: theme.border,
backgroundColor: theme.background
}
]}
>
<SegmentedControl
values={[
t('content.segments.left'),
t('content.segments.middle'),
t('content.segments.right')
]}
selectedIndex={accountState.segmentedIndex}
onChange={({ nativeEvent }) =>
accountDispatch({
type: 'segmentedIndex',
payload: nativeEvent.selectedSegmentIndex
})
}
appearance={mode}
/>
</Animated.View>
)
}
const styles = StyleSheet.create({
base: {
...StyleSheet.absoluteFillObject,
zIndex: 99,
borderTopWidth: StyleSheet.hairlineWidth,
padding: StyleConstants.Spacing.Global.PagePadding,
height: 33 + StyleConstants.Spacing.Global.PagePadding * 2
}
})
export default React.memo(AccountSegmentedControl, () => true)

View File

@@ -1,70 +0,0 @@
import Timeline from '@components/Timelines/Timeline'
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'
import { StyleConstants } from '@utils/styles/constants'
import React, { useCallback, useContext } from 'react'
import { Dimensions, StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { TabView } from 'react-native-tab-view'
import ViewPagerAdapter from 'react-native-tab-view-viewpager-adapter'
import AccountContext from './utils/createContext'
export interface Props {
id: Mastodon.Account['id']
}
const AccountToots: React.FC<Props> = ({ id }) => {
const { accountState, accountDispatch } = useContext(AccountContext)
const headerHeight = useSafeAreaInsets().top + 44
const footerHeight = useSafeAreaInsets().bottom + useBottomTabBarHeight()
const routes: { key: App.Pages }[] = [
{ key: 'Account_Default' },
{ key: 'Account_All' },
{ key: 'Account_Media' }
]
const renderScene = ({
route
}: {
route: {
key: App.Pages
}
}) => {
return <Timeline page={route.key} account={id} disableRefresh />
}
const renderPager = useCallback(props => <ViewPagerAdapter {...props} />, [])
return (
<TabView
lazy
swipeEnabled
renderPager={renderPager}
renderScene={renderScene}
renderTabBar={() => null}
initialLayout={{ width: Dimensions.get('window').width }}
navigationState={{ index: accountState.segmentedIndex, routes }}
onIndexChange={index =>
accountDispatch({ type: 'segmentedIndex', payload: index })
}
style={[
styles.base,
{
height:
Dimensions.get('window').height -
headerHeight -
footerHeight -
(33 + StyleConstants.Spacing.Global.PagePadding * 2)
}
]}
/>
)
}
const styles = StyleSheet.create({
base: {
marginTop: StyleConstants.Spacing.Global.PagePadding + 33
}
})
export default React.memo(AccountToots, () => true)

View File

@@ -1,7 +1,7 @@
import { AccountState } from "./types" import { AccountState } from './types'
const accountInitialState: AccountState = { const accountInitialState: AccountState = {
headerRatio: 0.4, headerRatio: 1 / 3,
informationLayout: { height: 0, y: 100 }, informationLayout: { height: 0, y: 100 },
segmentedIndex: 0 segmentedIndex: 0
} }