No physical phone to test
This commit is contained in:
xmflsct 2022-12-10 00:31:11 +01:00
parent 1b58bcad3e
commit e7fb9ed452
7 changed files with 115 additions and 175 deletions

View File

@ -3,15 +3,20 @@ import Icon from '@components/Icon'
import { MenuContainer, MenuRow } from '@components/Menu'
import CustomText from '@components/Text'
import browserPackage from '@helpers/browserPackage'
import { PERMISSION_MANAGE_REPORTS, PERMISSION_MANAGE_USERS } from '@helpers/permissions'
import { useAppDispatch } from '@root/store'
import { isDevelopment } from '@utils/checkEnvironment'
import { useProfileQuery } from '@utils/queryHooks/profile'
import { getExpoToken } from '@utils/slices/appSlice'
import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice'
import {
checkPushAdminPermission,
PUSH_ADMIN,
PUSH_DEFAULT,
setChannels
} from '@utils/slices/instances/push/utils'
import { updateInstancePush } from '@utils/slices/instances/updatePush'
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
import { getInstanceAccount, getInstancePush, getInstanceUri } from '@utils/slices/instancesSlice'
import { getInstance, getInstancePush } from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager'
@ -19,37 +24,13 @@ import * as Notifications from 'expo-notifications'
import * as WebBrowser from 'expo-web-browser'
import React, { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { AppState, Linking, ScrollView, View } from 'react-native'
import { AppState, Linking, Platform, ScrollView, View } from 'react-native'
import { useSelector } from 'react-redux'
export const PUSH_DEFAULT: [
'follow',
'follow_request',
'favourite',
'reblog',
'mention',
'poll',
'status'
] = ['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'status']
export const PUSH_ADMIN: { type: 'admin.sign_up' | 'admin.report'; permission: number }[] = [
{ type: 'admin.sign_up', permission: PERMISSION_MANAGE_USERS },
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS }
]
export const checkPushAdminPermission = (
permission: number,
permissions?: string | number
): boolean =>
permissions
? !!(
(typeof permissions === 'string' ? parseInt(permissions || '0') : permissions) & permission
)
: false
const TabMePush: React.FC = () => {
const { colors } = useTheme()
const { t } = useTranslation('screenTabs')
const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev?.acct === next?.acct)
const instanceUri = useSelector(getInstanceUri)
const instance = useSelector(getInstance)
const dispatch = useAppDispatch()
const instancePush = useSelector(getInstancePush)
@ -57,21 +38,31 @@ const TabMePush: React.FC = () => {
const [pushAvailable, setPushAvailable] = useState<boolean>()
const [pushEnabled, setPushEnabled] = useState<boolean>()
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
const checkPush = async () => {
const settings = await Notifications.getPermissionsAsync()
layoutAnimation()
setPushEnabled(settings.granted)
setPushCanAskAgain(settings.canAskAgain)
}
const expoToken = useSelector(getExpoToken)
const checkPush = async () => {
switch (Platform.OS) {
case 'ios':
const settings = await Notifications.getPermissionsAsync()
layoutAnimation()
setPushEnabled(settings.granted)
setPushCanAskAgain(settings.canAskAgain)
break
case 'android':
await setChannels(instance)
layoutAnimation()
dispatch(retrieveExpoToken())
break
}
}
useEffect(() => {
checkPush()
if (isDevelopment) {
setPushAvailable(true)
} else {
setPushAvailable(!!expoToken)
}
checkPush()
const subscription = AppState.addEventListener('change', checkPush)
return () => {
subscription.remove()
@ -157,7 +148,7 @@ const TabMePush: React.FC = () => {
<MenuContainer>
<MenuRow
title={t('me.push.global.heading', {
acct: `@${instanceAccount?.acct}@${instanceUri}`
acct: `@${instance.account.acct}@${instance.uri}`
})}
description={t('me.push.global.description')}
switchDisabled={!pushEnabled}

View File

@ -2,27 +2,22 @@ import haptics from '@components/haptics'
import { MenuContainer, MenuRow } from '@components/Menu'
import { LOCALES } from '@root/i18n/locales'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import { useProfileQuery } from '@utils/queryHooks/profile'
import androidDefaults from '@utils/slices/instances/push/androidDefaults'
import { setChannels } from '@utils/slices/instances/push/utils'
import { getInstances } from '@utils/slices/instancesSlice'
import { changeLanguage } from '@utils/slices/settingsSlice'
import * as Notifications from 'expo-notifications'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, Platform } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import { checkPushAdminPermission, PUSH_ADMIN, PUSH_DEFAULT } from './Push'
const TabMeSettingsLanguage: React.FC<TabMeStackScreenProps<'Tab-Me-Settings-Language'>> = ({
navigation
}) => {
const { i18n, t } = useTranslation('screenTabs')
const { i18n } = useTranslation('screenTabs')
const languages = Object.entries(LOCALES)
const instances = useSelector(getInstances)
const dispatch = useDispatch()
const profileQuery = useProfileQuery({ options: { enabled: Platform.OS === 'android' } })
const change = (lang: string) => {
haptics('Success')
@ -31,33 +26,7 @@ const TabMeSettingsLanguage: React.FC<TabMeStackScreenProps<'Tab-Me-Settings-Lan
// Update Android notification channel language
if (Platform.OS === 'android') {
instances.forEach(instance => {
const accountFull = `@${instance.account.acct}@${instance.uri}`
if (instance.push.decode === false) {
Notifications.setNotificationChannelAsync(`${accountFull}_default`, {
groupId: accountFull,
name: t('me.push.default.heading'),
...androidDefaults
})
} else {
for (const push of PUSH_DEFAULT) {
Notifications.setNotificationChannelAsync(`${accountFull}_${push}`, {
groupId: accountFull,
name: t(`me.push.${push}.heading`),
...androidDefaults
})
}
for (const { type, permission } of PUSH_ADMIN) {
if (checkPushAdminPermission(permission, profileQuery.data?.role?.permissions)) {
Notifications.setNotificationChannelAsync(`${accountFull}_${type}`, {
groupId: accountFull,
name: t(`me.push.${type}.heading`),
...androidDefaults
})
}
}
}
})
instances.forEach(setChannels)
}
navigation.pop(1)

View File

@ -10,10 +10,10 @@ import { useMutation, useQuery, UseQueryOptions } from 'react-query'
type AccountWithSource = Mastodon.Account & Required<Pick<Mastodon.Account, 'source'>>
type QueryKeyProfile = ['Profile']
export type QueryKeyProfile = ['Profile']
const queryKey: QueryKeyProfile = ['Profile']
const queryFunction = async () => {
const queryFunctionProfile = async () => {
const res = await apiInstance<AccountWithSource>({
method: 'get',
url: `accounts/verify_credentials`
@ -26,7 +26,7 @@ const useProfileQuery = (
options: UseQueryOptions<AccountWithSource, AxiosError>
} | void
) => {
return useQuery(queryKey, queryFunction, params?.options)
return useQuery(queryKey, queryFunctionProfile, params?.options)
}
type MutationVarsProfileBase =
@ -155,4 +155,4 @@ const useProfileMutation = () => {
)
}
export { useProfileQuery, useProfileMutation }
export { queryFunctionProfile, useProfileQuery, useProfileMutation }

View File

@ -1,11 +0,0 @@
import * as Notifications from 'expo-notifications'
const androidDefaults = {
importance: Notifications.AndroidImportance.DEFAULT,
bypassDnd: false,
showBadge: true,
enableLights: true,
enableVibrate: true
}
export default androidDefaults

View File

@ -1,17 +1,15 @@
import apiInstance from '@api/instance'
import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot'
import { displayMessage } from '@components/Message'
import i18n from '@root/i18n/i18n'
import { RootState } from '@root/store'
import * as Sentry from '@sentry/react-native'
import { InstanceLatest } from '@utils/migrations/instances/migration'
import { getInstance } from '@utils/slices/instancesSlice'
import * as Notifications from 'expo-notifications'
import * as Random from 'expo-random'
import i18next from 'i18next'
import { Platform } from 'react-native'
import base64 from 'react-native-base64'
import androidDefaults from './androidDefaults'
import { setChannels } from './utils'
const subscribe = async ({
expoToken,
@ -100,46 +98,7 @@ const pushRegister = async (
})
if (Platform.OS === 'android') {
Notifications.setNotificationChannelGroupAsync(accountFull, {
name: accountFull,
...androidDefaults
}).then(group => {
if (group) {
if (instancePush.decode === false) {
Notifications.setNotificationChannelAsync(`${group.id}_default`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.default.heading'),
...androidDefaults
})
} else {
Notifications.setNotificationChannelAsync(`${group.id}_follow`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.follow.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${group.id}_favourite`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.favourite.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${group.id}_reblog`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.reblog.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${group.id}_mention`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.mention.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${group.id}_poll`, {
groupId: group.id,
name: i18n.t('meSettingsPush:content.poll.heading'),
...androidDefaults
})
}
}
})
setChannels(instance)
}
return Promise.resolve(auth)

View File

@ -0,0 +1,76 @@
import { PERMISSION_MANAGE_REPORTS, PERMISSION_MANAGE_USERS } from '@helpers/permissions'
import queryClient from '@helpers/queryClient'
import i18n from '@root/i18n/i18n'
import { InstanceLatest } from '@utils/migrations/instances/migration'
import { queryFunctionProfile, QueryKeyProfile } from '@utils/queryHooks/profile'
import * as Notifications from 'expo-notifications'
export const PUSH_DEFAULT: [
'follow',
'follow_request',
'favourite',
'reblog',
'mention',
'poll',
'status'
] = ['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'status']
export const PUSH_ADMIN: { type: 'admin.sign_up' | 'admin.report'; permission: number }[] = [
{ type: 'admin.sign_up', permission: PERMISSION_MANAGE_USERS },
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS }
]
export const checkPushAdminPermission = (
permission: number,
permissions?: string | number
): boolean =>
permissions
? !!(
(typeof permissions === 'string' ? parseInt(permissions || '0') : permissions) & permission
)
: false
export const setChannels = async (instance: InstanceLatest) => {
const account = `@${instance.account.acct}@${instance.uri}`
const deleteChannel = async (type: string) =>
Notifications.deleteNotificationChannelAsync(`${account}_${type}`)
const setChannel = async (type: string) =>
Notifications.setNotificationChannelAsync(`${account}_${type}`, {
groupId: account,
name: i18n.t(`screenTabs:me.push.${type}.heading`),
importance: Notifications.AndroidImportance.DEFAULT,
bypassDnd: false,
showBadge: true,
enableLights: true,
enableVibrate: true
})
const queryKey: QueryKeyProfile = ['Profile']
const profileQuery = await queryClient.fetchQuery(queryKey, queryFunctionProfile)
const channelGroup = await Notifications.getNotificationChannelGroupAsync(account)
if (!channelGroup) {
await Notifications.setNotificationChannelGroupAsync(account, { name: account })
}
if (!instance.push.decode) {
await setChannel('default')
for (const push of PUSH_DEFAULT) {
await deleteChannel(push)
}
for (const { type } of PUSH_ADMIN) {
await deleteChannel(type)
}
} else {
await deleteChannel('default')
for (const push of PUSH_DEFAULT) {
await setChannel(push)
}
for (const { type, permission } of PUSH_ADMIN) {
if (checkPushAdminPermission(permission, profileQuery.role?.permissions)) {
await setChannel(type)
}
}
}
}

View File

@ -1,12 +1,10 @@
import apiTooot from '@api/tooot'
import { createAsyncThunk } from '@reduxjs/toolkit'
import i18n from '@root/i18n/i18n'
import { RootState } from '@root/store'
import { InstanceLatest } from '@utils/migrations/instances/migration'
import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native'
import { getInstance } from '../instancesSlice'
import androidDefaults from './push/androidDefaults'
import { setChannels } from './push/utils'
export const updateInstancePushDecode = createAsyncThunk(
'instances/updatePushDecode',
@ -34,49 +32,7 @@ export const updateInstancePushDecode = createAsyncThunk(
})
if (Platform.OS === 'android') {
const accountFull = `@${instance.account.acct}@${instance.uri}`
switch (disable) {
case true:
Notifications.deleteNotificationChannelAsync(`${accountFull}_default`)
Notifications.setNotificationChannelAsync(`${accountFull}_follow`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.follow.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_favourite`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.favourite.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_reblog`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.reblog.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_mention`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.mention.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_poll`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.poll.heading'),
...androidDefaults
})
break
case false:
Notifications.setNotificationChannelAsync(`${accountFull}_default`, {
groupId: accountFull,
name: i18n.t('meSettingsPush:content.default.heading'),
...androidDefaults
})
Notifications.deleteNotificationChannelAsync(`${accountFull}_follow`)
Notifications.deleteNotificationChannelAsync(`${accountFull}_favourite`)
Notifications.deleteNotificationChannelAsync(`${accountFull}_reblog`)
Notifications.deleteNotificationChannelAsync(`${accountFull}_mention`)
Notifications.deleteNotificationChannelAsync(`${accountFull}_poll`)
break
}
setChannels(instance)
}
return Promise.resolve({ disable })