diff --git a/src/api/helpers/index.ts b/src/api/helpers/index.ts index 65013f79..566280af 100644 --- a/src/api/helpers/index.ts +++ b/src/api/helpers/index.ts @@ -54,12 +54,12 @@ const handleError = console.error(ctx.bold(' API '), ctx.bold('request'), error) shouldReportToSentry && Sentry.captureMessage(config.message) - return Promise.reject() + return Promise.reject(error) } else { console.error(ctx.bold(' API '), ctx.bold('internal'), error?.message) shouldReportToSentry && Sentry.captureMessage(config.message) - return Promise.reject() + return Promise.reject(error) } } diff --git a/src/api/tooot.ts b/src/api/tooot.ts index 7d96c622..0ebb5b57 100644 --- a/src/api/tooot.ts +++ b/src/api/tooot.ts @@ -1,4 +1,3 @@ -import * as Sentry from '@sentry/react-native' import { mapEnvironment } from '@utils/checkEnvironment' import axios from 'axios' import { ctx, handleError, userAgent } from './helpers' @@ -37,7 +36,7 @@ const apiTooot = async ({ ) return axios({ - timeout: method === 'post' ? 1000 * 60 : 1000 * 15, + timeout: method === 'post' ? 1000 * 60 : 1000 * 30, method, baseURL: `https://${TOOOT_API_DOMAIN}/`, url: `${url}`, diff --git a/src/components/Instance/index.tsx b/src/components/Instance/index.tsx index e21c49de..eb7fbddc 100644 --- a/src/components/Instance/index.tsx +++ b/src/components/Instance/index.tsx @@ -276,7 +276,7 @@ const ComponentInstance: React.FC = ({ style={{ color: colors.blue }} onPress={async () => WebBrowser.openBrowserAsync('https://tooot.app/privacy-policy', { - browserPackage: await browserPackage() + ...(await browserPackage()) }) } />, @@ -285,7 +285,7 @@ const ComponentInstance: React.FC = ({ style={{ color: colors.blue }} onPress={async () => WebBrowser.openBrowserAsync('https://tooot.app/terms-of-service', { - browserPackage: await browserPackage() + ...(await browserPackage()) }) } /> diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index cb3a8f0d..0284cf07 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -55,10 +55,10 @@ const TimelineDefault: React.FC = ({ const status = item.reblog ? item.reblog : item const ownAccount = status.account?.id === instanceAccount?.id const [spoilerExpanded, setSpoilerExpanded] = useState( - instanceAccount?.preferences['reading:expand:spoilers'] || false + instanceAccount?.preferences?.['reading:expand:spoilers'] || false ) const spoilerHidden = status.spoiler_text?.length - ? !instanceAccount?.preferences['reading:expand:spoilers'] && !spoilerExpanded + ? !instanceAccount?.preferences?.['reading:expand:spoilers'] && !spoilerExpanded : false const copiableContent = useRef<{ content: string; complete: boolean }>({ content: '', diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index 7eff27d1..81ee0d57 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -42,10 +42,10 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { : notification.account const ownAccount = notification.account?.id === instanceAccount?.id const [spoilerExpanded, setSpoilerExpanded] = useState( - instanceAccount.preferences['reading:expand:spoilers'] || false + instanceAccount.preferences?.['reading:expand:spoilers'] || false ) const spoilerHidden = notification.status?.spoiler_text?.length - ? !instanceAccount.preferences['reading:expand:spoilers'] && !spoilerExpanded + ? !instanceAccount.preferences?.['reading:expand:spoilers'] && !spoilerExpanded : false const copiableContent = useRef<{ content: string; complete: boolean }>({ content: '', diff --git a/src/components/Timeline/Shared/Attachment.tsx b/src/components/Timeline/Shared/Attachment.tsx index 174894cc..7f0128ed 100644 --- a/src/components/Timeline/Shared/Attachment.tsx +++ b/src/components/Timeline/Shared/Attachment.tsx @@ -31,10 +31,10 @@ const TimelineAttachment = () => { const account = useSelector( getInstanceAccount, (prev, next) => - prev.preferences['reading:expand:media'] === next.preferences['reading:expand:media'] + prev.preferences?.['reading:expand:media'] === next.preferences?.['reading:expand:media'] ) const defaultSensitive = () => { - switch (account.preferences['reading:expand:media']) { + switch (account.preferences?.['reading:expand:media']) { case 'show_all': return false case 'hide_all': diff --git a/src/components/Timeline/Shared/Content.tsx b/src/components/Timeline/Shared/Content.tsx index d7b799da..c1bc69a9 100644 --- a/src/components/Timeline/Shared/Content.tsx +++ b/src/components/Timeline/Shared/Content.tsx @@ -47,7 +47,7 @@ const TimelineContent: React.FC = ({ notificationOwnToot = false, setSpoi mentions={status.mentions} tags={status.tags} numberOfLines={ - instanceAccount.preferences['reading:expand:spoilers'] || inThread + instanceAccount.preferences?.['reading:expand:spoilers'] || inThread ? notificationOwnToot ? 2 : 999 diff --git a/src/components/Timeline/Shared/HeaderNotification.tsx b/src/components/Timeline/Shared/HeaderNotification.tsx index 7b93b702..02cdef2f 100644 --- a/src/components/Timeline/Shared/HeaderNotification.tsx +++ b/src/components/Timeline/Shared/HeaderNotification.tsx @@ -65,7 +65,7 @@ const TimelineHeaderNotification: React.FC = ({ notification }) => { `https://${url}/admin/reports/${notification.report.id}`, 'tooot://tooot', { - browserPackage: await browserPackage(), + ...(await browserPackage()), dismissButtonStyle: 'done', readerMode: false } diff --git a/src/components/Timeline/Shared/HeaderShared/Account.tsx b/src/components/Timeline/Shared/HeaderShared/Account.tsx index 991be557..57fda463 100644 --- a/src/components/Timeline/Shared/HeaderShared/Account.tsx +++ b/src/components/Timeline/Shared/HeaderShared/Account.tsx @@ -7,41 +7,40 @@ import { useTranslation } from 'react-i18next' import { View } from 'react-native' export interface Props { - account: Mastodon.Account + account?: Mastodon.Account withoutName?: boolean // For notification follow request etc. } -const HeaderSharedAccount = React.memo( - ({ account, withoutName = false }: Props) => { - const { t } = useTranslation('componentTimeline') - const { colors } = useTheme() +const HeaderSharedAccount: React.FC = ({ account, withoutName = false }) => { + if (!account) return null - return ( - - {withoutName ? null : ( - - - - )} + const { t } = useTranslation('componentTimeline') + const { colors } = useTheme() + + return ( + + {withoutName ? null : ( - @{account.acct} + - - ) - }, - () => true -) + )} + + @{account.acct} + + + ) +} export default HeaderSharedAccount diff --git a/src/components/contextMenu/account.ts b/src/components/contextMenu/account.ts index c85aa935..bc2137ea 100644 --- a/src/components/contextMenu/account.ts +++ b/src/components/contextMenu/account.ts @@ -41,7 +41,7 @@ const menuAccount = ({ const menus: ContextMenu[][] = [[]] - const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id) + const instanceAccount = useSelector(getInstanceAccount) const ownAccount = instanceAccount?.id === account.id const [enabled, setEnabled] = useState(openChange) diff --git a/src/components/openLink.ts b/src/components/openLink.ts index fc1dea57..4e566f7d 100644 --- a/src/components/openLink.ts +++ b/src/components/openLink.ts @@ -92,7 +92,7 @@ const openLink = async (url: string, navigation?: any) => { await WebBrowser.openBrowserAsync(encodeURI(url), { dismissButtonStyle: 'close', enableBarCollapsing: true, - browserPackage: await browserPackage() + ...(await browserPackage()) }) break case 'external': diff --git a/src/helpers/browserPackage.ts b/src/helpers/browserPackage.ts index b5e3a1b0..a2219053 100644 --- a/src/helpers/browserPackage.ts +++ b/src/helpers/browserPackage.ts @@ -1,16 +1,18 @@ import * as WebBrowser from 'expo-web-browser' import { Platform } from 'react-native' -const browserPackage = async () => { - let browserPackage: string | undefined +const browserPackage = async (): Promise<{ browserPackage?: string }> => { if (Platform.OS === 'android') { const tabsSupportingBrowsers = await WebBrowser.getCustomTabsSupportingBrowsersAsync() - browserPackage = - tabsSupportingBrowsers?.preferredBrowserPackage || - tabsSupportingBrowsers.browserPackages[0] || - tabsSupportingBrowsers.servicePackages[0] + return { + browserPackage: + tabsSupportingBrowsers?.preferredBrowserPackage || + tabsSupportingBrowsers.browserPackages[0] || + tabsSupportingBrowsers.servicePackages[0] + } + } else { + return {} } - return browserPackage } export default browserPackage diff --git a/src/screens/Compose/utils/parseState.ts b/src/screens/Compose/utils/parseState.ts index 811c3371..1f461f24 100644 --- a/src/screens/Compose/utils/parseState.ts +++ b/src/screens/Compose/utils/parseState.ts @@ -7,9 +7,8 @@ import { ComposeState } from './types' const assignVisibility = ( target: ComposeState['visibility'] ): Pick => { - const accountPreference = getInstanceAccount(store.getState())?.preferences[ - 'posting:default:visibility' - ] + const accountPreference = + getInstanceAccount(store.getState())?.preferences?.['posting:default:visibility'] || 'public' switch (target) { case 'direct': diff --git a/src/screens/Tabs/Me/Push.tsx b/src/screens/Tabs/Me/Push.tsx index eebecc52..04c56cac 100644 --- a/src/screens/Tabs/Me/Push.tsx +++ b/src/screens/Tabs/Me/Push.tsx @@ -179,7 +179,7 @@ const TabMePush: React.FC = () => { iconBack='ExternalLink' onPress={async () => WebBrowser.openBrowserAsync('https://tooot.app/how-push-works', { - browserPackage: await browserPackage() + ...(await browserPackage()) }) } /> diff --git a/src/screens/Tabs/Me/Root/Settings.tsx b/src/screens/Tabs/Me/Root/Settings.tsx index 6ea7d071..b9384e6f 100644 --- a/src/screens/Tabs/Me/Root/Settings.tsx +++ b/src/screens/Tabs/Me/Root/Settings.tsx @@ -31,7 +31,7 @@ const Settings: React.FC = () => { `https://${url}/settings/preferences`, 'tooot://tooot', { - browserPackage: await browserPackage(), + ...(await browserPackage()), dismissButtonStyle: 'done', readerMode: false } diff --git a/src/screens/Tabs/Me/Settings/Tooot.tsx b/src/screens/Tabs/Me/Settings/Tooot.tsx index a8c3ef05..256e4866 100644 --- a/src/screens/Tabs/Me/Settings/Tooot.tsx +++ b/src/screens/Tabs/Me/Settings/Tooot.tsx @@ -64,7 +64,7 @@ const SettingsTooot: React.FC = () => { }) } else { WebBrowser.openBrowserAsync('https://social.xmflsct.com/@tooot', { - browserPackage: await browserPackage() + ...(await browserPackage()) }) } }} diff --git a/src/utils/migrations/instances/v11.ts b/src/utils/migrations/instances/v11.ts index e02d72bb..dffb36eb 100644 --- a/src/utils/migrations/instances/v11.ts +++ b/src/utils/migrations/instances/v11.ts @@ -14,7 +14,7 @@ export type InstanceV11 = { id: Mastodon.Account['id'] acct: Mastodon.Account['acct'] avatarStatic: Mastodon.Account['avatar_static'] - preferences: Mastodon.Preferences + preferences?: Mastodon.Preferences } version: string configuration?: Mastodon.Instance['configuration'] diff --git a/src/utils/push/useConnect.ts b/src/utils/push/useConnect.ts index dbb45104..9e961753 100644 --- a/src/utils/push/useConnect.ts +++ b/src/utils/push/useConnect.ts @@ -1,12 +1,13 @@ import apiGeneral from '@api/general' -import { handleError } from '@api/helpers' import apiTooot from '@api/tooot' import { displayMessage } from '@components/Message' import navigationRef from '@helpers/navigationRef' import { useAppDispatch } from '@root/store' import * as Sentry from '@sentry/react-native' +import { useQuery } from '@tanstack/react-query' import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice' import { disableAllPushes, getInstances } from '@utils/slices/instancesSlice' +import { AxiosError } from 'axios' import * as Notifications from 'expo-notifications' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' @@ -25,16 +26,22 @@ const pushUseConnect = () => { const instances = useSelector(getInstances, (prev, next) => prev.length === next.length) const pushEnabled = instances.filter(instance => instance.push.global) - const connect = () => { - apiTooot({ - method: 'get', - url: `/push/connect/${expoToken}` - }) - .then(() => Notifications.setBadgeCountAsync(0)) - .catch(error => { - handleError({ message: 'Push connect error', captureResponse: true }) - - Notifications.setBadgeCountAsync(0) + const connectQuery = useQuery( + ['tooot', { endpoint: 'push/connect' }], + () => + apiTooot({ + method: 'get', + url: `push/connect/${expoToken}` + }), + { + enabled: false, + retry: 10, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + onSettled: () => Notifications.setBadgeCountAsync(0), + onError: error => { if (error?.status == 404) { displayMessage({ type: 'danger', @@ -72,21 +79,22 @@ const pushUseConnect = () => { } }) } - }) - } + } + } + ) useEffect(() => { Sentry.setContext('Push', { expoToken, pushEnabledCount: pushEnabled.length }) if (expoToken && pushEnabled.length) { - connect() + connectQuery.refetch() } const appStateListener = AppState.addEventListener('change', state => { if (expoToken && pushEnabled.length && state === 'active') { Notifications.getBadgeCountAsync().then(count => { if (count > 0) { - connect() + connectQuery.refetch() } }) } diff --git a/src/utils/slices/instancesSlice.ts b/src/utils/slices/instancesSlice.ts index 54ace209..7704cef9 100644 --- a/src/utils/slices/instancesSlice.ts +++ b/src/utils/slices/instancesSlice.ts @@ -220,7 +220,8 @@ const instancesSlice = createSlice({ // Update Instance Configuration .addCase(updateConfiguration.fulfilled, (state, action) => { const activeIndex = findInstanceActive(state.instances) - state.instances[activeIndex].version = action.payload?.version || '0' + state.instances[activeIndex].version = + typeof action.payload.version === 'string' ? action.payload.version : '0' state.instances[activeIndex].configuration = action.payload.configuration }) .addCase(updateConfiguration.rejected, (_, action) => { @@ -250,6 +251,9 @@ const instancesSlice = createSlice({ .addCase(checkEmojis.fulfilled, (state, action) => { if (!action.payload || !action.payload.length) return const activeIndex = findInstanceActive(state.instances) + if (!Array.isArray(state.instances[activeIndex].frequentEmojis)) { + state.instances[activeIndex].frequentEmojis = [] + } state.instances[activeIndex].frequentEmojis = state.instances[ activeIndex ]?.frequentEmojis?.filter(emoji => {