mirror of
https://github.com/tooot-app/app
synced 2025-01-26 16:35:17 +01:00
Use simpler Account page
This commit is contained in:
parent
d39bc82909
commit
9f4a4e908c
@ -9,7 +9,12 @@ import { useScrollToTop } from '@react-navigation/native'
|
||||
import { localUpdateNotification } from '@utils/slices/instancesSlice'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
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 { useDispatch } from 'react-redux'
|
||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||
@ -24,6 +29,7 @@ export interface Props {
|
||||
account?: Mastodon.Account['id']
|
||||
disableRefresh?: boolean
|
||||
disableInfinity?: boolean
|
||||
customProps?: Partial<FlatListProps<any>>
|
||||
}
|
||||
|
||||
const Timeline: React.FC<Props> = ({
|
||||
@ -33,7 +39,8 @@ const Timeline: React.FC<Props> = ({
|
||||
toot,
|
||||
account,
|
||||
disableRefresh = false,
|
||||
disableInfinity = false
|
||||
disableInfinity = false,
|
||||
customProps
|
||||
}) => {
|
||||
const queryKeyParams = {
|
||||
page,
|
||||
@ -208,7 +215,6 @@ const Timeline: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
bounces={!disableRefresh}
|
||||
ref={flRef}
|
||||
windowSize={11}
|
||||
data={flattenData}
|
||||
@ -230,6 +236,7 @@ const Timeline: React.FC<Props> = ({
|
||||
// minIndexForVisible: 0,
|
||||
// autoscrollToTopThreshold: 2
|
||||
// }}
|
||||
{...customProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,19 +1,15 @@
|
||||
import BottomSheet from '@components/BottomSheet'
|
||||
import { HeaderRight } from '@components/Header'
|
||||
import Timeline from '@components/Timelines/Timeline'
|
||||
import HeaderActionsAccount from '@components/Timelines/Timeline/Shared/HeaderActions/ActionsAccount'
|
||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
||||
import React, { useEffect, useReducer, useState } from 'react'
|
||||
import Animated, {
|
||||
useAnimatedScrollHandler,
|
||||
useSharedValue
|
||||
} from 'react-native-reanimated'
|
||||
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
||||
import { useSharedValue } from 'react-native-reanimated'
|
||||
import { useSelector } from 'react-redux'
|
||||
import AccountHeader from './Account/Header'
|
||||
import AccountInformation from './Account/Information'
|
||||
import AccountNav from './Account/Nav'
|
||||
import AccountSegmentedControl from './Account/SegmentedControl'
|
||||
import AccountToots from './Account/Toots'
|
||||
import AccountContext from './Account/utils/createContext'
|
||||
import accountInitialState from './Account/utils/initialState'
|
||||
import accountReducer from './Account/utils/reducer'
|
||||
@ -50,26 +46,29 @@ const ScreenSharedAccount: React.FC<SharedAccountProp> = ({
|
||||
return updateHeaderRight()
|
||||
}, [])
|
||||
|
||||
const onScroll = useAnimatedScrollHandler(event => {
|
||||
scrollY.value = event.contentOffset.y
|
||||
})
|
||||
const onScroll = useCallback(({ nativeEvent }) => {
|
||||
scrollY.value = nativeEvent.contentOffset.y
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<AccountContext.Provider value={{ accountState, accountDispatch }}>
|
||||
<AccountNav scrollY={scrollY} account={data} />
|
||||
{accountState.informationLayout?.height &&
|
||||
accountState.informationLayout.y ? (
|
||||
<AccountSegmentedControl scrollY={scrollY} />
|
||||
) : null}
|
||||
<Animated.ScrollView
|
||||
scrollEventThrottle={16}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
<AccountHeader account={data} />
|
||||
<AccountInformation account={data} />
|
||||
<AccountToots id={account.id} />
|
||||
</Animated.ScrollView>
|
||||
|
||||
<Timeline
|
||||
page='Account_Default'
|
||||
account={account.id}
|
||||
disableRefresh
|
||||
customProps={{
|
||||
onScroll,
|
||||
scrollEventThrottle: 16,
|
||||
ListHeaderComponent: (
|
||||
<>
|
||||
<AccountHeader account={data} />
|
||||
<AccountInformation account={data} />
|
||||
</>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<BottomSheet
|
||||
visible={modalVisible}
|
||||
|
@ -43,8 +43,8 @@ const AccountHeader: React.FC<Props> = ({ account, limitHeight = false }) => {
|
||||
}, [account])
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
// source={{ uri: account?.header }}
|
||||
<Animated.Image
|
||||
source={{ uri: account?.header }}
|
||||
style={[styleHeight, { backgroundColor: theme.disabled }]}
|
||||
/>
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { createRef, useCallback, useContext, useEffect } from 'react'
|
||||
import { Animated, StyleSheet, View } from 'react-native'
|
||||
import AccountInformationAccount from './Information/Account'
|
||||
@ -21,7 +22,9 @@ const AccountInformation: React.FC<Props> = ({
|
||||
account,
|
||||
ownAccount = false
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
const { accountDispatch } = useContext(AccountContext)
|
||||
|
||||
const shimmerAvatarRef = createRef<any>()
|
||||
const shimmerNameRef = createRef<any>()
|
||||
const shimmerAccountRef = createRef<any>()
|
||||
@ -56,7 +59,10 @@ const AccountInformation: React.FC<Props> = ({
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.base} onLayout={onLayout}>
|
||||
<View
|
||||
style={[styles.base, { borderBottomColor: theme.border }]}
|
||||
onLayout={onLayout}
|
||||
>
|
||||
{/* <Text>Moved or not: {account.moved}</Text> */}
|
||||
<View style={styles.avatarAndActions}>
|
||||
<AccountInformationAvatar ref={shimmerAvatarRef} account={account} />
|
||||
@ -99,7 +105,8 @@ const AccountInformation: React.FC<Props> = ({
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
marginTop: -StyleConstants.Spacing.Global.PagePadding * 3,
|
||||
padding: StyleConstants.Spacing.Global.PagePadding
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth
|
||||
},
|
||||
avatarAndActions: {
|
||||
flexDirection: 'row',
|
||||
|
@ -23,7 +23,7 @@ const AccountInformationAccount = forwardRef<ShimmerPlaceholder, Props>(
|
||||
ref={ref}
|
||||
visible={account?.acct !== undefined}
|
||||
width={StyleConstants.Font.Size.M * 8}
|
||||
height={StyleConstants.Font.Size.M}
|
||||
height={StyleConstants.Font.LineHeight.M}
|
||||
style={{ marginBottom: StyleConstants.Spacing.L }}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
|
@ -24,7 +24,7 @@ const AccountInformationCreated = forwardRef<ShimmerPlaceholder, Props>(
|
||||
ref={ref}
|
||||
visible={account?.created_at !== undefined}
|
||||
width={StyleConstants.Font.Size.S * 8}
|
||||
height={StyleConstants.Font.Size.S}
|
||||
height={StyleConstants.Font.LineHeight.S}
|
||||
style={{ marginBottom: StyleConstants.Spacing.M }}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
|
@ -9,47 +9,52 @@ export interface Props {
|
||||
account: Mastodon.Account
|
||||
}
|
||||
|
||||
const AccountInformationFields: React.FC<Props> = ({ account }) => {
|
||||
const { theme } = useTheme()
|
||||
const AccountInformationFields = React.memo(
|
||||
({ account }: Props) => {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<View style={[styles.fields, { borderTopColor: theme.border }]}>
|
||||
{account.fields.map((field, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[styles.field, { borderBottomColor: theme.border }]}
|
||||
>
|
||||
<View style={[styles.fieldLeft, { borderRightColor: theme.border }]}>
|
||||
<ParseHTML
|
||||
content={field.name}
|
||||
size={'M'}
|
||||
emojis={account.emojis}
|
||||
showFullLink
|
||||
numberOfLines={3}
|
||||
/>
|
||||
{field.verified_at ? (
|
||||
<Icon
|
||||
name='CheckCircle'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={theme.primary}
|
||||
style={styles.fieldCheck}
|
||||
return (
|
||||
<View style={[styles.fields, { borderTopColor: theme.border }]}>
|
||||
{account.fields.map((field, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[styles.field, { borderBottomColor: theme.border }]}
|
||||
>
|
||||
<View
|
||||
style={[styles.fieldLeft, { borderRightColor: theme.border }]}
|
||||
>
|
||||
<ParseHTML
|
||||
content={field.name}
|
||||
size={'M'}
|
||||
emojis={account.emojis}
|
||||
showFullLink
|
||||
numberOfLines={3}
|
||||
/>
|
||||
) : 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 style={styles.fieldRight}>
|
||||
<ParseHTML
|
||||
content={field.value}
|
||||
size={'M'}
|
||||
emojis={account.emojis}
|
||||
showFullLink
|
||||
numberOfLines={3}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fields: {
|
||||
|
@ -24,7 +24,7 @@ const AccountInformationName = forwardRef<ShimmerPlaceholder, Props>(
|
||||
account?.display_name !== undefined || account?.username !== undefined
|
||||
}
|
||||
width={StyleConstants.Font.Size.L * 8}
|
||||
height={StyleConstants.Font.Size.L}
|
||||
height={StyleConstants.Font.LineHeight.L}
|
||||
style={styles.name}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
|
@ -7,13 +7,16 @@ export interface Props {
|
||||
account: Mastodon.Account
|
||||
}
|
||||
|
||||
const AccountInformationNotes: React.FC<Props> = ({ account }) => {
|
||||
return (
|
||||
<View style={styles.note}>
|
||||
<ParseHTML content={account.note!} size={'M'} emojis={account.emojis} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
const AccountInformationNotes = React.memo(
|
||||
({ account }: Props) => {
|
||||
return (
|
||||
<View style={styles.note}>
|
||||
<ParseHTML content={account.note!} size={'M'} emojis={account.emojis} />
|
||||
</View>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
note: {
|
||||
|
@ -40,7 +40,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
||||
ref={ref1}
|
||||
visible={account !== undefined}
|
||||
width={StyleConstants.Font.Size.S * 5}
|
||||
height={StyleConstants.Font.Size.S}
|
||||
height={StyleConstants.Font.LineHeight.S}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
<Text style={[styles.stat, { color: theme.primary }]}>
|
||||
@ -53,7 +53,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
||||
ref={ref2}
|
||||
visible={account !== undefined}
|
||||
width={StyleConstants.Font.Size.S * 5}
|
||||
height={StyleConstants.Font.Size.S}
|
||||
height={StyleConstants.Font.LineHeight.S}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
<Text
|
||||
@ -75,7 +75,7 @@ const AccountInformationStats = forwardRef<any, Props>(({ account }, ref) => {
|
||||
ref={ref3}
|
||||
visible={account !== undefined}
|
||||
width={StyleConstants.Font.Size.S * 5}
|
||||
height={StyleConstants.Font.Size.S}
|
||||
height={StyleConstants.Font.LineHeight.S}
|
||||
shimmerColors={[theme.shimmerDefault, theme.shimmerHighlight, theme.shimmerDefault]}
|
||||
>
|
||||
<Text
|
||||
|
@ -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)
|
@ -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)
|
@ -1,9 +1,9 @@
|
||||
import { AccountState } from "./types"
|
||||
import { AccountState } from './types'
|
||||
|
||||
const accountInitialState: AccountState = {
|
||||
headerRatio: 0.4,
|
||||
headerRatio: 1 / 3,
|
||||
informationLayout: { height: 0, y: 100 },
|
||||
segmentedIndex: 0
|
||||
}
|
||||
|
||||
export default accountInitialState
|
||||
export default accountInitialState
|
||||
|
Loading…
Reference in New Issue
Block a user