Push error tolerance

This commit is contained in:
Zhiyuan Zheng 2021-03-01 01:27:26 +01:00
parent 32aaf08574
commit 82cefdc80c
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
7 changed files with 121 additions and 18 deletions

View File

@ -58,7 +58,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
// Update Expo Token to server
useEffect(() => {
dispatch(connectInstancesPush())
dispatch(connectInstancesPush({ mode, t }))
}, [])
// Prevent screenshot alert

View File

@ -1,13 +1,18 @@
export default {
heading: '推送通知',
content: {
enable: {
direct: '启用tooot推送通知',
settings: '去系统设置启用'
},
global: {
heading: '启用通知',
description: '通知消息将经由tooot服务器转发'
},
decode: {
heading: '显示通知内容',
description: '经由tooot服务器中转的通知消息已被加密但可以允许tooot服务器解密并转发消息。tooot消息服务器源码开源且不留存任何日志。'
description:
'经由tooot服务器中转的通知消息已被加密但可以允许tooot服务器解密并转发消息。tooot消息服务器源码开源且不留存任何日志。'
},
follow: {
heading: '新关注者'
@ -25,5 +30,9 @@ export default {
heading: '投票'
},
howitworks: '了解通知消息转发如何工作'
},
error: {
message: '推送服务器错误',
description: '请在设置中重新尝试启用推送通知'
}
}

View File

@ -4,13 +4,39 @@ import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
import { getInstancePush } from '@utils/slices/instancesSlice'
import * as WebBrowser from 'expo-web-browser'
import React, { useMemo } from 'react'
import * as Notifications from 'expo-notifications'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler'
import { useDispatch, useSelector } from 'react-redux'
import layoutAnimation from '@utils/styles/layoutAnimation'
import Button from '@components/Button'
import { StyleConstants } from '@utils/styles/constants'
import { AppState, Linking } from 'react-native'
const ScreenMeSettingsPush: React.FC = () => {
const { t } = useTranslation('meSettingsPush')
const [appStateVisible, setAppStateVisible] = useState(AppState.currentState)
useEffect(() => {
AppState.addEventListener('change', state => setAppStateVisible(state))
return () => {
AppState.removeEventListener('change', state => setAppStateVisible(state))
}
}, [])
const [pushEnabled, setPushEnabled] = useState<boolean>()
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
useEffect(() => {
const checkPush = async () => {
const settings = await Notifications.getPermissionsAsync()
layoutAnimation()
setPushEnabled(settings.granted)
setPushCanAskAgain(settings.canAskAgain)
}
checkPush()
}, [appStateVisible])
const dispatch = useDispatch()
const instancePush = useSelector(getInstancePush)
@ -28,7 +54,9 @@ const ScreenMeSettingsPush: React.FC = () => {
<MenuRow
key={alert}
title={t(`content.${alert}.heading`)}
switchDisabled={!instancePush.global.value || isLoading}
switchDisabled={
!pushEnabled || !instancePush.global.value || isLoading
}
switchValue={instancePush?.alerts[alert].value}
switchOnValueChange={() =>
dispatch(
@ -51,13 +79,40 @@ const ScreenMeSettingsPush: React.FC = () => {
return (
<ScrollView>
{pushEnabled === false ? (
<MenuContainer>
<Button
type='text'
content={
pushCanAskAgain
? t('content.enable.direct')
: t('content.enable.settings')
}
style={{
marginTop: StyleConstants.Spacing.Global.PagePadding,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
}}
onPress={async () => {
if (pushCanAskAgain) {
const result = await Notifications.requestPermissionsAsync()
setPushEnabled(result.granted)
setPushCanAskAgain(result.canAskAgain)
} else {
Linking.openURL('app-settings:')
}
}}
/>
</MenuContainer>
) : null}
<MenuContainer>
<MenuRow
title={t('content.global.heading')}
description={t('content.global.description')}
loading={instancePush?.global.loading}
switchDisabled={isLoading}
switchValue={instancePush?.global.value}
switchDisabled={!pushEnabled || isLoading}
switchValue={
pushEnabled === false ? false : instancePush?.global.value
}
switchOnValueChange={() =>
dispatch(updateInstancePush(!instancePush?.global.value))
}
@ -68,7 +123,9 @@ const ScreenMeSettingsPush: React.FC = () => {
title={t('content.decode.heading')}
description={t('content.decode.description')}
loading={instancePush?.decode.loading}
switchDisabled={!instancePush?.global.value || isLoading}
switchDisabled={
!pushEnabled || !instancePush?.global.value || isLoading
}
switchValue={instancePush?.decode.value}
switchOnValueChange={() =>
dispatch(updateInstancePushDecode(!instancePush?.decode.value))

View File

@ -1,7 +1,7 @@
import Button from '@components/Button'
import haptics from '@root/components/haptics'
import removeInstance from '@utils/slices/instances/remove'
import { getInstance, getInstanceActive } from '@utils/slices/instancesSlice'
import { getInstance } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import React from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -2,11 +2,15 @@ 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 (_, { getState }): Promise<any> => {
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

View File

@ -110,16 +110,30 @@ const pushRegister = async (
removeKeys: instancePush.decode.value === false
})
return Promise.resolve(serverRes.body.keys)
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('follow', {
name: 'Follow',
importance: Notifications.AndroidImportance.DEFAULT
})
Notifications.setNotificationChannelAsync('favourite', {
name: 'Favourite',
importance: Notifications.AndroidImportance.DEFAULT
})
Notifications.setNotificationChannelAsync('reblog', {
name: 'Reblog',
importance: Notifications.AndroidImportance.DEFAULT
})
Notifications.setNotificationChannelAsync('mention', {
name: 'Mention',
importance: Notifications.AndroidImportance.DEFAULT
})
Notifications.setNotificationChannelAsync('poll', {
name: 'Poll',
importance: Notifications.AndroidImportance.DEFAULT
})
}
// if (Platform.OS === 'android') {
// Notifications.setNotificationChannelAsync('default', {
// name: 'default',
// importance: Notifications.AndroidImportance.MAX,
// vibrationPattern: [0, 250, 250, 250],
// lightColor: '#FF231F7C'
// })
// }
return Promise.resolve(serverRes.body.keys)
}
export default pushRegister

View File

@ -1,9 +1,12 @@
import analytics from '@components/analytics'
import { displayMessage } from '@components/Message'
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { RootState } from '@root/store'
import { ComposeStateDraft } from '@screens/Compose/utils/types'
import { findIndex } from 'lodash'
import { Appearance } from 'react-native'
import addInstance from './instances/add'
import { connectInstancesPush } from './instances/connectPush'
import removeInstance from './instances/remove'
import { updateAccountPreferences } from './instances/updateAccountPreferences'
import { updateInstancePush } from './instances/updatePush'
@ -264,6 +267,22 @@ const instancesSlice = createSlice({
action.meta.arg.changed
].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
})
})
}
})