diff --git a/src/components/Timeline/Conversation.tsx b/src/components/Timeline/Conversation.tsx index 53378308..212234d4 100644 --- a/src/components/Timeline/Conversation.tsx +++ b/src/components/Timeline/Conversation.tsx @@ -8,8 +8,9 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' +import { isEqual } from 'lodash' import React, { useCallback } from 'react' -import { Pressable, StyleSheet, View } from 'react-native' +import { Pressable, View } from 'react-native' import { useMutation, useQueryClient } from 'react-query' import { useSelector } from 'react-redux' import TimelineActions from './Shared/Actions' @@ -54,117 +55,107 @@ export interface Props { highlighted?: boolean } -const TimelineConversation: React.FC = ({ - conversation, - queryKey, - highlighted = false -}) => { - const instanceAccount = useSelector( - getInstanceAccount, - (prev, next) => prev?.id === next?.id - ) - const { colors } = useTheme() +const TimelineConversation = React.memo( + ({ conversation, queryKey, highlighted = false }: Props) => { + const instanceAccount = useSelector( + getInstanceAccount, + (prev, next) => prev?.id === next?.id + ) + const { colors } = useTheme() - const queryClient = useQueryClient() - const fireMutation = useCallback(() => { - return apiInstance({ - method: 'post', - url: `conversations/${conversation.id}/read` - }) - }, []) - const { mutate } = useMutation(fireMutation, { - onSettled: () => { - queryClient.invalidateQueries(queryKey) - } - }) - - const navigation = - useNavigation>() - const onPress = useCallback(() => { - analytics('timeline_conversation_press') - if (conversation.last_status) { - conversation.unread && mutate() - navigation.push('Tab-Shared-Toot', { - toot: conversation.last_status, - rootQueryKey: queryKey + const queryClient = useQueryClient() + const fireMutation = useCallback(() => { + return apiInstance({ + method: 'post', + url: `conversations/${conversation.id}/read` }) - } - }, []) + }, []) + const { mutate } = useMutation(fireMutation, { + onSettled: () => { + queryClient.invalidateQueries(queryKey) + } + }) - return ( - - - - - + const navigation = + useNavigation>() + const onPress = useCallback(() => { + analytics('timeline_conversation_press') + if (conversation.last_status) { + conversation.unread && mutate() + navigation.push('Tab-Shared-Toot', { + toot: conversation.last_status, + rootQueryKey: queryKey + }) + } + }, []) - {conversation.last_status ? ( - <> - - + + + + + + {conversation.last_status ? ( + <> + + + {conversation.last_status.poll ? ( + + ) : null} + + account.acct)} + reblog={false} /> - {conversation.last_status.poll ? ( - - ) : null} - - account.acct)} - reblog={false} - /> - - ) : null} - - ) -} - -const styles = StyleSheet.create({ - base: { - flex: 1, - flexDirection: 'column', - padding: StyleConstants.Spacing.Global.PagePadding, - paddingBottom: 0 + + ) : null} + + ) }, - header: { - flex: 1, - width: '100%', - flexDirection: 'row' - } -}) + (prev, next) => isEqual(prev.conversation, next.conversation) +) export default TimelineConversation diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 03f325fc..5a235b34 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -14,9 +14,9 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { uniqBy } from 'lodash' +import { isEqual, uniqBy } from 'lodash' import React, { useCallback } from 'react' -import { Pressable, StyleSheet, View } from 'react-native' +import { Pressable, View } from 'react-native' import { useSelector } from 'react-redux' import TimelineFeedback from './Shared/Feedback' import TimelineFiltered, { shouldFilter } from './Shared/Filtered' @@ -34,148 +34,143 @@ export interface Props { } // When the poll is long -const TimelineDefault: React.FC = ({ - item, - queryKey, - rootQueryKey, - origin, - highlighted = false, - disableDetails = false, - disableOnPress = false -}) => { - const { colors } = useTheme() - const instanceAccount = useSelector(getInstanceAccount, () => true) - const navigation = - useNavigation>() +const TimelineDefault = React.memo( + ({ + item, + queryKey, + rootQueryKey, + origin, + highlighted = false, + disableDetails = false, + disableOnPress = false + }: Props) => { + const { colors } = useTheme() + const instanceAccount = useSelector(getInstanceAccount, () => true) + const navigation = + useNavigation>() - const actualStatus = item.reblog ? item.reblog : item + const actualStatus = item.reblog ? item.reblog : item - const ownAccount = actualStatus.account?.id === instanceAccount?.id + const ownAccount = actualStatus.account?.id === instanceAccount?.id - if ( - !highlighted && - queryKey && - shouldFilter({ status: actualStatus, queryKey }) - ) { - return - } - - const onPress = useCallback(() => { - analytics('timeline_default_press', { - page: queryKey ? queryKey[1].page : origin - }) - !disableOnPress && + if ( !highlighted && - navigation.push('Tab-Shared-Toot', { - toot: actualStatus, - rootQueryKey: queryKey - }) - }, []) + queryKey && + shouldFilter({ status: actualStatus, queryKey }) + ) { + return + } - return ( - { + analytics('timeline_default_press', { + page: queryKey ? queryKey[1].page : origin + }) + !disableOnPress && + !highlighted && + navigation.push('Tab-Shared-Toot', { + toot: actualStatus, + rootQueryKey: queryKey + }) + }, []) + + return ( + - {item.reblog ? ( - - ) : item._pinned ? ( - - ) : null} - - - - - - - - {typeof actualStatus.content === 'string' && - actualStatus.content.length > 0 ? ( - + {item.reblog ? ( + + ) : item._pinned ? ( + ) : null} - {queryKey && actualStatus.poll ? ( - + + + + + + {typeof actualStatus.content === 'string' && + actualStatus.content.length > 0 ? ( + + ) : null} + {queryKey && actualStatus.poll ? ( + + ) : null} + {!disableDetails && + Array.isArray(actualStatus.media_attachments) && + actualStatus.media_attachments.length ? ( + + ) : null} + {!disableDetails && actualStatus.card ? ( + + ) : null} + {!disableDetails ? ( + + ) : null} + + + + + {queryKey && !disableDetails ? ( + d?.id !== instanceAccount?.id), + d => d?.id + ).map(d => d?.acct)} reblog={item.reblog ? true : false} - sameAccount={ownAccount} /> ) : null} - {!disableDetails && - Array.isArray(actualStatus.media_attachments) && - actualStatus.media_attachments.length ? ( - - ) : null} - {!disableDetails && actualStatus.card ? ( - - ) : null} - {!disableDetails ? ( - - ) : null} - - - - - {queryKey && !disableDetails ? ( - d?.id !== instanceAccount?.id), - d => d?.id - ).map(d => d?.acct)} - reblog={item.reblog ? true : false} - /> - ) : null} - - ) -} - -const styles = StyleSheet.create({ - statusView: { - padding: StyleConstants.Spacing.Global.PagePadding, - paddingBottom: 0 + + ) }, - header: { - flex: 1, - width: '100%', - flexDirection: 'row' - } -}) + (prev, next) => isEqual(prev.item, next.item) +) export default TimelineDefault diff --git a/src/components/Timeline/Empty.tsx b/src/components/Timeline/Empty.tsx index 8b7fdf4b..145db777 100644 --- a/src/components/Timeline/Empty.tsx +++ b/src/components/Timeline/Empty.tsx @@ -4,7 +4,7 @@ import Icon from '@components/Icon' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useMemo } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' import { StyleSheet, Text, View } from 'react-native' import { Circle } from 'react-native-animated-spinkit' @@ -20,10 +20,10 @@ const TimelineEmpty = React.memo( options: { notifyOnChangeProps: ['status'] } }) - const { colors, theme } = useTheme() - const { t, i18n } = useTranslation('componentTimeline') + const { colors } = useTheme() + const { t } = useTranslation('componentTimeline') - const children = useMemo(() => { + const children = () => { switch (status) { case 'loading': return ( @@ -67,24 +67,25 @@ const TimelineEmpty = React.memo( ) } - }, [theme, i18n.language, status]) + } return ( + style={{ + flex: 1, + minHeight: '100%', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: colors.backgroundDefault + }} + > + {children()} + ) }, () => true ) const styles = StyleSheet.create({ - base: { - flex: 1, - minHeight: '100%', - justifyContent: 'center', - alignItems: 'center' - }, error: { ...StyleConstants.FontStyle.M, marginTop: StyleConstants.Spacing.S, diff --git a/src/components/Timeline/Footer.tsx b/src/components/Timeline/Footer.tsx index d61df1ac..95392e59 100644 --- a/src/components/Timeline/Footer.tsx +++ b/src/components/Timeline/Footer.tsx @@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { Trans } from 'react-i18next' -import { StyleSheet, Text, View } from 'react-native' +import { Text, View } from 'react-native' import { Circle } from 'react-native-animated-spinkit' export interface Props { @@ -27,11 +27,20 @@ const TimelineFooter = React.memo( const { colors } = useTheme() return ( - + {!disableInfinity && hasNextPage ? ( ) : ( - + true ) -const styles = StyleSheet.create({ - base: { - flex: 1, - flexDirection: 'row', - justifyContent: 'center', - padding: StyleConstants.Spacing.M - }, - text: { - ...StyleConstants.FontStyle.S - } -}) - export default TimelineFooter diff --git a/src/components/Timeline/Lookback.tsx b/src/components/Timeline/Lookback.tsx index 25baf50f..ff4b78ca 100644 --- a/src/components/Timeline/Lookback.tsx +++ b/src/components/Timeline/Lookback.tsx @@ -2,7 +2,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { useTranslation } from 'react-i18next' -import { StyleSheet, Text, View } from 'react-native' +import { Text, View } from 'react-native' const TimelineLookback = React.memo( () => { @@ -11,10 +11,19 @@ const TimelineLookback = React.memo( return ( {t('lookback.message')} @@ -24,16 +33,4 @@ const TimelineLookback = React.memo( () => true ) -const styles = StyleSheet.create({ - base: { - flex: 1, - flexDirection: 'row', - justifyContent: 'center', - padding: StyleConstants.Spacing.S - }, - text: { - ...StyleConstants.FontStyle.S - } -}) - export default TimelineLookback diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index 5d5693dc..c4df686f 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -14,9 +14,9 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { uniqBy } from 'lodash' +import { isEqual, uniqBy } from 'lodash' import React, { useCallback } from 'react' -import { Pressable, StyleSheet, View } from 'react-native' +import { Pressable, View } from 'react-native' import { useSelector } from 'react-redux' import TimelineFiltered, { shouldFilter } from './Shared/Filtered' import TimelineFullConversation from './Shared/FullConversation' @@ -27,151 +27,137 @@ export interface Props { highlighted?: boolean } -const TimelineNotifications: React.FC = ({ - notification, - queryKey, - highlighted = false -}) => { - if ( - notification.status && - shouldFilter({ status: notification.status, queryKey }) - ) { - return - } +const TimelineNotifications = React.memo( + ({ notification, queryKey, highlighted = false }: Props) => { + if ( + notification.status && + shouldFilter({ status: notification.status, queryKey }) + ) { + return + } - const { colors } = useTheme() - const instanceAccount = useSelector( - getInstanceAccount, - (prev, next) => prev?.id === next?.id - ) - const navigation = - useNavigation>() + const { colors } = useTheme() + const instanceAccount = useSelector( + getInstanceAccount, + (prev, next) => prev?.id === next?.id + ) + const navigation = + useNavigation>() - const actualAccount = notification.status - ? notification.status.account - : notification.account + const actualAccount = notification.status + ? notification.status.account + : notification.account - const onPress = useCallback(() => { - analytics('timeline_notification_press') - notification.status && - navigation.push('Tab-Shared-Toot', { - toot: notification.status, - rootQueryKey: queryKey - }) - }, []) + const onPress = useCallback(() => { + analytics('timeline_notification_press') + notification.status && + navigation.push('Tab-Shared-Toot', { + toot: notification.status, + rootQueryKey: queryKey + }) + }, []) - return ( - - {notification.type !== 'mention' ? ( - - ) : null} - - - - - + ) : null} + + + + + + + + {notification.status ? ( + + {notification.status.content.length > 0 ? ( + + ) : null} + {notification.status.poll ? ( + + ) : null} + {notification.status.media_attachments.length > 0 ? ( + + ) : null} + {notification.status.card ? ( + + ) : null} + + + ) : null} {notification.status ? ( - - {notification.status.content.length > 0 ? ( - - ) : null} - {notification.status.poll ? ( - - ) : null} - {notification.status.media_attachments.length > 0 ? ( - - ) : null} - {notification.status.card ? ( - - ) : null} - - + d?.id !== instanceAccount?.id), + d => d?.id + ).map(d => d?.acct)} + reblog={false} + /> ) : null} - - - {notification.status ? ( - d?.id !== instanceAccount?.id), - d => d?.id - ).map(d => d?.acct)} - reblog={false} - /> - ) : null} - - ) -} - -const styles = StyleSheet.create({ - notificationView: { - padding: StyleConstants.Spacing.Global.PagePadding + + ) }, - header: { - flex: 1, - width: '100%', - flexDirection: 'row' - } -}) + (prev, next) => isEqual(prev.notification, next.notification) +) export default TimelineNotifications