mirror of
https://github.com/tooot-app/app
synced 2024-12-22 07:34:06 +01:00
parent
1b58bcad3e
commit
e7fb9ed452
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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 }
|
||||
|
@ -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
|
@ -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)
|
||||
|
76
src/utils/slices/instances/push/utils.ts
Normal file
76
src/utils/slices/instances/push/utils.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 })
|
||||
|
Loading…
Reference in New Issue
Block a user