This commit is contained in:
xmflsct 2022-12-15 19:31:20 +01:00
parent 2f3e398d70
commit 0e348dcef1
9 changed files with 122 additions and 103 deletions

View File

@ -395,6 +395,7 @@ declare namespace Mastodon {
mention: boolean mention: boolean
poll: boolean poll: boolean
status: boolean status: boolean
update: boolean
'admin.sign_up': boolean 'admin.sign_up': boolean
'admin.report': boolean 'admin.report': boolean
} }

View File

@ -85,7 +85,7 @@ const MenuRow: React.FC<Props> = ({
> >
<View <View
style={{ style={{
flexGrow: 3, flexShrink: 3,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginRight: StyleConstants.Spacing.M marginRight: StyleConstants.Spacing.M

View File

@ -1,47 +1,42 @@
[ [
{
"feature": "notification_type_status",
"version": 3.3
},
{ {
"feature": "account_return_suspended", "feature": "account_return_suspended",
"version": 3.3, "version": 3.3
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.3.0"
}, },
{ {
"feature": "edit_post", "feature": "edit_post",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "deprecate_auth_follow", "feature": "deprecate_auth_follow",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "notification_type_update", "feature": "notification_type_update",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "notification_type_admin_signup", "feature": "notification_type_admin_signup",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "notification_types_positive_filter", "feature": "notification_types_positive_filter",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "trends_new_path", "feature": "trends_new_path",
"version": 3.5, "version": 3.5
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
}, },
{ {
"feature": "follow_tags", "feature": "follow_tags",
"version": 4.0, "version": 4.0
"reference": "https://github.com/mastodon/mastodon/releases/tag/v4.0.0"
}, },
{ {
"feature": "notification_type_admin_report", "feature": "notification_type_admin_report",
"version": 4.0, "version": 4.0
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
} }
] ]

View File

@ -3,13 +3,17 @@ import Icon from '@components/Icon'
import { MenuContainer, MenuRow } from '@components/Menu' import { MenuContainer, MenuRow } from '@components/Menu'
import CustomText from '@components/Text' import CustomText from '@components/Text'
import browserPackage from '@helpers/browserPackage' import browserPackage from '@helpers/browserPackage'
import { checkPermission } from '@helpers/permissions'
import { useAppDispatch } from '@root/store' import { useAppDispatch } from '@root/store'
import { isDevelopment } from '@utils/checkEnvironment' import { isDevelopment } from '@utils/checkEnvironment'
import { useAppsQuery } from '@utils/queryHooks/apps' import { useAppsQuery } from '@utils/queryHooks/apps'
import { useProfileQuery } from '@utils/queryHooks/profile' import { useProfileQuery } from '@utils/queryHooks/profile'
import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice' import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice'
import { PUSH_ADMIN, PUSH_DEFAULT, setChannels } from '@utils/slices/instances/push/utils' import {
PUSH_ADMIN,
PUSH_DEFAULT,
setChannels,
usePushFeatures
} from '@utils/slices/instances/push/utils'
import { updateInstancePush } from '@utils/slices/instances/updatePush' import { updateInstancePush } from '@utils/slices/instances/updatePush'
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert' import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode' import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
@ -73,9 +77,11 @@ const TabMePush: React.FC = () => {
} }
}, [appsQuery.data?.vapid_key]) }, [appsQuery.data?.vapid_key])
const pushFeatures = usePushFeatures()
const alerts = () => const alerts = () =>
instancePush?.alerts instancePush?.alerts
? PUSH_DEFAULT.map(alert => ( ? PUSH_DEFAULT(pushFeatures).map(alert => (
<MenuRow <MenuRow
key={alert} key={alert}
title={t(`me.push.${alert}.heading`)} title={t(`me.push.${alert}.heading`)}
@ -98,26 +104,24 @@ const TabMePush: React.FC = () => {
const profileQuery = useProfileQuery() const profileQuery = useProfileQuery()
const adminAlerts = () => const adminAlerts = () =>
profileQuery.data?.role?.permissions profileQuery.data?.role?.permissions
? PUSH_ADMIN.map(({ type, permission }) => ? PUSH_ADMIN(pushFeatures, profileQuery.data?.role?.permissions).map(({ type }) => (
checkPermission(permission, profileQuery.data.role?.permissions) ? ( <MenuRow
<MenuRow key={type}
key={type} title={t(`me.push.${type}.heading`)}
title={t(`me.push.${type}.heading`)} switchDisabled={!pushEnabled || !instancePush.global}
switchDisabled={!pushEnabled || !instancePush.global} switchValue={instancePush?.alerts[type]}
switchValue={instancePush?.alerts[type]} switchOnValueChange={() =>
switchOnValueChange={() => dispatch(
dispatch( updateInstancePushAlert({
updateInstancePushAlert({ alerts: {
alerts: { ...instancePush?.alerts,
...instancePush?.alerts, [type]: instancePush?.alerts[type]
[type]: instancePush?.alerts[type] }
} })
}) )
) }
} />
/> ))
) : null
)
: null : null
return ( return (

View File

@ -4,7 +4,11 @@ import { useAppDispatch } from '@root/store'
import { useAnnouncementQuery } from '@utils/queryHooks/announcement' import { useAnnouncementQuery } from '@utils/queryHooks/announcement'
import { useListsQuery } from '@utils/queryHooks/lists' import { useListsQuery } from '@utils/queryHooks/lists'
import { useFollowedTagsQuery } from '@utils/queryHooks/tags' import { useFollowedTagsQuery } from '@utils/queryHooks/tags'
import { getInstanceMePage, updateInstanceMePage } from '@utils/slices/instancesSlice' import {
checkInstanceFeature,
getInstanceMePage,
updateInstanceMePage
} from '@utils/slices/instancesSlice'
import { getInstancePush } from '@utils/slices/instancesSlice' import { getInstancePush } from '@utils/slices/instancesSlice'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -17,8 +21,10 @@ const Collections: React.FC = () => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const mePage = useSelector(getInstanceMePage) const mePage = useSelector(getInstanceMePage)
const canFollowTags = useSelector(checkInstanceFeature('follow_tags'))
useFollowedTagsQuery({ useFollowedTagsQuery({
options: { options: {
enabled: canFollowTags,
onSuccess: data => onSuccess: data =>
dispatch( dispatch(
updateInstanceMePage({ updateInstanceMePage({

View File

@ -1,17 +1,12 @@
import { HeaderLeft, HeaderRight } from '@components/Header' import { HeaderLeft, HeaderRight } from '@components/Header'
import { MenuContainer, MenuRow } from '@components/Menu' import { MenuContainer, MenuRow } from '@components/Menu'
import {
checkPermission,
PERMISSION_MANAGE_REPORTS,
PERMISSION_MANAGE_USERS
} from '@helpers/permissions'
import { useAppDispatch } from '@root/store' import { useAppDispatch } from '@root/store'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { TabNotificationsStackScreenProps } from '@utils/navigation/navigators' import { TabNotificationsStackScreenProps } from '@utils/navigation/navigators'
import { useProfileQuery } from '@utils/queryHooks/profile' import { useProfileQuery } from '@utils/queryHooks/profile'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { PUSH_ADMIN, PUSH_DEFAULT, usePushFeatures } from '@utils/slices/instances/push/utils'
import { import {
checkInstanceFeature,
getInstanceNotificationsFilter, getInstanceNotificationsFilter,
updateInstanceNotificationsFilter updateInstanceNotificationsFilter
} from '@utils/slices/instancesSlice' } from '@utils/slices/instancesSlice'
@ -22,32 +17,12 @@ import { Alert } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler' import { ScrollView } from 'react-native-gesture-handler'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
export const NOTIFICATIONS_FILTERS_DEFAULT: [
'follow',
'follow_request',
'favourite',
'reblog',
'mention',
'poll',
'status',
'update'
] = ['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'status', 'update']
export const NOTIFICATIONS_FILTERS_ADMIN: {
type: 'admin.sign_up' | 'admin.report'
permission: number
}[] = [
{ type: 'admin.sign_up', permission: PERMISSION_MANAGE_USERS },
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS }
]
const TabNotificationsFilters: React.FC< const TabNotificationsFilters: React.FC<
TabNotificationsStackScreenProps<'Tab-Notifications-Filters'> TabNotificationsStackScreenProps<'Tab-Notifications-Filters'>
> = ({ navigation }) => { > = ({ navigation }) => {
const { t } = useTranslation('screenTabs') const { t } = useTranslation('screenTabs')
const hasTypeStatus = useSelector(checkInstanceFeature('notification_type_status')) const pushFeatures = usePushFeatures()
const hasTypeUpdate = useSelector(checkInstanceFeature('notification_type_update'))
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@ -103,16 +78,7 @@ const TabNotificationsFilters: React.FC<
return ( return (
<ScrollView style={{ flex: 1 }}> <ScrollView style={{ flex: 1 }}>
<MenuContainer> <MenuContainer>
{NOTIFICATIONS_FILTERS_DEFAULT.filter(type => { {PUSH_DEFAULT(pushFeatures).map((type, index) => (
switch (type) {
case 'status':
return hasTypeStatus
case 'update':
return hasTypeUpdate
default:
return true
}
}).map((type, index) => (
<MenuRow <MenuRow
key={index} key={index}
title={t(`notifications.filters.options.${type}`)} title={t(`notifications.filters.options.${type}`)}
@ -120,9 +86,7 @@ const TabNotificationsFilters: React.FC<
switchOnValueChange={() => setFilters({ ...filters, [type]: !filters[type] })} switchOnValueChange={() => setFilters({ ...filters, [type]: !filters[type] })}
/> />
))} ))}
{NOTIFICATIONS_FILTERS_ADMIN.filter(({ permission }) => {PUSH_ADMIN(pushFeatures, profileQuery.data?.role?.permissions).map(({ type }) => (
checkPermission(permission, profileQuery.data?.role?.permissions)
).map(({ type }) => (
<MenuRow <MenuRow
key={type} key={type}
title={t(`notifications.filters.options.${type}`)} title={t(`notifications.filters.options.${type}`)}

View File

@ -156,6 +156,7 @@ const instancesMigration = {
mention: instance.push.alerts.mention.value, mention: instance.push.alerts.mention.value,
poll: instance.push.alerts.poll.value, poll: instance.push.alerts.poll.value,
status: instance.push.alerts.status.value, status: instance.push.alerts.status.value,
update: false,
'admin.sign_up': false, 'admin.sign_up': false,
'admin.report': false 'admin.report': false
} }

View File

@ -102,6 +102,7 @@ const addInstance = createAsyncThunk(
mention: true, mention: true,
poll: true, poll: true,
status: true, status: true,
update: true,
'admin.sign_up': false, 'admin.sign_up': false,
'admin.report': false 'admin.report': false
}, },

View File

@ -1,3 +1,4 @@
import features from '@helpers/features'
import { import {
checkPermission, checkPermission,
PERMISSION_MANAGE_REPORTS, PERMISSION_MANAGE_REPORTS,
@ -7,22 +8,59 @@ import queryClient from '@helpers/queryClient'
import i18n from '@root/i18n/i18n' import i18n from '@root/i18n/i18n'
import { InstanceLatest } from '@utils/migrations/instances/migration' import { InstanceLatest } from '@utils/migrations/instances/migration'
import { queryFunctionProfile, QueryKeyProfile } from '@utils/queryHooks/profile' import { queryFunctionProfile, QueryKeyProfile } from '@utils/queryHooks/profile'
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
import { useSelector } from 'react-redux'
export const PUSH_DEFAULT: [ export const usePushFeatures = () => {
'follow', const hasTypeStatus = useSelector(checkInstanceFeature('notification_type_status'))
'follow_request', const hasTypeUpdate = useSelector(checkInstanceFeature('notification_type_update'))
'favourite', const hasTypeAdminSignup = useSelector(checkInstanceFeature('notification_type_admin_signup'))
'reblog', const hasTypeAdminReport = useSelector(checkInstanceFeature('notification_type_admin_report'))
'mention', return { hasTypeStatus, hasTypeUpdate, hasTypeAdminSignup, hasTypeAdminReport }
'poll', }
'status'
] = ['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'status']
export const PUSH_ADMIN: { type: 'admin.sign_up' | 'admin.report'; permission: number }[] = [ export const PUSH_DEFAULT = ({
{ type: 'admin.sign_up', permission: PERMISSION_MANAGE_USERS }, hasTypeUpdate,
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS } hasTypeStatus
] }: {
hasTypeUpdate: boolean
hasTypeStatus: boolean
}) =>
['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'update', 'status'].filter(
type => {
switch (type) {
case 'status':
return hasTypeStatus
case 'update':
return hasTypeUpdate
default:
return true
}
}
) as ['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'update', 'status']
export const PUSH_ADMIN = (
{
hasTypeAdminSignup,
hasTypeAdminReport
}: {
hasTypeAdminSignup: boolean
hasTypeAdminReport: boolean
},
permissions?: string | number | undefined
) =>
[
{ type: 'admin.sign_up', permission: PERMISSION_MANAGE_USERS },
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS }
].filter(({ type, permission }) => {
switch (type) {
case 'admin.sign_up':
return hasTypeAdminSignup && checkPermission(permission, permissions)
case 'admin.report':
return hasTypeAdminReport && checkPermission(permission, permissions)
}
}) as { type: 'admin.sign_up' | 'admin.report'; permission: number }[]
export const setChannels = async (instance: InstanceLatest) => { export const setChannels = async (instance: InstanceLatest) => {
const account = `@${instance.account.acct}@${instance.uri}` const account = `@${instance.account.acct}@${instance.uri}`
@ -48,23 +86,32 @@ export const setChannels = async (instance: InstanceLatest) => {
await Notifications.setNotificationChannelGroupAsync(account, { name: account }) await Notifications.setNotificationChannelGroupAsync(account, { name: account })
} }
const checkFeature = (feature: string) =>
features
.filter(f => f.feature === feature)
.filter(f => parseFloat(instance.version) >= f.version)?.length > 0
const checkFeatures = {
hasTypeStatus: checkFeature('notification_type_status'),
hasTypeUpdate: checkFeature('notification_type_update'),
hasTypeAdminSignup: checkFeature('notification_type_admin_signup'),
hasTypeAdminReport: checkFeature('notification_type_admin_report')
}
if (!instance.push.decode) { if (!instance.push.decode) {
await setChannel('default') await setChannel('default')
for (const push of PUSH_DEFAULT) { for (const push of PUSH_DEFAULT(checkFeatures)) {
await deleteChannel(push) await deleteChannel(push)
} }
for (const { type } of PUSH_ADMIN) { for (const { type } of PUSH_ADMIN(checkFeatures, profileQuery.role?.permissions)) {
await deleteChannel(type) await deleteChannel(type)
} }
} else { } else {
await deleteChannel('default') await deleteChannel('default')
for (const push of PUSH_DEFAULT) { for (const push of PUSH_DEFAULT(checkFeatures)) {
await setChannel(push) await setChannel(push)
} }
for (const { type, permission } of PUSH_ADMIN) { for (const { type } of PUSH_ADMIN(checkFeatures, profileQuery.role?.permissions)) {
if (checkPermission(permission, profileQuery.role?.permissions)) { await setChannel(type)
await setChannel(type)
}
} }
} }
} }