1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Merge branch 'main' into candidate

This commit is contained in:
xmflsct
2023-01-05 00:49:21 +01:00
43 changed files with 343 additions and 371 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/RNFastImage.podspec b/RNFastImage.podspec diff --git a/RNFastImage.podspec b/RNFastImage.podspec
index db0fada63fc06191f8620d336d244edde6c3dba3..b23cd91a9d70bdb8abd2f4ba1417c2c35e5a1d4c 100644 index db0fada63fc06191f8620d336d244edde6c3dba3..b6ffe6c77ab1fd5b821525f6f0b7363a13cba3a0 100644
--- a/RNFastImage.podspec --- a/RNFastImage.podspec
+++ b/RNFastImage.podspec +++ b/RNFastImage.podspec
@@ -16,6 +16,6 @@ Pod::Spec.new do |s| @@ -16,6 +16,6 @@ Pod::Spec.new do |s|
@@ -8,7 +8,7 @@ index db0fada63fc06191f8620d336d244edde6c3dba3..b23cd91a9d70bdb8abd2f4ba1417c2c3
s.dependency 'React-Core' s.dependency 'React-Core'
- s.dependency 'SDWebImage', '~> 5.11.1' - s.dependency 'SDWebImage', '~> 5.11.1'
- s.dependency 'SDWebImageWebPCoder', '~> 0.8.4' - s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
+ s.dependency 'SDWebImage', '~> 5.14.2' + s.dependency 'SDWebImage', '~> 5.14.3'
+ s.dependency 'SDWebImageWebPCoder', '~> 0.9.1' + s.dependency 'SDWebImageWebPCoder', '~> 0.9.1'
end end
diff --git a/android/build.gradle b/android/build.gradle diff --git a/android/build.gradle b/android/build.gradle

View File

@@ -1,4 +1,5 @@
Enjoy toooting! This version includes following improvements and fixes: Enjoy toooting! This version includes following improvements and fixes:
- Auto fetch remote content in conversations!
- Allowing adding more context of reports - Allowing adding more context of reports
- Option to disable autoplay gif - Option to disable autoplay gif
- Hide boosts from users - Hide boosts from users

View File

@@ -1,4 +1,5 @@
toooting愉快此版本包括以下改进和修复 toooting愉快此版本包括以下改进和修复
- 主动获取对话的远程内容
- 可添加举报细节 - 可添加举报细节
- 新增暂停自动播放gif动画选项 - 新增暂停自动播放gif动画选项
- 隐藏用户的转嘟 - 隐藏用户的转嘟

View File

@@ -398,7 +398,7 @@ PODS:
- React-Core - React-Core
- RNFastImage (8.6.3): - RNFastImage (8.6.3):
- React-Core - React-Core
- SDWebImage (~> 5.14.2) - SDWebImage (~> 5.14.3)
- SDWebImageWebPCoder (~> 0.9.1) - SDWebImageWebPCoder (~> 0.9.1)
- RNGestureHandler (2.8.0): - RNGestureHandler (2.8.0):
- React-Core - React-Core
@@ -439,9 +439,9 @@ PODS:
- React - React
- RNSVG (13.6.0): - RNSVG (13.6.0):
- React-Core - React-Core
- SDWebImage (5.14.2): - SDWebImage (5.14.3):
- SDWebImage/Core (= 5.14.2) - SDWebImage/Core (= 5.14.3)
- SDWebImage/Core (5.14.2) - SDWebImage/Core (5.14.3)
- SDWebImageWebPCoder (0.9.1): - SDWebImageWebPCoder (0.9.1):
- libwebp (~> 1.0) - libwebp (~> 1.0)
- SDWebImage/Core (~> 5.13) - SDWebImage/Core (~> 5.13)
@@ -769,14 +769,14 @@ SPEC CHECKSUMS:
ReactCommon: 349be31adeecffc7986a0de875d7fb0dcf4e251c ReactCommon: 349be31adeecffc7986a0de875d7fb0dcf4e251c
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60 RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
RNFastImage: c5dd1b551779c5826fe43b7d36788385da2021e2 RNFastImage: 756ab178acb5e3f11d8b0a931956fbd9da8d6e54
RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3 RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
RNReanimated: ce445c233a6ff5600223484a88ad5704945d972a RNReanimated: ce445c233a6ff5600223484a88ad5704945d972a
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
RNSentry: 4c09f4dd9740cb9b33e94303de5b6d0dbeb0737d RNSentry: 4c09f4dd9740cb9b33e94303de5b6d0dbeb0737d
RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3 RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3
RNSVG: 3a79c0c4992213e4f06c08e62730c5e7b9e4dc17 RNSVG: 3a79c0c4992213e4f06c08e62730c5e7b9e4dc17
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764
SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0 SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
Sentry: 08884c523575ec0f6690d94ed3ccb0246a1600bf Sentry: 08884c523575ec0f6690d94ed3ccb0246a1600bf
Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b

View File

