diff --git a/package.json b/package.json index 3677dd7b..ddb28a4f 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "react-query": "^3.12.0", "react-redux": "^7.2.2", "react-timeago": "^5.2.0", - "reconnecting-websocket": "^4.4.0", "redux-persist": "^6.0.0", "rn-placeholder": "^3.0.3", "sentry-expo": "^3.0.4", @@ -121,4 +120,4 @@ "typescript": "~4.1.3", "uri-scheme": "^1.0.67" } -} +} \ No newline at end of file diff --git a/src/api/websocket.ts b/src/api/websocket.ts deleted file mode 100644 index 3df4568a..00000000 --- a/src/api/websocket.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { QueryKeyTimeline } from '@utils/queryHooks/timeline' -import { - getInstance, - updateInstanceNotification -} from '@utils/slices/instancesSlice' -import { useEffect, useRef } from 'react' -import { useQueryClient } from 'react-query' -import { useDispatch, useSelector } from 'react-redux' -import ReconnectingWebSocket from 'reconnecting-websocket' - -const useWebsocket = ({ - stream, - event -}: { - stream: Mastodon.WebSocketStream - event: 'update' | 'delete' | 'notification' -}) => { - const queryClient = useQueryClient() - const dispatch = useDispatch() - const localInstance = useSelector( - getInstance, - (prev, next) => - prev?.urls.streaming_api === next?.urls.streaming_api && - prev?.token === next?.token - ) - - const rws = useRef() - useEffect(() => { - if (!localInstance) { - return - } - rws.current = new ReconnectingWebSocket( - `${localInstance.urls.streaming_api}/api/v1/streaming?stream=${stream}&access_token=${localInstance.token}` - ) - rws.current.addEventListener('message', ({ data }) => { - const message: Mastodon.WebSocket = JSON.parse(data) - if (message.event === event) { - switch (message.event) { - case 'notification': - const payload: Mastodon.Notification = JSON.parse(message.payload) - dispatch( - updateInstanceNotification({ latestTime: payload.created_at }) - ) - const queryKey: QueryKeyTimeline = [ - 'Timeline', - { page: 'Notifications' } - ] - queryClient.invalidateQueries(queryKey) - break - } - } - }) - }, [localInstance?.urls.streaming_api, localInstance?.token]) -} - -export default useWebsocket diff --git a/src/components/Message.tsx b/src/components/Message.tsx index 6342b960..323b76ed 100644 --- a/src/components/Message.tsx +++ b/src/components/Message.tsx @@ -7,6 +7,7 @@ import FlashMessage, { showMessage } from 'react-native-flash-message' import haptics from './haptics' const displayMessage = ({ + duration = 'short', autoHide = true, message, description, @@ -15,6 +16,7 @@ const displayMessage = ({ type }: | { + duration?: 'short' | 'long' autoHide?: boolean message: string description?: string @@ -23,6 +25,7 @@ const displayMessage = ({ type?: undefined } | { + duration?: 'short' | 'long' autoHide?: boolean message: string description?: string @@ -46,6 +49,7 @@ const displayMessage = ({ } showMessage({ + duration: duration === 'short' ? 1500 : 3000, autoHide, message, description, @@ -80,7 +84,7 @@ const Message = React.memo( backgroundColor: theme.background, shadowColor: theme.primary, shadowOffset: { width: 0, height: 0 }, - shadowOpacity: mode === 'light' ? 0.16 : 0.32, + shadowOpacity: mode === 'light' ? 0.16 : 0.24, shadowRadius: 4 }} titleStyle={{ diff --git a/src/components/Timeline.tsx b/src/components/Timeline.tsx index 62be75b7..2cf02347 100644 --- a/src/components/Timeline.tsx +++ b/src/components/Timeline.tsx @@ -1,6 +1,7 @@ import ComponentSeparator from '@components/Separator' import { useScrollToTop } from '@react-navigation/native' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' +import { getInstanceActive } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { RefObject, useCallback, useRef } from 'react' @@ -15,6 +16,7 @@ import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated' +import { useSelector } from 'react-redux' import TimelineEmpty from './Timeline/Empty' import TimelineFooter from './Timeline/Footer' import TimelineRefresh, { @@ -40,6 +42,9 @@ const Timeline: React.FC = ({ disableInfinity = false, customProps }) => { + // Switching account update timeline + useSelector(getInstanceActive) + const { theme } = useTheme() const { diff --git a/src/screens/Tabs.tsx b/src/screens/Tabs.tsx index bae16bb8..5e45b4bb 100644 --- a/src/screens/Tabs.tsx +++ b/src/screens/Tabs.tsx @@ -1,22 +1,20 @@ import apiInstance from '@api/instance' -import useWebsocket from '@api/websocket' import haptics from '@components/haptics' import Icon from '@components/Icon' +import { displayMessage } from '@components/Message' import { BottomTabNavigationOptions, createBottomTabNavigator } from '@react-navigation/bottom-tabs' import { NavigatorScreenParams } from '@react-navigation/native' -import { StackScreenProps } from '@react-navigation/stack' -import { useTimelineQuery } from '@utils/queryHooks/timeline' +import { StackNavigationProp, StackScreenProps } from '@react-navigation/stack' +import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getPreviousTab } from '@utils/slices/contextsSlice' import { getInstanceAccount, getInstanceActive, - getInstanceNotification, getInstances, - updateInstanceActive, - updateInstanceNotification + updateInstanceActive } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import * as Notifications from 'expo-notifications' @@ -24,6 +22,7 @@ import { findIndex } from 'lodash' import React, { useCallback, useEffect, useMemo } from 'react' import { Platform } from 'react-native' import FastImage from 'react-native-fast-image' +import { useQueryClient } from 'react-query' import { useDispatch, useSelector } from 'react-redux' import TabLocal from './Tabs/Local' import TabMe from './Tabs/Me' @@ -44,7 +43,7 @@ export type ScreenTabsProp = StackScreenProps< > const convertNotificationToToot = ( - navigation: any, + navigation: StackNavigationProp, id: Mastodon.Notification['id'] ) => { apiInstance({ @@ -70,11 +69,48 @@ const Tab = createBottomTabNavigator() const ScreenTabs = React.memo( ({ navigation }: ScreenTabsProp) => { // Push notifications + const queryClient = useQueryClient() const instances = useSelector( getInstances, (prev, next) => prev.length === next.length ) - const lastNotificationResponse = Notifications.useLastNotificationResponse() + useEffect(() => { + const subscription = Notifications.addNotificationReceivedListener( + notification => { + const queryKey: QueryKeyTimeline = [ + 'Timeline', + { page: 'Notifications' } + ] + queryClient.invalidateQueries(queryKey) + const payloadData = notification.request.content.data as { + notification_id?: string + instanceUrl: string + accountId: string + } + + const notificationIndex = findIndex( + instances, + instance => + instance.url === payloadData.instanceUrl && + instance.account.id === payloadData.accountId + ) + if (notificationIndex !== -1 && payloadData.notification_id) { + displayMessage({ + duration: 'long', + message: notification.request.content.title!, + description: notification.request.content.body!, + onPress: () => + convertNotificationToToot( + navigation, + // @ts-ignore Typescript is wrong + payloadData.notification_id + ) + }) + } + } + ) + return () => subscription.remove() + }, [instances]) useEffect(() => { const subscription = Notifications.addNotificationResponseReceivedListener( ({ notification }) => { @@ -93,46 +129,13 @@ const ScreenTabs = React.memo( if (notificationIndex !== -1) { dispatch(updateInstanceActive(instances[notificationIndex])) } - if (payloadData?.notification_id) { - convertNotificationToToot( - navigation, - notification.request.content.data.notification_id as string - ) + if (payloadData.notification_id) { + convertNotificationToToot(navigation, payloadData.notification_id) } } ) return () => subscription.remove() - - // if ( - // lastNotificationResponse && - // lastNotificationResponse.actionIdentifier === - // Notifications.DEFAULT_ACTION_IDENTIFIER - // ) { - // const payloadData = lastNotificationResponse.notification.request - // .content.data as { - // notification_id?: string - // instanceUrl: string - // accountId: string - // } - - // const notificationIndex = findIndex( - // instances, - // instance => - // instance.url === payloadData.instanceUrl && - // instance.account.id === payloadData.accountId - // ) - // if (notificationIndex !== -1) { - // dispatch(updateInstanceActive(instances[notificationIndex])) - // } - // if (payloadData?.notification_id) { - // convertNotificationToToot( - // navigation, - // lastNotificationResponse.notification.request.content.data - // .notification_id as string - // ) - // } - // } - }, [instances, lastNotificationResponse]) + }, [instances]) const { mode, theme } = useTheme() const dispatch = useDispatch() @@ -210,33 +213,6 @@ const ScreenTabs = React.memo( ) const composeComponent = useCallback(() => null, []) - // On launch check if there is any unread noficiations - useTimelineQuery({ - page: 'Notifications', - options: { - enabled: instanceActive !== -1, - notifyOnChangeProps: [], - select: data => { - if (data.pages[0].body.length) { - dispatch( - updateInstanceNotification({ - // @ts-ignore - latestTime: data.pages[0].body[0].created_at - }) - ) - } - return data - } - } - }) - useWebsocket({ stream: 'user', event: 'notification' }) - const localNotification = useSelector( - getInstanceNotification, - (prev, next) => - prev?.readTime === next?.readTime && - prev?.latestTime === next?.latestTime - ) - const previousTab = useSelector(getPreviousTab, () => true) return ( @@ -252,23 +228,7 @@ const ScreenTabs = React.memo( component={composeComponent} listeners={composeListeners} /> - + ) diff --git a/src/screens/Tabs/Me/Root/Logout.tsx b/src/screens/Tabs/Me/Root/Logout.tsx index ff9ca312..037ee9b6 100644 --- a/src/screens/Tabs/Me/Root/Logout.tsx +++ b/src/screens/Tabs/Me/Root/Logout.tsx @@ -1,7 +1,7 @@ import Button from '@components/Button' import haptics from '@root/components/haptics' import removeInstance from '@utils/slices/instances/remove' -import { getInstanceActive } from '@utils/slices/instancesSlice' +import { getInstance, getInstanceActive } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import React from 'react' import { useTranslation } from 'react-i18next' @@ -13,7 +13,7 @@ const Logout: React.FC = () => { const { t } = useTranslation('meRoot') const dispatch = useDispatch() const queryClient = useQueryClient() - const instanceActive = useSelector(getInstanceActive) + const instance = useSelector(getInstance) return (