mirror of
https://github.com/tooot-app/app
synced 2025-04-23 22:57:23 +02:00
Fixed #447
This commit is contained in:
parent
7f8a8de898
commit
4a35e910c1
4
src/@types/app.d.ts
vendored
4
src/@types/app.d.ts
vendored
@ -8,9 +8,7 @@ declare namespace App {
|
|||||||
| 'Hashtag'
|
| 'Hashtag'
|
||||||
| 'List'
|
| 'List'
|
||||||
| 'Toot'
|
| 'Toot'
|
||||||
| 'Account_Default'
|
| 'Account'
|
||||||
| 'Account_All'
|
|
||||||
| 'Account_Attachments'
|
|
||||||
| 'Conversations'
|
| 'Conversations'
|
||||||
| 'Bookmarks'
|
| 'Bookmarks'
|
||||||
| 'Favourites'
|
| 'Favourites'
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import Icon from '@components/Icon'
|
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
@ -9,16 +7,10 @@ export interface Props {
|
|||||||
content?: string
|
content?: string
|
||||||
inverted?: boolean
|
inverted?: boolean
|
||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
dropdown?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for Android mostly
|
// Used for Android mostly
|
||||||
const HeaderCenter: React.FC<Props> = ({
|
const HeaderCenter: React.FC<Props> = ({ content, inverted = false, onPress }) => {
|
||||||
content,
|
|
||||||
inverted = false,
|
|
||||||
onPress,
|
|
||||||
dropdown = false
|
|
||||||
}) => {
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -33,13 +25,6 @@ const HeaderCenter: React.FC<Props> = ({
|
|||||||
children={content}
|
children={content}
|
||||||
{...(onPress && { onPress })}
|
{...(onPress && { onPress })}
|
||||||
/>
|
/>
|
||||||
<Icon
|
|
||||||
name='ChevronDown'
|
|
||||||
size={StyleConstants.Font.Size.M}
|
|
||||||
color={colors.primaryDefault}
|
|
||||||
style={{ marginLeft: StyleConstants.Spacing.XS, opacity: dropdown ? undefined : 0 }}
|
|
||||||
strokeWidth={3}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ export interface Props {
|
|||||||
fill?: string
|
fill?: string
|
||||||
strokeWidth?: number
|
strokeWidth?: number
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
|
crossOut?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon: React.FC<Props> = ({
|
const Icon: React.FC<Props> = ({
|
||||||
@ -20,7 +21,8 @@ const Icon: React.FC<Props> = ({
|
|||||||
color,
|
color,
|
||||||
fill,
|
fill,
|
||||||
strokeWidth = 2,
|
strokeWidth = 2,
|
||||||
style
|
style,
|
||||||
|
crossOut = false
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@ -42,6 +44,17 @@ const Icon: React.FC<Props> = ({
|
|||||||
fill,
|
fill,
|
||||||
strokeWidth
|
strokeWidth
|
||||||
})}
|
})}
|
||||||
|
{crossOut ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
transform: [{ rotate: '45deg' }],
|
||||||
|
width: size * 1.35,
|
||||||
|
borderBottomColor: color,
|
||||||
|
borderBottomWidth: strokeWidth
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ const ParseHTML = React.memo(
|
|||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('componentParse')
|
const { t } = useTranslation('componentParse')
|
||||||
if (!expandHint) {
|
if (!expandHint) {
|
||||||
expandHint = t('HTML.defaultHint')
|
expandHint = t('HTML.defaultHint')
|
||||||
}
|
}
|
||||||
@ -304,7 +304,7 @@ const ParseHTML = React.memo(
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[theme, i18n.language]
|
[theme]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -30,8 +30,8 @@ const RelationshipOutgoing = React.memo(
|
|||||||
haptics('Success')
|
haptics('Success')
|
||||||
queryClient.setQueryData<Mastodon.Relationship[]>(queryKeyRelationship, [res])
|
queryClient.setQueryData<Mastodon.Relationship[]>(queryKeyRelationship, [res])
|
||||||
if (action === 'block') {
|
if (action === 'block') {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
const queryKey = ['Timeline', { page: 'Following' }]
|
||||||
queryClient.invalidateQueries(queryKey)
|
queryClient.invalidateQueries({ queryKey, exact: false })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (err: any, { payload: { action } }) => {
|
onError: (err: any, { payload: { action } }) => {
|
||||||
|
@ -45,6 +45,7 @@ export const shouldFilter = ({
|
|||||||
status: Mastodon.Status
|
status: Mastodon.Status
|
||||||
queryKey: QueryKeyTimeline
|
queryKey: QueryKeyTimeline
|
||||||
}): string | null => {
|
}): string | null => {
|
||||||
|
const page = queryKey[1]
|
||||||
const instance = getInstance(store.getState())
|
const instance = getInstance(store.getState())
|
||||||
const ownAccount = getInstanceAccount(store.getState())?.id === status.account?.id
|
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 'Following':
|
||||||
case 'Local':
|
case 'Local':
|
||||||
case 'List':
|
case 'List':
|
||||||
case 'Account_Default':
|
case 'Account':
|
||||||
if (filter.context.includes('home')) {
|
if (filter.context.includes('home')) {
|
||||||
checkFilter(filter)
|
checkFilter(filter)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import menuAccount from '@components/contextMenu/account'
|
import menuAccount from '@components/contextMenu/account'
|
||||||
import menuInstance from '@components/contextMenu/instance'
|
|
||||||
import menuShare from '@components/contextMenu/share'
|
import menuShare from '@components/contextMenu/share'
|
||||||
import menuStatus from '@components/contextMenu/status'
|
import menuStatus from '@components/contextMenu/status'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
@ -31,7 +30,6 @@ const TimelineHeaderAndroid: React.FC = () => {
|
|||||||
queryKey
|
queryKey
|
||||||
})
|
})
|
||||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||||
const mInstance = menuInstance({ status, queryKey, rootQueryKey })
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ position: 'absolute', top: 0, right: 0 }}>
|
<View style={{ position: 'absolute', top: 0, right: 0 }}>
|
||||||
@ -77,16 +75,6 @@ const TimelineHeaderAndroid: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{mInstance.map((mGroup, index) => (
|
|
||||||
<DropdownMenu.Group key={index}>
|
|
||||||
{mGroup.map(menu => (
|
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import menuAccount from '@components/contextMenu/account'
|
import menuAccount from '@components/contextMenu/account'
|
||||||
import menuInstance from '@components/contextMenu/instance'
|
|
||||||
import menuShare from '@components/contextMenu/share'
|
import menuShare from '@components/contextMenu/share'
|
||||||
import menuStatus from '@components/contextMenu/status'
|
import menuStatus from '@components/contextMenu/status'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
@ -38,7 +37,6 @@ const TimelineHeaderDefault: React.FC = () => {
|
|||||||
queryKey
|
queryKey
|
||||||
})
|
})
|
||||||
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
const mStatus = menuStatus({ status, queryKey, rootQueryKey })
|
||||||
const mInstance = menuInstance({ status, queryKey, rootQueryKey })
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
@ -116,17 +114,6 @@ const TimelineHeaderDefault: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{mInstance.map((mGroup, index) => (
|
|
||||||
<DropdownMenu.Group key={index}>
|
|
||||||
{mGroup.map(menu => (
|
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
|
||||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
@ -33,7 +33,7 @@ const TimelinePoll: React.FC = () => {
|
|||||||
const poll = status.poll
|
const poll = status.poll
|
||||||
|
|
||||||
const { colors, theme } = useTheme()
|
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))
|
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(
|
const isSelected = useCallback(
|
||||||
(index: number): string =>
|
(index: number): string =>
|
||||||
|
@ -50,7 +50,7 @@ const menuAccount = ({
|
|||||||
setEnabled(true)
|
setEnabled(true)
|
||||||
}
|
}
|
||||||
}, [openChange, enabled])
|
}, [openChange, enabled])
|
||||||
const { data, isFetching } = useRelationshipQuery({ id: account.id, options: { enabled } })
|
const { data, isFetched } = useRelationshipQuery({ id: account.id, options: { enabled } })
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const timelineMutation = useTimelineMutation({
|
const timelineMutation = useTimelineMutation({
|
||||||
@ -99,8 +99,8 @@ const menuAccount = ({
|
|||||||
haptics('Success')
|
haptics('Success')
|
||||||
queryClient.setQueryData<Mastodon.Relationship[]>(queryKeyRelationship, [res])
|
queryClient.setQueryData<Mastodon.Relationship[]>(queryKeyRelationship, [res])
|
||||||
if (action === 'block') {
|
if (action === 'block') {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
const queryKey = ['Timeline', { page: 'Following' }]
|
||||||
queryClient.invalidateQueries(queryKey)
|
queryClient.invalidateQueries({ queryKey, exact: false })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (err: any, { payload: { action } }) => {
|
onError: (err: any, { payload: { action } }) => {
|
||||||
@ -131,7 +131,7 @@ const menuAccount = ({
|
|||||||
type: 'outgoing',
|
type: 'outgoing',
|
||||||
payload: { action: 'follow', state: !data?.requested ? data.following : true }
|
payload: { action: 'follow', state: !data?.requested ? data.following : true }
|
||||||
}),
|
}),
|
||||||
disabled: !data || isFetching,
|
disabled: !data || !isFetched,
|
||||||
destructive: false,
|
destructive: false,
|
||||||
hidden: false
|
hidden: false
|
||||||
},
|
},
|
||||||
@ -152,9 +152,9 @@ const menuAccount = ({
|
|||||||
key: 'account-list',
|
key: 'account-list',
|
||||||
item: {
|
item: {
|
||||||
onSelect: () => navigation.navigate('Tab-Shared-Account-In-Lists', { account }),
|
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,
|
destructive: false,
|
||||||
hidden: isFetching ? false : !data?.following
|
hidden: !isFetched || !data?.following
|
||||||
},
|
},
|
||||||
title: t('account.inLists'),
|
title: t('account.inLists'),
|
||||||
icon: 'checklist'
|
icon: 'checklist'
|
||||||
@ -169,7 +169,7 @@ const menuAccount = ({
|
|||||||
id: account.id,
|
id: account.id,
|
||||||
payload: { property: 'mute', currentValue: data?.muting }
|
payload: { property: 'mute', currentValue: data?.muting }
|
||||||
}),
|
}),
|
||||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||||
destructive: false,
|
destructive: false,
|
||||||
hidden: false
|
hidden: false
|
||||||
},
|
},
|
||||||
@ -192,7 +192,7 @@ const menuAccount = ({
|
|||||||
id: account.id,
|
id: account.id,
|
||||||
payload: { property: 'block', currentValue: data?.blocking }
|
payload: { property: 'block', currentValue: data?.blocking }
|
||||||
}),
|
}),
|
||||||
disabled: Platform.OS !== 'android' ? !data || isFetching : false,
|
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||||
destructive: !data?.blocking,
|
destructive: !data?.blocking,
|
||||||
hidden: false
|
hidden: false
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"local": {
|
"local": {
|
||||||
"name": "Following"
|
"name": "Following",
|
||||||
|
"options": {
|
||||||
|
"showBoosts": "Show boosts",
|
||||||
|
"showReplies": "Show replies"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"public": {
|
"public": {
|
||||||
"segments": {
|
"segments": {
|
||||||
|
@ -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 Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
@ -6,66 +8,160 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
|||||||
import usePopToTop from '@utils/navigation/usePopToTop'
|
import usePopToTop from '@utils/navigation/usePopToTop'
|
||||||
import { useListsQuery } from '@utils/queryHooks/lists'
|
import { useListsQuery } from '@utils/queryHooks/lists'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
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 { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import * as DropdownMenu from 'zeego/dropdown-menu'
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
|
|
||||||
const Root: React.FC<NativeStackScreenProps<TabLocalStackParamList, 'Tab-Local-Root'>> = ({
|
const Root: React.FC<NativeStackScreenProps<TabLocalStackParamList, 'Tab-Local-Root'>> = ({
|
||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
|
const { colors, mode } = useTheme()
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
const { data: lists } = useListsQuery()
|
const { data: lists } = useListsQuery()
|
||||||
|
|
||||||
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>(['Timeline', { page: 'Following' }])
|
const dispatch = useDispatch()
|
||||||
|
const instanceFollowingPage = useSelector(getInstanceFollowingPage)
|
||||||
|
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
||||||
|
'Timeline',
|
||||||
|
{ page: 'Following', ...instanceFollowingPage }
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const page = queryKey[1]
|
||||||
|
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerTitle: () => (
|
headerTitle: () => (
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<HeaderCenter
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||||
dropdown={(lists?.length ?? 0) > 0}
|
{page.page === 'List' ? (
|
||||||
content={
|
<Icon
|
||||||
queryKey[1].page === 'List' && queryKey[1].list?.length
|
name='List'
|
||||||
? lists?.find(list => list.id === queryKey[1].list)?.title
|
size={StyleConstants.Font.Size.M}
|
||||||
: t('tabs.local.name')
|
color={colors.primaryDefault}
|
||||||
}
|
style={{ marginRight: StyleConstants.Spacing.S }}
|
||||||
/>
|
/>
|
||||||
|
) : null}
|
||||||
|
<CustomText
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
|
fontSize='L'
|
||||||
|
fontWeight='Bold'
|
||||||
|
numberOfLines={1}
|
||||||
|
children={
|
||||||
|
page.page === 'List' && page.list?.length
|
||||||
|
? lists?.find(list => list.id === page.list)?.title
|
||||||
|
: t('tabs.local.name')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{page.page === 'Following' && !instanceFollowingPage.showBoosts ? (
|
||||||
|
<Icon
|
||||||
|
name='MessageCircle'
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
color={colors.red}
|
||||||
|
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||||
|
crossOut
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{page.page === 'Following' && !instanceFollowingPage.showReplies ? (
|
||||||
|
<Icon
|
||||||
|
name='Repeat'
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
color={colors.red}
|
||||||
|
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||||
|
crossOut
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Icon
|
||||||
|
name='ChevronDown'
|
||||||
|
size={StyleConstants.Font.Size.M}
|
||||||
|
color={colors.primaryDefault}
|
||||||
|
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{lists?.length
|
<DropdownMenu.Group>
|
||||||
? [
|
<DropdownMenu.Item
|
||||||
{
|
key='default'
|
||||||
key: 'default',
|
onSelect={() =>
|
||||||
item: {
|
setQueryKey(['Timeline', { page: 'Following', ...instanceFollowingPage }])
|
||||||
onSelect: () => setQueryKey(['Timeline', { page: 'Following' }]),
|
}
|
||||||
disabled: queryKey[1].page === 'Following',
|
disabled={page.page === 'Following'}
|
||||||
destructive: false,
|
>
|
||||||
hidden: false
|
<DropdownMenu.ItemTitle children={t('tabs.local.name')} />
|
||||||
},
|
<DropdownMenu.ItemIcon iosIconName='house' />
|
||||||
title: t('tabs.local.name'),
|
</DropdownMenu.Item>
|
||||||
icon: ''
|
<DropdownMenu.CheckboxItem
|
||||||
},
|
key='showBoosts'
|
||||||
...lists?.map(list => ({
|
value={instanceFollowingPage.showBoosts ? 'on' : 'mixed'}
|
||||||
key: list.id,
|
onValueChange={() => {
|
||||||
item: {
|
setQueryKey([
|
||||||
onSelect: () => setQueryKey(['Timeline', { page: 'List', list: list.id }]),
|
'Timeline',
|
||||||
disabled: queryKey[1].page === 'List' && queryKey[1].list === list.id,
|
{
|
||||||
destructive: false,
|
page: 'Following',
|
||||||
hidden: false
|
showBoosts: !instanceFollowingPage.showBoosts,
|
||||||
},
|
showReplies: instanceFollowingPage.showReplies
|
||||||
title: list.title,
|
}
|
||||||
icon: ''
|
])
|
||||||
}))
|
dispatch(
|
||||||
].map(menu => (
|
updateInstanceFollowingPage({ showBoosts: !instanceFollowingPage.showBoosts })
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
)
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
}}
|
||||||
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
>
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.ItemIndicator />
|
||||||
))
|
<DropdownMenu.ItemTitle children={t('tabs.local.options.showBoosts')} />
|
||||||
: undefined}
|
</DropdownMenu.CheckboxItem>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
key='showReplies'
|
||||||
|
value={instanceFollowingPage.showReplies ? 'on' : 'mixed'}
|
||||||
|
onValueChange={() => {
|
||||||
|
setQueryKey([
|
||||||
|
'Timeline',
|
||||||
|
{
|
||||||
|
page: 'Following',
|
||||||
|
showBoosts: instanceFollowingPage.showBoosts,
|
||||||
|
showReplies: !instanceFollowingPage.showReplies
|
||||||
|
}
|
||||||
|
])
|
||||||
|
dispatch(
|
||||||
|
updateInstanceFollowingPage({ showReplies: !instanceFollowingPage.showReplies })
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle children={t('tabs.local.options.showReplies')} />
|
||||||
|
<DropdownMenu.ItemIndicator />
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
{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 => (
|
||||||
|
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
||||||
|
<DropdownMenu.ItemTitle children={menu.title} />
|
||||||
|
<DropdownMenu.ItemIcon iosIconName={menu.icon} />
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))
|
||||||
|
: undefined}
|
||||||
|
</DropdownMenu.Group>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
),
|
),
|
||||||
@ -78,7 +174,7 @@ const Root: React.FC<NativeStackScreenProps<TabLocalStackParamList, 'Tab-Local-R
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [mode, queryKey[1], instanceFollowingPage])
|
||||||
|
|
||||||
usePopToTop()
|
usePopToTop()
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ const TabMeProfileFields: React.FC<
|
|||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { mutateAsync, status } = useProfileMutation()
|
const { mutateAsync, status } = useProfileMutation()
|
||||||
|
|
||||||
const allProps: EmojisState['inputProps'] = []
|
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 (
|
return (
|
||||||
<ComponentEmojis inputProps={allProps}>
|
<ComponentEmojis inputProps={allProps}>
|
||||||
|
@ -23,7 +23,7 @@ const TabMeProfileName: React.FC<
|
|||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { mutateAsync, status } = useProfileMutation()
|
const { mutateAsync, status } = useProfileMutation()
|
||||||
|
|
||||||
const [value, setValue] = useState(display_name)
|
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 (
|
return (
|
||||||
<ComponentEmojis inputProps={[displayNameProps]}>
|
<ComponentEmojis inputProps={[displayNameProps]}>
|
||||||
|
@ -23,7 +23,7 @@ const TabMeProfileNote: React.FC<
|
|||||||
navigation
|
navigation
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { mutateAsync, status } = useProfileMutation()
|
const { mutateAsync, status } = useProfileMutation()
|
||||||
|
|
||||||
const [notes, setNotes] = useState(note)
|
const [notes, setNotes] = useState(note)
|
||||||
@ -90,7 +90,7 @@ const TabMeProfileNote: React.FC<
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}, [theme, i18n.language, dirty, status, notes])
|
}, [theme, dirty, status, notes])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ComponentEmojis inputProps={[notesProps]}>
|
<ComponentEmojis inputProps={[notesProps]}>
|
||||||
|
@ -26,7 +26,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
params: { account }
|
params: { account }
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { colors, mode } = useTheme()
|
const { colors, mode } = useTheme()
|
||||||
|
|
||||||
const mShare = menuShare({ type: 'account', url: account.url })
|
const mShare = menuShare({ type: 'account', url: account.url })
|
||||||
@ -89,8 +89,9 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
|
|
||||||
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
||||||
'Timeline',
|
'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 isFetchingTimeline = useIsFetching(queryKey)
|
||||||
const fetchedTimeline = useRef(false)
|
const fetchedTimeline = useRef(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -113,14 +114,32 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
appearance={mode}
|
appearance={mode}
|
||||||
values={[t('shared.account.toots.default'), t('shared.account.toots.all')]}
|
values={[t('shared.account.toots.default'), t('shared.account.toots.all')]}
|
||||||
selectedIndex={queryKey[1].page === 'Account_Default' ? 0 : 1}
|
selectedIndex={page.page === 'Account' ? 0 : 1}
|
||||||
onChange={({ nativeEvent }) => {
|
onChange={({ nativeEvent }) => {
|
||||||
switch (nativeEvent.selectedSegmentIndex) {
|
switch (nativeEvent.selectedSegmentIndex) {
|
||||||
case 0:
|
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
|
break
|
||||||
case 1:
|
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
|
break
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -152,7 +171,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
|||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}, [data, fetchedTimeline.current, queryKey[1].page, i18n.language, mode])
|
}, [data, fetchedTimeline.current, queryKey[1].page, mode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -3,11 +3,11 @@ import Icon from '@components/Icon'
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
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 { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
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 { FlatList } from 'react-native-gesture-handler'
|
||||||
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
||||||
|
|
||||||
@ -15,117 +15,111 @@ export interface Props {
|
|||||||
account: Mastodon.Account | undefined
|
account: Mastodon.Account | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccountAttachments = React.memo(
|
const AccountAttachments: React.FC<Props> = ({ account }) => {
|
||||||
({ account }: Props) => {
|
if (!account) return null
|
||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const DISPLAY_AMOUNT = 6
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
const { colors } = useTheme()
|
||||||
|
|
||||||
const width =
|
const DISPLAY_AMOUNT = 6
|
||||||
(Dimensions.get('screen').width - StyleConstants.Spacing.Global.PagePadding * 2) / 4
|
|
||||||
|
|
||||||
const queryKeyParams = {
|
const width = (Dimensions.get('screen').width - StyleConstants.Spacing.Global.PagePadding * 2) / 4
|
||||||
page: 'Account_Attachments' as 'Account_Attachments',
|
|
||||||
account: account?.id
|
|
||||||
}
|
|
||||||
const { data, refetch } = useTimelineQuery({
|
|
||||||
...queryKeyParams,
|
|
||||||
options: { enabled: false }
|
|
||||||
})
|
|
||||||
useEffect(() => {
|
|
||||||
if (account?.id) {
|
|
||||||
refetch()
|
|
||||||
}
|
|
||||||
}, [account])
|
|
||||||
|
|
||||||
const flattenData = data?.pages
|
const queryKeyParams: QueryKeyTimeline[1] = {
|
||||||
? data.pages
|
page: 'Account',
|
||||||
.flatMap(d => [...d.body])
|
account: account.id,
|
||||||
.filter(status => !(status as Mastodon.Status).sensitive)
|
exclude_reblogs: true,
|
||||||
.splice(0, DISPLAY_AMOUNT)
|
only_media: true
|
||||||
: []
|
|
||||||
|
|
||||||
const renderItem = useCallback<ListRenderItem<Mastodon.Status>>(
|
|
||||||
({ item, index }) => {
|
|
||||||
if (index === DISPLAY_AMOUNT - 1) {
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
onPress={() => {
|
|
||||||
account && navigation.push('Tab-Shared-Attachments', { account })
|
|
||||||
}}
|
|
||||||
children={
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
backgroundColor: colors.backgroundOverlayInvert,
|
|
||||||
width: width,
|
|
||||||
height: width,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
}}
|
|
||||||
children={
|
|
||||||
<Icon
|
|
||||||
name='MoreHorizontal'
|
|
||||||
color={colors.primaryOverlay}
|
|
||||||
size={StyleConstants.Font.Size.L * 1.5}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<GracefullyImage
|
|
||||||
uri={{
|
|
||||||
original: item.media_attachments[0]?.preview_url || item.media_attachments[0]?.url,
|
|
||||||
remote: item.media_attachments[0]?.remote_url
|
|
||||||
}}
|
|
||||||
blurhash={
|
|
||||||
item.media_attachments[0] && (item.media_attachments[0].blurhash || undefined)
|
|
||||||
}
|
|
||||||
dimension={{ width: width, height: width }}
|
|
||||||
style={{ marginLeft: StyleConstants.Spacing.Global.PagePadding }}
|
|
||||||
onPress={() => 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 (
|
|
||||||
<Animated.View style={[styles.base, styleContainer]}>
|
|
||||||
<FlatList
|
|
||||||
horizontal
|
|
||||||
data={flattenData}
|
|
||||||
renderItem={renderItem}
|
|
||||||
showsHorizontalScrollIndicator={false}
|
|
||||||
/>
|
|
||||||
</Animated.View>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(_, next) => next.account === undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1
|
|
||||||
}
|
}
|
||||||
})
|
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<ListRenderItem<Mastodon.Status>>(
|
||||||
|
({ item, index }) => {
|
||||||
|
if (index === DISPLAY_AMOUNT - 1) {
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
onPress={() => {
|
||||||
|
account && navigation.push('Tab-Shared-Attachments', { account })
|
||||||
|
}}
|
||||||
|
children={
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
backgroundColor: colors.backgroundOverlayInvert,
|
||||||
|
width: width,
|
||||||
|
height: width,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
children={
|
||||||
|
<Icon
|
||||||
|
name='MoreHorizontal'
|
||||||
|
color={colors.primaryOverlay}
|
||||||
|
size={StyleConstants.Font.Size.L * 1.5}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<GracefullyImage
|
||||||
|
uri={{
|
||||||
|
original: item.media_attachments[0]?.preview_url || item.media_attachments[0]?.url,
|
||||||
|
remote: item.media_attachments[0]?.remote_url
|
||||||
|
}}
|
||||||
|
blurhash={
|
||||||
|
item.media_attachments[0] && (item.media_attachments[0].blurhash || undefined)
|
||||||
|
}
|
||||||
|
dimension={{ width: width, height: width }}
|
||||||
|
style={{ marginLeft: StyleConstants.Spacing.Global.PagePadding }}
|
||||||
|
onPress={() => 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 (
|
||||||
|
<Animated.View style={[{ flex: 1 }, styleContainer]}>
|
||||||
|
<FlatList
|
||||||
|
horizontal
|
||||||
|
data={flattenData}
|
||||||
|
renderItem={renderItem}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
/>
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default AccountAttachments
|
export default AccountAttachments
|
||||||
|
@ -44,7 +44,7 @@ const TabSharedAttachments: React.FC<TabSharedStackScreenProps<'Tab-Shared-Attac
|
|||||||
|
|
||||||
const queryKey: QueryKeyTimeline = [
|
const queryKey: QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
{ page: 'Account_Attachments', account: account.id }
|
{ page: 'Account', account: account.id, exclude_reblogs: true, only_media: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -133,8 +133,11 @@ const instancesMigration = {
|
|||||||
11: (state: { instances: InstanceV10[] }): { instances: InstanceV11[] } => {
|
11: (state: { instances: InstanceV10[] }): { instances: InstanceV11[] } => {
|
||||||
return {
|
return {
|
||||||
instances: state.instances.map(instance => {
|
instances: state.instances.map(instance => {
|
||||||
|
delete instance.timelinesLookback
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...instance,
|
...instance,
|
||||||
|
followingPage: { showBoosts: true, showReplies: true },
|
||||||
mePage: { ...instance.mePage, followedTags: { shown: false } },
|
mePage: { ...instance.mePage, followedTags: { shown: false } },
|
||||||
notifications_filter: {
|
notifications_filter: {
|
||||||
...instance.notifications_filter,
|
...instance.notifications_filter,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
|
||||||
|
|
||||||
export type InstanceV11 = {
|
export type InstanceV11 = {
|
||||||
active: boolean
|
active: boolean
|
||||||
@ -42,11 +41,9 @@ export type InstanceV11 = {
|
|||||||
private?: string // legacy
|
private?: string // legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timelinesLookback?: {
|
followingPage: {
|
||||||
[key: string]: {
|
showBoosts: boolean
|
||||||
queryKey: QueryKeyTimeline
|
showReplies: boolean
|
||||||
ids: Mastodon.Status['id'][]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mePage: {
|
mePage: {
|
||||||
followedTags: { shown: boolean }
|
followedTags: { shown: boolean }
|
||||||
|
@ -18,25 +18,66 @@ import updateStatusProperty from './timeline/updateStatusProperty'
|
|||||||
|
|
||||||
export type QueryKeyTimeline = [
|
export type QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
{
|
(
|
||||||
page: App.Pages
|
| {
|
||||||
hashtag?: Mastodon.Tag['name']
|
page: Exclude<App.Pages, 'Following' | 'Hashtag' | 'List' | 'Toot' | 'Account'>
|
||||||
list?: Mastodon.List['id']
|
}
|
||||||
toot?: Mastodon.Status['id']
|
| {
|
||||||
account?: Mastodon.Account['id']
|
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<QueryKeyTimeline>) => {
|
const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext<QueryKeyTimeline>) => {
|
||||||
const { page, account, hashtag, list, toot } = queryKey[1]
|
const page = queryKey[1]
|
||||||
let params: { [key: string]: string } = { ...pageParam }
|
let params: { [key: string]: string } = { ...pageParam, limit: 40 }
|
||||||
|
|
||||||
switch (page) {
|
switch (page.page) {
|
||||||
case 'Following':
|
case 'Following':
|
||||||
return apiInstance<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: 'timelines/home',
|
url: 'timelines/home',
|
||||||
params
|
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':
|
case 'Local':
|
||||||
@ -91,62 +132,57 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext<Query
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Account_Default':
|
case 'Account':
|
||||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
if (page.exclude_reblogs) {
|
||||||
|
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
||||||
|
return apiInstance<Mastodon.Status[]>({
|
||||||
|
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<Mastodon.Status[]>({
|
||||||
|
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<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${page.account}/statuses`,
|
||||||
params: {
|
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<Mastodon.Status[]>({
|
|
||||||
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<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${account}/statuses`,
|
|
||||||
params
|
|
||||||
})
|
|
||||||
|
|
||||||
case 'Account_Attachments':
|
|
||||||
return apiInstance<Mastodon.Status[]>({
|
|
||||||
method: 'get',
|
|
||||||
url: `accounts/${account}/statuses`,
|
|
||||||
params: {
|
|
||||||
only_media: 'true',
|
|
||||||
...params
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
case 'Hashtag':
|
case 'Hashtag':
|
||||||
return apiInstance<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `timelines/tag/${hashtag}`,
|
url: `timelines/tag/${page.hashtag}`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -174,21 +210,21 @@ const queryFunction = async ({ queryKey, pageParam }: QueryFunctionContext<Query
|
|||||||
case 'List':
|
case 'List':
|
||||||
return apiInstance<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `timelines/list/${list}`,
|
url: `timelines/list/${page.list}`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Toot':
|
case 'Toot':
|
||||||
const res1_1 = await apiInstance<Mastodon.Status>({
|
const res1_1 = await apiInstance<Mastodon.Status>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `statuses/${toot}`
|
url: `statuses/${page.toot}`
|
||||||
})
|
})
|
||||||
const res2_1 = await apiInstance<{
|
const res2_1 = await apiInstance<{
|
||||||
ancestors: Mastodon.Status[]
|
ancestors: Mastodon.Status[]
|
||||||
descendants: Mastodon.Status[]
|
descendants: Mastodon.Status[]
|
||||||
}>({
|
}>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `statuses/${toot}/context`
|
url: `statuses/${page.toot}/context`
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
body: [...res2_1.body.ancestors, res1_1.body, ...res2_1.body.descendants]
|
body: [...res2_1.body.ancestors, res1_1.body, ...res2_1.body.descendants]
|
||||||
|
@ -107,7 +107,10 @@ const addInstance = createAsyncThunk(
|
|||||||
},
|
},
|
||||||
keys: { auth: undefined, public: undefined, private: undefined }
|
keys: { auth: undefined, public: undefined, private: undefined }
|
||||||
},
|
},
|
||||||
timelinesLookback: {},
|
followingPage: {
|
||||||
|
showBoosts: true,
|
||||||
|
showReplies: true
|
||||||
|
},
|
||||||
mePage: {
|
mePage: {
|
||||||
followedTags: { shown: false },
|
followedTags: { shown: false },
|
||||||
lists: { shown: false },
|
lists: { shown: false },
|
||||||
|
@ -81,16 +81,15 @@ const instancesSlice = createSlice({
|
|||||||
return newInstance
|
return newInstance
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateInstanceTimelineLookback: (
|
updateInstanceFollowingPage: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<InstanceLatest['timelinesLookback']>
|
action: PayloadAction<Partial<InstanceLatest['followingPage']>>
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex] &&
|
instances[activeIndex].followingPage = {
|
||||||
(instances[activeIndex].timelinesLookback = {
|
...instances[activeIndex].followingPage,
|
||||||
...instances[activeIndex].timelinesLookback,
|
...action.payload
|
||||||
...action.payload
|
}
|
||||||
})
|
|
||||||
},
|
},
|
||||||
updateInstanceMePage: (
|
updateInstanceMePage: (
|
||||||
{ instances },
|
{ instances },
|
||||||
@ -360,8 +359,8 @@ export const getInstanceNotificationsFilter = ({ instances: { instances } }: Roo
|
|||||||
export const getInstancePush = ({ instances: { instances } }: RootState) =>
|
export const getInstancePush = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.push
|
instances[findInstanceActive(instances)]?.push
|
||||||
|
|
||||||
export const getInstanceTimelinesLookback = ({ instances: { instances } }: RootState) =>
|
export const getInstanceFollowingPage = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.timelinesLookback
|
instances[findInstanceActive(instances)]?.followingPage
|
||||||
|
|
||||||
export const getInstanceMePage = ({ instances: { instances } }: RootState) =>
|
export const getInstanceMePage = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.mePage
|
instances[findInstanceActive(instances)]?.mePage
|
||||||
@ -379,7 +378,7 @@ export const {
|
|||||||
updateInstanceDraft,
|
updateInstanceDraft,
|
||||||
removeInstanceDraft,
|
removeInstanceDraft,
|
||||||
disableAllPushes,
|
disableAllPushes,
|
||||||
updateInstanceTimelineLookback,
|
updateInstanceFollowingPage,
|
||||||
updateInstanceMePage,
|
updateInstanceMePage,
|
||||||
countInstanceEmoji
|
countInstanceEmoji
|
||||||
} = instancesSlice.actions
|
} = instancesSlice.actions
|
||||||
|
Loading…
x
Reference in New Issue
Block a user