@@ -87,14 +87,14 @@ const ParseHTML: React.FC<Props> = ({
return '' return ''
} }
} }
const openingMentions = useRef<boolean>(true) const prevMentionRemoved = useRef<boolean>(false)
const renderNode = (node: ChildNode, index: number) => { const renderNode = (node: ChildNode, index: number) => {
switch (node.type) { switch (node.type) {
case ElementType.Text: case ElementType.Text:
let content: string = node.data let content: string = node.data
if (openingMentions.current) { if (prevMentionRemoved.current) {
prevMentionRemoved.current = false // Removing empty spaces appeared between tags and mentions
if (node.data.trim().length) { if (node.data.trim().length) {
openingMentions.current = false // Removing empty spaces appeared between tags and mentions
content = excludeMentions?.current.length content = excludeMentions?.current.length
? node.data.replace(new RegExp(/^\s+/), '') ? node.data.replace(new RegExp(/^\s+/), '')
: node.data : node.data
@@ -119,7 +119,6 @@ const ParseHTML: React.FC<Props> = ({
const href = node.attribs.href const href = node.attribs.href
if (classes) { if (classes) {
if (classes.includes('hashtag')) { if (classes.includes('hashtag')) {
openingMentions.current = false
const tag = href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1].toLowerCase() const tag = href.match(new RegExp(/\/tags?\/(.*)/, 'i'))?.[1].toLowerCase()
const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined) const paramsHashtag = (params as { hashtag: Mastodon.Tag['name'] } | undefined)
?.hashtag ?.hashtag
@@ -156,6 +155,7 @@ const ParseHTML: React.FC<Props> = ({
matchedMention && matchedMention &&
excludeMentions?.current.find(eM => eM.id === matchedMention.id) excludeMentions?.current.find(eM => eM.id === matchedMention.id)
) { ) {
prevMentionRemoved.current = true
return null return null
} }
const paramsAccount = (params as { account: Mastodon.Account } | undefined)?.account const paramsAccount = (params as { account: Mastodon.Account } | undefined)?.account
@@ -176,7 +176,6 @@ const ParseHTML: React.FC<Props> = ({
} }
} }
openingMentions.current = false
const content = node.children.map(child => unwrapNode(child)).join('') const content = node.children.map(child => unwrapNode(child)).join('')
const shouldBeTag = status?.tags?.find(tag => `#${tag.name}` === content) const shouldBeTag = status?.tags?.find(tag => `#${tag.name}` === content)
return ( return (
@@ -198,6 +197,12 @@ const ParseHTML: React.FC<Props> = ({
/> />
) )
break break
case 'br':
return (
<Text key={index} style={{ lineHeight: adaptedLineheight / 2 }}>
{'\n'}
</Text>
)
case 'p': case 'p':
if (index < document.children.length - 1) { if (index < document.children.length - 1) {
return ( return (

View File

@@ -41,10 +41,7 @@ const TimelineConversation: React.FC<Props> = ({ conversation, queryKey, highlig
const onPress = useCallback(() => { const onPress = useCallback(() => {
if (conversation.last_status) { if (conversation.last_status) {
conversation.unread && mutate() conversation.unread && mutate()
navigation.push('Tab-Shared-Toot', { navigation.push('Tab-Shared-Toot', { toot: conversation.last_status })
toot: conversation.last_status,
rootQueryKey: queryKey
})
} }
}, []) }, [])

View File

@@ -32,7 +32,6 @@ import TimelineTranslate from './Shared/Translate'
export interface Props { export interface Props {
item: Mastodon.Status & { _pinned?: boolean } // For account page, internal property item: Mastodon.Status & { _pinned?: boolean } // For account page, internal property
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
highlighted?: boolean highlighted?: boolean
disableDetails?: boolean disableDetails?: boolean
disableOnPress?: boolean disableOnPress?: boolean
@@ -43,7 +42,6 @@ export interface Props {
const TimelineDefault: React.FC<Props> = ({ const TimelineDefault: React.FC<Props> = ({
item, item,
queryKey, queryKey,
rootQueryKey,
highlighted = false, highlighted = false,
disableDetails = false, disableDetails = false,
disableOnPress = false, disableOnPress = false,
@@ -131,8 +129,8 @@ const TimelineDefault: React.FC<Props> = ({
url: status.url || status.uri, url: status.url || status.uri,
rawContent rawContent
}) })
const mStatus = menuStatus({ status, queryKey, rootQueryKey }) const mStatus = menuStatus({ status, queryKey })
const mInstance = menuInstance({ status, queryKey, rootQueryKey }) const mInstance = menuInstance({ status, queryKey })
if (!ownAccount) { if (!ownAccount) {
let filterResults: FilteredProps['filterResults'] = [] let filterResults: FilteredProps['filterResults'] = []
@@ -163,7 +161,6 @@ const TimelineDefault: React.FC<Props> = ({
<StatusContext.Provider <StatusContext.Provider
value={{ value={{
queryKey, queryKey,
rootQueryKey,
status, status,
ownAccount, ownAccount,
spoilerHidden, spoilerHidden,
@@ -188,12 +185,7 @@ const TimelineDefault: React.FC<Props> = ({
accessible={highlighted ? false : true} accessible={highlighted ? false : true}
style={mainStyle} style={mainStyle}
disabled={highlighted} disabled={highlighted}
onPress={() => onPress={() => navigation.push('Tab-Shared-Toot', { toot: status })}
navigation.push('Tab-Shared-Toot', {
toot: status,
rootQueryKey: queryKey
})
}
onLongPress={() => {}} onLongPress={() => {}}
children={main()} children={main()}
/> />

View File

@@ -154,10 +154,7 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
}} }}
onPress={() => onPress={() =>
notification.status && notification.status &&
navigation.push('Tab-Shared-Toot', { navigation.push('Tab-Shared-Toot', { toot: notification.status })
toot: notification.status,
rootQueryKey: queryKey
})
} }
onLongPress={() => {}} onLongPress={() => {}}
children={main()} children={main()}

View File

@@ -6,7 +6,7 @@ import { useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack' import { StackNavigationProp } from '@react-navigation/stack'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { androidActionSheetStyles } from '@utils/helpers/androidActionSheetStyles' import { androidActionSheetStyles } from '@utils/helpers/androidActionSheetStyles'
import { RootStackParamList } from '@utils/navigation/navigators' import { RootStackParamList, useNavState } from '@utils/navigation/navigators'
import { import {
MutationVarsTimelineUpdateStatusProperty, MutationVarsTimelineUpdateStatusProperty,
QueryKeyTimeline, QueryKeyTimeline,
@@ -22,10 +22,10 @@ import { Pressable, StyleSheet, View } from 'react-native'
import StatusContext from './Context' import StatusContext from './Context'
const TimelineActions: React.FC = () => { const TimelineActions: React.FC = () => {
const { queryKey, rootQueryKey, status, ownAccount, highlighted, disableDetails } = const { queryKey, status, ownAccount, highlighted, disableDetails } = useContext(StatusContext)
useContext(StatusContext)
if (!queryKey || !status || disableDetails) return null if (!queryKey || !status || disableDetails) return null
const navigationState = useNavState()
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>() const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
const { t } = useTranslation(['common', 'componentTimeline']) const { t } = useTranslation(['common', 'componentTimeline'])
const { colors, theme } = useTheme() const { colors, theme } = useTheme()
@@ -87,7 +87,7 @@ const TimelineActions: React.FC = () => {
type: 'reply', type: 'reply',
incomingStatus: status, incomingStatus: status,
accts, accts,
queryKey navigationState
}) })
} }
const { showActionSheetWithOptions } = useActionSheet() const { showActionSheetWithOptions } = useActionSheet()
@@ -109,8 +109,6 @@ const TimelineActions: React.FC = () => {
case 0: case 0:
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'reblogged', type: 'reblogged',
@@ -122,8 +120,6 @@ const TimelineActions: React.FC = () => {
case 1: case 1:
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'reblogged', type: 'reblogged',
@@ -138,8 +134,6 @@ const TimelineActions: React.FC = () => {
} else { } else {
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'reblogged', type: 'reblogged',
@@ -152,8 +146,6 @@ const TimelineActions: React.FC = () => {
const onPressFavourite = () => { const onPressFavourite = () => {
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'favourited', type: 'favourited',
@@ -164,8 +156,6 @@ const TimelineActions: React.FC = () => {
const onPressBookmark = () => { const onPressBookmark = () => {
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'bookmarked', type: 'bookmarked',

View File

@@ -24,6 +24,10 @@ const TimelineContent: React.FC<Props> = ({ notificationOwnToot = false, setSpoi
return ( return (
<View> <View>
{/* <CustomText
children={excludeMentions?.current.map(mention => mention.username).join(' - ')}
style={{ color: colors.secondary }}
/> */}
{status.spoiler_text?.length ? ( {status.spoiler_text?.length ? (
<> <>
<ParseHTML <ParseHTML

View File

@@ -5,7 +5,6 @@ export type HighlightedStatusContextType = {}
type StatusContextType = { type StatusContextType = {
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
status?: Mastodon.Status status?: Mastodon.Status

View File

@@ -10,7 +10,7 @@ import * as DropdownMenu from 'zeego/dropdown-menu'
import StatusContext from './Context' import StatusContext from './Context'
const TimelineHeaderAndroid: React.FC = () => { const TimelineHeaderAndroid: React.FC = () => {
const { queryKey, rootQueryKey, status, disableDetails, disableOnPress, rawContent } = const { queryKey, status, disableDetails, disableOnPress, rawContent } =
useContext(StatusContext) useContext(StatusContext)
if (Platform.OS !== 'android' || !status || disableDetails || disableOnPress) return null if (Platform.OS !== 'android' || !status || disableDetails || disableOnPress) return null
@@ -28,10 +28,9 @@ const TimelineHeaderAndroid: React.FC = () => {
type: 'status', type: 'status',
openChange, openChange,
account: status.account, account: status.account,
...(status && { status }), ...(status && { status })
queryKey
}) })
const mStatus = menuStatus({ status, queryKey, rootQueryKey }) const mStatus = menuStatus({ status, queryKey })
return ( return (
<View style={{ position: 'absolute', top: 0, right: 0 }}> <View style={{ position: 'absolute', top: 0, right: 0 }}>

View File

@@ -84,7 +84,6 @@ const HeaderConversation = ({ conversation }: Props) => {
mutation.mutate({ mutation.mutate({
type: 'deleteItem', type: 'deleteItem',
source: 'conversations', source: 'conversations',
queryKey,
id: conversation.id id: conversation.id
}) })
} }

View File

@@ -17,7 +17,7 @@ import HeaderSharedReplies from './HeaderShared/Replies'
import HeaderSharedVisibility from './HeaderShared/Visibility' import HeaderSharedVisibility from './HeaderShared/Visibility'
const TimelineHeaderDefault: React.FC = () => { const TimelineHeaderDefault: React.FC = () => {
const { queryKey, rootQueryKey, status, disableDetails, rawContent, isRemote } = const { queryKey, status, disableDetails, rawContent, isRemote } =
useContext(StatusContext) useContext(StatusContext)
if (!status) return null if (!status) return null
@@ -35,10 +35,9 @@ const TimelineHeaderDefault: React.FC = () => {
type: 'status', type: 'status',
openChange, openChange,
account: status.account, account: status.account,
...(status && { status }), ...(status && { status })
queryKey
}) })
const mStatus = menuStatus({ status, queryKey, rootQueryKey }) const mStatus = menuStatus({ status, queryKey })
return ( return (
<View style={{ flex: 1, flexDirection: 'row' }}> <View style={{ flex: 1, flexDirection: 'row' }}>

View File

@@ -41,8 +41,7 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
type: 'status', type: 'status',
openChange, openChange,
account: status?.account, account: status?.account,
...(status && { status }), ...(status && { status })
queryKey
}) })
const mStatus = menuStatus({ status, queryKey }) const mStatus = menuStatus({ status, queryKey })
const mInstance = menuInstance({ status, queryKey }) const mInstance = menuInstance({ status, queryKey })

View File

@@ -7,11 +7,11 @@ import { useTranslation } from 'react-i18next'
import StatusContext from '../Context' import StatusContext from '../Context'
const HeaderSharedApplication: React.FC = () => { const HeaderSharedApplication: React.FC = () => {
const { status } = useContext(StatusContext) const { status, isConversation } = useContext(StatusContext)
const { colors } = useTheme() const { colors } = useTheme()
const { t } = useTranslation('componentTimeline') const { t } = useTranslation('componentTimeline')
return status?.application?.name && status.application.name !== 'Web' ? ( return !isConversation && status?.application?.name && status.application.name !== 'Web' ? (
<CustomText <CustomText
fontStyle='S' fontStyle='S'
accessibilityRole='link' accessibilityRole='link'

View File

@@ -6,6 +6,7 @@ import { ParseEmojis } from '@components/Parse'
import RelativeTime from '@components/RelativeTime' import RelativeTime from '@components/RelativeTime'
import CustomText from '@components/Text' import CustomText from '@components/Text'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { useNavState } from '@utils/navigation/navigators'
import { import {
MutationVarsTimelineUpdateStatusProperty, MutationVarsTimelineUpdateStatusProperty,
useTimelineMutation useTimelineMutation
@@ -20,8 +21,7 @@ import { Pressable, View } from 'react-native'
import StatusContext from './Context' import StatusContext from './Context'
const TimelinePoll: React.FC = () => { const TimelinePoll: React.FC = () => {
const { queryKey, rootQueryKey, status, ownAccount, spoilerHidden, disableDetails } = const { queryKey, status, ownAccount, spoilerHidden, disableDetails } = useContext(StatusContext)
useContext(StatusContext)
if (!queryKey || !status || !status.poll) return null if (!queryKey || !status || !status.poll) return null
const poll = status.poll const poll = status.poll
@@ -30,17 +30,20 @@ const TimelinePoll: React.FC = () => {
const [allOptions, setAllOptions] = useState(new Array(status.poll.options.length).fill(false)) const [allOptions, setAllOptions] = useState(new Array(status.poll.options.length).fill(false))
const navigationState = useNavState()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutation = useTimelineMutation({ const mutation = useTimelineMutation({
onSuccess: ({ body }, params) => { onSuccess: ({ body }, params) => {
const theParams = params as MutationVarsTimelineUpdateStatusProperty const theParams = params as MutationVarsTimelineUpdateStatusProperty
queryClient.cancelQueries(queryKey) queryClient.cancelQueries(queryKey)
rootQueryKey && queryClient.cancelQueries(rootQueryKey)
haptics('Success') haptics('Success')
switch (theParams.payload.type) { switch (theParams.payload.type) {
case 'poll': case 'poll':
updateStatusProperty({ ...theParams, poll: body as unknown as Mastodon.Poll }) updateStatusProperty(
{ ...theParams, poll: body as unknown as Mastodon.Poll },
navigationState
)
break break
} }
}, },
@@ -74,8 +77,6 @@ const TimelinePoll: React.FC = () => {
onPress={() => onPress={() =>
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'poll', type: 'poll',
@@ -98,8 +99,6 @@ const TimelinePoll: React.FC = () => {
onPress={() => onPress={() =>
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'poll', type: 'poll',

View File

@@ -3,7 +3,7 @@ import { displayMessage } from '@components/Message'
import { useNavigation } from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { TabSharedStackParamList } from '@utils/navigation/navigators' import { TabSharedStackParamList, useNavState } from '@utils/navigation/navigators'
import { useAccountQuery } from '@utils/queryHooks/account' import { useAccountQuery } from '@utils/queryHooks/account'
import { import {
QueryKeyRelationship, QueryKeyRelationship,
@@ -12,7 +12,6 @@ import {
} from '@utils/queryHooks/relationship' } from '@utils/queryHooks/relationship'
import { import {
MutationVarsTimelineUpdateAccountProperty, MutationVarsTimelineUpdateAccountProperty,
QueryKeyTimeline,
useTimelineMutation useTimelineMutation
} from '@utils/queryHooks/timeline' } from '@utils/queryHooks/timeline'
import { useAccountStorage } from '@utils/storage/actions' import { useAccountStorage } from '@utils/storage/actions'
@@ -24,19 +23,16 @@ const menuAccount = ({
type, type,
openChange, openChange,
account, account,
status, status
queryKey,
rootQueryKey
}: { }: {
type: 'status' | 'account' // Where the action is coming from type: 'status' | 'account' // Where the action is coming from
openChange: boolean openChange: boolean
account?: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'> account?: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
status?: Mastodon.Status status?: Mastodon.Status
queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
}): ContextMenu[][] => { }): ContextMenu[][] => {
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>() useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
const navState = useNavState()
const { t } = useTranslation(['common', 'componentContextMenu', 'componentRelationship']) const { t } = useTranslation(['common', 'componentContextMenu', 'componentRelationship'])
const menus: ContextMenu[][] = [[]] const menus: ContextMenu[][] = [[]]
@@ -101,8 +97,9 @@ const menuAccount = ({
}) })
}, },
onSettled: () => { onSettled: () => {
queryKey && queryClient.invalidateQueries(queryKey) for (const key of navState) {
rootQueryKey && queryClient.invalidateQueries(rootQueryKey) queryClient.invalidateQueries(key)
}
} }
}) })
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id: actualAccount?.id }] const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id: actualAccount?.id }]
@@ -111,8 +108,10 @@ 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 = ['Timeline', { page: 'Following' }] queryClient.invalidateQueries({
queryClient.invalidateQueries({ queryKey, exact: false }) queryKey: ['Timeline', { page: 'Following' }],
exact: false
})
} }
}, },
onError: (err: any, { payload: { action } }) => { onError: (err: any, { payload: { action } }) => {
@@ -203,7 +202,6 @@ const menuAccount = ({
actualAccount && actualAccount &&
timelineMutation.mutate({ timelineMutation.mutate({
type: 'updateAccountProperty', type: 'updateAccountProperty',
queryKey,
id: actualAccount.id, id: actualAccount.id,
payload: { property: 'mute', currentValue: data?.muting } payload: { property: 'mute', currentValue: data?.muting }
}), }),
@@ -236,7 +234,6 @@ const menuAccount = ({
actualAccount && actualAccount &&
timelineMutation.mutate({ timelineMutation.mutate({
type: 'updateAccountProperty', type: 'updateAccountProperty',
queryKey,
id: actualAccount.id, id: actualAccount.id,
payload: { property: 'block', currentValue: data?.blocking } payload: { property: 'block', currentValue: data?.blocking }
}) })

View File

@@ -8,12 +8,10 @@ import parse from 'url-parse'
const menuInstance = ({ const menuInstance = ({
status, status,
queryKey, queryKey
rootQueryKey
}: { }: {
status?: Mastodon.Status status?: Mastodon.Status
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
}): ContextMenu[][] => { }): ContextMenu[][] => {
if (!status || !queryKey) return [] if (!status || !queryKey) return []
@@ -29,7 +27,6 @@ const menuInstance = ({
}) })
}) })
queryClient.invalidateQueries(queryKey) queryClient.invalidateQueries(queryKey)
rootQueryKey && queryClient.invalidateQueries(rootQueryKey)
} }
}) })
@@ -51,11 +48,7 @@ const menuInstance = ({
text: t('common:buttons.confirm'), text: t('common:buttons.confirm'),
style: 'destructive', style: 'destructive',
onPress: () => { onPress: () => {
mutation.mutate({ mutation.mutate({ type: 'domainBlock', domain: instance })
type: 'domainBlock',
queryKey,
domain: instance
})
} }
}, },
{ {

View File

@@ -4,7 +4,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import apiInstance from '@utils/api/instance' import apiInstance from '@utils/api/instance'
import { featureCheck } from '@utils/helpers/featureCheck' import { featureCheck } from '@utils/helpers/featureCheck'
import { RootStackParamList } from '@utils/navigation/navigators' import { RootStackParamList, useNavState } from '@utils/navigation/navigators'
import { import {
MutationVarsTimelineUpdateStatusProperty, MutationVarsTimelineUpdateStatusProperty,
QueryKeyTimeline, QueryKeyTimeline,
@@ -17,12 +17,10 @@ import { Alert } from 'react-native'
const menuStatus = ({ const menuStatus = ({
status, status,
queryKey, queryKey
rootQueryKey
}: { }: {
status?: Mastodon.Status status?: Mastodon.Status
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
}): ContextMenu[][] => { }): ContextMenu[][] => {
if (!status || !queryKey) return [] if (!status || !queryKey) return []
@@ -30,6 +28,8 @@ const menuStatus = ({
const { theme } = useTheme() const { theme } = useTheme()
const { t } = useTranslation(['common', 'componentContextMenu']) const { t } = useTranslation(['common', 'componentContextMenu'])
const navigationState = useNavState()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const mutation = useTimelineMutation({ const mutation = useTimelineMutation({
onMutate: true, onMutate: true,
@@ -90,8 +90,7 @@ const menuStatus = ({
spoiler_text: res.body.spoiler_text spoiler_text: res.body.spoiler_text
}, },
...(replyToStatus && { replyToStatus }), ...(replyToStatus && { replyToStatus }),
queryKey, navigationState
rootQueryKey
}) })
}) })
}, },
@@ -122,18 +121,13 @@ const menuStatus = ({
}).then(res => res.body) }).then(res => res.body)
} }
mutation mutation
.mutateAsync({ .mutateAsync({ type: 'deleteItem', source: 'statuses', id: status.id })
type: 'deleteItem',
source: 'statuses',
queryKey,
id: status.id
})
.then(res => { .then(res => {
navigation.navigate('Screen-Compose', { navigation.navigate('Screen-Compose', {
type: 'deleteEdit', type: 'deleteEdit',
incomingStatus: res.body as Mastodon.Status, incomingStatus: res.body as Mastodon.Status,
...(replyToStatus && { replyToStatus }), ...(replyToStatus && { replyToStatus }),
queryKey navigationState
}) })
}) })
} }
@@ -162,13 +156,7 @@ const menuStatus = ({
text: t('common:buttons.confirm'), text: t('common:buttons.confirm'),
style: 'destructive', style: 'destructive',
onPress: async () => { onPress: async () => {
mutation.mutate({ mutation.mutate({ type: 'deleteItem', source: 'statuses', id: status.id })
type: 'deleteItem',
source: 'statuses',
queryKey,
rootQueryKey,
id: status.id
})
} }
}, },
{ {
@@ -193,8 +181,6 @@ const menuStatus = ({
onSelect: () => onSelect: () =>
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'muted', type: 'muted',
@@ -218,8 +204,6 @@ const menuStatus = ({
// Also note that reblogs cannot be pinned. // Also note that reblogs cannot be pinned.
mutation.mutate({ mutation.mutate({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey,
rootQueryKey,
status, status,
payload: { payload: {
type: 'pinned', type: 'pinned',

View File

@@ -339,24 +339,25 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
StoreReview?.isAvailableAsync() StoreReview?.isAvailableAsync()
.then(() => StoreReview.requestReview()) .then(() => StoreReview.requestReview())
.catch(() => {}) .catch(() => {})
setGlobalStorage('app.count_till_store_review', (currentCount || 0) + 1)
} else { } else {
setGlobalStorage('app.count_till_store_review', (currentCount || 0) + 1) setGlobalStorage('app.count_till_store_review', (currentCount || 0) + 1)
} }
} }
switch (params?.type) { switch (params?.type) {
case 'edit': case undefined:
mutateTimeline.mutate({ case 'conversation':
type: 'editItem', queryClient.invalidateQueries({
queryKey: params.queryKey, queryKey: ['Timeline', { page: 'Following' }],
rootQueryKey: params.rootQueryKey, exact: false
status: res
}) })
break break
case 'edit': // doesn't work
case 'deleteEdit': case 'deleteEdit':
case 'reply': case 'reply':
if (params?.queryKey && params.queryKey[1].page === 'Toot') { for (const navState of params.navigationState) {
queryClient.invalidateQueries(params.queryKey) navState && queryClient.invalidateQueries(navState)
} }
break break
} }

View File

@@ -5,7 +5,6 @@ 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'
import { TabLocalStackParamList } from '@utils/navigation/navigators' import { TabLocalStackParamList } from '@utils/navigation/navigators'
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 { setAccountStorage, useAccountStorage } from '@utils/storage/actions' import { setAccountStorage, useAccountStorage } from '@utils/storage/actions'
@@ -176,6 +175,7 @@ const Root: React.FC<NativeStackScreenProps<TabLocalStackParamList, 'Tab-Local-R
/> />
) )
}) })
navigation.setParams({ queryKey: queryKey })
}, [mode, queryKey[1], pageLocal, lists]) }, [mode, queryKey[1], pageLocal, lists])
return ( return (

View File

@@ -1,10 +1,17 @@
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 { TabMeStackParamList } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react' import React, { useEffect } from 'react'
const TabMeBookmarks = () => { const TabMeBookmarks: React.FC<NativeStackScreenProps<TabMeStackParamList, 'Tab-Me-Bookmarks'>> = ({
navigation
}) => {
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }] const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }]
useEffect(() => {
navigation.setParams({ queryKey: queryKey })
}, [])
return ( return (
<Timeline <Timeline

View File

@@ -1,10 +1,17 @@
import Timeline from '@components/Timeline' import Timeline from '@components/Timeline'
import TimelineConversation from '@components/Timeline/Conversation' import TimelineConversation from '@components/Timeline/Conversation'
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import { TabMeStackParamList } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react' import React, { useEffect } from 'react'
const TabMeConversations = () => { const TabMeConversations: React.FC<
NativeStackScreenProps<TabMeStackParamList, 'Tab-Me-Conversations'>
> = ({ navigation }) => {
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Conversations' }] const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Conversations' }]
useEffect(() => {
navigation.setParams({ queryKey: queryKey })
}, [])
return ( return (
<Timeline <Timeline

View File

@@ -1,10 +1,17 @@
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 { TabMeStackParamList } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react' import React, { useEffect } from 'react'
const TabMeFavourites = () => { const TabMeFavourites: React.FC<
NativeStackScreenProps<TabMeStackParamList, 'Tab-Me-Favourites'>
> = ({ navigation }) => {
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }] const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }]
useEffect(() => {
navigation.setParams({ queryKey: queryKey })
}, [])
return ( return (
<Timeline <Timeline

View File

@@ -23,13 +23,13 @@ const TabMeListList: React.FC<TabMeStackScreenProps<'Tab-Me-List-List'>> = ({ na
return ( return (
<MenuContainer> <MenuContainer>
{data?.map((params, index) => ( {data?.map((list, index) => (
<MenuRow <MenuRow
key={index} key={index}
iconFront='List' iconFront='List'
iconBack='ChevronRight' iconBack='ChevronRight'
title={params.title} title={list.title}
onPress={() => navigation.navigate('Tab-Me-List', params)} onPress={() => navigation.navigate('Tab-Me-List', { list })}
/> />
))} ))}
</MenuContainer> </MenuContainer>

View File

@@ -2,8 +2,9 @@ import Icon from '@components/Icon'
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 { NativeStackScreenProps } from '@react-navigation/native-stack'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { TabMeStackScreenProps } from '@utils/navigation/navigators' import { TabMeStackParamList } from '@utils/navigation/navigators'
import { QueryKeyLists, useListsMutation } from '@utils/queryHooks/lists' import { QueryKeyLists, useListsMutation } from '@utils/queryHooks/lists'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
@@ -13,13 +14,16 @@ import { useTranslation } from 'react-i18next'
import * as DropdownMenu from 'zeego/dropdown-menu' import * as DropdownMenu from 'zeego/dropdown-menu'
import { menuListAccounts, menuListDelete, menuListEdit } from './menus' import { menuListAccounts, menuListDelete, menuListEdit } from './menus'
const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({ const TabMeList: React.FC<NativeStackScreenProps<TabMeStackParamList, 'Tab-Me-List'>> = ({
navigation, navigation,
route: { key, params } route: {
key,
params: { list }
}
}) => { }) => {
const { colors } = useTheme() const { colors } = useTheme()
const { t } = useTranslation(['common', 'screenTabs']) const { t } = useTranslation(['common', 'screenTabs'])
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list: params.id }] const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list: list.id }]
const queryKeyLists: QueryKeyLists = ['Lists'] const queryKeyLists: QueryKeyLists = ['Lists']
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -39,9 +43,9 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
}) })
useEffect(() => { useEffect(() => {
const listAccounts = menuListAccounts({ params }) const listAccounts = menuListAccounts({ list })
const listEdit = menuListEdit({ params, key }) const listEdit = menuListEdit({ list, key })
const listDelete = menuListDelete({ params, mutation }) const listDelete = menuListDelete({ list, mutation })
navigation.setOptions({ navigation.setOptions({
headerRight: () => ( headerRight: () => (
@@ -67,7 +71,8 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
</DropdownMenu.Root> </DropdownMenu.Root>
) )
}) })
}, [params]) navigation.setParams({ queryKey })
}, [list])
return ( return (
<Timeline <Timeline

View File

@@ -3,19 +3,19 @@ import navigationRef from '@utils/navigation/navigationRef'
import i18next from 'i18next' import i18next from 'i18next'
import { Alert } from 'react-native' import { Alert } from 'react-native'
export const menuListAccounts = ({ params }: { params: Mastodon.List }) => ({ export const menuListAccounts = ({ list }: { list: Mastodon.List }) => ({
key: 'list-accounts', key: 'list-accounts',
onSelect: () => navigationRef.navigate<any>('Tab-Me-List-Accounts', params), onSelect: () => navigationRef.navigate<any>('Tab-Me-List-Accounts', list),
title: i18next.t('screenTabs:me.listAccounts.heading'), title: i18next.t('screenTabs:me.listAccounts.heading'),
icon: 'person.crop.circle.fill.badge.checkmark' icon: 'person.crop.circle.fill.badge.checkmark'
}) })
export const menuListEdit = ({ params, key }: { params: Mastodon.List; key: string }) => ({ export const menuListEdit = ({ list, key }: { list: Mastodon.List; key: string }) => ({
key: 'list-edit', key: 'list-edit',
onSelect: () => onSelect: () =>
navigationRef.navigate<any>('Tab-Me-List-Edit', { navigationRef.navigate<any>('Tab-Me-List-Edit', {
type: 'edit', type: 'edit',
payload: params, payload: list,
key key
}), }),
title: i18next.t('screenTabs:me.listEdit.heading'), title: i18next.t('screenTabs:me.listEdit.heading'),
@@ -23,22 +23,22 @@ export const menuListEdit = ({ params, key }: { params: Mastodon.List; key: stri
}) })
export const menuListDelete = ({ export const menuListDelete = ({
params, list,
mutation mutation
}: { }: {
params: Mastodon.List list: Mastodon.List
mutation: UseMutationResult<any, any, unknown, unknown> mutation: UseMutationResult<any, any, unknown, unknown>
}) => ({ }) => ({
key: 'list-delete', key: 'list-delete',
onSelect: () => onSelect: () =>
Alert.alert( Alert.alert(
i18next.t('screenTabs:me.listDelete.confirm.title', { list: params.title.slice(0, 20) }), i18next.t('screenTabs:me.listDelete.confirm.title', { list: list.title.slice(0, 20) }),
i18next.t('screenTabs:me.listDelete.confirm.message'), i18next.t('screenTabs:me.listDelete.confirm.message'),
[ [
{ {
text: i18next.t('common:buttons.delete'), text: i18next.t('common:buttons.delete'),
style: 'destructive', style: 'destructive',
onPress: () => mutation.mutate({ type: 'delete', payload: params }) onPress: () => mutation.mutate({ type: 'delete', payload: list })
}, },
{ text: i18next.t('common:buttons.cancel') } { text: i18next.t('common:buttons.cancel') }
] ]

View File

@@ -1,19 +1,38 @@
import { HeaderRight } from '@components/Header' import { HeaderRight } from '@components/Header'
import Timeline from '@components/Timeline' import Timeline from '@components/Timeline'
import TimelineNotifications from '@components/Timeline/Notifications' import TimelineNotifications from '@components/Timeline/Notifications'
import { createNativeStackNavigator } from '@react-navigation/native-stack' import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'
import { ScreenTabsScreenProps, TabNotificationsStackParamList } from '@utils/navigation/navigators' import { TabNotificationsStackParamList } from '@utils/navigation/navigators'
import usePopToTop from '@utils/navigation/usePopToTop' import usePopToTop from '@utils/navigation/usePopToTop'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react' import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import TabShared from '../Shared' import TabShared from '../Shared'
import TabNotificationsFilters from './Filters' import TabNotificationsFilters from './Filters'
const Stack = createNativeStackNavigator<TabNotificationsStackParamList>() const Stack = createNativeStackNavigator<TabNotificationsStackParamList>()
const Root = () => { const Root: React.FC<
NativeStackScreenProps<TabNotificationsStackParamList, 'Tab-Notifications-Root'>
> = ({ navigation }) => {
const { t } = useTranslation('screenTabs')
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }] const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
useEffect(() => {
navigation.setOptions({
title: t('tabs.notifications.name'),
headerRight: () => (
<HeaderRight
accessibilityLabel={t('notifications.filters.accessibilityLabel')}
accessibilityHint={t('notifications.filters.accessibilityHint')}
content='Filter'
onPress={() => navigation.navigate('Tab-Notifications-Filters')}
/>
)
})
navigation.setParams({ queryKey })
}, [])
return ( return (
<Timeline <Timeline
queryKey={queryKey} queryKey={queryKey}
@@ -24,30 +43,12 @@ const Root = () => {
) )
} }
const TabNotifications = ({ navigation }: ScreenTabsScreenProps<'Tab-Notifications'>) => { const TabNotifications: React.FC = () => {
const { t } = useTranslation('screenTabs')
usePopToTop() usePopToTop()
return ( return (
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}> <Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
<Stack.Screen <Stack.Screen name='Tab-Notifications-Root' component={Root} />
name='Tab-Notifications-Root'
component={Root}
options={{
title: t('tabs.notifications.name'),
headerRight: () => (
<HeaderRight
accessibilityLabel={t('notifications.filters.accessibilityLabel')}
accessibilityHint={t('notifications.filters.accessibilityHint')}
content='Filter'
onPress={() =>
navigation.navigate('Tab-Notifications', { screen: 'Tab-Notifications-Filters' })
}
/>
)
}}
/>
<Stack.Screen <Stack.Screen
name='Tab-Notifications-Filters' name='Tab-Notifications-Filters'
component={TabNotificationsFilters} component={TabNotificationsFilters}

View File

@@ -2,7 +2,8 @@ import { HeaderRight } from '@components/Header'
import Timeline from '@components/Timeline' import Timeline from '@components/Timeline'
import TimelineDefault from '@components/Timeline/Default' import TimelineDefault from '@components/Timeline/Default'
import SegmentedControl from '@react-native-community/segmented-control' import SegmentedControl from '@react-native-community/segmented-control'
import { NativeStackScreenProps } from '@react-navigation/native-stack' import { useNavigation } from '@react-navigation/native'
import { NativeStackNavigationProp, NativeStackScreenProps } from '@react-navigation/native-stack'
import { TabPublicStackParamList } from '@utils/navigation/navigators' import { TabPublicStackParamList } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { getGlobalStorage, setGlobalStorage } from '@utils/storage/actions' import { getGlobalStorage, setGlobalStorage } from '@utils/storage/actions'
@@ -14,7 +15,12 @@ import { Dimensions } from 'react-native'
import { SceneMap, TabView } from 'react-native-tab-view' import { SceneMap, TabView } from 'react-native-tab-view'
const Route = ({ route: { key: page } }: { route: any }) => { const Route = ({ route: { key: page } }: { route: any }) => {
const navigation =
useNavigation<NativeStackNavigationProp<TabPublicStackParamList, 'Tab-Public-Root'>>()
const queryKey: QueryKeyTimeline = ['Timeline', { page }] const queryKey: QueryKeyTimeline = ['Timeline', { page }]
useEffect(() => {
navigation.setParams({ queryKey })
}, [])
return ( return (
<Timeline <Timeline
queryKey={queryKey} queryKey={queryKey}

View File

@@ -47,10 +47,22 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
} }
]) ])
} }
} },
onError: () => navigation.goBack()
} }
}) })
const queryClient = useQueryClient()
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
'Timeline',
{
page: 'Account',
id: account._remote ? data?.id : account.id,
exclude_reblogs: true,
only_media: false
}
])
const mShare = menuShare({ type: 'account', url: data?.url }) const mShare = menuShare({ type: 'account', url: data?.url })
const mAccount = menuAccount({ type: 'account', openChange: true, account: data }) const mAccount = menuAccount({ type: 'account', openChange: true, account: data })
useEffect(() => { useEffect(() => {
@@ -97,19 +109,12 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
} }
}) })
}, [mAccount]) }, [mAccount])
useEffect(() => {
navigation.setParams({ queryKey })
}, [queryKey[1]])
const scrollY = useSharedValue(0) const scrollY = useSharedValue(0)
const queryClient = useQueryClient()
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
'Timeline',
{
page: 'Account',
id: account._remote ? data?.id : account.id,
exclude_reblogs: true,
only_media: false
}
])
const page = queryKey[1] const page = queryKey[1]
const [segment, setSegment] = useState<number>(0) const [segment, setSegment] = useState<number>(0)

View File

@@ -41,6 +41,7 @@ const TabSharedAttachments: React.FC<TabSharedStackScreenProps<'Tab-Shared-Attac
</CustomText> </CustomText>
) )
}) })
navigation.setParams({ queryKey })
}, []) }, [])
const queryKey: QueryKeyTimeline = [ const queryKey: QueryKeyTimeline = [

View File

@@ -17,15 +17,16 @@ const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
params: { hashtag } params: { hashtag }
} }
}) => { }) => {
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />, headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />,
title: `#${decodeURIComponent(hashtag)}` title: `#${decodeURIComponent(hashtag)}`
}) })
navigation.setParams({ queryKey: queryKey })
}, []) }, [])
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
const { t } = useTranslation(['common', 'screenTabs']) const { t } = useTranslation(['common', 'screenTabs'])
const canFollowTags = featureCheck('follow_tags') const canFollowTags = featureCheck('follow_tags')

View File

@@ -50,6 +50,17 @@ const ContentView: React.FC<{
const changesContent = withoutBoundary const changesContent = withoutBoundary
? diffChars(removeHTML(prevItem?.content || item.content), removeHTML(item.content)) ? diffChars(removeHTML(prevItem?.content || item.content), removeHTML(item.content))
: diffWords(removeHTML(prevItem?.content || item.content), removeHTML(item.content)) : diffWords(removeHTML(prevItem?.content || item.content), removeHTML(item.content))
const changesPoll = item.poll
? item.poll.options.map((option, index) =>
withoutBoundary
? prevItem?.poll?.options[index].title
? diffChars(prevItem?.poll?.options[index].title, option.title)
: undefined
: prevItem?.poll?.options[index].title
? diffWords(prevItem?.poll?.options[index].title, option.title)
: undefined
)
: null
return ( return (
// @ts-ignore // @ts-ignore
@@ -92,12 +103,28 @@ const ContentView: React.FC<{
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M, paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.S marginRight: StyleConstants.Spacing.S
}} }}
name='Circle' name={item.poll?.multiple ? 'Square' : 'Circle'}
size={StyleConstants.Font.Size.M} size={StyleConstants.Font.Size.M}
color={colors.disabled} color={
prevItem?.poll?.multiple !== item.poll?.multiple ? colors.red : colors.disabled
}
/> />
<CustomText style={{ flex: 1 }}> <CustomText style={{ flex: 1, color: colors.primaryDefault }}>
{changesPoll?.[index]?.length ? (
changesPoll[index]?.map(({ value, added, removed }, index) => (
<ParseEmojis
key={index}
content={value}
emojis={item.poll?.emojis}
style={{
color: added ? colors.green : removed ? colors.red : undefined,
textDecorationLine: removed ? 'line-through' : undefined
}}
/>
))
) : (
<ParseEmojis content={option.title} emojis={item.poll?.emojis} /> <ParseEmojis content={option.title} emojis={item.poll?.emojis} />
)}
</CustomText> </CustomText>
</View> </View>
</View> </View>

View File

@@ -21,13 +21,17 @@ import { Path, Svg } from 'react-native-svg'
const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
navigation, navigation,
route: { route: {
params: { toot, rootQueryKey } params: { toot }
} }
}) => { }) => {
const { colors } = useTheme() const { colors } = useTheme()
const { t } = useTranslation(['componentTimeline', 'screenTabs']) const { t } = useTranslation(['componentTimeline', 'screenTabs'])
const [hasRemoteContent, setHasRemoteContent] = useState<boolean>(false) const [hasRemoteContent, setHasRemoteContent] = useState<boolean>(false)
const queryKey: { local: QueryKeyTimeline; remote: QueryKeyTimeline } = {
local: ['Timeline', { page: 'Toot', toot: toot.id, remote: false }],
remote: ['Timeline', { page: 'Toot', toot: toot.id, remote: true }]
}
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@@ -61,6 +65,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
), ),
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} /> headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
}) })
navigation.setParams({ toot, queryKey: toot._remote ? queryKey.remote : queryKey.local })
}, [hasRemoteContent]) }, [hasRemoteContent])
const flRef = useRef<FlatList>(null) const flRef = useRef<FlatList>(null)
@@ -71,10 +76,6 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
{ ...toot, _level: 0, key: 'cached' } { ...toot, _level: 0, key: 'cached' }
]) ])
const highlightIndex = useRef<number>(0) const highlightIndex = useRef<number>(0)
const queryKey: { local: QueryKeyTimeline; remote: QueryKeyTimeline } = {
local: ['Timeline', { page: 'Toot', toot: toot.id, remote: false }],
remote: ['Timeline', { page: 'Toot', toot: toot.id, remote: true }]
}
const queryLocal = useQuery( const queryLocal = useQuery(
queryKey.local, queryKey.local,
async () => { async () => {
@@ -280,7 +281,6 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
<TimelineDefault <TimelineDefault
item={item} item={item}
queryKey={item._remote ? queryKey.remote : queryKey.local} queryKey={item._remote ? queryKey.remote : queryKey.local}
rootQueryKey={rootQueryKey}
highlighted={toot.id === item.id || item.id === 'cached'} highlighted={toot.id === item.id || item.id === 'cached'}
isConversation={toot.id !== item.id && item.id !== 'cached'} isConversation={toot.id !== item.id && item.id !== 'cached'}
/> />
@@ -368,7 +368,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
}) })
: null} : null}
{/* <CustomText {/* <CustomText
children={data?.body[index - 1]?._level} children={finalData.current[index - 1]?._level}
style={{ position: 'absolute', top: 4, left: 4, color: colors.red }} style={{ position: 'absolute', top: 4, left: 4, color: colors.red }}
/> />
<CustomText <CustomText
@@ -376,7 +376,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
style={{ position: 'absolute', top: 20, left: 4, color: colors.yellow }} style={{ position: 'absolute', top: 20, left: 4, color: colors.yellow }}
/> />
<CustomText <CustomText
children={data?.body[index + 1]?._level} children={finalData.current[index + 1]?._level}
style={{ position: 'absolute', top: 36, left: 4, color: colors.green }} style={{ position: 'absolute', top: 36, left: 4, color: colors.green }}
/> */} /> */}
</View> </View>

