mirror of
https://github.com/tooot-app/app
synced 2025-04-22 22:27:37 +02:00
Ready for push feature
This commit is contained in:
parent
a4a6e9316b
commit
cc02626adb
@ -8,7 +8,7 @@ ensure_env_vars(
|
|||||||
VERSIONS = read_json( json_path: "./package.json" )[:versions]
|
VERSIONS = read_json( json_path: "./package.json" )[:versions]
|
||||||
ENVIRONMENT = ENV["TOOOT_ENVIRONMENT"]
|
ENVIRONMENT = ENV["TOOOT_ENVIRONMENT"]
|
||||||
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
|
VERSION = "#{VERSIONS[:major]}.#{VERSIONS[:minor]}"
|
||||||
RELEASE_CHANNEL = "#{VERSIONS[:major]}-#{ENVIRONMENT}"
|
RELEASE_CHANNEL = "#{VERSIONS[:major]}-#{VERSIONS[:minor]}-#{ENVIRONMENT}"
|
||||||
BUILD_NUMBER = ENV["GITHUB_RUN_NUMBER"]
|
BUILD_NUMBER = ENV["GITHUB_RUN_NUMBER"]
|
||||||
GITHUB_REPO = "tooot-app/app"
|
GITHUB_REPO = "tooot-app/app"
|
||||||
case ENVIRONMENT
|
case ENVIRONMENT
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "tooot",
|
"name": "tooot",
|
||||||
"versions": {
|
"versions": {
|
||||||
"native": "210201",
|
"native": "210304",
|
||||||
"major": 0,
|
"major": 0,
|
||||||
"minor": 5,
|
"minor": 6,
|
||||||
"patch": 0,
|
"patch": 0,
|
||||||
"expo": "40.0.0"
|
"expo": "40.0.0"
|
||||||
},
|
},
|
||||||
|
@ -8,10 +8,12 @@ import ScreenAnnouncements from '@screens/Announcements'
|
|||||||
import ScreenCompose from '@screens/Compose'
|
import ScreenCompose from '@screens/Compose'
|
||||||
import ScreenImagesViewer from '@screens/ImagesViewer'
|
import ScreenImagesViewer from '@screens/ImagesViewer'
|
||||||
import ScreenTabs from '@screens/Tabs'
|
import ScreenTabs from '@screens/Tabs'
|
||||||
|
import pushUseConnect from '@utils/push/useConnect'
|
||||||
|
import pushUseReceive from '@utils/push/useReceive'
|
||||||
|
import pushUseRespond from '@utils/push/useRespond'
|
||||||
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
||||||
import { connectInstancesPush } from '@utils/slices/instances/connectPush'
|
|
||||||
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||||
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { themes } from '@utils/styles/themes'
|
import { themes } from '@utils/styles/themes'
|
||||||
import * as Analytics from 'expo-firebase-analytics'
|
import * as Analytics from 'expo-firebase-analytics'
|
||||||
@ -20,6 +22,7 @@ import React, { createRef, useCallback, useEffect, useRef } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert, Platform, StatusBar } from 'react-native'
|
import { Alert, Platform, StatusBar } from 'react-native'
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import * as Sentry from 'sentry-expo'
|
import * as Sentry from 'sentry-expo'
|
||||||
|
|
||||||
@ -56,10 +59,15 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
// }
|
// }
|
||||||
// }, [isConnected, firstRender])
|
// }, [isConnected, firstRender])
|
||||||
|
|
||||||
// Update Expo Token to server
|
// Push hooks
|
||||||
useEffect(() => {
|
const instances = useSelector(
|
||||||
dispatch(connectInstancesPush({ mode, t }))
|
getInstances,
|
||||||
}, [])
|
(prev, next) => prev.length === next.length
|
||||||
|
)
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
pushUseConnect({ navigationRef, mode, t, instances, dispatch })
|
||||||
|
pushUseReceive({ navigationRef, queryClient, instances })
|
||||||
|
pushUseRespond({ navigationRef, queryClient, instances, dispatch })
|
||||||
|
|
||||||
// Prevent screenshot alert
|
// Prevent screenshot alert
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -56,8 +56,7 @@ const displayMessage = ({
|
|||||||
onPress,
|
onPress,
|
||||||
...(mode &&
|
...(mode &&
|
||||||
type && {
|
type && {
|
||||||
renderFlashMessageIcon: props => {
|
renderFlashMessageIcon: () => {
|
||||||
console.log(props)
|
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
name={iconMapping[type]}
|
name={iconMapping[type]}
|
||||||
@ -92,7 +91,7 @@ const Message = React.memo(
|
|||||||
...StyleConstants.FontStyle.M,
|
...StyleConstants.FontStyle.M,
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold
|
fontWeight: StyleConstants.Font.Weight.Bold
|
||||||
}}
|
}}
|
||||||
textStyle={{ color: theme.primary, ...StyleConstants.FontStyle.M }}
|
textStyle={{ color: theme.primary, ...StyleConstants.FontStyle.S }}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
textProps={{ numberOfLines: 2 }}
|
textProps={{ numberOfLines: 2 }}
|
||||||
/>
|
/>
|
||||||
|
@ -209,7 +209,6 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
: iconColorAction(status.reblogged)
|
: iconColorAction(status.reblogged)
|
||||||
}
|
}
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
strokeWidth={status.reblogged ? 3 : undefined}
|
|
||||||
/>
|
/>
|
||||||
{status.reblogs_count > 0 && (
|
{status.reblogs_count > 0 && (
|
||||||
<Text
|
<Text
|
||||||
@ -233,7 +232,6 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
name='Heart'
|
name='Heart'
|
||||||
color={iconColorAction(status.favourited)}
|
color={iconColorAction(status.favourited)}
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
strokeWidth={status.favourited ? 3 : undefined}
|
|
||||||
/>
|
/>
|
||||||
{status.favourites_count > 0 && (
|
{status.favourites_count > 0 && (
|
||||||
<Text
|
<Text
|
||||||
@ -257,7 +255,6 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
name='Bookmark'
|
name='Bookmark'
|
||||||
color={iconColorAction(status.bookmarked)}
|
color={iconColorAction(status.bookmarked)}
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
strokeWidth={status.bookmarked ? 3 : undefined}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[status.bookmarked]
|
[status.bookmarked]
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
export default {
|
export default {
|
||||||
heading: 'Push Notification',
|
heading: 'Push Notification',
|
||||||
content: {
|
content: {
|
||||||
|
enable: {
|
||||||
|
direct: 'Enable push notification',
|
||||||
|
settings: 'Enable in settings'
|
||||||
|
},
|
||||||
global: {
|
global: {
|
||||||
heading: 'Enable push notification',
|
heading: 'Enable push notification',
|
||||||
description: "Messages are routed through tooot's server"
|
description: "Messages are routed through tooot's server"
|
||||||
@ -10,6 +14,9 @@ export default {
|
|||||||
description:
|
description:
|
||||||
"Messages routed through tooot's server are encrypted, but you can choose to decode the message on the server. Our server source code is open source, and no log policy."
|
"Messages routed through tooot's server are encrypted, but you can choose to decode the message on the server. Our server source code is open source, and no log policy."
|
||||||
},
|
},
|
||||||
|
default: {
|
||||||
|
heading: 'Default' // Android notification channel name only
|
||||||
|
},
|
||||||
follow: {
|
follow: {
|
||||||
heading: 'New follower'
|
heading: 'New follower'
|
||||||
},
|
},
|
||||||
@ -26,5 +33,9 @@ export default {
|
|||||||
heading: 'Poll updates'
|
heading: 'Poll updates'
|
||||||
},
|
},
|
||||||
howitworks: 'Learn how routing works'
|
howitworks: 'Learn how routing works'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
message: 'Push service error',
|
||||||
|
description: 'Please re-enable push notification in settings'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export default {
|
|||||||
howitworks: '了解通知消息转发如何工作'
|
howitworks: '了解通知消息转发如何工作'
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
message: '推送服务器错误',
|
message: '推送服务错误',
|
||||||
description: '请在设置中重新尝试启用推送通知'
|
description: '请在设置中重新尝试启用推送通知'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,19 @@ import {
|
|||||||
import { NavigatorScreenParams } from '@react-navigation/native'
|
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||||
import { StackScreenProps } from '@react-navigation/stack'
|
import { StackScreenProps } from '@react-navigation/stack'
|
||||||
import { getPreviousTab } from '@utils/slices/contextsSlice'
|
import { getPreviousTab } from '@utils/slices/contextsSlice'
|
||||||
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
import {
|
||||||
|
getInstanceAccount,
|
||||||
|
getInstanceActive
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useSelector } from 'react-redux'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
import TabLocal from './Tabs/Local'
|
import TabLocal from './Tabs/Local'
|
||||||
import TabMe from './Tabs/Me'
|
import TabMe from './Tabs/Me'
|
||||||
import TabNotifications from './Tabs/Notifications'
|
import TabNotifications from './Tabs/Notifications'
|
||||||
import TabPublic from './Tabs/Public'
|
import TabPublic from './Tabs/Public'
|
||||||
import pushReceive from './Tabs/utils/pushReceive'
|
|
||||||
import pushRespond from './Tabs/utils/pushRespond'
|
|
||||||
|
|
||||||
export type ScreenTabsParamList = {
|
export type ScreenTabsParamList = {
|
||||||
'Tab-Local': NavigatorScreenParams<Nav.TabLocalStackParamList>
|
'Tab-Local': NavigatorScreenParams<Nav.TabLocalStackParamList>
|
||||||
@ -40,18 +40,12 @@ const ScreenTabs = React.memo(
|
|||||||
({ navigation }: ScreenTabsProp) => {
|
({ navigation }: ScreenTabsProp) => {
|
||||||
const { mode, theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
const instanceActive = useSelector(getInstanceActive)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const instances = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getInstances,
|
getInstanceAccount,
|
||||||
(prev, next) => prev.length === next.length
|
(prev, next) => prev?.avatarStatic === next?.avatarStatic
|
||||||
)
|
)
|
||||||
|
|
||||||
pushReceive({ navigation, queryClient, instances })
|
|
||||||
pushRespond({ navigation, queryClient, instances, dispatch })
|
|
||||||
|
|
||||||
const screenOptions = useCallback(
|
const screenOptions = useCallback(
|
||||||
({ route }): BottomTabNavigationOptions => ({
|
({ route }): BottomTabNavigationOptions => ({
|
||||||
tabBarVisible: instanceActive !== -1,
|
tabBarVisible: instanceActive !== -1,
|
||||||
@ -77,7 +71,7 @@ const ScreenTabs = React.memo(
|
|||||||
return instanceActive !== -1 ? (
|
return instanceActive !== -1 ? (
|
||||||
<FastImage
|
<FastImage
|
||||||
source={{
|
source={{
|
||||||
uri: instances[instanceActive].account.avatarStatic
|
uri: instanceAccount?.avatarStatic
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
width: size,
|
width: size,
|
||||||
@ -99,7 +93,7 @@ const ScreenTabs = React.memo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[instances, instanceActive]
|
[instanceAccount, instanceActive]
|
||||||
)
|
)
|
||||||
const tabBarOptions = useMemo(
|
const tabBarOptions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import apiInstance from '@api/instance'
|
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
|
||||||
|
|
||||||
const pushNavigate = (
|
|
||||||
navigation: StackNavigationProp<Nav.RootStackParamList, 'Screen-Tabs'>,
|
|
||||||
id?: Mastodon.Notification['id']
|
|
||||||
) => {
|
|
||||||
// @ts-ignore
|
|
||||||
navigation.navigate('Tab-Notifications', {
|
|
||||||
screen: 'Tab-Notifications-Root'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
apiInstance<Mastodon.Notification>({
|
|
||||||
method: 'get',
|
|
||||||
url: `notifications/${id}`
|
|
||||||
}).then(({ body }) => {
|
|
||||||
if (body.status) {
|
|
||||||
// @ts-ignore
|
|
||||||
navigation.navigate('Tab-Notifications', {
|
|
||||||
screen: 'Tab-Shared-Toot',
|
|
||||||
params: { toot: body.status }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default pushNavigate
|
|
35
src/store.ts
35
src/store.ts
@ -5,10 +5,11 @@ import {
|
|||||||
configureStore,
|
configureStore,
|
||||||
getDefaultMiddleware
|
getDefaultMiddleware
|
||||||
} from '@reduxjs/toolkit'
|
} from '@reduxjs/toolkit'
|
||||||
|
import { InstancesV3 } from '@utils/migrations/instances/v3'
|
||||||
import contextsSlice from '@utils/slices/contextsSlice'
|
import contextsSlice from '@utils/slices/contextsSlice'
|
||||||
import instancesSlice from '@utils/slices/instancesSlice'
|
import instancesSlice from '@utils/slices/instancesSlice'
|
||||||
import settingsSlice from '@utils/slices/settingsSlice'
|
import settingsSlice from '@utils/slices/settingsSlice'
|
||||||
import { persistReducer, persistStore } from 'redux-persist'
|
import { createMigrate, persistReducer, persistStore } from 'redux-persist'
|
||||||
|
|
||||||
const secureStorage = createSecureStore()
|
const secureStorage = createSecureStore()
|
||||||
|
|
||||||
@ -20,10 +21,40 @@ const contextsPersistConfig = {
|
|||||||
storage: AsyncStorage
|
storage: AsyncStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const instancesMigration = {
|
||||||
|
4: (state: InstancesV3) => {
|
||||||
|
return {
|
||||||
|
instances: state.local.instances.map((instance, index) => {
|
||||||
|
// @ts-ignore
|
||||||
|
delete instance.notification
|
||||||
|
return {
|
||||||
|
...instance,
|
||||||
|
active: state.local.activeIndex === index,
|
||||||
|
push: {
|
||||||
|
global: { loading: false, value: false },
|
||||||
|
decode: { loading: false, value: false },
|
||||||
|
alerts: {
|
||||||
|
follow: { loading: false, value: true },
|
||||||
|
favourite: { loading: false, value: true },
|
||||||
|
reblog: { loading: false, value: true },
|
||||||
|
mention: { loading: false, value: true },
|
||||||
|
poll: { loading: false, value: true }
|
||||||
|
},
|
||||||
|
keys: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const instancesPersistConfig = {
|
const instancesPersistConfig = {
|
||||||
key: 'instances',
|
key: 'instances',
|
||||||
prefix,
|
prefix,
|
||||||
storage: secureStorage
|
storage: secureStorage,
|
||||||
|
version: 4,
|
||||||
|
// @ts-ignore
|
||||||
|
migrate: createMigrate(instancesMigration)
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsPersistConfig = {
|
const settingsPersistConfig = {
|
||||||
|
33
src/utils/migrations/instances/v3.ts
Normal file
33
src/utils/migrations/instances/v3.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
type InstanceLocal = {
|
||||||
|
appData: {
|
||||||
|
clientId: string
|
||||||
|
clientSecret: string
|
||||||
|
}
|
||||||
|
url: string
|
||||||
|
token: string
|
||||||
|
uri: Mastodon.Instance['uri']
|
||||||
|
urls: Mastodon.Instance['urls']
|
||||||
|
max_toot_chars: number
|
||||||
|
account: {
|
||||||
|
id: Mastodon.Account['id']
|
||||||
|
acct: Mastodon.Account['acct']
|
||||||
|
avatarStatic: Mastodon.Account['avatar_static']
|
||||||
|
preferences: Mastodon.Preferences
|
||||||
|
}
|
||||||
|
notification: {
|
||||||
|
readTime?: Mastodon.Notification['created_at']
|
||||||
|
latestTime?: Mastodon.Notification['created_at']
|
||||||
|
}
|
||||||
|
drafts: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InstancesV3 = {
|
||||||
|
local: {
|
||||||
|
activeIndex: number | null
|
||||||
|
instances: InstanceLocal[]
|
||||||
|
}
|
||||||
|
|
||||||
|
remote: {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
}
|
91
src/utils/push/useConnect.ts
Normal file
91
src/utils/push/useConnect.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import apiGeneral from '@api/general'
|
||||||
|
import { displayMessage } from '@components/Message'
|
||||||
|
import { NavigationContainerRef } from '@react-navigation/native'
|
||||||
|
import { Dispatch } from '@reduxjs/toolkit'
|
||||||
|
import {
|
||||||
|
disableAllPushes,
|
||||||
|
Instance,
|
||||||
|
PUSH_SERVER
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
|
import * as Notifications from 'expo-notifications'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { TFunction } from 'react-i18next'
|
||||||
|
|
||||||
|
export interface Params {
|
||||||
|
navigationRef: React.RefObject<NavigationContainerRef>
|
||||||
|
mode: 'light' | 'dark'
|
||||||
|
t: TFunction<'common'>
|
||||||
|
instances: Instance[]
|
||||||
|
dispatch: Dispatch<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushUseConnect = ({
|
||||||
|
navigationRef,
|
||||||
|
mode,
|
||||||
|
t,
|
||||||
|
instances,
|
||||||
|
dispatch
|
||||||
|
}: Params) => {
|
||||||
|
return useEffect(() => {
|
||||||
|
const connect = async () => {
|
||||||
|
const expoToken = (
|
||||||
|
await Notifications.getExpoPushTokenAsync({
|
||||||
|
experienceId: '@xmflsct/tooot'
|
||||||
|
})
|
||||||
|
).data
|
||||||
|
|
||||||
|
apiGeneral({
|
||||||
|
method: 'post',
|
||||||
|
domain: PUSH_SERVER,
|
||||||
|
url: 'v1/connect',
|
||||||
|
body: {
|
||||||
|
expoToken
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
displayMessage({
|
||||||
|
mode,
|
||||||
|
type: 'error',
|
||||||
|
duration: 'long',
|
||||||
|
message: t('meSettingsPush:error.message'),
|
||||||
|
description: t('meSettingsPush:error.description'),
|
||||||
|
onPress: () => {
|
||||||
|
navigationRef.current?.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Me',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Me-Root'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
navigationRef.current?.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Me',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Me-Settings'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(disableAllPushes())
|
||||||
|
|
||||||
|
instances.forEach(instance => {
|
||||||
|
if (instance.push.global.value) {
|
||||||
|
apiGeneral<{}>({
|
||||||
|
method: 'delete',
|
||||||
|
domain: instance.url,
|
||||||
|
url: 'api/v1/push/subscription',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${instance.token}`
|
||||||
|
}
|
||||||
|
}).catch(() => console.log('error!!!'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushEnabled = instances.filter(instance => instance.push.global.value)
|
||||||
|
if (pushEnabled.length) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
}, [instances])
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pushUseConnect
|
35
src/utils/push/useNavigate.ts
Normal file
35
src/utils/push/useNavigate.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import { NavigationContainerRef } from '@react-navigation/native'
|
||||||
|
|
||||||
|
const pushUseNavigate = (
|
||||||
|
navigationRef: React.RefObject<NavigationContainerRef>,
|
||||||
|
id?: Mastodon.Notification['id']
|
||||||
|
) => {
|
||||||
|
navigationRef.current?.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Notifications',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Notifications-Root'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiInstance<Mastodon.Notification>({
|
||||||
|
method: 'get',
|
||||||
|
url: `notifications/${id}`
|
||||||
|
}).then(({ body }) => {
|
||||||
|
if (body.status) {
|
||||||
|
navigationRef.current?.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Notifications',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Shared-Toot',
|
||||||
|
params: { toot: body.status }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pushUseNavigate
|
@ -1,5 +1,5 @@
|
|||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { NavigationContainerRef } from '@react-navigation/native'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { Instance, updateInstanceActive } from '@utils/slices/instancesSlice'
|
import { Instance, updateInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
@ -7,15 +7,15 @@ import { findIndex } from 'lodash'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { QueryClient } from 'react-query'
|
import { QueryClient } from 'react-query'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch } from 'react-redux'
|
||||||
import pushNavigate from './pushNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
navigation: StackNavigationProp<Nav.RootStackParamList, 'Screen-Tabs'>
|
navigationRef: React.RefObject<NavigationContainerRef>
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
instances: Instance[]
|
instances: Instance[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushReceive = ({ navigation, queryClient, instances }: Params) => {
|
const pushUseReceive = ({ navigationRef, queryClient, instances }: Params) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
@ -46,7 +46,7 @@ const pushReceive = ({ navigation, queryClient, instances }: Params) => {
|
|||||||
if (notificationIndex !== -1) {
|
if (notificationIndex !== -1) {
|
||||||
dispatch(updateInstanceActive(instances[notificationIndex]))
|
dispatch(updateInstanceActive(instances[notificationIndex]))
|
||||||
}
|
}
|
||||||
pushNavigate(navigation, payloadData.notification_id)
|
pushUseNavigate(navigationRef, payloadData.notification_id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -55,4 +55,4 @@ const pushReceive = ({ navigation, queryClient, instances }: Params) => {
|
|||||||
}, [instances])
|
}, [instances])
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pushReceive
|
export default pushUseReceive
|
@ -1,4 +1,4 @@
|
|||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { NavigationContainerRef } from '@react-navigation/native'
|
||||||
import { Dispatch } from '@reduxjs/toolkit'
|
import { Dispatch } from '@reduxjs/toolkit'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { Instance, updateInstanceActive } from '@utils/slices/instancesSlice'
|
import { Instance, updateInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
@ -6,17 +6,17 @@ import * as Notifications from 'expo-notifications'
|
|||||||
import { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { QueryClient } from 'react-query'
|
import { QueryClient } from 'react-query'
|
||||||
import pushNavigate from './pushNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
navigation: StackNavigationProp<Nav.RootStackParamList, 'Screen-Tabs'>
|
navigationRef: React.RefObject<NavigationContainerRef>
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
instances: Instance[]
|
instances: Instance[]
|
||||||
dispatch: Dispatch<any>
|
dispatch: Dispatch<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushRespond = ({
|
const pushUseRespond = ({
|
||||||
navigation,
|
navigationRef,
|
||||||
queryClient,
|
queryClient,
|
||||||
instances,
|
instances,
|
||||||
dispatch
|
dispatch
|
||||||
@ -44,11 +44,11 @@ const pushRespond = ({
|
|||||||
if (notificationIndex !== -1) {
|
if (notificationIndex !== -1) {
|
||||||
dispatch(updateInstanceActive(instances[notificationIndex]))
|
dispatch(updateInstanceActive(instances[notificationIndex]))
|
||||||
}
|
}
|
||||||
pushNavigate(navigation, payloadData.notification_id)
|
pushUseNavigate(navigationRef, payloadData.notification_id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return () => subscription.remove()
|
return () => subscription.remove()
|
||||||
}, [instances])
|
}, [instances])
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pushRespond
|
export default pushUseRespond
|
@ -1,38 +0,0 @@
|
|||||||
import apiGeneral from '@api/general'
|
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
|
||||||
import { RootState } from '@root/store'
|
|
||||||
import * as Notifications from 'expo-notifications'
|
|
||||||
import { TFunction } from 'react-i18next'
|
|
||||||
import { PUSH_SERVER } from '../instancesSlice'
|
|
||||||
|
|
||||||
export const connectInstancesPush = createAsyncThunk(
|
|
||||||
'instances/connectPush',
|
|
||||||
async (
|
|
||||||
{ mode, t }: { mode: 'light' | 'dark'; t: TFunction<'common'> },
|
|
||||||
{ getState }
|
|
||||||
): Promise<any> => {
|
|
||||||
const state = getState() as RootState
|
|
||||||
const pushEnabled = state.instances.instances.filter(
|
|
||||||
instance => instance.push.global.value
|
|
||||||
)
|
|
||||||
|
|
||||||
if (pushEnabled.length) {
|
|
||||||
const expoToken = (
|
|
||||||
await Notifications.getExpoPushTokenAsync({
|
|
||||||
experienceId: '@xmflsct/tooot'
|
|
||||||
})
|
|
||||||
).data
|
|
||||||
|
|
||||||
return apiGeneral({
|
|
||||||
method: 'post',
|
|
||||||
domain: PUSH_SERVER,
|
|
||||||
url: 'v1/connect',
|
|
||||||
body: {
|
|
||||||
expoToken
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
@ -1,11 +1,9 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { displayMessage } from '@components/Message'
|
|
||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
import { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
import addInstance from './instances/add'
|
import addInstance from './instances/add'
|
||||||
import { connectInstancesPush } from './instances/connectPush'
|
|
||||||
import removeInstance from './instances/remove'
|
import removeInstance from './instances/remove'
|
||||||
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
||||||
import { updateInstancePush } from './instances/updatePush'
|
import { updateInstancePush } from './instances/updatePush'
|
||||||
@ -150,6 +148,13 @@ const instancesSlice = createSlice({
|
|||||||
instances[activeIndex].drafts = instances[activeIndex].drafts?.filter(
|
instances[activeIndex].drafts = instances[activeIndex].drafts?.filter(
|
||||||
draft => draft.timestamp !== action.payload
|
draft => draft.timestamp !== action.payload
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
disableAllPushes: ({ instances }) => {
|
||||||
|
instances = instances.map(instance => {
|
||||||
|
let newInstance = instance
|
||||||
|
newInstance.push.global.value = false
|
||||||
|
return newInstance
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
@ -266,22 +271,6 @@ const instancesSlice = createSlice({
|
|||||||
action.meta.arg.changed
|
action.meta.arg.changed
|
||||||
].loading = true
|
].loading = true
|
||||||
})
|
})
|
||||||
|
|
||||||
// If Expo token does not exist on the server, disable all existing ones
|
|
||||||
.addCase(connectInstancesPush.rejected, (state, action) => {
|
|
||||||
state.instances = state.instances.map(instance => {
|
|
||||||
let newInstance = instance
|
|
||||||
newInstance.push.global.value = false
|
|
||||||
displayMessage({
|
|
||||||
mode: action.meta.arg.mode,
|
|
||||||
type: 'error',
|
|
||||||
autoHide: false,
|
|
||||||
message: action.meta.arg.t('meSettingsPush:error.message'),
|
|
||||||
description: action.meta.arg.t('meSettingsPush:error.description')
|
|
||||||
})
|
|
||||||
return newInstance
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -337,7 +326,8 @@ export const {
|
|||||||
updateInstanceActive,
|
updateInstanceActive,
|
||||||
updateInstanceAccount,
|
updateInstanceAccount,
|
||||||
updateInstanceDraft,
|
updateInstanceDraft,
|
||||||
removeInstanceDraft
|
removeInstanceDraft,
|
||||||
|
disableAllPushes
|
||||||
} = instancesSlice.actions
|
} = instancesSlice.actions
|
||||||
|
|
||||||
export default instancesSlice.reducer
|
export default instancesSlice.reducer
|
||||||
|
Loading…
x
Reference in New Issue
Block a user