mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Added push server error messaging
Also clean up <Message> component
This commit is contained in:
		| @@ -39,7 +39,7 @@ export interface Props { | |||||||
| } | } | ||||||
|  |  | ||||||
| const Screens: React.FC<Props> = ({ localCorrupt }) => { | const Screens: React.FC<Props> = ({ localCorrupt }) => { | ||||||
|   const { i18n, t } = useTranslation('screens') |   const { t } = useTranslation('screens') | ||||||
|   const dispatch = useAppDispatch() |   const dispatch = useAppDispatch() | ||||||
|   const instanceActive = useSelector(getInstanceActive) |   const instanceActive = useSelector(getInstanceActive) | ||||||
|   const { colors, theme } = useTheme() |   const { colors, theme } = useTheme() | ||||||
| @@ -70,8 +70,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => { | |||||||
|         displayMessage({ |         displayMessage({ | ||||||
|           message: t('localCorrupt.message'), |           message: t('localCorrupt.message'), | ||||||
|           description: localCorrupt.length ? localCorrupt : undefined, |           description: localCorrupt.length ? localCorrupt : undefined, | ||||||
|           type: 'error', |           type: 'danger' | ||||||
|           theme |  | ||||||
|         }) |         }) | ||||||
|         // @ts-ignore |         // @ts-ignore | ||||||
|         navigationRef.navigate('Screen-Tabs', { |         navigationRef.navigate('Screen-Tabs', { | ||||||
| @@ -183,8 +182,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => { | |||||||
|               message: t('shareError.imageNotSupported', { |               message: t('shareError.imageNotSupported', { | ||||||
|                 type: mime.split('/')[1] |                 type: mime.split('/')[1] | ||||||
|               }), |               }), | ||||||
|               type: 'error', |               type: 'danger' | ||||||
|               theme |  | ||||||
|             }) |             }) | ||||||
|             return |             return | ||||||
|           } |           } | ||||||
| @@ -196,8 +194,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => { | |||||||
|               message: t('shareError.videoNotSupported', { |               message: t('shareError.videoNotSupported', { | ||||||
|                 type: mime.split('/')[1] |                 type: mime.split('/')[1] | ||||||
|               }), |               }), | ||||||
|               type: 'error', |               type: 'danger' | ||||||
|               theme |  | ||||||
|             }) |             }) | ||||||
|             return |             return | ||||||
|           } |           } | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| import Icon from '@components/Icon' | import Icon from '@components/Icon' | ||||||
| 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 { getColors, Theme } from '@utils/styles/themes' |  | ||||||
| import React, { RefObject } from 'react' | import React, { RefObject } from 'react' | ||||||
| import { AccessibilityInfo } from 'react-native' | import { AccessibilityInfo } from 'react-native' | ||||||
| import FlashMessage, { hideMessage, showMessage } from 'react-native-flash-message' | import FlashMessage, { MessageType, showMessage } from 'react-native-flash-message' | ||||||
| import { useSafeAreaInsets } from 'react-native-safe-area-context' | import { useSafeAreaInsets } from 'react-native-safe-area-context' | ||||||
| import haptics from './haptics' | import haptics from './haptics' | ||||||
|  |  | ||||||
| @@ -15,107 +14,80 @@ const displayMessage = ({ | |||||||
|   message, |   message, | ||||||
|   description, |   description, | ||||||
|   onPress, |   onPress, | ||||||
|   theme, |  | ||||||
|   type |   type | ||||||
| }: | }: { | ||||||
|   | { |  | ||||||
|   ref?: RefObject<FlashMessage> |   ref?: RefObject<FlashMessage> | ||||||
|   duration?: 'short' | 'long' |   duration?: 'short' | 'long' | ||||||
|   autoHide?: boolean |   autoHide?: boolean | ||||||
|   message: string |   message: string | ||||||
|   description?: string |   description?: string | ||||||
|   onPress?: () => void |   onPress?: () => void | ||||||
|       theme?: undefined |   type?: MessageType | ||||||
|       type?: undefined |  | ||||||
|     } |  | ||||||
|   | { |  | ||||||
|       ref?: RefObject<FlashMessage> |  | ||||||
|       duration?: 'short' | 'long' |  | ||||||
|       autoHide?: boolean |  | ||||||
|       message: string |  | ||||||
|       description?: string |  | ||||||
|       onPress?: () => void |  | ||||||
|       theme: Theme |  | ||||||
|       type: 'success' | 'error' | 'warning' |  | ||||||
| }) => { | }) => { | ||||||
|   AccessibilityInfo.announceForAccessibility(message + '.' + description) |   AccessibilityInfo.announceForAccessibility(message + '.' + description) | ||||||
|  |  | ||||||
|   enum iconMapping { |   if (type && type === 'danger') { | ||||||
|     success = 'CheckCircle', |  | ||||||
|     error = 'XCircle', |  | ||||||
|     warning = 'AlertCircle' |  | ||||||
|   } |  | ||||||
|   enum colorMapping { |  | ||||||
|     success = 'blue', |  | ||||||
|     error = 'red', |  | ||||||
|     warning = 'secondary' |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (type && type === 'error') { |  | ||||||
|     haptics('Error') |     haptics('Error') | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (ref) { |   if (ref) { | ||||||
|     ref.current?.showMessage({ |     ref.current?.showMessage({ | ||||||
|       duration: type === 'error' ? 8000 : duration === 'short' ? 3000 : 5000, |       duration: type === 'danger' ? 8000 : duration === 'short' ? 3000 : 5000, | ||||||
|       autoHide, |       autoHide, | ||||||
|       message, |       message, | ||||||
|       description, |       description, | ||||||
|       onPress, |       onPress, | ||||||
|       ...(theme && |       type | ||||||
|         type && { |  | ||||||
|           renderFlashMessageIcon: () => { |  | ||||||
|             return ( |  | ||||||
|               <Icon |  | ||||||
|                 name={iconMapping[type]} |  | ||||||
|                 size={StyleConstants.Font.LineHeight.M} |  | ||||||
|                 color={getColors(theme)[colorMapping[type]]} |  | ||||||
|                 style={{ marginRight: StyleConstants.Spacing.S }} |  | ||||||
|               /> |  | ||||||
|             ) |  | ||||||
|           } |  | ||||||
|         }) |  | ||||||
|     }) |     }) | ||||||
|   } else { |   } else { | ||||||
|     showMessage({ |     showMessage({ | ||||||
|       duration: type === 'error' ? 8000 : duration === 'short' ? 3000 : 5000, |       duration: type === 'danger' ? 8000 : duration === 'short' ? 3000 : 5000, | ||||||
|       autoHide, |       autoHide, | ||||||
|       message, |       message, | ||||||
|       description, |       description, | ||||||
|       onPress, |       onPress, | ||||||
|       ...(theme && |       type | ||||||
|         type && { |  | ||||||
|           renderFlashMessageIcon: () => { |  | ||||||
|             return ( |  | ||||||
|               <Icon |  | ||||||
|                 name={iconMapping[type]} |  | ||||||
|                 size={StyleConstants.Font.LineHeight.M} |  | ||||||
|                 color={getColors(theme)[colorMapping[type]]} |  | ||||||
|                 style={{ marginRight: StyleConstants.Spacing.S }} |  | ||||||
|               /> |  | ||||||
|             ) |  | ||||||
|           } |  | ||||||
|         }) |  | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| const removeMessage = () => { |  | ||||||
|   // if (ref) { |  | ||||||
|   //   ref.current?.hideMessage() |  | ||||||
|   // } else { |  | ||||||
|   hideMessage() |  | ||||||
|   // } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const Message = React.forwardRef<FlashMessage>((_, ref) => { | const Message = React.forwardRef<FlashMessage>((_, ref) => { | ||||||
|   const { colors, theme } = useTheme() |   const { colors, theme } = useTheme() | ||||||
|   const insets = useSafeAreaInsets() |   const insets = useSafeAreaInsets() | ||||||
|  |  | ||||||
|  |   enum iconMapping { | ||||||
|  |     success = 'CheckCircle', | ||||||
|  |     danger = 'XCircle', | ||||||
|  |     warning = 'AlertCircle', | ||||||
|  |     none = '', | ||||||
|  |     default = '', | ||||||
|  |     info = '', | ||||||
|  |     auto = '' | ||||||
|  |   } | ||||||
|  |   enum colorMapping { | ||||||
|  |     success = 'blue', | ||||||
|  |     danger = 'red', | ||||||
|  |     warning = 'secondary', | ||||||
|  |     none = 'secondary', | ||||||
|  |     default = 'secondary', | ||||||
|  |     info = 'secondary', | ||||||
|  |     auto = 'secondary' | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <FlashMessage |     <FlashMessage | ||||||
|       ref={ref} |       ref={ref} | ||||||
|       icon='auto' |       icon='auto' | ||||||
|  |       renderFlashMessageIcon={type => { | ||||||
|  |         return typeof type === 'string' && ['success', 'danger', 'warning'].includes(type) ? ( | ||||||
|  |           <Icon | ||||||
|  |             name={iconMapping[type]} | ||||||
|  |             size={StyleConstants.Font.LineHeight.M} | ||||||
|  |             color={colors[colorMapping[type]]} | ||||||
|  |             style={{ marginRight: StyleConstants.Spacing.S }} | ||||||
|  |           /> | ||||||
|  |         ) : null | ||||||
|  |       }} | ||||||
|       position='top' |       position='top' | ||||||
|       floating |       floating | ||||||
|       style={{ |       style={{ | ||||||
| @@ -142,4 +114,4 @@ const Message = React.forwardRef<FlashMessage>((_, ref) => { | |||||||
|   ) |   ) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| export { Message, displayMessage, removeMessage } | export { Message, displayMessage } | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ import { | |||||||
|   useTimelineMutation |   useTimelineMutation | ||||||
| } from '@utils/queryHooks/timeline' | } from '@utils/queryHooks/timeline' | ||||||
| import { getInstanceAccount } from '@utils/slices/instancesSlice' | import { getInstanceAccount } from '@utils/slices/instancesSlice' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import { useEffect, useState } from 'react' | import { useEffect, useState } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { Platform } from 'react-native' | import { Platform } from 'react-native' | ||||||
| @@ -38,7 +37,6 @@ const menuAccount = ({ | |||||||
|  |  | ||||||
|   const navigation = |   const navigation = | ||||||
|     useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>() |     useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>() | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentContextMenu') |   const { t } = useTranslation('componentContextMenu') | ||||||
|  |  | ||||||
|   const menus: ContextMenu[][] = [[]] |   const menus: ContextMenu[][] = [[]] | ||||||
| @@ -60,7 +58,6 @@ const menuAccount = ({ | |||||||
|       queryClient.refetchQueries(['Relationship', { id: account.id }]) |       queryClient.refetchQueries(['Relationship', { id: account.id }]) | ||||||
|       const theParams = params as MutationVarsTimelineUpdateAccountProperty |       const theParams = params as MutationVarsTimelineUpdateAccountProperty | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |  | ||||||
|         type: 'success', |         type: 'success', | ||||||
|         message: t('common:message.success.message', { |         message: t('common:message.success.message', { | ||||||
|           function: t(`account.${theParams.payload.property}.action`, { |           function: t(`account.${theParams.payload.property}.action`, { | ||||||
| @@ -74,8 +71,7 @@ const menuAccount = ({ | |||||||
|     onError: (err: any, params) => { |     onError: (err: any, params) => { | ||||||
|       const theParams = params as MutationVarsTimelineUpdateAccountProperty |       const theParams = params as MutationVarsTimelineUpdateAccountProperty | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |         message: t('common:message.error.message', { | ||||||
|           function: t(`account.${theParams.payload.property}.action`, { |           function: t(`account.${theParams.payload.property}.action`, { | ||||||
|             ...(theParams.payload.property !== 'reports' && { |             ...(theParams.payload.property !== 'reports' && { | ||||||
| @@ -109,8 +105,7 @@ const menuAccount = ({ | |||||||
|     }, |     }, | ||||||
|     onError: (err: any, { payload: { action } }) => { |     onError: (err: any, { payload: { action } }) => { | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |         message: t('common:message.error.message', { | ||||||
|           function: t(`${action}.function`) |           function: t(`${action}.function`) | ||||||
|         }), |         }), | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import { displayMessage } from '@components/Message' | import { displayMessage } from '@components/Message' | ||||||
| import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' | import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' | ||||||
| import { getInstanceUrl } from '@utils/slices/instancesSlice' | import { getInstanceUrl } from '@utils/slices/instancesSlice' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { Alert } from 'react-native' | import { Alert } from 'react-native' | ||||||
| import { useQueryClient } from 'react-query' | import { useQueryClient } from 'react-query' | ||||||
| @@ -18,14 +17,12 @@ const menuInstance = ({ | |||||||
| }): ContextMenu[][] => { | }): ContextMenu[][] => { | ||||||
|   if (!status || !queryKey) return [] |   if (!status || !queryKey) return [] | ||||||
|  |  | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentContextMenu') |   const { t } = useTranslation('componentContextMenu') | ||||||
|  |  | ||||||
|   const queryClient = useQueryClient() |   const queryClient = useQueryClient() | ||||||
|   const mutation = useTimelineMutation({ |   const mutation = useTimelineMutation({ | ||||||
|     onSettled: () => { |     onSettled: () => { | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |  | ||||||
|         type: 'success', |         type: 'success', | ||||||
|         message: t('common:message.success.message', { |         message: t('common:message.success.message', { | ||||||
|           function: t(`instance.block.action`, { instance }) |           function: t(`instance.block.action`, { instance }) | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import { displayMessage } from '@components/Message' | import { displayMessage } from '@components/Message' | ||||||
| import Clipboard from '@react-native-clipboard/clipboard' | import Clipboard from '@react-native-clipboard/clipboard' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { Platform, Share } from 'react-native' | import { Platform, Share } from 'react-native' | ||||||
|  |  | ||||||
| @@ -22,7 +21,6 @@ const menuShare = ( | |||||||
| ): ContextMenu[][] => { | ): ContextMenu[][] => { | ||||||
|   if (params.type === 'status' && params.visibility === 'direct') return [] |   if (params.type === 'status' && params.visibility === 'direct') return [] | ||||||
|  |  | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentContextMenu') |   const { t } = useTranslation('componentContextMenu') | ||||||
|  |  | ||||||
|   const menus: ContextMenu[][] = [[]] |   const menus: ContextMenu[][] = [[]] | ||||||
| @@ -56,11 +54,7 @@ const menuShare = ( | |||||||
|       item: { |       item: { | ||||||
|         onSelect: () => { |         onSelect: () => { | ||||||
|           Clipboard.setString(params.copiableContent?.current.content || '') |           Clipboard.setString(params.copiableContent?.current.content || '') | ||||||
|           displayMessage({ |           displayMessage({ type: 'success', message: t(`copy.succeed`) }) | ||||||
|             theme, |  | ||||||
|             type: 'success', |  | ||||||
|             message: t(`copy.succeed`) |  | ||||||
|           }) |  | ||||||
|         }, |         }, | ||||||
|         disabled: false, |         disabled: false, | ||||||
|         destructive: false, |         destructive: false, | ||||||
|   | |||||||
| @@ -178,6 +178,10 @@ | |||||||
|         "direct": "Enable push notification", |         "direct": "Enable push notification", | ||||||
|         "settings": "Enable in settings" |         "settings": "Enable in settings" | ||||||
|       }, |       }, | ||||||
|  |       "missingServerKey": { | ||||||
|  |         "message": "Server misconfigured for push", | ||||||
|  |         "description": "Please contact your server admin to configure push support" | ||||||
|  |       }, | ||||||
|       "global": { |       "global": { | ||||||
|         "heading": "Enable for {{acct}}", |         "heading": "Enable for {{acct}}", | ||||||
|         "description": "Messages are routed through tooot's server" |         "description": "Messages are routed through tooot's server" | ||||||
|   | |||||||
| @@ -1,123 +0,0 @@ | |||||||
| import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu' |  | ||||||
| import { displayMessage } from '@components/Message' |  | ||||||
| import { |  | ||||||
|   MutationVarsTimelineUpdateAccountProperty, |  | ||||||
|   QueryKeyTimeline, |  | ||||||
|   useTimelineMutation |  | ||||||
| } from '@utils/queryHooks/timeline' |  | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import React from 'react' |  | ||||||
| import { useTranslation } from 'react-i18next' |  | ||||||
| import { useQueryClient } from 'react-query' |  | ||||||
|  |  | ||||||
| export interface Props { |  | ||||||
|   queryKey?: QueryKeyTimeline |  | ||||||
|   rootQueryKey?: QueryKeyTimeline |  | ||||||
|   account: Mastodon.Account |  | ||||||
|   dismiss: () => void |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ActionsAccount: React.FC<Props> = ({ |  | ||||||
|   queryKey, |  | ||||||
|   rootQueryKey, |  | ||||||
|   account, |  | ||||||
|   dismiss |  | ||||||
| }) => { |  | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentTimeline') |  | ||||||
|  |  | ||||||
|   const queryClient = useQueryClient() |  | ||||||
|   const mutation = useTimelineMutation({ |  | ||||||
|     onSuccess: (_, params) => { |  | ||||||
|       const theParams = params as MutationVarsTimelineUpdateAccountProperty |  | ||||||
|       displayMessage({ |  | ||||||
|         theme, |  | ||||||
|         type: 'success', |  | ||||||
|         message: t('common:message.success.message', { |  | ||||||
|           function: t( |  | ||||||
|             `shared.header.actions.account.${theParams.payload.property}.function`, |  | ||||||
|             { |  | ||||||
|               acct: account.acct |  | ||||||
|             } |  | ||||||
|           ) |  | ||||||
|         }) |  | ||||||
|       }) |  | ||||||
|     }, |  | ||||||
|     onError: (err: any, params) => { |  | ||||||
|       const theParams = params as MutationVarsTimelineUpdateAccountProperty |  | ||||||
|       displayMessage({ |  | ||||||
|         theme, |  | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |  | ||||||
|           function: t( |  | ||||||
|             `shared.header.actions.account.${theParams.payload.property}.function` |  | ||||||
|           ) |  | ||||||
|         }), |  | ||||||
|         ...(err.status && |  | ||||||
|           typeof err.status === 'number' && |  | ||||||
|           err.data && |  | ||||||
|           err.data.error && |  | ||||||
|           typeof err.data.error === 'string' && { |  | ||||||
|             description: err.data.error |  | ||||||
|           }) |  | ||||||
|       }) |  | ||||||
|     }, |  | ||||||
|     onSettled: () => { |  | ||||||
|       queryKey && queryClient.invalidateQueries(queryKey) |  | ||||||
|       rootQueryKey && queryClient.invalidateQueries(rootQueryKey) |  | ||||||
|     } |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <MenuContainer> |  | ||||||
|       <MenuHeader heading={t('shared.header.actions.account.heading')} /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           dismiss() |  | ||||||
|           mutation.mutate({ |  | ||||||
|             type: 'updateAccountProperty', |  | ||||||
|             queryKey, |  | ||||||
|             id: account.id, |  | ||||||
|             payload: { property: 'mute' } |  | ||||||
|           }) |  | ||||||
|         }} |  | ||||||
|         iconFront='EyeOff' |  | ||||||
|         title={t('shared.header.actions.account.mute.button', { |  | ||||||
|           acct: account.acct |  | ||||||
|         })} |  | ||||||
|       /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           dismiss() |  | ||||||
|           mutation.mutate({ |  | ||||||
|             type: 'updateAccountProperty', |  | ||||||
|             queryKey, |  | ||||||
|             id: account.id, |  | ||||||
|             payload: { property: 'block' } |  | ||||||
|           }) |  | ||||||
|         }} |  | ||||||
|         iconFront='XCircle' |  | ||||||
|         title={t('shared.header.actions.account.block.button', { |  | ||||||
|           acct: account.acct |  | ||||||
|         })} |  | ||||||
|       /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           dismiss() |  | ||||||
|           mutation.mutate({ |  | ||||||
|             type: 'updateAccountProperty', |  | ||||||
|             queryKey, |  | ||||||
|             id: account.id, |  | ||||||
|             payload: { property: 'reports' } |  | ||||||
|           }) |  | ||||||
|         }} |  | ||||||
|         iconFront='Flag' |  | ||||||
|         title={t('shared.header.actions.account.reports.button', { |  | ||||||
|           acct: account.acct |  | ||||||
|         })} |  | ||||||
|       /> |  | ||||||
|     </MenuContainer> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionsAccount |  | ||||||
| @@ -1,74 +0,0 @@ | |||||||
| import MenuContainer from '@components/Menu/Container' |  | ||||||
| import MenuHeader from '@components/Menu/Header' |  | ||||||
| import MenuRow from '@components/Menu/Row' |  | ||||||
| import { displayMessage } from '@components/Message' |  | ||||||
| import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' |  | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import React from 'react' |  | ||||||
| import { useTranslation } from 'react-i18next' |  | ||||||
| import { Alert } from 'react-native' |  | ||||||
| import { useQueryClient } from 'react-query' |  | ||||||
|  |  | ||||||
| export interface Props { |  | ||||||
|   queryKey: QueryKeyTimeline |  | ||||||
|   rootQueryKey?: QueryKeyTimeline |  | ||||||
|   domain: string |  | ||||||
|   dismiss: () => void |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ActionsDomain: React.FC<Props> = ({ queryKey, rootQueryKey, domain, dismiss }) => { |  | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentTimeline') |  | ||||||
|   const queryClient = useQueryClient() |  | ||||||
|   const mutation = useTimelineMutation({ |  | ||||||
|     onSettled: () => { |  | ||||||
|       displayMessage({ |  | ||||||
|         theme, |  | ||||||
|         type: 'success', |  | ||||||
|         message: t('common:message.success.message', { |  | ||||||
|           function: t(`shared.header.actions.domain.block.function`) |  | ||||||
|         }) |  | ||||||
|       }) |  | ||||||
|       queryClient.invalidateQueries(queryKey) |  | ||||||
|       rootQueryKey && queryClient.invalidateQueries(rootQueryKey) |  | ||||||
|     } |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <MenuContainer> |  | ||||||
|       <MenuHeader heading={t(`shared.header.actions.domain.heading`)} /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => |  | ||||||
|           Alert.alert( |  | ||||||
|             t('shared.header.actions.domain.alert.title', { domain }), |  | ||||||
|             t('shared.header.actions.domain.alert.message'), |  | ||||||
|             [ |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.domain.alert.buttons.confirm'), |  | ||||||
|                 style: 'destructive', |  | ||||||
|                 onPress: () => { |  | ||||||
|                   dismiss() |  | ||||||
|                   mutation.mutate({ |  | ||||||
|                     type: 'domainBlock', |  | ||||||
|                     queryKey, |  | ||||||
|                     domain: domain |  | ||||||
|                   }) |  | ||||||
|                 } |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.domain.alert.buttons.cancel'), |  | ||||||
|                 style: 'default' |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           ) |  | ||||||
|         } |  | ||||||
|         iconFront='CloudOff' |  | ||||||
|         title={t(`shared.header.actions.domain.block.button`, { |  | ||||||
|           domain |  | ||||||
|         })} |  | ||||||
|       /> |  | ||||||
|     </MenuContainer> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionsDomain |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| import MenuContainer from '@components/Menu/Container' |  | ||||||
| import MenuHeader from '@components/Menu/Header' |  | ||||||
| import MenuRow from '@components/Menu/Row' |  | ||||||
| import React from 'react' |  | ||||||
| import { useTranslation } from 'react-i18next' |  | ||||||
| import { Platform, Share } from 'react-native' |  | ||||||
|  |  | ||||||
| export interface Props { |  | ||||||
|   type: 'status' | 'account' |  | ||||||
|   url: string |  | ||||||
|   dismiss: () => void |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ActionsShare: React.FC<Props> = ({ type, url, dismiss }) => { |  | ||||||
|   const { t } = useTranslation('componentTimeline') |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <MenuContainer> |  | ||||||
|       <MenuHeader heading={t(`shared.header.actions.share.${type}.heading`)} /> |  | ||||||
|       <MenuRow |  | ||||||
|         iconFront='Share2' |  | ||||||
|         title={t(`shared.header.actions.share.${type}.button`)} |  | ||||||
|         onPress={async () => { |  | ||||||
|           switch (Platform.OS) { |  | ||||||
|             case 'ios': |  | ||||||
|               await Share.share({ |  | ||||||
|                 url |  | ||||||
|               }) |  | ||||||
|               break |  | ||||||
|             case 'android': |  | ||||||
|               await Share.share({ |  | ||||||
|                 message: url |  | ||||||
|               }) |  | ||||||
|               break |  | ||||||
|           } |  | ||||||
|           dismiss() |  | ||||||
|         }} |  | ||||||
|       /> |  | ||||||
|     </MenuContainer> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionsShare |  | ||||||
| @@ -1,231 +0,0 @@ | |||||||
| import React from 'react' |  | ||||||
| import { useTranslation } from 'react-i18next' |  | ||||||
| import { Alert } from 'react-native' |  | ||||||
| import { useQueryClient } from 'react-query' |  | ||||||
| import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu' |  | ||||||
| import { |  | ||||||
|   MutationVarsTimelineUpdateStatusProperty, |  | ||||||
|   QueryKeyTimeline, |  | ||||||
|   useTimelineMutation |  | ||||||
| } from '@utils/queryHooks/timeline' |  | ||||||
| import { displayMessage } from '@components/Message' |  | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import apiInstance from '@api/instance' |  | ||||||
| import { NativeStackNavigationProp } from '@react-navigation/native-stack' |  | ||||||
| import { RootStackParamList } from '@utils/navigation/navigators' |  | ||||||
| import { useSelector } from 'react-redux' |  | ||||||
| import { checkInstanceFeature } from '@utils/slices/instancesSlice' |  | ||||||
|  |  | ||||||
| export interface Props { |  | ||||||
|   navigation: NativeStackNavigationProp<RootStackParamList, 'Screen-Actions'> |  | ||||||
|   queryKey: QueryKeyTimeline |  | ||||||
|   rootQueryKey?: QueryKeyTimeline |  | ||||||
|   status: Mastodon.Status |  | ||||||
|   dismiss: () => void |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const ActionsStatus: React.FC<Props> = ({ |  | ||||||
|   navigation, |  | ||||||
|   queryKey, |  | ||||||
|   rootQueryKey, |  | ||||||
|   status, |  | ||||||
|   dismiss |  | ||||||
| }) => { |  | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('componentTimeline') |  | ||||||
|  |  | ||||||
|   const queryClient = useQueryClient() |  | ||||||
|   const mutation = useTimelineMutation({ |  | ||||||
|     onMutate: true, |  | ||||||
|     onError: (err: any, params, oldData) => { |  | ||||||
|       const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload |  | ||||||
|         ? (params as MutationVarsTimelineUpdateStatusProperty).payload.property |  | ||||||
|         : 'delete' |  | ||||||
|       displayMessage({ |  | ||||||
|         theme, |  | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |  | ||||||
|           function: t(`shared.header.actions.status.${theFunction}.function`) |  | ||||||
|         }), |  | ||||||
|         ...(err?.status && |  | ||||||
|           typeof err.status === 'number' && |  | ||||||
|           err.data && |  | ||||||
|           err.data.error && |  | ||||||
|           typeof err.data.error === 'string' && { |  | ||||||
|             description: err.data.error |  | ||||||
|           }) |  | ||||||
|       }) |  | ||||||
|       queryClient.setQueryData(queryKey, oldData) |  | ||||||
|     } |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   const canEditPost = useSelector(checkInstanceFeature('edit_post')) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     <MenuContainer> |  | ||||||
|       <MenuHeader heading={t('shared.header.actions.status.heading')} /> |  | ||||||
|       {canEditPost ? ( |  | ||||||
|         <MenuRow |  | ||||||
|           onPress={async () => { |  | ||||||
|             let replyToStatus: Mastodon.Status | undefined = undefined |  | ||||||
|             if (status.in_reply_to_id) { |  | ||||||
|               replyToStatus = await apiInstance<Mastodon.Status>({ |  | ||||||
|                 method: 'get', |  | ||||||
|                 url: `statuses/${status.in_reply_to_id}` |  | ||||||
|               }).then(res => res.body) |  | ||||||
|             } |  | ||||||
|             apiInstance<{ |  | ||||||
|               id: Mastodon.Status['id'] |  | ||||||
|               text: NonNullable<Mastodon.Status['text']> |  | ||||||
|               spoiler_text: Mastodon.Status['spoiler_text'] |  | ||||||
|             }>({ |  | ||||||
|               method: 'get', |  | ||||||
|               url: `statuses/${status.id}/source` |  | ||||||
|             }).then(res => { |  | ||||||
|               dismiss() |  | ||||||
|               navigation.navigate('Screen-Compose', { |  | ||||||
|                 type: 'edit', |  | ||||||
|                 incomingStatus: { |  | ||||||
|                   ...status, |  | ||||||
|                   text: res.body.text, |  | ||||||
|                   spoiler_text: res.body.spoiler_text |  | ||||||
|                 }, |  | ||||||
|                 ...(replyToStatus && { replyToStatus }), |  | ||||||
|                 queryKey, |  | ||||||
|                 rootQueryKey |  | ||||||
|               }) |  | ||||||
|             }) |  | ||||||
|           }} |  | ||||||
|           iconFront='Edit3' |  | ||||||
|           title={t('shared.header.actions.status.edit.button')} |  | ||||||
|         /> |  | ||||||
|       ) : null} |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           Alert.alert( |  | ||||||
|             t('shared.header.actions.status.delete.alert.title'), |  | ||||||
|             t('shared.header.actions.status.delete.alert.message'), |  | ||||||
|             [ |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.status.delete.alert.buttons.confirm'), |  | ||||||
|                 style: 'destructive', |  | ||||||
|                 onPress: async () => { |  | ||||||
|                   dismiss() |  | ||||||
|                   mutation.mutate({ |  | ||||||
|                     type: 'deleteItem', |  | ||||||
|                     source: 'statuses', |  | ||||||
|                     queryKey, |  | ||||||
|                     rootQueryKey, |  | ||||||
|                     id: status.id |  | ||||||
|                   }) |  | ||||||
|                 } |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.status.delete.alert.buttons.cancel'), |  | ||||||
|                 style: 'default' |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           ) |  | ||||||
|         }} |  | ||||||
|         iconFront='Trash' |  | ||||||
|         title={t('shared.header.actions.status.delete.button')} |  | ||||||
|       /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           Alert.alert( |  | ||||||
|             t('shared.header.actions.status.deleteEdit.alert.title'), |  | ||||||
|             t('shared.header.actions.status.deleteEdit.alert.message'), |  | ||||||
|             [ |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.status.deleteEdit.alert.buttons.confirm'), |  | ||||||
|                 style: 'destructive', |  | ||||||
|                 onPress: async () => { |  | ||||||
|                   let replyToStatus: Mastodon.Status | undefined = undefined |  | ||||||
|                   if (status.in_reply_to_id) { |  | ||||||
|                     replyToStatus = await apiInstance<Mastodon.Status>({ |  | ||||||
|                       method: 'get', |  | ||||||
|                       url: `statuses/${status.in_reply_to_id}` |  | ||||||
|                     }).then(res => res.body) |  | ||||||
|                   } |  | ||||||
|                   mutation |  | ||||||
|                     .mutateAsync({ |  | ||||||
|                       type: 'deleteItem', |  | ||||||
|                       source: 'statuses', |  | ||||||
|                       queryKey, |  | ||||||
|                       id: status.id |  | ||||||
|                     }) |  | ||||||
|                     .then(res => { |  | ||||||
|                       dismiss() |  | ||||||
|                       navigation.navigate('Screen-Compose', { |  | ||||||
|                         type: 'deleteEdit', |  | ||||||
|                         incomingStatus: res.body as Mastodon.Status, |  | ||||||
|                         ...(replyToStatus && { replyToStatus }), |  | ||||||
|                         queryKey |  | ||||||
|                       }) |  | ||||||
|                     }) |  | ||||||
|                 } |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 text: t('shared.header.actions.status.deleteEdit.alert.buttons.cancel'), |  | ||||||
|                 style: 'default' |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           ) |  | ||||||
|         }} |  | ||||||
|         iconFront='Edit' |  | ||||||
|         title={t('shared.header.actions.status.deleteEdit.button')} |  | ||||||
|       /> |  | ||||||
|       <MenuRow |  | ||||||
|         onPress={() => { |  | ||||||
|           dismiss() |  | ||||||
|           mutation.mutate({ |  | ||||||
|             type: 'updateStatusProperty', |  | ||||||
|             queryKey, |  | ||||||
|             rootQueryKey, |  | ||||||
|             id: status.id, |  | ||||||
|             payload: { |  | ||||||
|               property: 'muted', |  | ||||||
|               currentValue: status.muted, |  | ||||||
|               propertyCount: undefined, |  | ||||||
|               countValue: undefined |  | ||||||
|             } |  | ||||||
|           }) |  | ||||||
|         }} |  | ||||||
|         iconFront='VolumeX' |  | ||||||
|         title={ |  | ||||||
|           status.muted |  | ||||||
|             ? t('shared.header.actions.status.mute.button.negative') |  | ||||||
|             : t('shared.header.actions.status.mute.button.positive') |  | ||||||
|         } |  | ||||||
|       /> |  | ||||||
|       {/* Also note that reblogs cannot be pinned. */} |  | ||||||
|       {(status.visibility === 'public' || status.visibility === 'unlisted') && ( |  | ||||||
|         <MenuRow |  | ||||||
|           onPress={() => { |  | ||||||
|             dismiss() |  | ||||||
|             mutation.mutate({ |  | ||||||
|               type: 'updateStatusProperty', |  | ||||||
|               queryKey, |  | ||||||
|               rootQueryKey, |  | ||||||
|               id: status.id, |  | ||||||
|               payload: { |  | ||||||
|                 property: 'pinned', |  | ||||||
|                 currentValue: status.pinned, |  | ||||||
|                 propertyCount: undefined, |  | ||||||
|                 countValue: undefined |  | ||||||
|               } |  | ||||||
|             }) |  | ||||||
|           }} |  | ||||||
|           iconFront='Anchor' |  | ||||||
|           title={ |  | ||||||
|             status.pinned |  | ||||||
|               ? t('shared.header.actions.status.pin.button.negative') |  | ||||||
|               : t('shared.header.actions.status.pin.button.positive') |  | ||||||
|           } |  | ||||||
|         /> |  | ||||||
|       )} |  | ||||||
|     </MenuContainer> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionsStatus |  | ||||||
| @@ -2,22 +2,19 @@ import haptics from '@components/haptics' | |||||||
| import { displayMessage } from '@components/Message' | import { displayMessage } from '@components/Message' | ||||||
| import { CameraRoll } from '@react-native-camera-roll/camera-roll' | import { CameraRoll } from '@react-native-camera-roll/camera-roll' | ||||||
| import { RootStackParamList } from '@utils/navigation/navigators' | import { RootStackParamList } from '@utils/navigation/navigators' | ||||||
| import { Theme } from '@utils/styles/themes' |  | ||||||
| import * as FileSystem from 'expo-file-system' | import * as FileSystem from 'expo-file-system' | ||||||
| import i18next from 'i18next' | import i18next from 'i18next' | ||||||
| import { PermissionsAndroid, Platform } from 'react-native' | import { PermissionsAndroid, Platform } from 'react-native' | ||||||
|  |  | ||||||
| type CommonProps = { | type CommonProps = { | ||||||
|   theme: Theme |  | ||||||
|   image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0] |   image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0] | ||||||
| } | } | ||||||
|  |  | ||||||
| const saveIos = async ({ theme, image }: CommonProps) => { | const saveIos = async ({ image }: CommonProps) => { | ||||||
|   CameraRoll.save(image.url) |   CameraRoll.save(image.url) | ||||||
|     .then(() => { |     .then(() => { | ||||||
|       haptics('Success') |       haptics('Success') | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |  | ||||||
|         type: 'success', |         type: 'success', | ||||||
|         message: i18next.t('screenImageViewer:content.save.succeed') |         message: i18next.t('screenImageViewer:content.save.succeed') | ||||||
|       }) |       }) | ||||||
| @@ -28,7 +25,6 @@ const saveIos = async ({ theme, image }: CommonProps) => { | |||||||
|           .then(() => { |           .then(() => { | ||||||
|             haptics('Success') |             haptics('Success') | ||||||
|             displayMessage({ |             displayMessage({ | ||||||
|               theme, |  | ||||||
|               type: 'success', |               type: 'success', | ||||||
|               message: i18next.t('screenImageViewer:content.save.succeed') |               message: i18next.t('screenImageViewer:content.save.succeed') | ||||||
|             }) |             }) | ||||||
| @@ -36,32 +32,31 @@ const saveIos = async ({ theme, image }: CommonProps) => { | |||||||
|           .catch(() => { |           .catch(() => { | ||||||
|             haptics('Error') |             haptics('Error') | ||||||
|             displayMessage({ |             displayMessage({ | ||||||
|               theme, |               type: 'danger', | ||||||
|               type: 'error', |  | ||||||
|               message: i18next.t('screenImageViewer:content.save.failed') |               message: i18next.t('screenImageViewer:content.save.failed') | ||||||
|             }) |             }) | ||||||
|           }) |           }) | ||||||
|       } else { |       } else { | ||||||
|         haptics('Error') |         haptics('Error') | ||||||
|         displayMessage({ |         displayMessage({ | ||||||
|           theme, |           type: 'danger', | ||||||
|           type: 'error', |  | ||||||
|           message: i18next.t('screenImageViewer:content.save.failed') |           message: i18next.t('screenImageViewer:content.save.failed') | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| const saveAndroid = async ({ theme, image }: CommonProps) => { | const saveAndroid = async ({ image }: CommonProps) => { | ||||||
|   const fileUri: string = `${FileSystem.documentDirectory}${image.id}.jpg` |   const fileUri: string = `${FileSystem.documentDirectory}${image.id}.jpg` | ||||||
|   const downloadedFile: FileSystem.FileSystemDownloadResult = |   const downloadedFile: FileSystem.FileSystemDownloadResult = await FileSystem.downloadAsync( | ||||||
|     await FileSystem.downloadAsync(image.url, fileUri) |     image.url, | ||||||
|  |     fileUri | ||||||
|  |   ) | ||||||
|  |  | ||||||
|   if (downloadedFile.status != 200) { |   if (downloadedFile.status != 200) { | ||||||
|     haptics('Error') |     haptics('Error') | ||||||
|     displayMessage({ |     displayMessage({ | ||||||
|       theme, |       type: 'danger', | ||||||
|       type: 'error', |  | ||||||
|       message: i18next.t('screenImageViewer:content.save.failed') |       message: i18next.t('screenImageViewer:content.save.failed') | ||||||
|     }) |     }) | ||||||
|     return |     return | ||||||
| @@ -75,8 +70,7 @@ const saveAndroid = async ({ theme, image }: CommonProps) => { | |||||||
|     if (status !== 'granted') { |     if (status !== 'granted') { | ||||||
|       haptics('Error') |       haptics('Error') | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: i18next.t('screenImageViewer:content.save.failed') |         message: i18next.t('screenImageViewer:content.save.failed') | ||||||
|       }) |       }) | ||||||
|       return |       return | ||||||
| @@ -87,7 +81,6 @@ const saveAndroid = async ({ theme, image }: CommonProps) => { | |||||||
|     .then(() => { |     .then(() => { | ||||||
|       haptics('Success') |       haptics('Success') | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |  | ||||||
|         type: 'success', |         type: 'success', | ||||||
|         message: i18next.t('screenImageViewer:content.save.succeed') |         message: i18next.t('screenImageViewer:content.save.succeed') | ||||||
|       }) |       }) | ||||||
| @@ -95,8 +88,7 @@ const saveAndroid = async ({ theme, image }: CommonProps) => { | |||||||
|     .catch(() => { |     .catch(() => { | ||||||
|       haptics('Error') |       haptics('Error') | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: i18next.t('screenImageViewer:content.save.failed') |         message: i18next.t('screenImageViewer:content.save.failed') | ||||||
|       }) |       }) | ||||||
|     }) |     }) | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ const ScreenImagesViewer = ({ | |||||||
|  |  | ||||||
|   const insets = useSafeAreaInsets() |   const insets = useSafeAreaInsets() | ||||||
|  |  | ||||||
|   const { mode, theme } = useTheme() |   const { mode } = useTheme() | ||||||
|   const { t } = useTranslation('screenImageViewer') |   const { t } = useTranslation('screenImageViewer') | ||||||
|  |  | ||||||
|   const initialIndex = imageUrls.findIndex(image => image.id === id) |   const initialIndex = imageUrls.findIndex(image => image.id === id) | ||||||
| @@ -61,7 +61,7 @@ const ScreenImagesViewer = ({ | |||||||
|       async buttonIndex => { |       async buttonIndex => { | ||||||
|         switch (buttonIndex) { |         switch (buttonIndex) { | ||||||
|           case 0: |           case 0: | ||||||
|             saveImage({ theme, image: imageUrls[currentIndex] }) |             saveImage({ image: imageUrls[currentIndex] }) | ||||||
|             break |             break | ||||||
|           case 1: |           case 1: | ||||||
|             switch (Platform.OS) { |             switch (Platform.OS) { | ||||||
| @@ -188,7 +188,7 @@ const ScreenImagesViewer = ({ | |||||||
|             async buttonIndex => { |             async buttonIndex => { | ||||||
|               switch (buttonIndex) { |               switch (buttonIndex) { | ||||||
|                 case 0: |                 case 0: | ||||||
|                   saveImage({ theme, image: imageUrls[currentIndex] }) |                   saveImage({ image: imageUrls[currentIndex] }) | ||||||
|                   break |                   break | ||||||
|                 case 1: |                 case 1: | ||||||
|                   switch (Platform.OS) { |                   switch (Platform.OS) { | ||||||
|   | |||||||
| @@ -129,15 +129,11 @@ const TabMe = React.memo( | |||||||
|           name='Tab-Me-Push' |           name='Tab-Me-Push' | ||||||
|           component={TabMePush} |           component={TabMePush} | ||||||
|           options={({ navigation }) => ({ |           options={({ navigation }) => ({ | ||||||
|             presentation: 'modal', |  | ||||||
|             headerShown: true, |  | ||||||
|             title: t('me.stacks.push.name'), |             title: t('me.stacks.push.name'), | ||||||
|             ...(Platform.OS === 'android' && { |             ...(Platform.OS === 'android' && { | ||||||
|               headerCenter: () => <HeaderCenter content={t('me.stacks.push.name')} /> |               headerCenter: () => <HeaderCenter content={t('me.stacks.push.name')} /> | ||||||
|             }), |             }), | ||||||
|             headerLeft: () => ( |             headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} /> | ||||||
|               <HeaderLeft content='ChevronDown' onPress={() => navigation.goBack()} /> |  | ||||||
|             ) |  | ||||||
|           })} |           })} | ||||||
|         /> |         /> | ||||||
|         <Stack.Screen |         <Stack.Screen | ||||||
|   | |||||||
| @@ -41,8 +41,7 @@ const TabMeListAccounts: React.FC<TabMeStackScreenProps<'Tab-Me-List-Accounts'>> | |||||||
|     }, |     }, | ||||||
|     onError: () => { |     onError: () => { | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |         message: t('common:message.error.message', { | ||||||
|           function: t('me.listAccounts.error') |           function: t('me.listAccounts.error') | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -41,8 +41,7 @@ const TabMeListEdit: React.FC<TabMeStackScreenProps<'Tab-Me-List-Edit'>> = ({ | |||||||
|     onError: () => { |     onError: () => { | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         ref: messageRef, |         ref: messageRef, | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |         message: t('common:message.error.message', { | ||||||
|           function: |           function: | ||||||
|             params.type === 'add' ? t('me.stacks.listAdd.name') : t('me.stacks.listEdit.name') |             params.type === 'add' ? t('me.stacks.listAdd.name') : t('me.stacks.listEdit.name') | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({ | |||||||
|   navigation, |   navigation, | ||||||
|   route: { key, params } |   route: { key, params } | ||||||
| }) => { | }) => { | ||||||
|   const { colors, theme } = useTheme() |   const { colors } = useTheme() | ||||||
|   const { t } = useTranslation('screenTabs') |   const { t } = useTranslation('screenTabs') | ||||||
|   const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list: params.id }] |   const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list: params.id }] | ||||||
|  |  | ||||||
| @@ -30,8 +30,7 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({ | |||||||
|     }, |     }, | ||||||
|     onError: () => { |     onError: () => { | ||||||
|       displayMessage({ |       displayMessage({ | ||||||
|         theme, |         type: 'danger', | ||||||
|         type: 'error', |  | ||||||
|         message: t('common:message.error.message', { |         message: t('common:message.error.message', { | ||||||
|           function: t('me.listDelete.heading') |           function: t('me.listDelete.heading') | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -121,7 +121,6 @@ const TabMeProfileFields: React.FC< | |||||||
|           content='Save' |           content='Save' | ||||||
|           onPress={async () => { |           onPress={async () => { | ||||||
|             mutateAsync({ |             mutateAsync({ | ||||||
|               theme, |  | ||||||
|               messageRef, |               messageRef, | ||||||
|               message: { |               message: { | ||||||
|                 text: 'me.profile.root.note.title', |                 text: 'me.profile.root.note.title', | ||||||
|   | |||||||
| @@ -75,7 +75,6 @@ const TabMeProfileName: React.FC< | |||||||
|           content='Save' |           content='Save' | ||||||
|           onPress={async () => { |           onPress={async () => { | ||||||
|             mutateAsync({ |             mutateAsync({ | ||||||
|               theme, |  | ||||||
|               messageRef, |               messageRef, | ||||||
|               message: { |               message: { | ||||||
|                 text: 'me.profile.root.name.title', |                 text: 'me.profile.root.name.title', | ||||||
|   | |||||||
| @@ -75,7 +75,6 @@ const TabMeProfileNote: React.FC< | |||||||
|           content='Save' |           content='Save' | ||||||
|           onPress={async () => { |           onPress={async () => { | ||||||
|             mutateAsync({ |             mutateAsync({ | ||||||
|               theme, |  | ||||||
|               messageRef, |               messageRef, | ||||||
|               message: { |               message: { | ||||||
|                 text: 'me.profile.root.note.title', |                 text: 'me.profile.root.note.title', | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import { TabMeProfileStackScreenProps } from '@utils/navigation/navigators' | |||||||
| import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile' | import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile' | ||||||
| import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences' | import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' | import { useTheme } from '@utils/styles/ThemeManager' | ||||||
| import React, { RefObject, useCallback } from 'react' | import React, { RefObject } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import FlashMessage from 'react-native-flash-message' | import FlashMessage from 'react-native-flash-message' | ||||||
| import { ScrollView } from 'react-native-gesture-handler' | import { ScrollView } from 'react-native-gesture-handler' | ||||||
| @@ -16,7 +16,7 @@ const TabMeProfileRoot: React.FC< | |||||||
|     messageRef: RefObject<FlashMessage> |     messageRef: RefObject<FlashMessage> | ||||||
|   } |   } | ||||||
| > = ({ messageRef, navigation }) => { | > = ({ messageRef, navigation }) => { | ||||||
|   const { mode, theme } = useTheme() |   const { mode } = useTheme() | ||||||
|   const { t } = useTranslation('screenTabs') |   const { t } = useTranslation('screenTabs') | ||||||
|  |  | ||||||
|   const { showActionSheetWithOptions } = useActionSheet() |   const { showActionSheetWithOptions } = useActionSheet() | ||||||
| @@ -25,90 +25,6 @@ const TabMeProfileRoot: React.FC< | |||||||
|   const { mutateAsync } = useProfileMutation() |   const { mutateAsync } = useProfileMutation() | ||||||
|   const dispatch = useAppDispatch() |   const dispatch = useAppDispatch() | ||||||
|  |  | ||||||
|   const onPressVisibility = useCallback(() => { |  | ||||||
|     showActionSheetWithOptions( |  | ||||||
|       { |  | ||||||
|         title: t('me.profile.root.visibility.title'), |  | ||||||
|         options: [ |  | ||||||
|           t('me.profile.root.visibility.options.public'), |  | ||||||
|           t('me.profile.root.visibility.options.unlisted'), |  | ||||||
|           t('me.profile.root.visibility.options.private'), |  | ||||||
|           t('common:buttons.cancel') |  | ||||||
|         ], |  | ||||||
|         cancelButtonIndex: 3, |  | ||||||
|         userInterfaceStyle: mode |  | ||||||
|       }, |  | ||||||
|       async buttonIndex => { |  | ||||||
|         switch (buttonIndex) { |  | ||||||
|           case 0: |  | ||||||
|           case 1: |  | ||||||
|           case 2: |  | ||||||
|             const indexVisibilityMapping = ['public', 'unlisted', 'private'] as [ |  | ||||||
|               'public', |  | ||||||
|               'unlisted', |  | ||||||
|               'private' |  | ||||||
|             ] |  | ||||||
|             if (data?.source.privacy !== indexVisibilityMapping[buttonIndex]) { |  | ||||||
|               mutateAsync({ |  | ||||||
|                 theme, |  | ||||||
|                 messageRef, |  | ||||||
|                 message: { |  | ||||||
|                   text: 'me.profile.root.visibility.title', |  | ||||||
|                   succeed: false, |  | ||||||
|                   failed: true |  | ||||||
|                 }, |  | ||||||
|                 type: 'source[privacy]', |  | ||||||
|                 data: indexVisibilityMapping[buttonIndex] |  | ||||||
|               }).then(() => dispatch(updateAccountPreferences())) |  | ||||||
|             } |  | ||||||
|             break |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     ) |  | ||||||
|   }, [theme, data?.source?.privacy]) |  | ||||||
|  |  | ||||||
|   const onPressSensitive = useCallback(() => { |  | ||||||
|     mutateAsync({ |  | ||||||
|       theme, |  | ||||||
|       messageRef, |  | ||||||
|       message: { |  | ||||||
|         text: 'me.profile.root.sensitive.title', |  | ||||||
|         succeed: false, |  | ||||||
|         failed: true |  | ||||||
|       }, |  | ||||||
|       type: 'source[sensitive]', |  | ||||||
|       data: data?.source.sensitive === undefined ? true : !data.source.sensitive |  | ||||||
|     }).then(() => dispatch(updateAccountPreferences())) |  | ||||||
|   }, [data?.source.sensitive]) |  | ||||||
|  |  | ||||||
|   const onPressLock = useCallback(() => { |  | ||||||
|     mutateAsync({ |  | ||||||
|       theme, |  | ||||||
|       messageRef, |  | ||||||
|       message: { |  | ||||||
|         text: 'me.profile.root.lock.title', |  | ||||||
|         succeed: false, |  | ||||||
|         failed: true |  | ||||||
|       }, |  | ||||||
|       type: 'locked', |  | ||||||
|       data: data?.locked === undefined ? true : !data.locked |  | ||||||
|     }) |  | ||||||
|   }, [theme, data?.locked]) |  | ||||||
|  |  | ||||||
|   const onPressBot = useCallback(() => { |  | ||||||
|     mutateAsync({ |  | ||||||
|       theme, |  | ||||||
|       messageRef, |  | ||||||
|       message: { |  | ||||||
|         text: 'me.profile.root.bot.title', |  | ||||||
|         succeed: false, |  | ||||||
|         failed: true |  | ||||||
|       }, |  | ||||||
|       type: 'bot', |  | ||||||
|       data: data?.bot === undefined ? true : !data.bot |  | ||||||
|     }) |  | ||||||
|   }, [theme, data?.bot]) |  | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <ScrollView> |     <ScrollView> | ||||||
|       <MenuContainer> |       <MenuContainer> | ||||||
| @@ -166,12 +82,62 @@ const TabMeProfileRoot: React.FC< | |||||||
|           } |           } | ||||||
|           loading={isLoading} |           loading={isLoading} | ||||||
|           iconBack='ChevronRight' |           iconBack='ChevronRight' | ||||||
|           onPress={onPressVisibility} |           onPress={() => | ||||||
|  |             showActionSheetWithOptions( | ||||||
|  |               { | ||||||
|  |                 title: t('me.profile.root.visibility.title'), | ||||||
|  |                 options: [ | ||||||
|  |                   t('me.profile.root.visibility.options.public'), | ||||||
|  |                   t('me.profile.root.visibility.options.unlisted'), | ||||||
|  |                   t('me.profile.root.visibility.options.private'), | ||||||
|  |                   t('common:buttons.cancel') | ||||||
|  |                 ], | ||||||
|  |                 cancelButtonIndex: 3, | ||||||
|  |                 userInterfaceStyle: mode | ||||||
|  |               }, | ||||||
|  |               async buttonIndex => { | ||||||
|  |                 switch (buttonIndex) { | ||||||
|  |                   case 0: | ||||||
|  |                   case 1: | ||||||
|  |                   case 2: | ||||||
|  |                     const indexVisibilityMapping = ['public', 'unlisted', 'private'] as [ | ||||||
|  |                       'public', | ||||||
|  |                       'unlisted', | ||||||
|  |                       'private' | ||||||
|  |                     ] | ||||||
|  |                     if (data?.source.privacy !== indexVisibilityMapping[buttonIndex]) { | ||||||
|  |                       mutateAsync({ | ||||||
|  |                         messageRef, | ||||||
|  |                         message: { | ||||||
|  |                           text: 'me.profile.root.visibility.title', | ||||||
|  |                           succeed: false, | ||||||
|  |                           failed: true | ||||||
|  |                         }, | ||||||
|  |                         type: 'source[privacy]', | ||||||
|  |                         data: indexVisibilityMapping[buttonIndex] | ||||||
|  |                       }).then(() => dispatch(updateAccountPreferences())) | ||||||
|  |                     } | ||||||
|  |                     break | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             ) | ||||||
|  |           } | ||||||
|         /> |         /> | ||||||
|         <MenuRow |         <MenuRow | ||||||
|           title={t('me.profile.root.sensitive.title')} |           title={t('me.profile.root.sensitive.title')} | ||||||
|           switchValue={data?.source.sensitive} |           switchValue={data?.source.sensitive} | ||||||
|           switchOnValueChange={onPressSensitive} |           switchOnValueChange={() => | ||||||
|  |             mutateAsync({ | ||||||
|  |               messageRef, | ||||||
|  |               message: { | ||||||
|  |                 text: 'me.profile.root.sensitive.title', | ||||||
|  |                 succeed: false, | ||||||
|  |                 failed: true | ||||||
|  |               }, | ||||||
|  |               type: 'source[sensitive]', | ||||||
|  |               data: data?.source.sensitive === undefined ? true : !data.source.sensitive | ||||||
|  |             }).then(() => dispatch(updateAccountPreferences())) | ||||||
|  |           } | ||||||
|           loading={isLoading} |           loading={isLoading} | ||||||
|         /> |         /> | ||||||
|       </MenuContainer> |       </MenuContainer> | ||||||
| @@ -180,14 +146,36 @@ const TabMeProfileRoot: React.FC< | |||||||
|           title={t('me.profile.root.lock.title')} |           title={t('me.profile.root.lock.title')} | ||||||
|           description={t('me.profile.root.lock.description')} |           description={t('me.profile.root.lock.description')} | ||||||
|           switchValue={data?.locked} |           switchValue={data?.locked} | ||||||
|           switchOnValueChange={onPressLock} |           switchOnValueChange={() => | ||||||
|  |             mutateAsync({ | ||||||
|  |               messageRef, | ||||||
|  |               message: { | ||||||
|  |                 text: 'me.profile.root.lock.title', | ||||||
|  |                 succeed: false, | ||||||
|  |                 failed: true | ||||||
|  |               }, | ||||||
|  |               type: 'locked', | ||||||
|  |               data: data?.locked === undefined ? true : !data.locked | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|           loading={isLoading} |           loading={isLoading} | ||||||
|         /> |         /> | ||||||
|         <MenuRow |         <MenuRow | ||||||
|           title={t('me.profile.root.bot.title')} |           title={t('me.profile.root.bot.title')} | ||||||
|           description={t('me.profile.root.bot.description')} |           description={t('me.profile.root.bot.description')} | ||||||
|           switchValue={data?.bot} |           switchValue={data?.bot} | ||||||
|           switchOnValueChange={onPressBot} |           switchOnValueChange={() => | ||||||
|  |             mutateAsync({ | ||||||
|  |               messageRef, | ||||||
|  |               message: { | ||||||
|  |                 text: 'me.profile.root.bot.title', | ||||||
|  |                 succeed: false, | ||||||
|  |                 failed: true | ||||||
|  |               }, | ||||||
|  |               type: 'bot', | ||||||
|  |               data: data?.bot === undefined ? true : !data.bot | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|           loading={isLoading} |           loading={isLoading} | ||||||
|         /> |         /> | ||||||
|       </MenuContainer> |       </MenuContainer> | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ import { MenuRow } from '@components/Menu' | |||||||
| import { displayMessage } from '@components/Message' | import { displayMessage } from '@components/Message' | ||||||
| import { useActionSheet } from '@expo/react-native-action-sheet' | import { useActionSheet } from '@expo/react-native-action-sheet' | ||||||
| import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile' | import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import React, { RefObject } from 'react' | import React, { RefObject } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import FlashMessage from 'react-native-flash-message' | import FlashMessage from 'react-native-flash-message' | ||||||
| @@ -14,7 +13,6 @@ export interface Props { | |||||||
| } | } | ||||||
|  |  | ||||||
| const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => { | const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => { | ||||||
|   const { theme } = useTheme() |  | ||||||
|   const { t } = useTranslation('screenTabs') |   const { t } = useTranslation('screenTabs') | ||||||
|  |  | ||||||
|   const { showActionSheetWithOptions } = useActionSheet() |   const { showActionSheetWithOptions } = useActionSheet() | ||||||
| @@ -40,7 +38,6 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => { | |||||||
|         }) |         }) | ||||||
|         if (image[0].uri) { |         if (image[0].uri) { | ||||||
|           mutation.mutate({ |           mutation.mutate({ | ||||||
|             theme, |  | ||||||
|             messageRef, |             messageRef, | ||||||
|             message: { |             message: { | ||||||
|               text: `me.profile.root.${type}.title`, |               text: `me.profile.root.${type}.title`, | ||||||
| @@ -54,8 +51,7 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => { | |||||||
|           displayMessage({ |           displayMessage({ | ||||||
|             ref: messageRef, |             ref: messageRef, | ||||||
|             message: t('screenTabs:me.profile.mediaSelectionFailed'), |             message: t('screenTabs:me.profile.mediaSelectionFailed'), | ||||||
|             theme: theme, |             type: 'danger' | ||||||
|             type: 'error' |  | ||||||
|           }) |           }) | ||||||
|         } |         } | ||||||
|       }} |       }} | ||||||
|   | |||||||
| @@ -7,7 +7,6 @@ import { useAppDispatch } from '@root/store' | |||||||
| import * as Sentry from '@sentry/react-native' | import * as Sentry from '@sentry/react-native' | ||||||
| import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice' | import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice' | ||||||
| import { disableAllPushes, getInstances } from '@utils/slices/instancesSlice' | import { disableAllPushes, getInstances } from '@utils/slices/instancesSlice' | ||||||
| import { useTheme } from '@utils/styles/ThemeManager' |  | ||||||
| import * as Notifications from 'expo-notifications' | import * as Notifications from 'expo-notifications' | ||||||
| import { useEffect } from 'react' | import { useEffect } from 'react' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| @@ -16,7 +15,6 @@ import { useSelector } from 'react-redux' | |||||||
|  |  | ||||||
| const pushUseConnect = () => { | const pushUseConnect = () => { | ||||||
|   const { t } = useTranslation('screens') |   const { t } = useTranslation('screens') | ||||||
|   const { theme } = useTheme() |  | ||||||
|  |  | ||||||
|   const dispatch = useAppDispatch() |   const dispatch = useAppDispatch() | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
| @@ -39,8 +37,7 @@ const pushUseConnect = () => { | |||||||
|         Notifications.setBadgeCountAsync(0) |         Notifications.setBadgeCountAsync(0) | ||||||
|         if (error?.status == 404) { |         if (error?.status == 404) { | ||||||
|           displayMessage({ |           displayMessage({ | ||||||
|             theme, |             type: 'danger', | ||||||
|             type: 'error', |  | ||||||
|             duration: 'long', |             duration: 'long', | ||||||
|             message: t('pushError.message'), |             message: t('pushError.message'), | ||||||
|             description: t('pushError.description'), |             description: t('pushError.description'), | ||||||
|   | |||||||
| @@ -2,15 +2,13 @@ import apiInstance from '@api/instance' | |||||||
| import haptics from '@components/haptics' | import haptics from '@components/haptics' | ||||||
| import { displayMessage } from '@components/Message' | import { displayMessage } from '@components/Message' | ||||||
| import queryClient from '@helpers/queryClient' | import queryClient from '@helpers/queryClient' | ||||||
| import { Theme } from '@utils/styles/themes' |  | ||||||
| import { AxiosError } from 'axios' | import { AxiosError } from 'axios' | ||||||
| import i18next from 'i18next' | import i18next from 'i18next' | ||||||
| import { RefObject } from 'react' | import { RefObject } from 'react' | ||||||
| import FlashMessage from 'react-native-flash-message' | import FlashMessage from 'react-native-flash-message' | ||||||
| import { useMutation, useQuery, UseQueryOptions } from 'react-query' | import { useMutation, useQuery, UseQueryOptions } from 'react-query' | ||||||
|  |  | ||||||
| type AccountWithSource = Mastodon.Account & | type AccountWithSource = Mastodon.Account & Required<Pick<Mastodon.Account, 'source'>> | ||||||
|   Required<Pick<Mastodon.Account, 'source'>> |  | ||||||
|  |  | ||||||
| type QueryKeyProfile = ['Profile'] | type QueryKeyProfile = ['Profile'] | ||||||
| const queryKey: QueryKeyProfile = ['Profile'] | const queryKey: QueryKeyProfile = ['Profile'] | ||||||
| @@ -52,7 +50,6 @@ type MutationVarsProfileBase = | |||||||
|     } |     } | ||||||
|  |  | ||||||
| type MutationVarsProfile = MutationVarsProfileBase & { | type MutationVarsProfile = MutationVarsProfileBase & { | ||||||
|   theme: Theme |  | ||||||
|   messageRef: RefObject<FlashMessage> |   messageRef: RefObject<FlashMessage> | ||||||
|   message: { |   message: { | ||||||
|     text: string |     text: string | ||||||
| @@ -92,11 +89,9 @@ const mutationFunction = async ({ type, data }: MutationVarsProfile) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| const useProfileMutation = () => { | const useProfileMutation = () => { | ||||||
|   return useMutation< |   return useMutation<{ body: AccountWithSource }, AxiosError, MutationVarsProfile>( | ||||||
|     { body: AccountWithSource }, |     mutationFunction, | ||||||
|     AxiosError, |     { | ||||||
|     MutationVarsProfile |  | ||||||
|   >(mutationFunction, { |  | ||||||
|       onMutate: async variables => { |       onMutate: async variables => { | ||||||
|         await queryClient.cancelQueries(queryKey) |         await queryClient.cancelQueries(queryKey) | ||||||
|  |  | ||||||
| @@ -137,8 +132,7 @@ const useProfileMutation = () => { | |||||||
|               type: i18next.t(`screenTabs:${variables.message.text}`) |               type: i18next.t(`screenTabs:${variables.message.text}`) | ||||||
|             }), |             }), | ||||||
|             ...(err && { description: err.message }), |             ...(err && { description: err.message }), | ||||||
|           theme: variables.theme, |             type: 'danger' | ||||||
|           type: 'error' |  | ||||||
|           }) |           }) | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
| @@ -150,7 +144,6 @@ const useProfileMutation = () => { | |||||||
|             message: i18next.t('screenTabs:me.profile.feedback.succeed', { |             message: i18next.t('screenTabs:me.profile.feedback.succeed', { | ||||||
|               type: i18next.t(`screenTabs:${variables.message.text}`) |               type: i18next.t(`screenTabs:${variables.message.text}`) | ||||||
|             }), |             }), | ||||||
|           theme: variables.theme, |  | ||||||
|             type: 'success' |             type: 'success' | ||||||
|           }) |           }) | ||||||
|         } |         } | ||||||
| @@ -158,7 +151,8 @@ const useProfileMutation = () => { | |||||||
|       onSettled: () => { |       onSettled: () => { | ||||||
|         queryClient.invalidateQueries(queryKey) |         queryClient.invalidateQueries(queryKey) | ||||||
|       } |       } | ||||||
|   }) |     } | ||||||
|  |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| export { useProfileQuery, useProfileMutation } | export { useProfileQuery, useProfileMutation } | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
| import apiInstance from '@api/instance' | import apiInstance from '@api/instance' | ||||||
| import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot' | import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot' | ||||||
|  | import { displayMessage } from '@components/Message' | ||||||
| import i18n from '@root/i18n/i18n' | import i18n from '@root/i18n/i18n' | ||||||
| import { RootState } from '@root/store' | import { RootState } from '@root/store' | ||||||
| import * as Sentry from '@sentry/react-native' | import * as Sentry from '@sentry/react-native' | ||||||
| import { InstanceLatest } from '@utils/migrations/instances/migration' | import { InstanceLatest } from '@utils/migrations/instances/migration' | ||||||
| import { getInstance } from '@utils/slices/instancesSlice' | import { getInstance } from '@utils/slices/instancesSlice' | ||||||
|  | import { Theme } from '@utils/styles/themes' | ||||||
| import * as Notifications from 'expo-notifications' | import * as Notifications from 'expo-notifications' | ||||||
| import * as Random from 'expo-random' | import * as Random from 'expo-random' | ||||||
|  | import i18next from 'i18next' | ||||||
| import { Platform } from 'react-native' | import { Platform } from 'react-native' | ||||||
| import base64 from 'react-native-base64' | import base64 from 'react-native-base64' | ||||||
| import androidDefaults from './androidDefaults' | import androidDefaults from './androidDefaults' | ||||||
| @@ -74,6 +77,12 @@ const pushRegister = async ( | |||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   if (!res.body.server_key?.length) { |   if (!res.body.server_key?.length) { | ||||||
|  |     displayMessage({ | ||||||
|  |       type: 'danger', | ||||||
|  |       duration: 'long', | ||||||
|  |       message: i18next.t('screenTabs:me.push.missingServerKey.message'), | ||||||
|  |       description: i18next.t('screenTabs:me.push.missingServerKey.description') | ||||||
|  |     }) | ||||||
|     Sentry.setContext('Push server key', { |     Sentry.setContext('Push server key', { | ||||||
|       instance: instanceUri, |       instance: instanceUri, | ||||||
|       resBody: res.body |       resBody: res.body | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user