diff --git a/src/@types/app.d.ts b/src/@types/app.d.ts index d3d4f400..3b2b9525 100644 --- a/src/@types/app.d.ts +++ b/src/@types/app.d.ts @@ -8,9 +8,7 @@ declare namespace App { | 'Hashtag' | 'List' | 'Toot' - | 'Account_Default' - | 'Account_All' - | 'Account_Attachments' + | 'Account' | 'Conversations' | 'Bookmarks' | 'Favourites' diff --git a/src/components/Header/Center.tsx b/src/components/Header/Center.tsx index 2a700fab..d61e3bfa 100644 --- a/src/components/Header/Center.tsx +++ b/src/components/Header/Center.tsx @@ -1,6 +1,4 @@ -import Icon from '@components/Icon' import CustomText from '@components/Text' -import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { View } from 'react-native' @@ -9,16 +7,10 @@ export interface Props { content?: string inverted?: boolean onPress?: () => void - dropdown?: boolean } // Used for Android mostly -const HeaderCenter: React.FC = ({ - content, - inverted = false, - onPress, - dropdown = false -}) => { +const HeaderCenter: React.FC = ({ content, inverted = false, onPress }) => { const { colors } = useTheme() return ( @@ -33,13 +25,6 @@ const HeaderCenter: React.FC = ({ children={content} {...(onPress && { onPress })} /> - ) } diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 735620ed..37c7e61e 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -11,6 +11,7 @@ export interface Props { fill?: string strokeWidth?: number style?: StyleProp + crossOut?: boolean } const Icon: React.FC = ({ @@ -20,7 +21,8 @@ const Icon: React.FC = ({ color, fill, strokeWidth = 2, - style + style, + crossOut = false }) => { return ( = ({ fill, strokeWidth })} + {crossOut ? ( + + ) : null} ) } diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index 68b9f48c..1867ed89 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -196,7 +196,7 @@ const ParseHTML = React.memo( const navigation = useNavigation>() const route = useRoute() const { colors, theme } = useTheme() - const { t, i18n } = useTranslation('componentParse') + const { t } = useTranslation('componentParse') if (!expandHint) { expandHint = t('HTML.defaultHint') } @@ -304,7 +304,7 @@ const ParseHTML = React.memo( ) }, - [theme, i18n.language] + [theme] ) return ( diff --git a/src/components/Relationship/Outgoing.tsx b/src/components/Relationship/Outgoing.tsx index f41ea157..70a643e0 100644 --- a/src/components/Relationship/Outgoing.tsx +++ b/src/components/Relationship/Outgoing.tsx @@ -30,8 +30,8 @@ const RelationshipOutgoing = React.memo( haptics('Success') queryClient.setQueryData(queryKeyRelationship, [res]) if (action === 'block') { - const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }] - queryClient.invalidateQueries(queryKey) + const queryKey = ['Timeline', { page: 'Following' }] + queryClient.invalidateQueries({ queryKey, exact: false }) } }, onError: (err: any, { payload: { action } }) => { diff --git a/src/components/Timeline/Shared/Filtered.tsx b/src/components/Timeline/Shared/Filtered.tsx index a9495366..28c582e5 100644 --- a/src/components/Timeline/Shared/Filtered.tsx +++ b/src/components/Timeline/Shared/Filtered.tsx @@ -45,6 +45,7 @@ export const shouldFilter = ({ status: Mastodon.Status queryKey: QueryKeyTimeline }): string | null => { + const page = queryKey[1] const instance = getInstance(store.getState()) const ownAccount = getInstanceAccount(store.getState())?.id === status.account?.id @@ -93,11 +94,11 @@ export const shouldFilter = ({ } } - switch (queryKey[1].page) { + switch (page.page) { case 'Following': case 'Local': case 'List': - case 'Account_Default': + case 'Account': if (filter.context.includes('home')) { checkFilter(filter) } diff --git a/src/components/Timeline/Shared/HeaderAndroid.tsx b/src/components/Timeline/Shared/HeaderAndroid.tsx index d05ab384..c0926e24 100644 --- a/src/components/Timeline/Shared/HeaderAndroid.tsx +++ b/src/components/Timeline/Shared/HeaderAndroid.tsx @@ -1,5 +1,4 @@ import menuAccount from '@components/contextMenu/account' -import menuInstance from '@components/contextMenu/instance' import menuShare from '@components/contextMenu/share' import menuStatus from '@components/contextMenu/status' import Icon from '@components/Icon' @@ -31,7 +30,6 @@ const TimelineHeaderAndroid: React.FC = () => { queryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey }) - const mInstance = menuInstance({ status, queryKey, rootQueryKey }) return ( @@ -77,16 +75,6 @@ const TimelineHeaderAndroid: React.FC = () => { ))} ))} - - {mInstance.map((mGroup, index) => ( - - {mGroup.map(menu => ( - - - - ))} - - ))} ) : null} diff --git a/src/components/Timeline/Shared/HeaderDefault.tsx b/src/components/Timeline/Shared/HeaderDefault.tsx index 1e482239..f4f699fa 100644 --- a/src/components/Timeline/Shared/HeaderDefault.tsx +++ b/src/components/Timeline/Shared/HeaderDefault.tsx @@ -1,5 +1,4 @@ import menuAccount from '@components/contextMenu/account' -import menuInstance from '@components/contextMenu/instance' import menuShare from '@components/contextMenu/share' import menuStatus from '@components/contextMenu/status' import Icon from '@components/Icon' @@ -38,7 +37,6 @@ const TimelineHeaderDefault: React.FC = () => { queryKey }) const mStatus = menuStatus({ status, queryKey, rootQueryKey }) - const mInstance = menuInstance({ status, queryKey, rootQueryKey }) return ( @@ -116,17 +114,6 @@ const TimelineHeaderDefault: React.FC = () => { ))} ))} - - {mInstance.map((mGroup, index) => ( - - {mGroup.map(menu => ( - - - - - ))} - - ))} diff --git a/src/components/Timeline/Shared/Poll.tsx b/src/components/Timeline/Shared/Poll.tsx index 933d3da9..3339b3bb 100644 --- a/src/components/Timeline/Shared/Poll.tsx +++ b/src/components/Timeline/Shared/Poll.tsx @@ -33,7 +33,7 @@ const TimelinePoll: React.FC = () => { const poll = status.poll const { colors, theme } = useTheme() - const { t, i18n } = useTranslation('componentTimeline') + const { t } = useTranslation('componentTimeline') const [allOptions, setAllOptions] = useState(new Array(status.poll.options.length).fill(false)) @@ -127,7 +127,7 @@ const TimelinePoll: React.FC = () => { ) } } - }, [theme, i18n.language, poll.expired, poll.voted, allOptions, mutation.isLoading]) + }, [theme, poll.expired, poll.voted, allOptions, mutation.isLoading]) const isSelected = useCallback( (index: number): string => diff --git a/src/components/contextMenu/account.ts b/src/components/contextMenu/account.ts index 8d4c7893..c85aa935 100644 --- a/src/components/contextMenu/account.ts +++ b/src/components/contextMenu/account.ts @@ -50,7 +50,7 @@ const menuAccount = ({ setEnabled(true) } }, [openChange, enabled]) - const { data, isFetching } = useRelationshipQuery({ id: account.id, options: { enabled } }) + const { data, isFetched } = useRelationshipQuery({ id: account.id, options: { enabled } }) const queryClient = useQueryClient() const timelineMutation = useTimelineMutation({ @@ -99,8 +99,8 @@ const menuAccount = ({ haptics('Success') queryClient.setQueryData(queryKeyRelationship, [res]) if (action === 'block') { - const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }] - queryClient.invalidateQueries(queryKey) + const queryKey = ['Timeline', { page: 'Following' }] + queryClient.invalidateQueries({ queryKey, exact: false }) } }, onError: (err: any, { payload: { action } }) => { @@ -131,7 +131,7 @@ const menuAccount = ({ type: 'outgoing', payload: { action: 'follow', state: !data?.requested ? data.following : true } }), - disabled: !data || isFetching, + disabled: !data || !isFetched, destructive: false, hidden: false }, @@ -152,9 +152,9 @@ const menuAccount = ({ key: 'account-list', item: { onSelect: () => navigation.navigate('Tab-Shared-Account-In-Lists', { account }), - disabled: Platform.OS !== 'android' ? !data || isFetching : false, + disabled: Platform.OS !== 'android' ? !data || !isFetched : false, destructive: false, - hidden: isFetching ? false : !data?.following + hidden: !isFetched || !data?.following }, title: t('account.inLists'), icon: 'checklist' @@ -169,7 +169,7 @@ const menuAccount = ({ id: account.id, payload: { property: 'mute', currentValue: data?.muting } }), - disabled: Platform.OS !== 'android' ? !data || isFetching : false, + disabled: Platform.OS !== 'android' ? !data || !isFetched : false, destructive: false, hidden: false }, @@ -192,7 +192,7 @@ const menuAccount = ({ id: account.id, payload: { property: 'block', currentValue: data?.blocking } }), - disabled: Platform.OS !== 'android' ? !data || isFetching : false, + disabled: Platform.OS !== 'android' ? !data || !isFetched : false, destructive: !data?.blocking, hidden: false }, diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index 6512b438..98930190 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -1,7 +1,11 @@ { "tabs": { "local": { - "name": "Following" + "name": "Following", + "options": { + "showBoosts": "Show boosts", + "showReplies": "Show replies" + } }, "public": { "segments": { diff --git a/src/screens/Tabs/Local/Root.tsx b/src/screens/Tabs/Local/Root.tsx index 0120fc1c..5bd4d507 100644 --- a/src/screens/Tabs/Local/Root.tsx +++ b/src/screens/Tabs/Local/Root.tsx @@ -1,4 +1,6 @@ -import { HeaderCenter, HeaderRight } from '@components/Header' +import { HeaderRight } from '@components/Header' +import Icon from '@components/Icon' +import CustomText from '@components/Text' import Timeline from '@components/Timeline' import TimelineDefault from '@components/Timeline/Default' import { NativeStackScreenProps } from '@react-navigation/native-stack' @@ -6,66 +8,160 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators' import usePopToTop from '@utils/navigation/usePopToTop' import { useListsQuery } from '@utils/queryHooks/lists' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' +import { getInstanceFollowingPage, updateInstanceFollowingPage } from '@utils/slices/instancesSlice' +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { View } from 'react-native' +import { useDispatch, useSelector } from 'react-redux' import * as DropdownMenu from 'zeego/dropdown-menu' const Root: React.FC> = ({ navigation }) => { + const { colors, mode } = useTheme() const { t } = useTranslation('screenTabs') const { data: lists } = useListsQuery() - const [queryKey, setQueryKey] = useState(['Timeline', { page: 'Following' }]) + const dispatch = useDispatch() + const instanceFollowingPage = useSelector(getInstanceFollowingPage) + const [queryKey, setQueryKey] = useState([ + 'Timeline', + { page: 'Following', ...instanceFollowingPage } + ]) useEffect(() => { + const page = queryKey[1] + navigation.setOptions({ headerTitle: () => ( - 0} - content={ - queryKey[1].page === 'List' && queryKey[1].list?.length - ? lists?.find(list => list.id === queryKey[1].list)?.title - : t('tabs.local.name') - } - /> + + {page.page === 'List' ? ( + + ) : null} + list.id === page.list)?.title + : t('tabs.local.name') + } + /> + {page.page === 'Following' && !instanceFollowingPage.showBoosts ? ( + + ) : null} + {page.page === 'Following' && !instanceFollowingPage.showReplies ? ( + + ) : null} + + - {lists?.length - ? [ - { - key: 'default', - item: { - onSelect: () => setQueryKey(['Timeline', { page: 'Following' }]), - disabled: queryKey[1].page === 'Following', - destructive: false, - hidden: false - }, - title: t('tabs.local.name'), - icon: '' - }, - ...lists?.map(list => ({ - key: list.id, - item: { - onSelect: () => setQueryKey(['Timeline', { page: 'List', list: list.id }]), - disabled: queryKey[1].page === 'List' && queryKey[1].list === list.id, - destructive: false, - hidden: false - }, - title: list.title, - icon: '' - })) - ].map(menu => ( - - - - - )) - : undefined} + + + setQueryKey(['Timeline', { page: 'Following', ...instanceFollowingPage }]) + } + disabled={page.page === 'Following'} + > + + + + { + setQueryKey([ + 'Timeline', + { + page: 'Following', + showBoosts: !instanceFollowingPage.showBoosts, + showReplies: instanceFollowingPage.showReplies + } + ]) + dispatch( + updateInstanceFollowingPage({ showBoosts: !instanceFollowingPage.showBoosts }) + ) + }} + > + + + + { + setQueryKey([ + 'Timeline', + { + page: 'Following', + showBoosts: instanceFollowingPage.showBoosts, + showReplies: !instanceFollowingPage.showReplies + } + ]) + dispatch( + updateInstanceFollowingPage({ showReplies: !instanceFollowingPage.showReplies }) + ) + }} + > + + + + + + + {lists?.length + ? [ + ...lists?.map(list => ({ + key: list.id, + item: { + onSelect: () => setQueryKey(['Timeline', { page: 'List', list: list.id }]), + disabled: page.page === 'List' && page.list === list.id, + destructive: false, + hidden: false + }, + title: list.title, + icon: 'list.bullet' + })) + ].map(menu => ( + + + + + )) + : undefined} + ), @@ -78,7 +174,7 @@ const Root: React.FC ) }) - }, []) + }, [mode, queryKey[1], instanceFollowingPage]) usePopToTop() diff --git a/src/screens/Tabs/Me/Profile/Fields.tsx b/src/screens/Tabs/Me/Profile/Fields.tsx index 6f7a78bf..e461c7b6 100644 --- a/src/screens/Tabs/Me/Profile/Fields.tsx +++ b/src/screens/Tabs/Me/Profile/Fields.tsx @@ -79,7 +79,7 @@ const TabMeProfileFields: React.FC< navigation }) => { const { theme } = useTheme() - const { t, i18n } = useTranslation('screenTabs') + const { t } = useTranslation('screenTabs') const { mutateAsync, status } = useProfileMutation() const allProps: EmojisState['inputProps'] = [] @@ -144,7 +144,7 @@ const TabMeProfileFields: React.FC< /> ) }) - }, [theme, i18n.language, dirty, status, allProps.map(p => p.value)]) + }, [theme, dirty, status, allProps.map(p => p.value)]) return ( diff --git a/src/screens/Tabs/Me/Profile/Name.tsx b/src/screens/Tabs/Me/Profile/Name.tsx index 81dce1d7..c280d57b 100644 --- a/src/screens/Tabs/Me/Profile/Name.tsx +++ b/src/screens/Tabs/Me/Profile/Name.tsx @@ -23,7 +23,7 @@ const TabMeProfileName: React.FC< navigation }) => { const { theme } = useTheme() - const { t, i18n } = useTranslation('screenTabs') + const { t } = useTranslation('screenTabs') const { mutateAsync, status } = useProfileMutation() const [value, setValue] = useState(display_name) @@ -90,7 +90,7 @@ const TabMeProfileName: React.FC< /> ) }) - }, [theme, i18n.language, dirty, status, value]) + }, [theme, dirty, status, value]) return ( diff --git a/src/screens/Tabs/Me/Profile/Note.tsx b/src/screens/Tabs/Me/Profile/Note.tsx index 7fa08dbf..085315f2 100644 --- a/src/screens/Tabs/Me/Profile/Note.tsx +++ b/src/screens/Tabs/Me/Profile/Note.tsx @@ -23,7 +23,7 @@ const TabMeProfileNote: React.FC< navigation }) => { const { theme } = useTheme() - const { t, i18n } = useTranslation('screenTabs') + const { t } = useTranslation('screenTabs') const { mutateAsync, status } = useProfileMutation() const [notes, setNotes] = useState(note) @@ -90,7 +90,7 @@ const TabMeProfileNote: React.FC< /> ) }) - }, [theme, i18n.language, dirty, status, notes]) + }, [theme, dirty, status, notes]) return ( diff --git a/src/screens/Tabs/Shared/Account.tsx b/src/screens/Tabs/Shared/Account.tsx index f467d351..c2becf37 100644 --- a/src/screens/Tabs/Shared/Account.tsx +++ b/src/screens/Tabs/Shared/Account.tsx @@ -26,7 +26,7 @@ const TabSharedAccount: React.FC params: { account } } }) => { - const { t, i18n } = useTranslation('screenTabs') + const { t } = useTranslation('screenTabs') const { colors, mode } = useTheme() const mShare = menuShare({ type: 'account', url: account.url }) @@ -89,8 +89,9 @@ const TabSharedAccount: React.FC const [queryKey, setQueryKey] = useState([ 'Timeline', - { page: 'Account_Default', account: account.id } + { page: 'Account', account: account.id, exclude_reblogs: true, only_media: false } ]) + const page = queryKey[1] const isFetchingTimeline = useIsFetching(queryKey) const fetchedTimeline = useRef(false) useEffect(() => { @@ -113,14 +114,32 @@ const TabSharedAccount: React.FC { switch (nativeEvent.selectedSegmentIndex) { case 0: - setQueryKey([queryKey[0], { ...queryKey[1], page: 'Account_Default' }]) + setQueryKey([ + queryKey[0], + { + ...page, + page: 'Account', + account: account.id, + exclude_reblogs: true, + only_media: false + } + ]) break case 1: - setQueryKey([queryKey[0], { ...queryKey[1], page: 'Account_All' }]) + setQueryKey([ + queryKey[0], + { + ...page, + page: 'Account', + account: account.id, + exclude_reblogs: false, + only_media: false + } + ]) break } }} @@ -152,7 +171,7 @@ const TabSharedAccount: React.FC ) : null} ) - }, [data, fetchedTimeline.current, queryKey[1].page, i18n.language, mode]) + }, [data, fetchedTimeline.current, queryKey[1].page, mode]) return ( <> diff --git a/src/screens/Tabs/Shared/Account/Attachments.tsx b/src/screens/Tabs/Shared/Account/Attachments.tsx index dd6be7d7..778a4f60 100644 --- a/src/screens/Tabs/Shared/Account/Attachments.tsx +++ b/src/screens/Tabs/Shared/Account/Attachments.tsx @@ -3,11 +3,11 @@ import Icon from '@components/Icon' import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' import { TabLocalStackParamList } from '@utils/navigation/navigators' -import { useTimelineQuery } from '@utils/queryHooks/timeline' +import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useCallback, useEffect } from 'react' -import { Dimensions, ListRenderItem, Pressable, StyleSheet, View } from 'react-native' +import { Dimensions, ListRenderItem, Pressable, View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated' @@ -15,117 +15,111 @@ export interface Props { account: Mastodon.Account | undefined } -const AccountAttachments = React.memo( - ({ account }: Props) => { - const navigation = useNavigation>() - const { colors } = useTheme() +const AccountAttachments: React.FC = ({ account }) => { + if (!account) return null - const DISPLAY_AMOUNT = 6 + const navigation = useNavigation>() + const { colors } = useTheme() - const width = - (Dimensions.get('screen').width - StyleConstants.Spacing.Global.PagePadding * 2) / 4 + const DISPLAY_AMOUNT = 6 - const queryKeyParams = { - page: 'Account_Attachments' as 'Account_Attachments', - account: account?.id - } - const { data, refetch } = useTimelineQuery({ - ...queryKeyParams, - options: { enabled: false } - }) - useEffect(() => { - if (account?.id) { - refetch() - } - }, [account]) + const width = (Dimensions.get('screen').width - StyleConstants.Spacing.Global.PagePadding * 2) / 4 - const flattenData = data?.pages - ? data.pages - .flatMap(d => [...d.body]) - .filter(status => !(status as Mastodon.Status).sensitive) - .splice(0, DISPLAY_AMOUNT) - : [] - - const renderItem = useCallback>( - ({ item, index }) => { - if (index === DISPLAY_AMOUNT - 1) { - return ( - { - account && navigation.push('Tab-Shared-Attachments', { account }) - }} - children={ - - } - /> - } - /> - ) - } else { - return ( - navigation.push('Tab-Shared-Toot', { toot: item })} - /> - ) - } - }, - [account] - ) - - const styleContainer = useAnimatedStyle(() => { - if (flattenData.length) { - return { - height: withTiming(width + StyleConstants.Spacing.Global.PagePadding * 2), - paddingVertical: StyleConstants.Spacing.Global.PagePadding, - borderTopWidth: 1, - borderTopColor: colors.border - } - } else { - return {} - } - }, [flattenData.length]) - - return ( - - - - ) - }, - (_, next) => next.account === undefined -) - -const styles = StyleSheet.create({ - base: { - flex: 1 + const queryKeyParams: QueryKeyTimeline[1] = { + page: 'Account', + account: account.id, + exclude_reblogs: true, + only_media: true } -}) + const { data, refetch } = useTimelineQuery({ + ...queryKeyParams, + options: { enabled: false } + }) + useEffect(() => { + if (account?.id) { + refetch() + } + }, [account]) + + const flattenData = data?.pages + ? data.pages + .flatMap(d => [...d.body]) + .filter(status => !(status as Mastodon.Status).sensitive) + .splice(0, DISPLAY_AMOUNT) + : [] + + const renderItem = useCallback>( + ({ item, index }) => { + if (index === DISPLAY_AMOUNT - 1) { + return ( + { + account && navigation.push('Tab-Shared-Attachments', { account }) + }} + children={ + + } + /> + } + /> + ) + } else { + return ( + navigation.push('Tab-Shared-Toot', { toot: item })} + /> + ) + } + }, + [account] + ) + + const styleContainer = useAnimatedStyle(() => { + if (flattenData.length) { + return { + height: withTiming(width + StyleConstants.Spacing.Global.PagePadding * 2), + paddingVertical: StyleConstants.Spacing.Global.PagePadding, + borderTopWidth: 1, + borderTopColor: colors.border + } + } else { + return {} + } + }, [flattenData.length]) + + return ( + + + + ) +} export default AccountAttachments diff --git a/src/screens/Tabs/Shared/Attachments.tsx b/src/screens/Tabs/Shared/Attachments.tsx index 948d2552..a4e75f03 100644 --- a/src/screens/Tabs/Shared/Attachments.tsx +++ b/src/screens/Tabs/Shared/Attachments.tsx @@ -44,7 +44,7 @@ const TabSharedAttachments: React.FC { return { instances: state.instances.map(instance => { + delete instance.timelinesLookback + return { ...instance, + followingPage: { showBoosts: true, showReplies: true }, mePage: { ...instance.mePage, followedTags: { shown: false } }, notifications_filter: { ...instance.notifications_filter, diff --git a/src/utils/migrations/instances/v11.ts b/src/utils/migrations/instances/v11.ts index fc5a578d..e02d72bb 100644 --- a/src/utils/migrations/instances/v11.ts +++ b/src/utils/migrations/instances/v11.ts @@ -1,5 +1,4 @@ import { ComposeStateDraft } from '@screens/Compose/utils/types' -import { QueryKeyTimeline } from '@utils/queryHooks/timeline' export type InstanceV11 = { active: boolean @@ -42,11 +41,9 @@ export type InstanceV11 = { private?: string // legacy } } - timelinesLookback?: { - [key: string]: { - queryKey: QueryKeyTimeline - ids: Mastodon.Status['id'][] - } + followingPage: { + showBoosts: boolean + showReplies: boolean } mePage: { followedTags: { shown: boolean } diff --git a/src/utils/queryHooks/timeline.ts b/src/utils/queryHooks/timeline.ts index 17f811df..ea7c0fe6 100644 --- a/src/utils/queryHooks/timeline.ts +++ b/src/utils/queryHooks/timeline.ts @@ -18,25 +18,66 @@ import updateStatusProperty from './timeline/updateStatusProperty' export type QueryKeyTimeline = [ 'Timeline', - { - page: App.Pages - hashtag?: Mastodon.Tag['name'] - list?: Mastodon.List['id'] - toot?: Mastodon.Status['id'] - account?: Mastodon.Account['id'] - } + ( + | { + page: Exclude + } + | { + page: 'Following' + showBoosts: boolean + showReplies: boolean + } + | { + page: 'Hashtag' + hashtag: Mastodon.Tag['name'] + } + | { + page: 'List' + list: Mastodon.List['id'] + } + | { + page: 'Toot' + toot: Mastodon.Status['id'] + } + | { + page: 'Account' + account: Mastodon.Account['id'] + exclude_reblogs: boolean + only_media: boolean + } + ) ] const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext) => { - const { page, account, hashtag, list, toot } = queryKey[1] - let params: { [key: string]: string } = { ...pageParam } + const page = queryKey[1] + let params: { [key: string]: string } = { ...pageParam, limit: 40 } - switch (page) { + switch (page.page) { case 'Following': return apiInstance({ method: 'get', url: 'timelines/home', params + }).then(res => { + if (!page.showBoosts || !page.showReplies) { + return { + ...res, + body: res.body + .filter(status => { + if (!page.showBoosts && status.reblog) { + return null + } + if (!page.showReplies && status.in_reply_to_id?.length) { + return null + } + + return status + }) + .filter(s => s) + } + } else { + return res + } }) case 'Local': @@ -91,62 +132,57 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext({ + method: 'get', + url: `accounts/${page.account}/statuses`, + params: { + exclude_replies: 'true', + ...params + } + }) + } else { + const res1 = await apiInstance<(Mastodon.Status & { _pinned: boolean })[]>({ + method: 'get', + url: `accounts/${page.account}/statuses`, + params: { + pinned: 'true' + } + }) + res1.body = res1.body.map(status => { + status._pinned = true + return status + }) + const res2 = await apiInstance({ + method: 'get', + url: `accounts/${page.account}/statuses`, + params: { + exclude_replies: 'true' + } + }) + return { + body: uniqBy([...res1.body, ...res2.body], 'id'), + ...(res2.links.next && { links: { next: res2.links.next } }) + } + } + } else { return apiInstance({ method: 'get', - url: `accounts/${account}/statuses`, + url: `accounts/${page.account}/statuses`, params: { - exclude_replies: 'true', - ...params + ...params, + exclude_replies: page.exclude_reblogs.toString(), + only_media: page.only_media.toString() } }) - } else { - const res1 = await apiInstance<(Mastodon.Status & { _pinned: boolean })[]>({ - method: 'get', - url: `accounts/${account}/statuses`, - params: { - pinned: 'true' - } - }) - res1.body = res1.body.map(status => { - status._pinned = true - return status - }) - const res2 = await apiInstance({ - method: 'get', - url: `accounts/${account}/statuses`, - params: { - exclude_replies: 'true' - } - }) - return { - body: uniqBy([...res1.body, ...res2.body], 'id'), - ...(res2.links.next && { links: { next: res2.links.next } }) - } } - case 'Account_All': - return apiInstance({ - method: 'get', - url: `accounts/${account}/statuses`, - params - }) - - case 'Account_Attachments': - return apiInstance({ - method: 'get', - url: `accounts/${account}/statuses`, - params: { - only_media: 'true', - ...params - } - }) - case 'Hashtag': return apiInstance({ method: 'get', - url: `timelines/tag/${hashtag}`, + url: `timelines/tag/${page.hashtag}`, params }) @@ -174,21 +210,21 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext({ method: 'get', - url: `timelines/list/${list}`, + url: `timelines/list/${page.list}`, params }) case 'Toot': const res1_1 = await apiInstance({ method: 'get', - url: `statuses/${toot}` + url: `statuses/${page.toot}` }) const res2_1 = await apiInstance<{ ancestors: Mastodon.Status[] descendants: Mastodon.Status[] }>({ method: 'get', - url: `statuses/${toot}/context` + url: `statuses/${page.toot}/context` }) return { body: [...res2_1.body.ancestors, res1_1.body, ...res2_1.body.descendants] diff --git a/src/utils/slices/instances/add.ts b/src/utils/slices/instances/add.ts index bb9d3ba4..c059a344 100644 --- a/src/utils/slices/instances/add.ts +++ b/src/utils/slices/instances/add.ts @@ -107,7 +107,10 @@ const addInstance = createAsyncThunk( }, keys: { auth: undefined, public: undefined, private: undefined } }, - timelinesLookback: {}, + followingPage: { + showBoosts: true, + showReplies: true + }, mePage: { followedTags: { shown: false }, lists: { shown: false }, diff --git a/src/utils/slices/instancesSlice.ts b/src/utils/slices/instancesSlice.ts index 87abf486..54ace209 100644 --- a/src/utils/slices/instancesSlice.ts +++ b/src/utils/slices/instancesSlice.ts @@ -81,16 +81,15 @@ const instancesSlice = createSlice({ return newInstance }) }, - updateInstanceTimelineLookback: ( + updateInstanceFollowingPage: ( { instances }, - action: PayloadAction + action: PayloadAction> ) => { const activeIndex = findInstanceActive(instances) - instances[activeIndex] && - (instances[activeIndex].timelinesLookback = { - ...instances[activeIndex].timelinesLookback, - ...action.payload - }) + instances[activeIndex].followingPage = { + ...instances[activeIndex].followingPage, + ...action.payload + } }, updateInstanceMePage: ( { instances }, @@ -360,8 +359,8 @@ export const getInstanceNotificationsFilter = ({ instances: { instances } }: Roo export const getInstancePush = ({ instances: { instances } }: RootState) => instances[findInstanceActive(instances)]?.push -export const getInstanceTimelinesLookback = ({ instances: { instances } }: RootState) => - instances[findInstanceActive(instances)]?.timelinesLookback +export const getInstanceFollowingPage = ({ instances: { instances } }: RootState) => + instances[findInstanceActive(instances)]?.followingPage export const getInstanceMePage = ({ instances: { instances } }: RootState) => instances[findInstanceActive(instances)]?.mePage @@ -379,7 +378,7 @@ export const { updateInstanceDraft, removeInstanceDraft, disableAllPushes, - updateInstanceTimelineLookback, + updateInstanceFollowingPage, updateInstanceMePage, countInstanceEmoji } = instancesSlice.actions