Using tab-view

Cannot scroll separately
This commit is contained in:
Zhiyuan Zheng 2020-12-17 09:44:03 +01:00
parent a3335a1f88
commit 3427b613aa
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
15 changed files with 462 additions and 173 deletions

View File

@ -44,6 +44,7 @@
"react-native-screens": "~2.15.0",
"react-native-shimmer-placeholder": "^2.0.6",
"react-native-svg": "12.1.0",
"react-native-tab-view": "^2.15.2",
"react-native-toast-message": "^1.3.4",
"react-native-webview": "11.0.0",
"react-navigation": "^4.4.3",
@ -70,4 +71,4 @@
"typescript": "~4.0.0"
},
"private": true
}
}

View File

@ -1,5 +1,5 @@
import React, { useCallback, useState } from 'react'
import { Pressable, Text } from 'react-native'
import { Pressable, Text, View } from 'react-native'
import HTMLView from 'react-native-htmlview'
import { useNavigation } from '@react-navigation/native'
@ -98,6 +98,12 @@ const renderNode = ({
</Text>
)
}
} else {
if (node.name === 'p') {
if (!node.children.length) {
return <View key={index}></View> // bug when the tag is empty
}
}
}
}
@ -134,26 +140,24 @@ const ParseContent: React.FC<Props> = ({
}),
[]
)
const textComponent = useCallback(
({ children }) =>
emojis && children ? (
<Emojis
content={children.toString()}
emojis={emojis}
size={StyleConstants.Font.Size[size]}
/>
) : (
<Text>{children}</Text>
),
[]
)
const textComponent = useCallback(({ children }) => {
return emojis && children ? (
<Emojis
content={children.toString()}
emojis={emojis}
size={StyleConstants.Font.Size[size]}
/>
) : (
<Text>{children}</Text>
)
}, [])
const rootComponent = useCallback(({ children }) => {
const { theme } = useTheme()
const [textLoaded, setTextLoaded] = useState(false)
const [totalLines, setTotalLines] = useState<number | undefined>()
const [lineHeight, setLineHeight] = useState<number | undefined>()
const [shownLines, setShownLines] = useState(numberOfLines)
// console.log(children)
return (
<>
<Text

View File

@ -39,7 +39,7 @@ const fireMutation = async ({
} else {
toast({
type: 'error',
content: '隐藏域名失败,请重试',
content: '投票失败,请重试',
autoHide: false
})
return Promise.reject()
@ -104,7 +104,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
const pollExpiration = useMemo(() => {
// how many voted
if (poll.expired) {
if (poll!.expired) {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
@ -113,20 +113,20 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
} else {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
{relativeTime(poll.expires_at)}
{relativeTime(poll!.expires_at)}
</Text>
)
}
}, [])
const [singleOptions, setSingleOptions] = useState({
...[false, false, false, false].slice(0, poll.options.length)
...[false, false, false, false].slice(0, poll!.options.length)
})
const [multipleOptions, setMultipleOptions] = useState({
...[false, false, false, false].slice(0, poll.options.length)
...[false, false, false, false].slice(0, poll!.options.length)
})
const isSelected = (index: number) => {
if (poll.multiple) {
if (poll!.multiple) {
return multipleOptions[index] ? 'check-square' : 'square'
} else {
return singleOptions[index] ? 'check-circle' : 'circle'
@ -135,28 +135,28 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
return (
<View style={styles.base}>
{poll.options.map((option, index) =>
poll.voted ? (
{poll!.options.map((option, index) =>
poll!.voted ? (
<View key={index} style={styles.poll}>
<View style={styles.optionSelected}>
<View style={styles.contentSelected}>
<Emojis
content={option.title}
emojis={poll.emojis}
emojis={poll!.emojis}
size={StyleConstants.Font.Size.M}
numberOfLines={1}
/>
{poll.own_votes!.includes(index) && (
{poll!.own_votes!.includes(index) && (
<Feather
style={styles.voted}
name={poll.multiple ? 'check-square' : 'check-circle'}
name={poll!.multiple ? 'check-square' : 'check-circle'}
size={StyleConstants.Font.Size.M}
color={theme.primary}
/>
)}
</View>
<Text style={[styles.percentage, { color: theme.primary }]}>
{Math.round((option.votes_count / poll.votes_count) * 100)}%
{Math.round((option.votes_count / poll!.votes_count) * 100)}%
</Text>
</View>
@ -165,7 +165,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
styles.background,
{
width: `${Math.round(
(option.votes_count / poll.votes_count) * 100
(option.votes_count / poll!.votes_count) * 100
)}%`,
backgroundColor: theme.border
}
@ -177,7 +177,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
<Pressable
style={[styles.optionUnselected]}
onPress={() => {
if (poll.multiple) {
if (poll!.multiple) {
setMultipleOptions({
...multipleOptions,
[index]: !multipleOptions[index]
@ -189,7 +189,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
index === 1,
index === 2,
index === 3
].slice(0, poll.options.length)
].slice(0, poll!.options.length)
})
}
}}
@ -203,7 +203,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
<View style={styles.contentUnselected}>
<Emojis
content={option.title}
emojis={poll.emojis}
emojis={poll!.emojis}
size={StyleConstants.Font.Size.M}
/>
</View>
@ -227,7 +227,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
</View>
)}
<Text style={[styles.votes, { color: theme.secondary }]}>
{poll.voters_count}{' • '}
{poll.voters_count || 0}{' • '}
</Text>
{pollExpiration}
</View>

View File

@ -21,9 +21,10 @@ import { useTranslation } from 'react-i18next'
import { StyleConstants } from '@utils/styles/constants'
import { ButtonRow } from '@components/Button'
import ParseContent from '@root/components/ParseContent'
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'
import { Feather } from '@expo/vector-icons'
import { applicationFetch } from '@root/utils/fetches/applicationFetch'
import { LinearGradient } from 'expo-linear-gradient'
const Login: React.FC = () => {
const { t } = useTranslation('meRoot')
@ -36,6 +37,8 @@ const Login: React.FC = () => {
clientSecret: string
}>()
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
const instanceQuery = useQuery(
['Instance', { instanceDomain }],
instanceFetch,

View File

@ -1,5 +1,5 @@
import React from 'react'
import { ScrollView } from 'react-native'
import React, { createContext, Dispatch, useReducer, useRef } from 'react'
import { Animated, ScrollView } from 'react-native'
// import * as relationshipsSlice from 'src/stacks/common/relationshipsSlice'
@ -8,6 +8,8 @@ import { accountFetch } from '@utils/fetches/accountFetch'
import AccountToots from '@screens/Shared/Account/Toots'
import AccountHeader from '@screens/Shared/Account/Header'
import AccountInformation from '@screens/Shared/Account/Information'
import AccountNav from './Account/Nav'
import AccountSegmentedControl from './Account/SegmentedControl'
// Moved account example: https://m.cmx.im/web/accounts/27812
@ -19,6 +21,53 @@ export interface Props {
}
}
export type AccountState = {
headerRatio: number
informationLayout?: {
y: number
height: number
}
segmentedIndex: number
}
export type AccountAction =
| {
type: 'headerRatio'
payload: AccountState['headerRatio']
}
| {
type: 'informationLayout'
payload: AccountState['informationLayout']
}
| {
type: 'segmentedIndex'
payload: AccountState['segmentedIndex']
}
const AccountInitialState: AccountState = {
headerRatio: 0.4,
informationLayout: { height: 0, y: 100 },
segmentedIndex: 0
}
const accountReducer = (
state: AccountState,
action: AccountAction
): AccountState => {
switch (action.type) {
case 'headerRatio':
return { ...state, headerRatio: action.payload }
case 'informationLayout':
return { ...state, informationLayout: action.payload }
case 'segmentedIndex':
return { ...state, segmentedIndex: action.payload }
default:
throw new Error('Unexpected action')
}
}
type ContextType = {
accountState: AccountState
accountDispatch: Dispatch<AccountAction>
}
export const AccountContext = createContext<ContextType>({} as ContextType)
const ScreenSharedAccount: React.FC<Props> = ({
route: {
params: { id }
@ -27,13 +76,30 @@ const ScreenSharedAccount: React.FC<Props> = ({
const { data } = useQuery(['Account', { id }], accountFetch)
// const stateRelationships = useSelector(relationshipsState)
const scrollY = useRef(new Animated.Value(0)).current
const [accountState, accountDispatch] = useReducer(
accountReducer,
AccountInitialState
)
return (
<ScrollView bounces={false}>
<AccountHeader uri={data?.header} />
<AccountInformation account={data} />
<AccountToots id={id} />
</ScrollView>
<AccountContext.Provider value={{ accountState, accountDispatch }}>
<AccountNav scrollY={scrollY} account={data} />
<AccountSegmentedControl scrollY={scrollY} />
<ScrollView
contentContainerStyle={{ zIndex: 99 }}
bounces={false}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ useNativeDriver: false }
)}
scrollEventThrottle={16}
>
<AccountHeader uri={data?.header} />
<AccountInformation account={data} />
<AccountToots id={id} />
</ScrollView>
</AccountContext.Provider>
)
}

View File

@ -1,37 +1,44 @@
import React, { useEffect, useRef } from 'react'
import React, { useContext, useEffect, useRef } from 'react'
import { Animated, Dimensions, Image, StyleSheet } from 'react-native'
import { AccountContext } from '../Account'
export interface Props {
uri?: Mastodon.Account['header']
limitHeight?: boolean
}
const limitRatio = 0.4
const AccountHeader: React.FC<Props> = ({ uri, limitHeight = false }) => {
const { accountState, accountDispatch } = useContext(AccountContext)
useEffect(() => {
if (uri) {
if (uri.includes('/headers/original/missing.png')) {
animateNewSize(limitRatio)
animateNewSize(accountState.headerRatio)
} else {
Image.getSize(
uri,
(width, height) => {
animateNewSize(limitHeight ? limitRatio : height / width)
if (!limitHeight) {
accountDispatch({ type: 'headerRatio', payload: height / width })
}
animateNewSize(
limitHeight ? accountState.headerRatio : height / width
)
},
() => {
animateNewSize(limitRatio)
animateNewSize(accountState.headerRatio)
}
)
}
} else {
animateNewSize(limitRatio)
animateNewSize(accountState.headerRatio)
}
}, [uri])
const windowWidth = Dimensions.get('window').width
const imageHeight = useRef(new Animated.Value(windowWidth * limitRatio))
.current
const imageHeight = useRef(
new Animated.Value(windowWidth * accountState.headerRatio)
).current
const animateNewSize = (ratio: number) => {
Animated.timing(imageHeight, {
toValue: windowWidth * ratio,

View File

@ -1,6 +1,6 @@
import React, { createRef, useEffect, useState } from 'react'
import React, { createRef, useContext, useEffect, useState } from 'react'
import { Animated, Image, StyleSheet, Text, View } from 'react-native'
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'
import { Feather } from '@expo/vector-icons'
import ParseContent from '@components/ParseContent'
@ -8,23 +8,27 @@ import { useTheme } from '@utils/styles/ThemeManager'
import { StyleConstants } from '@utils/styles/constants'
import { useTranslation } from 'react-i18next'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
import { LinearGradient } from 'expo-linear-gradient'
import { AccountContext } from '../Account'
export interface Props {
account: Mastodon.Account | undefined
}
const AccountInformation: React.FC<Props> = ({ account }) => {
const { accountDispatch } = useContext(AccountContext)
const { t } = useTranslation('sharedAccount')
const { theme } = useTheme()
const [avatarLoaded, setAvatarLoaded] = useState(false)
const shimmerAvatarRef = createRef<ShimmerPlaceholder>()
const shimmerNameRef = createRef<ShimmerPlaceholder>()
const shimmerAccountRef = createRef<ShimmerPlaceholder>()
const shimmerCreatedRef = createRef<ShimmerPlaceholder>()
const shimmerStatTootRef = createRef<ShimmerPlaceholder>()
const shimmerStatFolloingRef = createRef<ShimmerPlaceholder>()
const shimmerStatFollowerRef = createRef<ShimmerPlaceholder>()
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
const shimmerAvatarRef = createRef<any>()
const shimmerNameRef = createRef<any>()
const shimmerAccountRef = createRef<any>()
const shimmerCreatedRef = createRef<any>()
const shimmerStatTootRef = createRef<any>()
const shimmerStatFolloingRef = createRef<any>()
const shimmerStatFollowerRef = createRef<any>()
useEffect(() => {
const informationAnimated = Animated.stagger(400, [
Animated.parallel([
@ -41,7 +45,18 @@ const AccountInformation: React.FC<Props> = ({ account }) => {
}, [])
return (
<View style={styles.information}>
<View
style={styles.information}
onLayout={({ nativeEvent }) =>
accountDispatch({
type: 'informationLayout',
payload: {
y: nativeEvent.layout.y,
height: nativeEvent.layout.height
}
})
}
>
{/* <Text>Moved or not: {account.moved}</Text> */}
<ShimmerPlaceholder
ref={shimmerAvatarRef}
@ -246,7 +261,7 @@ const AccountInformation: React.FC<Props> = ({ account }) => {
const styles = StyleSheet.create({
information: {
marginTop: -30 - StyleConstants.Spacing.Global.PagePadding,
marginTop: -StyleConstants.Spacing.Global.PagePadding * 3,
padding: StyleConstants.Spacing.Global.PagePadding
},
avatar: {

View File

@ -0,0 +1,101 @@
import Emojis from '@root/components/Timelines/Timeline/Shared/Emojis'
import { StyleConstants } from '@root/utils/styles/constants'
import { useTheme } from '@root/utils/styles/ThemeManager'
import React, { useContext } from 'react'
import { Animated, Dimensions, StyleSheet, Text, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { AccountContext } from '../Account'
export interface Props {
scrollY: Animated.Value
account: Mastodon.Account | undefined
}
const AccountNav: React.FC<Props> = ({ scrollY, account }) => {
const { accountState } = useContext(AccountContext)
const { theme } = useTheme()
const headerHeight = useSafeAreaInsets().top + 44
const nameY =
Dimensions.get('screen').width * accountState.headerRatio +
StyleConstants.Avatar.L -
StyleConstants.Spacing.Global.PagePadding * 2 +
StyleConstants.Spacing.M -
headerHeight
return (
<Animated.View
style={[
styles.base,
{
backgroundColor: theme.background,
opacity: scrollY.interpolate({
inputRange: [0, 200],
outputRange: [0, 1],
extrapolate: 'clamp'
}),
height: headerHeight
}
]}
>
<View
style={[
styles.content,
{
marginTop:
useSafeAreaInsets().top + (44 - StyleConstants.Font.Size.L) / 2
}
]}
>
<Animated.View
style={[
styles.display_name,
{
marginTop: scrollY.interpolate({
inputRange: [nameY, nameY + 20],
outputRange: [50, 0],
extrapolate: 'clamp'
})
}
]}
>
{account?.emojis ? (
<Emojis
content={account?.display_name || account?.username}
emojis={account.emojis}
size={StyleConstants.Font.Size.L}
fontBold={true}
/>
) : (
<Text
style={{
color: theme.primary,
fontSize: StyleConstants.Font.Size.L,
fontWeight: StyleConstants.Font.Weight.Bold
}}
>
{account?.display_name || account?.username}
</Text>
)}
</Animated.View>
</View>
</Animated.View>
)
}
const styles = StyleSheet.create({
base: {
...StyleSheet.absoluteFillObject,
zIndex: 99
},
content: {
flex: 1,
alignItems: 'center',
overflow: 'hidden'
},
display_name: {
flexDirection: 'row'
}
})
export default AccountNav

View File

@ -0,0 +1,81 @@
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 { Animated, StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { AccountContext } from '../Account'
export interface Props {
scrollY: Animated.Value
}
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 translateY = scrollY.interpolate({
inputRange: [
0,
(accountState.informationLayout?.y || 0) +
(accountState.informationLayout?.height || 0) -
headerHeight
],
outputRange: [
0,
-(accountState.informationLayout?.y || 0) -
(accountState.informationLayout?.height || 0) +
headerHeight
],
extrapolate: 'clamp'
})
return (
<Animated.View
style={[
styles.base,
{
top:
(accountState.informationLayout?.y || 0) +
(accountState.informationLayout?.height || 0),
transform: [{ translateY }],
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,
position: 'absolute',
left: 0,
right: 0,
zIndex: 99,
borderTopWidth: StyleSheet.hairlineWidth,
padding: StyleConstants.Spacing.Global.PagePadding
}
})
export default AccountSegmentedControl

View File

@ -1,87 +1,60 @@
import React, { useRef, useState } from 'react'
import { Dimensions, FlatList, View } from 'react-native'
import SegmentedControl from '@react-native-community/segmented-control'
import React, { useCallback, useContext } from 'react'
import { Dimensions, StyleSheet } from 'react-native'
import { TabView, SceneMap } from 'react-native-tab-view'
import Timeline from '@components/Timelines/Timeline'
import { useTranslation } from 'react-i18next'
import { AccountContext } from '../Account'
import { StyleConstants } from '@root/utils/styles/constants'
export interface Props {
id: Mastodon.Account['id']
}
const AccountToots: React.FC<Props> = ({ id }) => {
const { t } = useTranslation('sharedAccount')
const [segment, setSegment] = useState(0)
const [segmentManuallyTriggered, setSegmentManuallyTriggered] = useState(
false
)
const horizontalPaging = useRef<any>()
const { accountState, accountDispatch } = useContext(AccountContext)
const pages: ['Account_Default', 'Account_All', 'Account_Media'] = [
'Account_Default',
'Account_All',
'Account_Media'
]
const [routes] = React.useState([
{ key: 'Account_Default' },
{ key: 'Account_All' },
{ key: 'Account_Media' }
])
const singleScene = useCallback(
({ route }) => (
<Timeline
page={route.key}
account={id}
disableRefresh
scrollEnabled={false}
/>
),
[]
)
const renderScene = SceneMap({
Account_Default: singleScene,
Account_All: singleScene,
Account_Media: singleScene
})
return (
<>
<SegmentedControl
values={[
t('content.segments.left'),
t('content.segments.middle'),
t('content.segments.right')
]}
selectedIndex={segment}
onChange={({ nativeEvent }) => {
setSegmentManuallyTriggered(true)
setSegment(nativeEvent.selectedSegmentIndex)
horizontalPaging.current.scrollToIndex({
index: nativeEvent.selectedSegmentIndex
})
}}
style={{ width: '100%', height: 30 }}
/>
<FlatList
style={{ width: Dimensions.get('window').width, height: '100%' }}
data={pages}
keyExtractor={page => page}
renderItem={({ item, index }) => {
return (
<View style={{ width: Dimensions.get('window').width }}>
<Timeline
key={index}
page={item}
account={id}
disableRefresh
scrollEnabled={false}
/>
</View>
)
}}
ref={horizontalPaging}
bounces={false}
getItemLayout={(data, index) => ({
length: Dimensions.get('window').width,
offset: Dimensions.get('window').width * index,
index
})}
horizontal
onMomentumScrollEnd={() => {
setSegmentManuallyTriggered(false)
}}
onScroll={({ nativeEvent }) =>
!segmentManuallyTriggered &&
setSegment(
nativeEvent.contentOffset.x <= Dimensions.get('window').width / 3
? 0
: 1
)
}
pagingEnabled
showsHorizontalScrollIndicator={false}
/>
</>
<TabView
style={styles.base}
navigationState={{ index: accountState.segmentedIndex, routes }}
renderScene={renderScene}
renderTabBar={() => null}
onIndexChange={index =>
accountDispatch({ type: 'segmentedIndex', payload: index })
}
initialLayout={{ width: Dimensions.get('window').width }}
lazy
swipeEnabled
/>
)
}
const styles = StyleSheet.create({
base: {
marginTop: StyleConstants.Spacing.Global.PagePadding + 33
}
})
export default AccountToots

View File

@ -90,7 +90,7 @@ export type ComposeState = {
}
}
export type PostAction =
export type ComposeAction =
| {
type: 'spoiler'
payload: Partial<ComposeState['spoiler']>
@ -251,7 +251,10 @@ const composeExistingState = ({
}
}
}
const postReducer = (state: ComposeState, action: PostAction): ComposeState => {
const composeReducer = (
state: ComposeState,
action: ComposeAction
): ComposeState => {
switch (action.type) {
case 'spoiler':
return { ...state, spoiler: { ...state.spoiler, ...action.payload } }
@ -294,7 +297,7 @@ const postReducer = (state: ComposeState, action: PostAction): ComposeState => {
type ContextType = {
composeState: ComposeState
composeDispatch: Dispatch<PostAction>
composeDispatch: Dispatch<ComposeAction>
}
export const ComposeContext = createContext<ContextType>({} as ContextType)
@ -332,7 +335,7 @@ const Compose: React.FC<Props> = ({ route: { params }, navigation }) => {
}
const [composeState, composeDispatch] = useReducer(
postReducer,
composeReducer,
params?.type && params?.incomingStatus
? composeExistingState({
type: params.type,

View File

@ -12,10 +12,11 @@ import { ComposeContext } from '@screens/Shared/Compose'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import { useNavigation } from '@react-navigation/native'
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'
import { ButtonRound } from '@components/Button'
import addAttachments from '@screens/Shared/Compose/addAttachments'
import { Feather } from '@expo/vector-icons'
import { LinearGradient } from 'expo-linear-gradient'
const DEFAULT_HEIGHT = 200
@ -97,6 +98,8 @@ const ComposeAttachments: React.FC = () => {
)
const listFooter = useCallback(() => {
const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient)
return (
<ShimmerPlaceholder
style={styles.progressContainer}

View File

@ -18,14 +18,21 @@ const sharedScreens = (Stack: any) => {
key='Screen-Shared-Account'
name='Screen-Shared-Account'
component={ScreenSharedAccount}
options={({ navigation }: any) => ({
headerTranslucent: true,
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
headerCenter: () => null,
headerLeft: () => (
<HeaderLeft icon='chevron-left' onPress={() => navigation.goBack()} />
)
})}
options={({ navigation }: any) => {
return {
headerTranslucent: true,
headerStyle: {
backgroundColor: `rgba(255, 255, 255, 0)`
},
headerCenter: () => null,
headerLeft: () => (
<HeaderLeft
icon='chevron-left'
onPress={() => navigation.goBack()}
/>
)
}
}}
/>,
<Stack.Screen
key='Screen-Shared-Hashtag'

View File

@ -91,26 +91,53 @@ export const timelineFetch = async (
return Promise.resolve({ toots: res.body })
case 'Account_Default':
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
pinned: 'true'
if (pagination && pagination.id) {
if (pagination.direction === 'prev') {
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
pinned: 'true',
...params
}
})
return Promise.resolve({ toots: res.body })
} else {
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true',
...params
}
})
return Promise.resolve({ toots: res.body })
}
})
const pinnedLength = res.body.length
let toots: Mastodon.Status[] = res.body
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true'
}
})
toots = uniqBy([...toots, ...res.body], 'id')
return Promise.resolve({ toots: toots, pinnedLength })
} else {
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
pinned: 'true'
}
})
const pinnedLength = res.body.length
let toots: Mastodon.Status[] = res.body
res = await client({
method: 'get',
instance: 'local',
url: `accounts/${account}/statuses`,
params: {
exclude_replies: 'true'
}
})
toots = uniqBy([...toots, ...res.body], 'id')
return Promise.resolve({ toots: toots, pinnedLength })
}
break
case 'Account_All':
res = await client({

View File

@ -2775,13 +2775,6 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
expo-app-loading@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/expo-app-loading/-/expo-app-loading-1.0.1.tgz#a2ef73235c5dd0a99c0319ad9d3f4d278237564c"
integrity sha512-qgsystchwMHep43YMAMHJAD5bRUikolQpb1d+ySWF4ZFTtNInmLAPqiBcVSey5taRuHD3cAvEijx3wc0W/HfVw==
dependencies:
expo-splash-screen "0.8.1"
expo-application@~2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-2.4.1.tgz#f8eb4a3a05a0b8a8f38f2e981f587a815945b685"
@ -2915,7 +2908,7 @@ expo-secure-store@~9.3.0:
resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-9.3.0.tgz#b716d5d115cc50a34037d1afef84fe4b8ea0745c"
integrity sha512-dNhKcoUUn+1kmEfFVxSU7h+YsqODqlExZQJcQgxgeiuCeeDvJWkE10t3jjrO6aNfrdM5i/X2l3oh401EDslWsQ==
expo-splash-screen@0.8.1, expo-splash-screen@~0.8.1:
expo-splash-screen@~0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.8.1.tgz#de4a018c82af879eeaa9b697013ef37d9567453a"
integrity sha512-7NQo8OgkfQ4sv4mIHE58fkiQTGyl5dOP70uAUTb5YXC/QUsHIqEpKQ6ZXDpkLU1cbZ32c8Vtok4r2FRIUrzxcg==
@ -5305,6 +5298,11 @@ react-native-svg@12.1.0:
css-select "^2.1.0"
css-tree "^1.0.0-alpha.39"
react-native-tab-view@^2.15.2:
version "2.15.2"
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.15.2.tgz#4bc7832d33a119306614efee667509672a7ee64e"
integrity sha512-2hxLkBnZtEKFDyfvNO5EUywhy3f/EiLOBO8SWqKj4BMBTO0QwnybaPE5MVF00Fhz+VA4+h/iI40Dkrrtq70dGg==
react-native-toast-message@^1.3.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/react-native-toast-message/-/react-native-toast-message-1.4.1.tgz#8be7b91d8a5405e86179f83e52e56de58bbce25f"