With unread notifications

This commit is contained in:
Zhiyuan Zheng 2020-12-24 10:28:51 +01:00
parent 4461cd1fa4
commit b5f267e6bf
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
6 changed files with 109 additions and 32 deletions

View File

@ -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

View File

@ -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<RootStackParamList>()
@ -55,6 +58,7 @@ export const Index: React.FC<Props> = ({ 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<Props> = ({ localCorrupt }) => {
return showLocalCorrect
}, [localCorrupt])
// On launch check if there is any unread announcements
const navigationRef = useRef<NavigationContainerRef>(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<NavigationContainerRef>(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 (
<>
<StatusBar barStyle={barStyle[mode]} />
@ -147,7 +190,7 @@ export const Index: React.FC<Props> = ({ localCorrupt }) => {
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
<Tab.Screen
name='Screen-Post'
listeners={({ navigation, route }) => ({
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault()
localInstance &&
@ -162,6 +205,10 @@ export const Index: React.FC<Props> = ({ localCorrupt }) => {
<Tab.Screen
name='Screen-Notifications'
component={ScreenNotifications}
options={{
tabBarBadge: prevNotification.unread ? '' : undefined,
tabBarBadgeStyle: { transform: [{ scale: 0.5 }] }
}}
listeners={{
tabPress: e => {
if (!localInstance) {

View File

@ -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<any> => {
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 =

View File

@ -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 (
<Stack.Navigator
screenOptions={{ headerTitle: t('notifications:heading') }}

View File

@ -1,4 +1,4 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { RootState } from '@root/store'
import client from '@api/client'
@ -11,13 +11,17 @@ export type InstancesState = {
id: Mastodon.Account['id'] | undefined
preferences: Mastodon.Preferences
}
notification: {
unread: boolean
latestTime?: Mastodon.Notification['created_at']
}
}
remote: {
url: string
}
}
const initialStateLocal = {
const initialStateLocal: InstancesState['local'] = {
url: undefined,
token: undefined,
account: {
@ -29,6 +33,10 @@ const initialStateLocal = {
'reading:expand:media': undefined,
'reading:expand:spoilers': undefined
}
},
notification: {
unread: false,
latestTime: undefined
}
}
@ -94,6 +102,15 @@ const instancesSlice = createSlice({
reducers: {
resetLocal: state => {
state.local = initialStateLocal
},
updateNotification: (
state,
action: PayloadAction<Partial<InstancesState['local']['notification']>>
) => {
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

View File

@ -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==