This commit is contained in:
Zhiyuan Zheng 2020-12-18 00:00:45 +01:00
parent b679b56a0e
commit 7be25ae516
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
13 changed files with 144 additions and 78 deletions

View File

@ -74,7 +74,7 @@ const renderNode = ({
) )
} }
} else { } else {
const domain = href.split(new RegExp(/:\/\/(.*?)\//)) const domain = href.split(new RegExp(/:\/\/(.[^\/]+)/))
return ( return (
<Text <Text
key={index} key={index}

View File

@ -19,7 +19,6 @@ export interface Props {
toot?: Mastodon.Status toot?: Mastodon.Status
account?: string account?: string
disableRefresh?: boolean disableRefresh?: boolean
scrollEnabled?: boolean
} }
const Timeline: React.FC<Props> = ({ const Timeline: React.FC<Props> = ({
@ -28,8 +27,7 @@ const Timeline: React.FC<Props> = ({
list, list,
toot, toot,
account, account,
disableRefresh = false, disableRefresh = false
scrollEnabled = true
}) => { }) => {
setFocusHandler(handleFocus => { setFocusHandler(handleFocus => {
const handleAppStateChange = (appState: string) => { const handleAppStateChange = (appState: string) => {
@ -160,7 +158,6 @@ const Timeline: React.FC<Props> = ({
renderItem={flRenderItem} renderItem={flRenderItem}
onEndReached={flOnEndReach} onEndReached={flOnEndReach}
keyExtractor={flKeyExtrator} keyExtractor={flKeyExtrator}
scrollEnabled={scrollEnabled} // For timeline in Account view
ListFooterComponent={flFooter} ListFooterComponent={flFooter}
ListEmptyComponent={flItemEmptyComponent} ListEmptyComponent={flItemEmptyComponent}
ItemSeparatorComponent={flItemSeparatorComponent} ItemSeparatorComponent={flItemSeparatorComponent}

View File

@ -44,7 +44,7 @@ const TimelineDefault: React.FC<Props> = ({
const tootOnPress = useCallback( const tootOnPress = useCallback(
() => () =>
!isRemotePublic && !isRemotePublic &&
navigation.navigate('Screen-Shared-Toot', { navigation.push('Screen-Shared-Toot', {
toot: actualStatus toot: actualStatus
}), }),
[] []

View File

@ -1,7 +1,7 @@
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { Image, Pressable, StyleSheet } from 'react-native' import { Image, Pressable, StyleSheet } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useNavigation } from '@react-navigation/native'
export interface Props { export interface Props {
queryKey?: App.QueryKey queryKey?: App.QueryKey
@ -13,7 +13,7 @@ const TimelineAvatar: React.FC<Props> = ({ queryKey, account }) => {
// Need to fix go back root // Need to fix go back root
const onPress = useCallback(() => { const onPress = useCallback(() => {
queryKey && queryKey &&
navigation.navigate('Screen-Shared-Account', { navigation.push('Screen-Shared-Account', {
id: account.id id: account.id
}) })
}, []) }, [])

View File

@ -1,5 +1,5 @@
import React, { useRef } from 'react' import React, { useRef, useState } from 'react'
import { ScrollView } from 'react-native' import { Animated, ScrollView } from 'react-native'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { getLocalUrl } from '@utils/slices/instancesSlice' import { getLocalUrl } from '@utils/slices/instancesSlice'
@ -10,6 +10,8 @@ import Collections from '@screens/Me/Root/Collections'
import Settings from '@screens/Me/Root/Settings' import Settings from '@screens/Me/Root/Settings'
import Logout from '@screens/Me/Root/Logout' import Logout from '@screens/Me/Root/Logout'
import { useScrollToTop } from '@react-navigation/native' import { useScrollToTop } from '@react-navigation/native'
import { AccountState } from '../Shared/Account'
import AccountNav from '../Shared/Account/Nav'
const ScreenMeRoot: React.FC = () => { const ScreenMeRoot: React.FC = () => {
const localRegistered = useSelector(getLocalUrl) const localRegistered = useSelector(getLocalUrl)
@ -17,13 +19,34 @@ const ScreenMeRoot: React.FC = () => {
const scrollRef = useRef<ScrollView>(null) const scrollRef = useRef<ScrollView>(null)
useScrollToTop(scrollRef) useScrollToTop(scrollRef)
const scrollY = useRef(new Animated.Value(0)).current
const [data, setData] = useState<Mastodon.Account>()
return ( return (
<ScrollView ref={scrollRef} keyboardShouldPersistTaps='handled'> <>
{localRegistered ? <MyInfo /> : <Login />} {localRegistered && data ? (
{localRegistered && <Collections />} <AccountNav
<Settings /> accountState={{ headerRatio: 0.4 } as AccountState}
{localRegistered && <Logout />} scrollY={scrollY}
</ScrollView> account={data}
/>
) : null}
<ScrollView
ref={scrollRef}
keyboardShouldPersistTaps='handled'
bounces={false}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ useNativeDriver: false }
)}
scrollEventThrottle={8}
>
{localRegistered ? <MyInfo setData={setData} /> : <Login />}
{localRegistered && <Collections />}
<Settings />
{localRegistered && <Logout />}
</ScrollView>
</>
) )
} }

View File

@ -1,4 +1,4 @@
import React from 'react' import React, { useEffect } from 'react'
import { useQuery } from 'react-query' import { useQuery } from 'react-query'
import { accountFetch } from '@utils/fetches/accountFetch' import { accountFetch } from '@utils/fetches/accountFetch'
@ -6,14 +6,28 @@ import AccountHeader from '@screens/Shared/Account/Header'
import AccountInformation from '@screens/Shared/Account/Information' import AccountInformation from '@screens/Shared/Account/Information'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { getLocalAccountId } from '@utils/slices/instancesSlice' import { getLocalAccountId } from '@utils/slices/instancesSlice'
import { AccountState } from '@root/screens/Shared/Account'
const MyInfo: React.FC = () => { export interface Props {
setData: React.Dispatch<React.SetStateAction<Mastodon.Account | undefined>>
}
const MyInfo: React.FC<Props> = ({ setData }) => {
const localAccountId = useSelector(getLocalAccountId) const localAccountId = useSelector(getLocalAccountId)
const { data } = useQuery(['Account', { id: localAccountId }], accountFetch) const { data } = useQuery(['Account', { id: localAccountId }], accountFetch)
useEffect(() => {
if (data) {
setData(data)
}
}, [data])
return ( return (
<> <>
<AccountHeader uri={data?.header} limitHeight /> <AccountHeader
accountState={{ headerRatio: 0.4 } as AccountState}
account={data}
limitHeight
/>
<AccountInformation account={data} /> <AccountInformation account={data} />
</> </>
) )

View File

@ -1,4 +1,4 @@
import React, { createContext, Dispatch, useReducer, useRef } from 'react' import React, { useReducer, useRef } from 'react'
import { Animated, ScrollView } from 'react-native' import { Animated, ScrollView } from 'react-native'
// import * as relationshipsSlice from 'src/stacks/common/relationshipsSlice' // import * as relationshipsSlice from 'src/stacks/common/relationshipsSlice'
@ -62,11 +62,6 @@ const accountReducer = (
throw new Error('Unexpected action') throw new Error('Unexpected action')
} }
} }
type ContextType = {
accountState: AccountState
accountDispatch: Dispatch<AccountAction>
}
export const AccountContext = createContext<ContextType>({} as ContextType)
const ScreenSharedAccount: React.FC<Props> = ({ const ScreenSharedAccount: React.FC<Props> = ({
route: { route: {
@ -83,23 +78,38 @@ const ScreenSharedAccount: React.FC<Props> = ({
) )
return ( return (
<AccountContext.Provider value={{ accountState, accountDispatch }}> <>
<AccountNav scrollY={scrollY} account={data} /> <AccountNav
<AccountSegmentedControl scrollY={scrollY} /> accountState={accountState}
scrollY={scrollY}
account={data}
/>
<AccountSegmentedControl
accountState={accountState}
accountDispatch={accountDispatch}
scrollY={scrollY}
/>
<ScrollView <ScrollView
contentContainerStyle={{ zIndex: 99 }}
bounces={false} bounces={false}
onScroll={Animated.event( onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }], [{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ useNativeDriver: false } { useNativeDriver: false }
)} )}
scrollEventThrottle={16} scrollEventThrottle={8}
> >
<AccountHeader uri={data?.header} /> <AccountHeader
<AccountInformation account={data} /> accountState={accountState}
<AccountToots id={id} /> accountDispatch={accountDispatch}
account={data}
/>
<AccountInformation accountDispatch={accountDispatch} account={data} />
<AccountToots
accountState={accountState}
accountDispatch={accountDispatch}
id={id}
/>
</ScrollView> </ScrollView>
</AccountContext.Provider> </>
) )
} }

View File

@ -1,25 +1,39 @@
import React, { useContext, useEffect, useRef } from 'react' import React, { Dispatch, useEffect, useRef, useState } from 'react'
import { Animated, Dimensions, Image, StyleSheet } from 'react-native' import { Animated, Dimensions, Image, StyleSheet } from 'react-native'
import { AccountContext } from '../Account' import { AccountAction, AccountState } from '../Account'
export interface Props { export interface Props {
uri?: Mastodon.Account['header'] accountState: AccountState
accountDispatch?: Dispatch<AccountAction>
account?: Mastodon.Account
limitHeight?: boolean limitHeight?: boolean
} }
const AccountHeader: React.FC<Props> = ({ uri, limitHeight = false }) => { const AccountHeader: React.FC<Props> = ({
const { accountState, accountDispatch } = useContext(AccountContext) accountState,
accountDispatch,
account,
limitHeight = false
}) => {
const [imageShown, setImageShown] = useState(true)
useEffect(() => { useEffect(() => {
if (uri) { if (account?.header) {
if (uri.includes('/headers/original/missing.png')) { if (account.header.includes('/headers/original/missing.png')) {
animateNewSize(accountState.headerRatio) animateNewSize(accountState.headerRatio)
} else { } else {
if (account.header !== account.header_static) {
setImageShown(false)
}
Image.getSize( Image.getSize(
uri, account.header,
(width, height) => { (width, height) => {
if (!limitHeight) { if (!limitHeight) {
accountDispatch({ type: 'headerRatio', payload: height / width }) accountDispatch &&
accountDispatch({
type: 'headerRatio',
payload: height / width
})
} }
animateNewSize( animateNewSize(
limitHeight ? accountState.headerRatio : height / width limitHeight ? accountState.headerRatio : height / width
@ -30,10 +44,12 @@ const AccountHeader: React.FC<Props> = ({ uri, limitHeight = false }) => {
} }
) )
} }
} else {
animateNewSize(accountState.headerRatio)
} }
}, [uri]) }, [account])
const theImage = imageShown ? (
<Image source={{ uri: account?.header }} style={styles.image} />
) : null
const windowWidth = Dimensions.get('window').width const windowWidth = Dimensions.get('window').width
const imageHeight = useRef( const imageHeight = useRef(
@ -44,12 +60,16 @@ const AccountHeader: React.FC<Props> = ({ uri, limitHeight = false }) => {
toValue: windowWidth * ratio, toValue: windowWidth * ratio,
duration: 350, duration: 350,
useNativeDriver: false useNativeDriver: false
}).start() }).start(({ finished }) => {
if (finished) {
setImageShown(true)
}
})
} }
return ( return (
<Animated.View style={[styles.imageContainer, { height: imageHeight }]}> <Animated.View style={[styles.imageContainer, { height: imageHeight }]}>
<Image source={{ uri: uri }} style={styles.image} /> {theImage}
</Animated.View> </Animated.View>
) )
} }

View File

@ -1,4 +1,4 @@
import React, { createRef, useContext, useEffect, useState } from 'react' import React, { createRef, Dispatch, useEffect, useState } from 'react'
import { Animated, Image, StyleSheet, Text, View } from 'react-native' import { Animated, Image, StyleSheet, Text, View } from 'react-native'
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder' import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder'
import { Feather } from '@expo/vector-icons' import { Feather } from '@expo/vector-icons'
@ -9,14 +9,14 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis' import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
import { LinearGradient } from 'expo-linear-gradient' import { LinearGradient } from 'expo-linear-gradient'
import { AccountContext } from '../Account' import { AccountAction } from '../Account'
export interface Props { export interface Props {
accountDispatch?: Dispatch<AccountAction>
account: Mastodon.Account | undefined account: Mastodon.Account | undefined
} }
const AccountInformation: React.FC<Props> = ({ account }) => { const AccountInformation: React.FC<Props> = ({ accountDispatch, account }) => {
const { accountDispatch } = useContext(AccountContext)
const { t } = useTranslation('sharedAccount') const { t } = useTranslation('sharedAccount')
const { theme } = useTheme() const { theme } = useTheme()
const [avatarLoaded, setAvatarLoaded] = useState(false) const [avatarLoaded, setAvatarLoaded] = useState(false)
@ -48,6 +48,7 @@ const AccountInformation: React.FC<Props> = ({ account }) => {
<View <View
style={styles.information} style={styles.information}
onLayout={({ nativeEvent }) => onLayout={({ nativeEvent }) =>
accountDispatch &&
accountDispatch({ accountDispatch({
type: 'informationLayout', type: 'informationLayout',
payload: { payload: {

View File

@ -1,18 +1,18 @@
import Emojis from '@root/components/Timelines/Timeline/Shared/Emojis' import Emojis from '@root/components/Timelines/Timeline/Shared/Emojis'
import { StyleConstants } from '@root/utils/styles/constants' import { StyleConstants } from '@root/utils/styles/constants'
import { useTheme } from '@root/utils/styles/ThemeManager' import { useTheme } from '@root/utils/styles/ThemeManager'
import React, { useContext } from 'react' import React from 'react'
import { Animated, Dimensions, StyleSheet, Text, View } from 'react-native' import { Animated, Dimensions, StyleSheet, Text, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { AccountContext } from '../Account' import { AccountState } from '../Account'
export interface Props { export interface Props {
accountState: AccountState
scrollY: Animated.Value scrollY: Animated.Value
account: Mastodon.Account | undefined account: Mastodon.Account | undefined
} }
const AccountNav: React.FC<Props> = ({ scrollY, account }) => { const AccountNav: React.FC<Props> = ({ accountState, scrollY, account }) => {
const { accountState } = useContext(AccountContext)
const { theme } = useTheme() const { theme } = useTheme()
const headerHeight = useSafeAreaInsets().top + 44 const headerHeight = useSafeAreaInsets().top + 44

View File

@ -1,18 +1,23 @@
import SegmentedControl from '@react-native-community/segmented-control' import SegmentedControl from '@react-native-community/segmented-control'
import { StyleConstants } from '@root/utils/styles/constants' import { StyleConstants } from '@root/utils/styles/constants'
import { useTheme } from '@root/utils/styles/ThemeManager' import { useTheme } from '@root/utils/styles/ThemeManager'
import React, { useContext } from 'react' import React, { Dispatch } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Animated, StyleSheet } from 'react-native' import { Animated, StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { AccountContext } from '../Account' import { AccountAction, AccountState } from '../Account'
export interface Props { export interface Props {
accountState: AccountState
accountDispatch: Dispatch<AccountAction>
scrollY: Animated.Value scrollY: Animated.Value
} }
const AccountSegmentedControl: React.FC<Props> = ({ scrollY }) => { const AccountSegmentedControl: React.FC<Props> = ({
const { accountState, accountDispatch } = useContext(AccountContext) accountState,
accountDispatch,
scrollY
}) => {
const { t } = useTranslation('sharedAccount') const { t } = useTranslation('sharedAccount')
const { mode, theme } = useTheme() const { mode, theme } = useTheme()
@ -69,12 +74,10 @@ const AccountSegmentedControl: React.FC<Props> = ({ scrollY }) => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
base: { base: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFillObject,
position: 'absolute',
left: 0,
right: 0,
zIndex: 99, zIndex: 99,
borderTopWidth: StyleSheet.hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
padding: StyleConstants.Spacing.Global.PagePadding padding: StyleConstants.Spacing.Global.PagePadding,
paddingBottom: StyleConstants.Spacing.Global.PagePadding * 3
} }
}) })

View File

@ -1,32 +1,29 @@
import React, { useCallback, useContext, useState } from 'react' import React, { Dispatch, useCallback, useState } from 'react'
import { Dimensions, StyleSheet } from 'react-native' import { Dimensions, StyleSheet } from 'react-native'
import { TabView, SceneMap } from 'react-native-tab-view' import { SceneMap, TabView } from 'react-native-tab-view'
import Timeline from '@components/Timelines/Timeline' import Timeline from '@components/Timelines/Timeline'
import { AccountContext } from '../Account' import { AccountAction, AccountState } from '../Account'
import { StyleConstants } from '@root/utils/styles/constants' import { StyleConstants } from '@root/utils/styles/constants'
export interface Props { export interface Props {
accountState: AccountState
accountDispatch: Dispatch<AccountAction>
id: Mastodon.Account['id'] id: Mastodon.Account['id']
} }
const AccountToots: React.FC<Props> = ({ id }) => { const AccountToots: React.FC<Props> = ({
const { accountState, accountDispatch } = useContext(AccountContext) accountState,
accountDispatch,
id
}) => {
const [routes] = useState([ const [routes] = useState([
{ key: 'Account_Default' }, { key: 'Account_Default' },
{ key: 'Account_All' }, { key: 'Account_All' },
{ key: 'Account_Media' } { key: 'Account_Media' }
]) ])
const singleScene = useCallback( const singleScene = useCallback(
({ route }) => ( ({ route }) => <Timeline page={route.key} account={id} disableRefresh />,
<Timeline
page={route.key}
account={id}
disableRefresh
scrollEnabled={false}
/>
),
[] []
) )
const renderScene = SceneMap({ const renderScene = SceneMap({

View File

@ -51,6 +51,7 @@ const ScreenSharedWebview: React.FC<Props> = ({
{() => ( {() => (
<> <>
<WebView <WebView
allowsBackForwardNavigationGestures
ref={webview} ref={webview}
source={{ uri }} source={{ uri }}
decelerationRate='normal' decelerationRate='normal'