View File

@@ -1,10 +1,19 @@
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs' import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
import { NavigatorScreenParams } from '@react-navigation/native' import { NavigatorScreenParams, useNavigationState } from '@react-navigation/native'
import { NativeStackScreenProps } from '@react-navigation/native-stack' import { NativeStackScreenProps } from '@react-navigation/native-stack'
import { StackNavigationProp } from '@react-navigation/stack' import { StackNavigationProp } from '@react-navigation/stack'
import { ComposeState } from '@screens/Compose/utils/types' import { ComposeState } from '@screens/Compose/utils/types'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
export const useNavState = () =>
useNavigationState(state =>
state.routes.map(
route => (route.params as { queryKey?: QueryKeyTimeline } | undefined)?.queryKey
)
)
.filter(key => key?.[0] === 'Timeline')
.reverse()
export type RootStackParamList = { export type RootStackParamList = {
'Screen-Tabs': NavigatorScreenParams<ScreenTabsStackParamList> 'Screen-Tabs': NavigatorScreenParams<ScreenTabsStackParamList>
'Screen-Announcements': { showAll: boolean } 'Screen-Announcements': { showAll: boolean }
@@ -13,20 +22,19 @@ export type RootStackParamList = {
type: 'edit' type: 'edit'
incomingStatus: Mastodon.Status incomingStatus: Mastodon.Status
replyToStatus?: Mastodon.Status replyToStatus?: Mastodon.Status
queryKey?: QueryKeyTimeline navigationState: (QueryKeyTimeline | undefined)[]
rootQueryKey?: QueryKeyTimeline
} }
| { | {
type: 'deleteEdit' type: 'deleteEdit'
incomingStatus: Mastodon.Status incomingStatus: Mastodon.Status
replyToStatus?: Mastodon.Status replyToStatus?: Mastodon.Status
queryKey?: QueryKeyTimeline navigationState: (QueryKeyTimeline | undefined)[]
} }
| { | {
type: 'reply' type: 'reply'
incomingStatus: Mastodon.Status incomingStatus: Mastodon.Status
accts: Mastodon.Account['acct'][] accts: Mastodon.Account['acct'][]
queryKey?: QueryKeyTimeline navigationState: (QueryKeyTimeline | undefined)[]
} }
| { | {
type: 'conversation' type: 'conversation'
@@ -84,28 +92,20 @@ export type ScreenTabsScreenProps<T extends keyof ScreenTabsStackParamList> = Bo
export type TabSharedStackParamList = { export type TabSharedStackParamList = {
'Tab-Shared-Account': { 'Tab-Shared-Account': {
account: Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url' | '_remote'> account: Partial<Pick<Mastodon.Account, 'id' | 'url' | '_remote'>> &
} Pick<Mastodon.Account, 'acct' | 'url'>
'Tab-Shared-Account-In-Lists': { queryKey?: QueryKeyTimeline
account: Pick<Mastodon.Account, 'id' | 'username'>
}
'Tab-Shared-Attachments': { account: Mastodon.Account }
'Tab-Shared-Hashtag': {
hashtag: Mastodon.Tag['name']
}
'Tab-Shared-History': {
status: Mastodon.Status
detectedLanguage: string
} }
'Tab-Shared-Account-In-Lists': { account: Pick<Mastodon.Account, 'id' | 'username'> }
'Tab-Shared-Attachments': { account: Mastodon.Account; queryKey?: QueryKeyTimeline }
'Tab-Shared-Hashtag': { hashtag: Mastodon.Tag['name']; queryKey?: QueryKeyTimeline }
'Tab-Shared-History': { status: Mastodon.Status; detectedLanguage: string }
'Tab-Shared-Report': { 'Tab-Shared-Report': {
account: Pick<Mastodon.Account, 'id' | 'acct' | 'username' | 'url'> account: Pick<Mastodon.Account, 'id' | 'acct' | 'username' | 'url'>
status?: Pick<Mastodon.Status, 'id' | '_remote' | 'uri'> status?: Pick<Mastodon.Status, 'id' | '_remote' | 'uri'>
} }
'Tab-Shared-Search': undefined 'Tab-Shared-Search': undefined
'Tab-Shared-Toot': { 'Tab-Shared-Toot': { toot: Mastodon.Status; queryKey?: QueryKeyTimeline }
toot: Mastodon.Status
rootQueryKey?: QueryKeyTimeline
}
'Tab-Shared-Users': 'Tab-Shared-Users':
| { | {
reference: 'accounts' reference: 'accounts'
@@ -124,15 +124,15 @@ export type TabSharedStackScreenProps<T extends keyof TabSharedStackParamList> =
NativeStackScreenProps<TabSharedStackParamList, T> NativeStackScreenProps<TabSharedStackParamList, T>
export type TabLocalStackParamList = { export type TabLocalStackParamList = {
'Tab-Local-Root': undefined 'Tab-Local-Root': { queryKey?: QueryKeyTimeline }
} & TabSharedStackParamList } & TabSharedStackParamList
export type TabPublicStackParamList = { export type TabPublicStackParamList = {
'Tab-Public-Root': undefined 'Tab-Public-Root': { queryKey?: QueryKeyTimeline }
} & TabSharedStackParamList } & TabSharedStackParamList
export type TabNotificationsStackParamList = { export type TabNotificationsStackParamList = {
'Tab-Notifications-Root': undefined 'Tab-Notifications-Root': { queryKey?: QueryKeyTimeline }
'Tab-Notifications-Filters': undefined 'Tab-Notifications-Filters': undefined
} & TabSharedStackParamList } & TabSharedStackParamList
export type TabNotificationsStackScreenProps<T extends keyof TabNotificationsStackParamList> = export type TabNotificationsStackScreenProps<T extends keyof TabNotificationsStackParamList> =
@@ -140,11 +140,11 @@ export type TabNotificationsStackScreenProps<T extends keyof TabNotificationsSta
export type TabMeStackParamList = { export type TabMeStackParamList = {
'Tab-Me-Root': undefined 'Tab-Me-Root': undefined
'Tab-Me-Bookmarks': undefined 'Tab-Me-Bookmarks': { queryKey?: QueryKeyTimeline }
'Tab-Me-Conversations': undefined 'Tab-Me-Conversations': { queryKey?: QueryKeyTimeline }
'Tab-Me-Favourites': undefined 'Tab-Me-Favourites': { queryKey?: QueryKeyTimeline }
'Tab-Me-FollowedTags': undefined 'Tab-Me-FollowedTags': undefined
'Tab-Me-List': Mastodon.List 'Tab-Me-List': { list: Mastodon.List; queryKey?: QueryKeyTimeline }
'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':
| { | {

View File

@@ -4,9 +4,7 @@ import navigationRef from '@utils/navigation/navigationRef'
const pushUseNavigate = (id?: Mastodon.Notification['id']) => { const pushUseNavigate = (id?: Mastodon.Notification['id']) => {
navigationRef.navigate('Screen-Tabs', { navigationRef.navigate('Screen-Tabs', {
screen: 'Tab-Notifications', screen: 'Tab-Notifications',
params: { params: { screen: 'Tab-Notifications-Root', params: {} }
screen: 'Tab-Notifications-Root'
}
}) })
if (!id) { if (!id) {

View File

@@ -9,13 +9,13 @@ import {
import { PagedResponse } from '@utils/api/helpers' import { PagedResponse } from '@utils/api/helpers'
import apiInstance from '@utils/api/instance' import apiInstance from '@utils/api/instance'
import { featureCheck } from '@utils/helpers/featureCheck' import { featureCheck } from '@utils/helpers/featureCheck'
import { useNavState } from '@utils/navigation/navigators'
import { queryClient } from '@utils/queryHooks' import { queryClient } from '@utils/queryHooks'
import { getAccountStorage } from '@utils/storage/actions' import { getAccountStorage } from '@utils/storage/actions'
import { AxiosError } from 'axios' import { AxiosError } from 'axios'
import { uniqBy } from 'lodash' import { uniqBy } from 'lodash'
import { searchLocalStatus } from './search' import { searchLocalStatus } from './search'
import deleteItem from './timeline/deleteItem' import deleteItem from './timeline/deleteItem'
import editItem from './timeline/editItem'
import updateStatusProperty from './timeline/updateStatusProperty' import updateStatusProperty from './timeline/updateStatusProperty'
export type QueryKeyTimeline = [ export type QueryKeyTimeline = [
@@ -248,8 +248,6 @@ enum MapPropertyToUrl {
export type MutationVarsTimelineUpdateStatusProperty = { export type MutationVarsTimelineUpdateStatusProperty = {
// This is status in general, including "status" inside conversation and notification // This is status in general, including "status" inside conversation and notification
type: 'updateStatusProperty' type: 'updateStatusProperty'
queryKey: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
status: Mastodon.Status status: Mastodon.Status
payload: payload:
| { | {
@@ -279,7 +277,6 @@ export type MutationVarsTimelineUpdateStatusProperty = {
export type MutationVarsTimelineUpdateAccountProperty = { export type MutationVarsTimelineUpdateAccountProperty = {
// This is status in general, including "status" inside conversation and notification // This is status in general, including "status" inside conversation and notification
type: 'updateAccountProperty' type: 'updateAccountProperty'
queryKey?: QueryKeyTimeline
id: Mastodon.Account['id'] id: Mastodon.Account['id']
payload: { payload: {
property: 'mute' | 'block' | 'reports' property: 'mute' | 'block' | 'reports'
@@ -287,34 +284,22 @@ export type MutationVarsTimelineUpdateAccountProperty = {
} }
} }
export type MutationVarsTimelineEditItem = {
// This is for editing status
type: 'editItem'
queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
status: Mastodon.Status
}
export type MutationVarsTimelineDeleteItem = { export type MutationVarsTimelineDeleteItem = {
// This is for deleting status and conversation // This is for deleting status and conversation
type: 'deleteItem' type: 'deleteItem'
source: 'statuses' | 'conversations' source: 'statuses' | 'conversations'
queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline
id: Mastodon.Status['id'] id: Mastodon.Status['id']
} }
export type MutationVarsTimelineDomainBlock = { export type MutationVarsTimelineDomainBlock = {
// This is for deleting status and conversation // This is for deleting status and conversation
type: 'domainBlock' type: 'domainBlock'
queryKey: QueryKeyTimeline
domain: string domain: string
} }
export type MutationVarsTimeline = export type MutationVarsTimeline =
| MutationVarsTimelineUpdateStatusProperty | MutationVarsTimelineUpdateStatusProperty
| MutationVarsTimelineUpdateAccountProperty | MutationVarsTimelineUpdateAccountProperty
| MutationVarsTimelineEditItem
| MutationVarsTimelineDeleteItem | MutationVarsTimelineDeleteItem
| MutationVarsTimelineDomainBlock | MutationVarsTimelineDomainBlock
@@ -380,8 +365,6 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
} }
}) })
} }
case 'editItem':
return { body: params.status }
case 'deleteItem': case 'deleteItem':
return apiInstance<Mastodon.Conversation>({ return apiInstance<Mastodon.Conversation>({
method: 'delete', method: 'delete',
@@ -415,6 +398,8 @@ const useTimelineMutation = ({
onSettled?: MutationOptionsTimeline['onSettled'] onSettled?: MutationOptionsTimeline['onSettled']
onSuccess?: MutationOptionsTimeline['onSuccess'] onSuccess?: MutationOptionsTimeline['onSuccess']
}) => { }) => {
const navigationState = useNavState()
return useMutation< return useMutation<
{ body: Mastodon.Conversation | Mastodon.Notification | Mastodon.Status }, { body: Mastodon.Conversation | Mastodon.Notification | Mastodon.Status },
AxiosError, AxiosError,
@@ -425,19 +410,16 @@ const useTimelineMutation = ({
onSuccess, onSuccess,
...(onMutate && { ...(onMutate && {
onMutate: params => { onMutate: params => {
queryClient.cancelQueries(params.queryKey) queryClient.cancelQueries(navigationState[0])
const oldData = params.queryKey && queryClient.getQueryData(params.queryKey) const oldData = navigationState[0] && queryClient.getQueryData(navigationState[0])
haptics('Light') haptics('Light')
switch (params.type) { switch (params.type) {
case 'updateStatusProperty': case 'updateStatusProperty':
updateStatusProperty(params) updateStatusProperty(params, navigationState)
break
case 'editItem':
editItem(params)
break break
case 'deleteItem': case 'deleteItem':
deleteItem(params) deleteItem(params, navigationState)
break break
} }
return oldData return oldData

View File

@@ -1,29 +1,31 @@
import { InfiniteData } from '@tanstack/react-query' import { InfiniteData } from '@tanstack/react-query'
import { queryClient } from '@utils/queryHooks' import { queryClient } from '@utils/queryHooks'
import { MutationVarsTimelineDeleteItem } from '../timeline' import { MutationVarsTimelineDeleteItem, QueryKeyTimeline, TimelineData } from '../timeline'
const deleteItem = ({ queryKey, rootQueryKey, id }: MutationVarsTimelineDeleteItem) => { const deleteItem = (
queryKey && { id }: MutationVarsTimelineDeleteItem,
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => { navigationState: (QueryKeyTimeline | undefined)[]
) => {
for (const key of navigationState) {
if (!key) continue
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(key, old => {
if (old) { if (old) {
let foundToot: boolean = false
old.pages = old.pages.map(page => { old.pages = old.pages.map(page => {
page.body = page.body.filter((item: Mastodon.Status) => item.id !== id) if (foundToot) return page
page.body = (page.body as Mastodon.Status[]).filter(
(item: Mastodon.Status) => item.id !== id
)
return page return page
}) })
return old
} }
})
rootQueryKey &&
queryClient.setQueryData<InfiniteData<any> | undefined>(rootQueryKey, old => {
if (old) {
old.pages = old.pages.map(page => {
page.body = page.body.filter((item: Mastodon.Status) => item.id !== id)
return page
})
return old return old
}
}) })
} }
}
export default deleteItem export default deleteItem

View File

@@ -1,39 +0,0 @@
import { InfiniteData } from '@tanstack/react-query'
import { queryClient } from '@utils/queryHooks'
import { MutationVarsTimelineEditItem } from '../timeline'
const editItem = ({ queryKey, rootQueryKey, status }: MutationVarsTimelineEditItem) => {
queryKey &&
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
if (old) {
old.pages = old.pages.map(page => {
page.body = page.body.map((item: Mastodon.Status) => {
if (item.id === status.id) {
item = status
}
return item
})
return page
})
return old
}
})
rootQueryKey &&
queryClient.setQueryData<InfiniteData<any> | undefined>(rootQueryKey, old => {
if (old) {
old.pages = old.pages.map(page => {
page.body = page.body.map((item: Mastodon.Status) => {
if (item.id === status.id) {
item = status
}
return item
})
return page
})
return old
}
})
}
export default editItem

View File

@@ -1,72 +1,78 @@
import { InfiniteData } from '@tanstack/react-query' import { InfiniteData } from '@tanstack/react-query'
import { queryClient } from '@utils/queryHooks' import { queryClient } from '@utils/queryHooks'
import { MutationVarsTimelineUpdateStatusProperty, TimelineData } from '../timeline' import {
MutationVarsTimelineUpdateStatusProperty,
QueryKeyTimeline,
TimelineData
} from '../timeline'
const updateStatusProperty = ({ const updateStatusProperty = (
queryKey, { status, payload, poll }: MutationVarsTimelineUpdateStatusProperty & { poll?: Mastodon.Poll },
rootQueryKey, navigationState: (QueryKeyTimeline | undefined)[]
status, ) => {
payload, const update = (to?: Mastodon.Status): boolean => {
poll if (!to) return false
}: MutationVarsTimelineUpdateStatusProperty & { poll?: Mastodon.Poll }) => {
for (const key of [queryKey, rootQueryKey]) {
if (!key) continue
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(key, old => {
if (old) {
let foundToot: Mastodon.Status | undefined = undefined
old.pages = old.pages.map(page => {
if (foundToot) {
return page
} else {
if (typeof (page.body as Mastodon.Conversation[])[0].unread === 'boolean') {
foundToot = (page.body as Mastodon.Conversation[]).find(({ last_status }) =>
last_status?.reblog
? last_status.reblog.id === status.id
: last_status?.id === status.id
)?.last_status
} else if (typeof (page.body as Mastodon.Notification[])[0].type === 'string') {
foundToot = (page.body as Mastodon.Notification[]).find(no =>
no.status?.reblog ? no.status.reblog.id === status.id : no.status?.id === status.id
)?.status
} else {
foundToot = (page.body as Mastodon.Status[]).find(toot =>
toot.reblog ? toot.reblog.id === status.id : toot.id === status.id
)
}
return page
}
})
if (foundToot) {
const toot = foundToot as Mastodon.Status
enum MapPropertyToCount { enum MapPropertyToCount {
favourited = 'favourites_count', favourited = 'favourites_count',
reblogged = 'reblogs_count' reblogged = 'reblogs_count'
} }
switch (payload.type) { switch (payload.type) {
case 'poll': case 'poll':
toot.poll = poll to.poll = poll
break return true
default: default:
toot[payload.type] = payload.to to[payload.type] = payload.to
switch (payload.type) { switch (payload.type) {
case 'favourited': case 'favourited':
case 'reblogged': case 'reblogged':
if (payload.to) { if (payload.to) {
toot[MapPropertyToCount[payload.type]]++ to[MapPropertyToCount[payload.type]]++
} else { } else {
toot[MapPropertyToCount[payload.type]]-- to[MapPropertyToCount[payload.type]]--
}
break
} }
break break
} }
return true
} }
} }
for (const key of navigationState) {
if (!key) continue
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(key, old => {
if (old) {
let updated: boolean = false
old.pages = old.pages.map(page => {
if (updated) return page
if (typeof (page.body as Mastodon.Conversation[])[0].unread === 'boolean') {
;(page.body as Mastodon.Conversation[]).forEach(({ last_status }) => {
if (last_status?.reblog?.id === status.id) {
updated = update(last_status.reblog)
} else if (last_status?.id === status.id) {
updated = update(last_status)
}
})
} else if (typeof (page.body as Mastodon.Notification[])[0].type === 'string') {
;(page.body as Mastodon.Notification[]).forEach(no => {
if (no.status?.reblog?.id === status.id) {
updated = update(no.status.reblog)
} else if (no.status?.id === status.id) {
updated = update(no.status)
}
})
} else {
;(page.body as Mastodon.Status[]).forEach(toot => {
if (toot.reblog?.id === status.id) {
updated = update(toot.reblog)
} else if (toot.id === status.id) {
updated = update(toot)
}
})
}
return page
})
}
return old return old
}) })
} }

View File

@@ -5,8 +5,8 @@ export const dev = () => {
if (__DEV__) { if (__DEV__) {
log('log', 'dev', 'loading tools') log('log', 'dev', 'loading tools')
// @ts-ignore // @ts-ignore
import('react-query-native-devtools').then(({ addPlugin }) => { // import('react-query-native-devtools').then(({ addPlugin }) => {
addPlugin({ queryClient }) // addPlugin({ queryClient })
}) // })
} }
} }

View File

@@ -9491,11 +9491,11 @@ __metadata:
"react-native-fast-image@patch:react-native-fast-image@npm%3A8.6.3#./.yarn/patches/react-native-fast-image-npm-8.6.3-03ee2d23c0.patch::locator=tooot%40workspace%3A.": "react-native-fast-image@patch:react-native-fast-image@npm%3A8.6.3#./.yarn/patches/react-native-fast-image-npm-8.6.3-03ee2d23c0.patch::locator=tooot%40workspace%3A.":
version: 8.6.3 version: 8.6.3
resolution: "react-native-fast-image@patch:react-native-fast-image@npm%3A8.6.3#./.yarn/patches/react-native-fast-image-npm-8.6.3-03ee2d23c0.patch::version=8.6.3&hash=61017c&locator=tooot%40workspace%3A." resolution: "react-native-fast-image@patch:react-native-fast-image@npm%3A8.6.3#./.yarn/patches/react-native-fast-image-npm-8.6.3-03ee2d23c0.patch::version=8.6.3&hash=d37d57&locator=tooot%40workspace%3A."
peerDependencies: peerDependencies:
react: ^17 || ^18 react: ^17 || ^18
react-native: ">=0.60.0" react-native: ">=0.60.0"
checksum: ffdd0bdf16ef618235bdac64771bb41fb26142737aa98c2c49679025ab1f97e3c8e8d09e23ba2ebe780691f1d11df5e154576f40e057797923a09fa945db8c1b checksum: e5749406ed7608d606df8a7d298a6adaeefdf1077e5349ceb51b344e1fc9de62923f0af05c753c0baa59f476be6d46aee20a20c92c7840a30d8b473a156dfec5
languageName: node languageName: node
linkType: hard linkType: hard