mirror of
https://github.com/tooot-app/app
synced 2025-04-23 14:47:21 +02:00
Fixed #536
This commit is contained in:
parent
357c4039cb
commit
bdbacf579e
@ -3,8 +3,8 @@ import { StackNavigationProp } from '@react-navigation/stack'
|
|||||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
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, useState } from 'react'
|
import React, { PropsWithChildren, useCallback, useState } from 'react'
|
||||||
import { Dimensions, Pressable } from 'react-native'
|
import { Dimensions, Pressable, View } from 'react-native'
|
||||||
import Sparkline from './Sparkline'
|
import Sparkline from './Sparkline'
|
||||||
import CustomText from './Text'
|
import CustomText from './Text'
|
||||||
|
|
||||||
@ -13,7 +13,11 @@ export interface Props {
|
|||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComponentHashtag: React.FC<Props> = ({ hashtag, onPress: customOnPress }) => {
|
const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
||||||
|
hashtag,
|
||||||
|
onPress: customOnPress,
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
@ -31,15 +35,11 @@ const ComponentHashtag: React.FC<Props> = ({ hashtag, onPress: customOnPress })
|
|||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
padding
|
padding
|
||||||
}}
|
}}
|
||||||
onPress={customOnPress || onPress}
|
onPress={customOnPress || onPress}
|
||||||
onLayout={({
|
|
||||||
nativeEvent: {
|
|
||||||
layout: { height }
|
|
||||||
}
|
|
||||||
}) => setHeight(height - padding * 2 - 1)}
|
|
||||||
>
|
>
|
||||||
<CustomText
|
<CustomText
|
||||||
fontStyle='M'
|
fontStyle='M'
|
||||||
@ -52,11 +52,22 @@ const ComponentHashtag: React.FC<Props> = ({ hashtag, onPress: customOnPress })
|
|||||||
>
|
>
|
||||||
#{hashtag.name}
|
#{hashtag.name}
|
||||||
</CustomText>
|
</CustomText>
|
||||||
|
<View
|
||||||
|
style={{ flexDirection: 'row', alignItems: 'center' }}
|
||||||
|
onLayout={({
|
||||||
|
nativeEvent: {
|
||||||
|
layout: { height }
|
||||||
|
}
|
||||||
|
}) => setHeight(height)}
|
||||||
|
>
|
||||||
<Sparkline
|
<Sparkline
|
||||||
data={hashtag.history.map(h => parseInt(h.uses)).reverse()}
|
data={hashtag.history.map(h => parseInt(h.uses)).reverse()}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
|
margin={children ? StyleConstants.Spacing.S : undefined}
|
||||||
/>
|
/>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { maxBy, minBy } from 'lodash'
|
import { maxBy, minBy } from 'lodash'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Platform } from 'react-native'
|
|
||||||
import Svg, { G, Path } from 'react-native-svg'
|
import Svg, { G, Path } from 'react-native-svg'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -69,7 +68,7 @@ const Sparkline: React.FC<Props> = ({ data, width, height, margin = 0 }) => {
|
|||||||
const fillPoints = linePoints.concat(closePolyPoints)
|
const fillPoints = linePoints.concat(closePolyPoints)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Svg height={Platform.OS !== 'android' ? 'auto' : 24} width={width}>
|
<Svg height={height} width={width} style={{ marginRight: margin }}>
|
||||||
<G>
|
<G>
|
||||||
<Path d={'M' + fillPoints.join(' ')} fill={colors.blue} fillOpacity={0.1} />
|
<Path d={'M' + fillPoints.join(' ')} fill={colors.blue} fillOpacity={0.1} />
|
||||||
<Path
|
<Path
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"discard": "Discard",
|
"discard": "Discard",
|
||||||
"continue": "Continue",
|
"continue": "Continue",
|
||||||
|
"create": "Create",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"done": "Done"
|
"done": "Done"
|
||||||
},
|
},
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
"favourites": {
|
"favourites": {
|
||||||
"name": "Favourites"
|
"name": "Favourites"
|
||||||
},
|
},
|
||||||
|
"followedTags": {
|
||||||
|
"name": "Followed hashtags"
|
||||||
|
},
|
||||||
"fontSize": {
|
"fontSize": {
|
||||||
"name": "Toot Font Size"
|
"name": "Toot Font Size"
|
||||||
},
|
},
|
||||||
@ -53,7 +56,7 @@
|
|||||||
"name": "Users in list: {{list}}"
|
"name": "Users in list: {{list}}"
|
||||||
},
|
},
|
||||||
"listAdd": {
|
"listAdd": {
|
||||||
"name": "Add a List"
|
"name": "Create a List"
|
||||||
},
|
},
|
||||||
"listEdit": {
|
"listEdit": {
|
||||||
"name": "Edit List Details"
|
"name": "Edit List Details"
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { HeaderCenter, HeaderLeft } from '@components/Header'
|
import { HeaderLeft } from '@components/Header'
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||||
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
import { TabMeStackParamList } from '@utils/navigation/navigators'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
|
||||||
import TabMeBookmarks from './Me/Bookmarks'
|
import TabMeBookmarks from './Me/Bookmarks'
|
||||||
import TabMeConversations from './Me/Cconversations'
|
import TabMeConversations from './Me/Cconversations'
|
||||||
import TabMeFavourites from './Me/Favourites'
|
import TabMeFavourites from './Me/Favourites'
|
||||||
|
import TabMeFollowedTags from './Me/FollowedTags'
|
||||||
import TabMeList from './Me/List'
|
import TabMeList from './Me/List'
|
||||||
import TabMeListAccounts from './Me/List/Accounts'
|
import TabMeListAccounts from './Me/List/Accounts'
|
||||||
import TabMeListEdit from './Me/List/Edit'
|
import TabMeListEdit from './Me/List/Edit'
|
||||||
@ -42,9 +42,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeBookmarks}
|
component={TabMeBookmarks}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.bookmarks.name'),
|
title: t('me.stacks.bookmarks.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.bookmarks.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -53,9 +50,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeConversations}
|
component={TabMeConversations}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.conversations.name'),
|
title: t('me.stacks.conversations.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.conversations.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -64,9 +58,14 @@ const TabMe = React.memo(
|
|||||||
component={TabMeFavourites}
|
component={TabMeFavourites}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.favourites.name'),
|
title: t('me.stacks.favourites.name'),
|
||||||
...(Platform.OS === 'android' && {
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.favourites.name')} />
|
})}
|
||||||
}),
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Tab-Me-FollowedTags'
|
||||||
|
component={TabMeFollowedTags}
|
||||||
|
options={({ navigation }: any) => ({
|
||||||
|
title: t('me.stacks.followedTags.name'),
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -75,15 +74,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeList}
|
component={TabMeList}
|
||||||
options={({ route, navigation }: any) => ({
|
options={({ route, navigation }: any) => ({
|
||||||
title: t('me.stacks.list.name', { list: route.params.title }),
|
title: t('me.stacks.list.name', { list: route.params.title }),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => (
|
|
||||||
<HeaderCenter
|
|
||||||
content={t('me.stacks.list.name', {
|
|
||||||
list: route.params.title
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -92,9 +82,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeListAccounts}
|
component={TabMeListAccounts}
|
||||||
options={({ navigation, route: { params } }) => ({
|
options={({ navigation, route: { params } }) => ({
|
||||||
title: t('me.stacks.listAccounts.name', { list: params.title }),
|
title: t('me.stacks.listAccounts.name', { list: params.title }),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.listsAdd.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -111,9 +98,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeListList}
|
component={TabMeListList}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.lists.name'),
|
title: t('me.stacks.lists.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.lists.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -130,9 +114,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMePush}
|
component={TabMePush}
|
||||||
options={({ navigation }) => ({
|
options={({ navigation }) => ({
|
||||||
title: t('me.stacks.push.name'),
|
title: t('me.stacks.push.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.push.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -149,9 +130,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeSettingsFontsize}
|
component={TabMeSettingsFontsize}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.fontSize.name'),
|
title: t('me.stacks.fontSize.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.fontSize.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -160,9 +138,6 @@ const TabMe = React.memo(
|
|||||||
component={TabMeSettingsLanguage}
|
component={TabMeSettingsLanguage}
|
||||||
options={({ navigation }: any) => ({
|
options={({ navigation }: any) => ({
|
||||||
title: t('me.stacks.language.name'),
|
title: t('me.stacks.language.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.language.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@ -173,9 +148,6 @@ const TabMe = React.memo(
|
|||||||
presentation: 'modal',
|
presentation: 'modal',
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
title: t('me.stacks.switch.name'),
|
title: t('me.stacks.switch.name'),
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('me.stacks.switch.name')} />
|
|
||||||
}),
|
|
||||||
headerLeft: () => (
|
headerLeft: () => (
|
||||||
<HeaderLeft content='ChevronDown' onPress={() => navigation.goBack()} />
|
<HeaderLeft content='ChevronDown' onPress={() => navigation.goBack()} />
|
||||||
)
|
)
|
||||||
|
71
src/screens/Tabs/Me/FollowedTags.tsx
Normal file
71
src/screens/Tabs/Me/FollowedTags.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import Button from '@components/Button'
|
||||||
|
import haptics from '@components/haptics'
|
||||||
|
import ComponentHashtag from '@components/Hashtag'
|
||||||
|
import { displayMessage } from '@components/Message'
|
||||||
|
import ComponentSeparator from '@components/Separator'
|
||||||
|
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
|
||||||
|
import { useFollowedTagsQuery, useTagsMutation } from '@utils/queryHooks/tags'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { FlatList } from 'react-native-gesture-handler'
|
||||||
|
|
||||||
|
const TabMeFollowedTags: React.FC<TabMeStackScreenProps<'Tab-Me-FollowedTags'>> = ({
|
||||||
|
navigation
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
|
const { data, fetchNextPage, refetch } = useFollowedTagsQuery()
|
||||||
|
const flattenData = data?.pages ? data.pages.flatMap(page => [...page.body]) : []
|
||||||
|
useEffect(() => {
|
||||||
|
if (flattenData.length === 0) {
|
||||||
|
navigation.goBack()
|
||||||
|
}
|
||||||
|
}, [flattenData.length])
|
||||||
|
|
||||||
|
const mutation = useTagsMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
haptics('Light')
|
||||||
|
refetch()
|
||||||
|
},
|
||||||
|
onError: (err: any, { to }) => {
|
||||||
|
displayMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: t('common:message.error.message', {
|
||||||
|
function: to ? t('shared.hashtag.follow') : t('shared.hashtag.unfollow')
|
||||||
|
}),
|
||||||
|
...(err.status &&
|
||||||
|
typeof err.status === 'number' &&
|
||||||
|
err.data &&
|
||||||
|
err.data.error &&
|
||||||
|
typeof err.data.error === 'string' && {
|
||||||
|
description: err.data.error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FlatList
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
data={flattenData}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<ComponentHashtag
|
||||||
|
hashtag={item}
|
||||||
|
onPress={() => {}}
|
||||||
|
children={
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
content={t('shared.hashtag.unfollow')}
|
||||||
|
onPress={() => mutation.mutate({ tag: item.name, to: !item.following })}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
onEndReached={() => fetchNextPage()}
|
||||||
|
onEndReachedThreshold={0.5}
|
||||||
|
ItemSeparatorComponent={ComponentSeparator}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabMeFollowedTags
|
@ -7,14 +7,14 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
const TabMeListList: React.FC<TabMeStackScreenProps<'Tab-Me-List-List'>> = ({ navigation }) => {
|
const TabMeListList: React.FC<TabMeStackScreenProps<'Tab-Me-List-List'>> = ({ navigation }) => {
|
||||||
const { data } = useListsQuery({})
|
const { data } = useListsQuery({})
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<HeaderRight
|
<HeaderRight
|
||||||
accessibilityLabel={t('me.stacks.listAdd.name')}
|
type='text'
|
||||||
content='Plus'
|
content={t('common:buttons.create')}
|
||||||
onPress={() => navigation.navigate('Tab-Me-List-Edit', { type: 'add' })}
|
onPress={() => navigation.navigate('Tab-Me-List-Edit', { type: 'add' })}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import { useNavigation } from '@react-navigation/native'
|
|||||||
import { useAppDispatch } from '@root/store'
|
import { useAppDispatch } from '@root/store'
|
||||||
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
|
||||||
import { useListsQuery } from '@utils/queryHooks/lists'
|
import { useListsQuery } from '@utils/queryHooks/lists'
|
||||||
|
import { useFollowedTagsQuery } from '@utils/queryHooks/tags'
|
||||||
import { getInstanceMePage, updateInstanceMePage } from '@utils/slices/instancesSlice'
|
import { getInstanceMePage, updateInstanceMePage } from '@utils/slices/instancesSlice'
|
||||||
import { getInstancePush } from '@utils/slices/instancesSlice'
|
import { getInstancePush } from '@utils/slices/instancesSlice'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
@ -16,39 +17,40 @@ const Collections: React.FC = () => {
|
|||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const mePage = useSelector(getInstanceMePage)
|
const mePage = useSelector(getInstanceMePage)
|
||||||
|
|
||||||
const listsQuery = useListsQuery({
|
useFollowedTagsQuery({
|
||||||
options: {
|
options: {
|
||||||
notifyOnChangeProps: ['data']
|
onSuccess: data =>
|
||||||
}
|
|
||||||
})
|
|
||||||
useEffect(() => {
|
|
||||||
if (listsQuery.isSuccess) {
|
|
||||||
dispatch(
|
dispatch(
|
||||||
updateInstanceMePage({
|
updateInstanceMePage({
|
||||||
lists: { shown: listsQuery.data?.length ? true : false }
|
followedTags: { shown: !!data?.pages?.[0].body?.length }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [listsQuery.isSuccess, listsQuery.data?.length])
|
})
|
||||||
|
useListsQuery({
|
||||||
const announcementsQuery = useAnnouncementQuery({
|
|
||||||
showAll: true,
|
|
||||||
options: {
|
options: {
|
||||||
notifyOnChangeProps: ['data']
|
onSuccess: data =>
|
||||||
|
dispatch(
|
||||||
|
updateInstanceMePage({
|
||||||
|
lists: { shown: !!data?.length }
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
useEffect(() => {
|
useAnnouncementQuery({
|
||||||
if (announcementsQuery.data) {
|
showAll: true,
|
||||||
|
options: {
|
||||||
|
onSuccess: data =>
|
||||||
dispatch(
|
dispatch(
|
||||||
updateInstanceMePage({
|
updateInstanceMePage({
|
||||||
announcements: {
|
announcements: {
|
||||||
shown: announcementsQuery.data.length ? true : false,
|
shown: !!data?.length ? true : false,
|
||||||
unread: announcementsQuery.data.filter(announcement => !announcement.read).length
|
unread: data?.filter(announcement => !announcement.read).length
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [announcementsQuery.data])
|
})
|
||||||
|
|
||||||
const instancePush = useSelector(getInstancePush, (prev, next) => prev?.global === next?.global)
|
const instancePush = useSelector(getInstancePush, (prev, next) => prev?.global === next?.global)
|
||||||
|
|
||||||
@ -80,6 +82,14 @@ const Collections: React.FC = () => {
|
|||||||
onPress={() => navigation.navigate('Tab-Me-List-List')}
|
onPress={() => navigation.navigate('Tab-Me-List-List')}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{mePage.followedTags.shown ? (
|
||||||
|
<MenuRow
|
||||||
|
iconFront='Hash'
|
||||||
|
iconBack='ChevronRight'
|
||||||
|
title={t('me.stacks.followedTags.name')}
|
||||||
|
onPress={() => navigation.navigate('Tab-Me-FollowedTags')}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{mePage.announcements.shown ? (
|
{mePage.announcements.shown ? (
|
||||||
<MenuRow
|
<MenuRow
|
||||||
iconFront='Clipboard'
|
iconFront='Clipboard'
|
||||||
|
@ -3,11 +3,11 @@ import { HeaderLeft, HeaderRight } from '@components/Header'
|
|||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { useTagsMutation, useTagsQuery } from '@utils/queryHooks/tags'
|
import { QueryKeyFollowedTags, useTagsMutation, useTagsQuery } from '@utils/queryHooks/tags'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
@ -27,7 +27,6 @@ const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
|
|||||||
|
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
|
||||||
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
const canFollowTags = useSelector(checkInstanceFeature('follow_tags'))
|
const canFollowTags = useSelector(checkInstanceFeature('follow_tags'))
|
||||||
@ -35,14 +34,16 @@ const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
|
|||||||
tag: hashtag,
|
tag: hashtag,
|
||||||
options: { enabled: canFollowTags }
|
options: { enabled: canFollowTags }
|
||||||
})
|
})
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const mutation = useTagsMutation({
|
const mutation = useTagsMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
haptics('Success')
|
haptics('Success')
|
||||||
refetch()
|
refetch()
|
||||||
|
const queryKeyFollowedTags: QueryKeyFollowedTags = ['FollowedTags']
|
||||||
|
queryClient.invalidateQueries({ queryKey: queryKeyFollowedTags })
|
||||||
},
|
},
|
||||||
onError: (err: any, { to }) => {
|
onError: (err: any, { to }) => {
|
||||||
displayMessage({
|
displayMessage({
|
||||||
theme,
|
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t('common:message.error.message', {
|
message: t('common:message.error.message', {
|
||||||
function: to ? t('shared.hashtag.follow') : t('shared.hashtag.unfollow')
|
function: to ? t('shared.hashtag.follow') : t('shared.hashtag.unfollow')
|
||||||
@ -68,7 +69,7 @@ const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
|
|||||||
content={data?.following ? t('shared.hashtag.unfollow') : t('shared.hashtag.follow')}
|
content={data?.following ? t('shared.hashtag.unfollow') : t('shared.hashtag.follow')}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
typeof data?.following === 'boolean' &&
|
typeof data?.following === 'boolean' &&
|
||||||
mutation.mutate({ tag: hashtag, type: 'follow', to: !data.following })
|
mutation.mutate({ tag: hashtag, to: !data.following })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -135,6 +135,7 @@ const instancesMigration = {
|
|||||||
instances: state.instances.map(instance => {
|
instances: state.instances.map(instance => {
|
||||||
return {
|
return {
|
||||||
...instance,
|
...instance,
|
||||||
|
mePage: { ...instance.mePage, followedTags: { shown: false } },
|
||||||
push: {
|
push: {
|
||||||
...instance.push,
|
...instance.push,
|
||||||
global: instance.push.global.value,
|
global: instance.push.global.value,
|
||||||
|
@ -47,6 +47,7 @@ export type InstanceV11 = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mePage: {
|
mePage: {
|
||||||
|
followedTags: { shown: boolean }
|
||||||
lists: { shown: boolean }
|
lists: { shown: boolean }
|
||||||
announcements: { shown: boolean; unread: number }
|
announcements: { shown: boolean; unread: number }
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ export type TabMeStackParamList = {
|
|||||||
'Tab-Me-Bookmarks': undefined
|
'Tab-Me-Bookmarks': undefined
|
||||||
'Tab-Me-Conversations': undefined
|
'Tab-Me-Conversations': undefined
|
||||||
'Tab-Me-Favourites': undefined
|
'Tab-Me-Favourites': undefined
|
||||||
|
'Tab-Me-FollowedTags': undefined
|
||||||
'Tab-Me-List': Mastodon.List
|
'Tab-Me-List': Mastodon.List
|
||||||
'Tab-Me-List-Accounts': Omit<Mastodon.List, 'replies_policy'>
|
'Tab-Me-List-Accounts': Omit<Mastodon.List, 'replies_policy'>
|
||||||
'Tab-Me-List-Edit':
|
'Tab-Me-List-Edit':
|
||||||
|
@ -1,32 +1,40 @@
|
|||||||
import apiInstance from '@api/instance'
|
import apiInstance, { InstanceResponse } from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import {
|
import {
|
||||||
QueryFunctionContext,
|
QueryFunctionContext,
|
||||||
|
useInfiniteQuery,
|
||||||
|
UseInfiniteQueryOptions,
|
||||||
useMutation,
|
useMutation,
|
||||||
UseMutationOptions,
|
UseMutationOptions,
|
||||||
useQuery,
|
useQuery,
|
||||||
UseQueryOptions
|
UseQueryOptions
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
import { infinitePageParams } from './utils'
|
||||||
|
|
||||||
type QueryKeyFollowedTags = ['FollowedTags']
|
export type QueryKeyFollowedTags = ['FollowedTags']
|
||||||
const useFollowedTagsQuery = ({
|
const useFollowedTagsQuery = (
|
||||||
options
|
params: {
|
||||||
}: {
|
options?: Omit<
|
||||||
options?: UseQueryOptions<Mastodon.Tag, AxiosError>
|
UseInfiniteQueryOptions<InstanceResponse<Mastodon.Tag[]>, AxiosError>,
|
||||||
}) => {
|
'getPreviousPageParam' | 'getNextPageParam'
|
||||||
|
>
|
||||||
|
} | void
|
||||||
|
) => {
|
||||||
const queryKey: QueryKeyFollowedTags = ['FollowedTags']
|
const queryKey: QueryKeyFollowedTags = ['FollowedTags']
|
||||||
return useQuery(
|
return useInfiniteQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
async ({ pageParam }: QueryFunctionContext<QueryKeyFollowedTags>) => {
|
async ({ pageParam }: QueryFunctionContext<QueryKeyFollowedTags>) => {
|
||||||
const params: { [key: string]: string } = { ...pageParam }
|
const params: { [key: string]: string } = { ...pageParam }
|
||||||
const res = await apiInstance<Mastodon.Tag>({ method: 'get', url: `followed_tags`, params })
|
return await apiInstance<Mastodon.Tag[]>({ method: 'get', url: `followed_tags`, params })
|
||||||
return res.body
|
|
||||||
},
|
},
|
||||||
options
|
{
|
||||||
|
...params?.options,
|
||||||
|
...infinitePageParams
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryKeyTags = ['Tags', { tag: string }]
|
export type QueryKeyTags = ['Tags', { tag: string }]
|
||||||
const queryFunction = async ({ queryKey }: QueryFunctionContext<QueryKeyTags>) => {
|
const queryFunction = async ({ queryKey }: QueryFunctionContext<QueryKeyTags>) => {
|
||||||
const { tag } = queryKey[1]
|
const { tag } = queryKey[1]
|
||||||
|
|
||||||
@ -43,15 +51,12 @@ const useTagsQuery = ({
|
|||||||
return useQuery(queryKey, queryFunction, options)
|
return useQuery(queryKey, queryFunction, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MutationVarsAnnouncement = { tag: string; type: 'follow'; to: boolean }
|
type MutationVarsAnnouncement = { tag: string; to: boolean }
|
||||||
const mutationFunction = async ({ tag, type, to }: MutationVarsAnnouncement) => {
|
const mutationFunction = async ({ tag, to }: MutationVarsAnnouncement) => {
|
||||||
switch (type) {
|
|
||||||
case 'follow':
|
|
||||||
return apiInstance<{}>({
|
return apiInstance<{}>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `tags/${tag}/${to ? 'follow' : 'unfollow'}`
|
url: `tags/${tag}/${to ? 'follow' : 'unfollow'}`
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const useTagsMutation = (options: UseMutationOptions<{}, AxiosError, MutationVarsAnnouncement>) => {
|
const useTagsMutation = (options: UseMutationOptions<{}, AxiosError, MutationVarsAnnouncement>) => {
|
||||||
return useMutation(mutationFunction, options)
|
return useMutation(mutationFunction, options)
|
||||||
|
8
src/utils/queryHooks/utils.ts
Normal file
8
src/utils/queryHooks/utils.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { InstanceResponse } from '@api/instance'
|
||||||
|
|
||||||
|
export const infinitePageParams = {
|
||||||
|
getPreviousPageParam: (firstPage: InstanceResponse<any>) =>
|
||||||
|
firstPage.links?.prev && { since_id: firstPage.links.next },
|
||||||
|
getNextPageParam: (lastPage: InstanceResponse<any>) =>
|
||||||
|
lastPage.links?.next && { max_id: lastPage.links.next }
|
||||||
|
}
|
@ -107,6 +107,7 @@ const addInstance = createAsyncThunk(
|
|||||||
},
|
},
|
||||||
timelinesLookback: {},
|
timelinesLookback: {},
|
||||||
mePage: {
|
mePage: {
|
||||||
|
followedTags: { shown: false },
|
||||||
lists: { shown: false },
|
lists: { shown: false },
|
||||||
announcements: { shown: false, unread: 0 }
|
announcements: { shown: false, unread: 0 }
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user