tooot/src/screens/Tabs.tsx

280 lines
8.8 KiB
TypeScript
Raw Normal View History

2021-02-28 17:41:21 +01:00
import apiInstance from '@api/instance'
import useWebsocket from '@api/websocket'
2021-01-30 01:29:15 +01:00
import haptics from '@components/haptics'
import Icon from '@components/Icon'
import {
BottomTabNavigationOptions,
createBottomTabNavigator
} from '@react-navigation/bottom-tabs'
import { NavigatorScreenParams } from '@react-navigation/native'
import { StackScreenProps } from '@react-navigation/stack'
2021-02-10 00:40:44 +01:00
import { useTimelineQuery } from '@utils/queryHooks/timeline'
2021-02-11 23:42:13 +01:00
import { getPreviousTab } from '@utils/slices/contextsSlice'
2021-01-30 01:29:15 +01:00
import {
2021-02-20 19:12:44 +01:00
getInstanceAccount,
getInstanceActive,
getInstanceNotification,
2021-02-28 17:41:21 +01:00
getInstances,
updateInstanceActive,
2021-02-20 19:12:44 +01:00
updateInstanceNotification
2021-01-30 01:29:15 +01:00
} from '@utils/slices/instancesSlice'
import { useTheme } from '@utils/styles/ThemeManager'
2021-02-28 17:41:21 +01:00
import * as Notifications from 'expo-notifications'
import { findIndex } from 'lodash'
import React, { useCallback, useEffect, useMemo } from 'react'
2021-01-30 01:29:15 +01:00
import { Platform } from 'react-native'
import FastImage from 'react-native-fast-image'
2021-02-10 00:40:44 +01:00
import { useDispatch, useSelector } from 'react-redux'
2021-01-30 01:29:15 +01:00
import TabLocal from './Tabs/Local'
import TabMe from './Tabs/Me'
import TabNotifications from './Tabs/Notifications'
import TabPublic from './Tabs/Public'
export type ScreenTabsParamList = {
'Tab-Local': NavigatorScreenParams<Nav.TabLocalStackParamList>
'Tab-Public': NavigatorScreenParams<Nav.TabPublicStackParamList>
2021-01-31 03:09:35 +01:00
'Tab-Compose': NavigatorScreenParams<Nav.ScreenComposeStackParamList>
2021-01-30 01:29:15 +01:00
'Tab-Notifications': NavigatorScreenParams<Nav.TabNotificationsStackParamList>
'Tab-Me': NavigatorScreenParams<Nav.TabMeStackParamList>
}
export type ScreenTabsProp = StackScreenProps<
Nav.RootStackParamList,
'Screen-Tabs'
>
2021-02-28 17:41:21 +01:00
const convertNotificationToToot = (
navigation: any,
id: Mastodon.Notification['id']
) => {
apiInstance<Mastodon.Notification>({
method: 'get',
url: `notifications/${id}`
}).then(({ body }) => {
// @ts-ignore
navigation.navigate('Tab-Notifications', {
screen: 'Tab-Notifications-Root'
})
if (body.status) {
// @ts-ignore
navigation.navigate('Tab-Notifications', {
screen: 'Tab-Shared-Toot',
params: { toot: body.status }
})
}
})
}
2021-01-30 01:29:15 +01:00
const Tab = createBottomTabNavigator<Nav.ScreenTabsStackParamList>()
2021-02-10 00:40:44 +01:00
const ScreenTabs = React.memo(
({ navigation }: ScreenTabsProp) => {
2021-02-28 17:41:21 +01:00
// Push notifications
const instances = useSelector(
getInstances,
(prev, next) => prev.length === next.length
)
const lastNotificationResponse = Notifications.useLastNotificationResponse()
useEffect(() => {
const subscription = Notifications.addNotificationResponseReceivedListener(
({ notification }) => {
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) {
dispatch(updateInstanceActive(instances[notificationIndex]))
}
if (payloadData?.notification_id) {
convertNotificationToToot(
navigation,
notification.request.content.data.notification_id as string
)
}
}
)
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])
2021-02-11 23:42:13 +01:00
const { mode, theme } = useTheme()
2021-02-10 00:40:44 +01:00
const dispatch = useDispatch()
2021-02-20 19:12:44 +01:00
const instanceActive = useSelector(getInstanceActive)
2021-02-10 00:40:44 +01:00
const localAccount = useSelector(
2021-02-20 19:12:44 +01:00
getInstanceAccount,
2021-02-10 00:40:44 +01:00
(prev, next) => prev?.avatarStatic === next?.avatarStatic
)
2021-01-30 01:29:15 +01:00
2021-02-10 00:40:44 +01:00
const screenOptions = useCallback(
({ route }): BottomTabNavigationOptions => ({
2021-02-20 19:12:44 +01:00
tabBarVisible: instanceActive !== -1,
2021-02-10 00:40:44 +01:00
tabBarIcon: ({
focused,
color,
size
}: {
focused: boolean
color: string
size: number
}) => {
switch (route.name) {
case 'Tab-Local':
return <Icon name='Home' size={size} color={color} />
case 'Tab-Public':
return <Icon name='Globe' size={size} color={color} />
case 'Tab-Compose':
return <Icon name='Plus' size={size} color={color} />
case 'Tab-Notifications':
return <Icon name='Bell' size={size} color={color} />
case 'Tab-Me':
2021-02-20 19:12:44 +01:00
return instanceActive !== -1 ? (
2021-02-10 00:40:44 +01:00
<FastImage
source={{ uri: localAccount?.avatarStatic }}
style={{
width: size,
height: size,
borderRadius: size,
borderWidth: focused ? 2 : 0,
borderColor: focused ? theme.secondary : color
}}
/>
) : (
<Icon
name={focused ? 'Meh' : 'Smile'}
size={size}
color={!focused ? theme.secondary : color}
/>
)
default:
return <Icon name='AlertOctagon' size={size} color={color} />
}
2021-01-30 01:29:15 +01:00
}
2021-02-10 00:40:44 +01:00
}),
2021-02-20 19:12:44 +01:00
[instanceActive, localAccount?.avatarStatic]
2021-02-10 00:40:44 +01:00
)
const tabBarOptions = useMemo(
() => ({
activeTintColor: theme.primary,
2021-02-20 19:12:44 +01:00
inactiveTintColor: theme.secondary,
2021-02-10 00:40:44 +01:00
showLabel: false,
...(Platform.OS === 'android' && { keyboardHidesTabBar: true })
}),
2021-02-20 19:12:44 +01:00
[mode]
2021-02-10 00:40:44 +01:00
)
const composeListeners = useMemo(
() => ({
tabPress: (e: any) => {
2021-01-30 01:29:15 +01:00
e.preventDefault()
2021-02-20 19:12:44 +01:00
haptics('Light')
navigation.navigate('Screen-Compose')
2021-01-30 01:29:15 +01:00
}
2021-02-10 00:40:44 +01:00
}),
2021-02-20 19:12:44 +01:00
[]
2021-02-10 00:40:44 +01:00
)
const composeComponent = useCallback(() => null, [])
// On launch check if there is any unread noficiations
useTimelineQuery({
page: 'Notifications',
options: {
2021-02-20 19:12:44 +01:00
enabled: instanceActive !== -1,
2021-02-10 00:40:44 +01:00
notifyOnChangeProps: [],
select: data => {
2021-02-11 01:33:31 +01:00
if (data.pages[0].body.length) {
2021-02-10 00:40:44 +01:00
dispatch(
2021-02-20 19:12:44 +01:00
updateInstanceNotification({
2021-02-11 01:33:31 +01:00
// @ts-ignore
latestTime: data.pages[0].body[0].created_at
2021-02-10 00:40:44 +01:00
})
)
}
return data
2021-01-30 01:29:15 +01:00
}
}
2021-02-10 00:40:44 +01:00
})
useWebsocket({ stream: 'user', event: 'notification' })
const localNotification = useSelector(
2021-02-20 19:12:44 +01:00
getInstanceNotification,
2021-02-10 00:40:44 +01:00
(prev, next) =>
prev?.readTime === next?.readTime &&
prev?.latestTime === next?.latestTime
)
2021-01-30 01:29:15 +01:00
2021-02-20 19:12:44 +01:00
const previousTab = useSelector(getPreviousTab, () => true)
2021-02-10 00:40:44 +01:00
return (
<Tab.Navigator
2021-02-20 19:12:44 +01:00
initialRouteName={instanceActive !== -1 ? previousTab : 'Tab-Me'}
2021-02-10 00:40:44 +01:00
screenOptions={screenOptions}
tabBarOptions={tabBarOptions}
>
2021-02-20 19:12:44 +01:00
<Tab.Screen name='Tab-Local' component={TabLocal} />
2021-02-10 00:40:44 +01:00
<Tab.Screen name='Tab-Public' component={TabPublic} />
<Tab.Screen
name='Tab-Compose'
component={composeComponent}
listeners={composeListeners}
/>
<Tab.Screen
name='Tab-Notifications'
component={TabNotifications}
options={{
tabBarBadge: localNotification?.latestTime
? !localNotification.readTime ||
new Date(localNotification.readTime) <
new Date(localNotification.latestTime)
? ''
: undefined
: undefined,
tabBarBadgeStyle: {
transform: [{ scale: 0.5 }],
backgroundColor: theme.red
}
}}
/>
<Tab.Screen name='Tab-Me' component={TabMe} />
</Tab.Navigator>
)
},
() => true
)
2021-01-30 01:29:15 +01:00
export default ScreenTabs