diff --git a/package.json b/package.json index 46c0ccec..0fb2aba0 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@types/react-redux": "^7.1.12", "@welldone-software/why-did-you-render": "^6.0.3", "babel-plugin-module-resolver": "^4.1.0", + "chalk": "^4.1.0", "typescript": "~4.0.0" }, "private": true diff --git a/src/Index.tsx b/src/Index.tsx index 74e184f0..0646e0c0 100644 --- a/src/Index.tsx +++ b/src/Index.tsx @@ -6,7 +6,7 @@ import { } from '@react-navigation/native' import { enableScreens } from 'react-native-screens' -import React, { useEffect, useRef } from 'react' +import React, { useEffect, useMemo, useRef } from 'react' import { StatusBar } from 'react-native' import Toast from 'react-native-toast-message' import { Feather } from '@expo/vector-icons' @@ -23,12 +23,15 @@ import { toast, toastConfig } from '@components/toast' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { + getLocalNotification, getLocalUrl, - updateLocalAccountPreferences + updateLocalAccountPreferences, + updateNotification } from '@utils/slices/instancesSlice' -import { useQuery } from 'react-query' +import { useInfiniteQuery, useQuery } from 'react-query' import { announcementFetch } from './utils/fetches/announcementsFetch' import client from './api/client' +import { timelineFetch } from './utils/fetches/timelineFetch' enableScreens() const Tab = createBottomTabNavigator() @@ -55,6 +58,7 @@ export const Index: React.FC = ({ localCorrupt }) => { dark = 'light-content' } + // On launch display login credentials corrupt information useEffect(() => { const showLocalCorrect = localCorrupt ? toast({ @@ -67,27 +71,66 @@ export const Index: React.FC = ({ localCorrupt }) => { return showLocalCorrect }, [localCorrupt]) + // On launch check if there is any unread announcements + const navigationRef = useRef(null) + useEffect(() => { + localInstance && + client({ + method: 'get', + instance: 'local', + url: `announcements` + }) + .then(({ body }: { body?: Mastodon.Announcement[] }) => { + if (body?.filter(announcement => !announcement.read).length) { + navigationRef.current?.navigate('Screen-Shared-Announcements') + } + }) + .catch(() => {}) + }, []) + + // On launch check if there is any unread noficiations + const queryNotification = useInfiniteQuery( + ['Notifications', {}] as QueryKey.Timeline, + timelineFetch, + { enabled: localInstance ? true : false, cacheTime: 1000 * 30 } + ) + const prevNotification = useSelector(getLocalNotification) + useEffect(() => { + if (queryNotification.data?.pages) { + const flattenData = queryNotification.data.pages.flatMap(d => [ + ...d?.toots + ]) + const latestNotificationTime = flattenData.length + ? flattenData[0].created_at + : undefined + + if (!prevNotification || !prevNotification.latestTime) { + dispatch( + updateNotification({ + unread: true + }) + ) + } else if ( + latestNotificationTime && + new Date(prevNotification.latestTime) < new Date(latestNotificationTime) + ) { + dispatch( + updateNotification({ + unread: true, + latestTime: latestNotificationTime + }) + ) + } + } + }, [queryNotification.data?.pages]) + + // Lazily update users's preferences, for e.g. composing default visibility useEffect(() => { if (localInstance) { dispatch(updateLocalAccountPreferences()) } }, []) - const navigationRef = useRef(null) - useEffect(() => { - client({ - method: 'get', - instance: 'local', - url: `announcements` - }) - .then(({ body }: { body?: Mastodon.Announcement[] }) => { - if (body?.filter(announcement => !announcement.read).length) { - navigationRef.current?.navigate('Screen-Shared-Announcements') - } - }) - .catch(() => {}) - }, []) - return ( <> @@ -147,7 +190,7 @@ export const Index: React.FC = ({ localCorrupt }) => { ({ + listeners={({ navigation }) => ({ tabPress: e => { e.preventDefault() localInstance && @@ -162,6 +205,10 @@ export const Index: React.FC = ({ localCorrupt }) => { { if (!localInstance) { diff --git a/src/api/client.ts b/src/api/client.ts index 7da5df0d..80b3943d 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -1,6 +1,9 @@ import axios from 'axios' +import chalk from 'chalk' import { store, RootState } from '@root/store' +const ctx = new chalk.Instance({ level: 3 }) + const client = async ({ method, instance, @@ -25,13 +28,13 @@ const client = async ({ onUploadProgress?: (progressEvent: any) => void }): Promise => { console.log( - 'API call:', - 'Method ->', - method, - 'Endpoint ->', - url, - 'Params ->', - params + ctx.bgGreen.bold(' API ') + + ' ' + + method + + ctx.green(' -> ') + + `/${url}` + + (params ? ctx.green(' -> ') : ''), + params ? params : '' ) const state: RootState['instances'] = store.getState().instances const domain = diff --git a/src/screens/Notifications.tsx b/src/screens/Notifications.tsx index 06b86bee..faf63310 100644 --- a/src/screens/Notifications.tsx +++ b/src/screens/Notifications.tsx @@ -1,11 +1,12 @@ -import React from 'react' +import React, { useEffect } from 'react' import { createNativeStackNavigator } from 'react-native-screens/native-stack' import Timeline from '@components/Timelines/Timeline' import sharedScreens from '@screens/Shared/sharedScreens' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { RootState } from '@root/store' import { useTranslation } from 'react-i18next' +import { updateNotification } from '@root/utils/slices/instancesSlice' const Stack = createNativeStackNavigator() @@ -15,6 +16,12 @@ const ScreenNotifications: React.FC = () => { (state: RootState) => state.instances.local.url ) + // Clear notification unread, but keep the time + const dispatch = useDispatch() + useEffect(() => { + dispatch(updateNotification({ unread: false })) + }, []) + return ( { state.local = initialStateLocal + }, + updateNotification: ( + state, + action: PayloadAction> + ) => { + state.local.notification = { + ...state.local.notification, + ...action.payload + } } }, extraReducers: builder => { @@ -109,12 +126,14 @@ const instancesSlice = createSlice({ export const getLocalUrl = (state: RootState) => state.instances.local.url export const getLocalToken = (state: RootState) => state.instances.local.token +export const getLocalNotification = (state: RootState) => + state.instances.local.notification export const getRemoteUrl = (state: RootState) => state.instances.remote.url export const getLocalAccountId = (state: RootState) => state.instances.local.account.id export const getLocalAccountPreferences = (state: RootState) => state.instances.local.account.preferences -export const { resetLocal } = instancesSlice.actions +export const { resetLocal, updateNotification } = instancesSlice.actions export default instancesSlice.reducer diff --git a/yarn.lock b/yarn.lock index 452ecb08..fa2e9c25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2107,7 +2107,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==