mirror of
https://github.com/tooot-app/app
synced 2024-12-22 07:34:06 +01:00
Add admin notifications besides push #535
This commit is contained in:
parent
bdbacf579e
commit
213328ef1a
@ -5,7 +5,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { ColorDefinitions } from '@utils/styles/themes'
|
import { ColorDefinitions } from '@utils/styles/themes'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Flow } from 'react-native-animated-spinkit'
|
import { Flow } from 'react-native-animated-spinkit'
|
||||||
import { State, Switch, TapGestureHandler } from 'react-native-gesture-handler'
|
import { State, Switch, TapGestureHandler } from 'react-native-gesture-handler'
|
||||||
|
|
||||||
|
@ -1,2 +1,9 @@
|
|||||||
export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010
|
export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010
|
||||||
export const PERMISSION_MANAGE_USERS = 0x0000000000000400
|
export const PERMISSION_MANAGE_USERS = 0x0000000000000400
|
||||||
|
|
||||||
|
export const checkPermission = (permission: number, permissions?: string | number): boolean =>
|
||||||
|
permissions
|
||||||
|
? !!(
|
||||||
|
(typeof permissions === 'string' ? parseInt(permissions || '0') : permissions) & permission
|
||||||
|
)
|
||||||
|
: false
|
||||||
|
@ -2,19 +2,6 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"altText": {
|
"altText": {
|
||||||
"heading": "Alternative Text"
|
"heading": "Alternative Text"
|
||||||
},
|
|
||||||
"notificationsFilter": {
|
|
||||||
"heading": "Show notification types",
|
|
||||||
"content": {
|
|
||||||
"follow": "$t(screenTabs:me.push.follow.heading)",
|
|
||||||
"follow_request": "Follow request",
|
|
||||||
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
|
||||||
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
|
||||||
"mention": "$t(screenTabs:me.push.mention.heading)",
|
|
||||||
"poll": "$t(screenTabs:me.push.poll.heading)",
|
|
||||||
"status": "Toot from subscribed users",
|
|
||||||
"update": "Reblog has been edited"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,9 +24,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"filter": {
|
"filters": {
|
||||||
"accessibilityLabel": "Filter",
|
"accessibilityLabel": "Filter",
|
||||||
"accessibilityHint": "Filter shown notifications' types"
|
"accessibilityHint": "Filter shown notifications' types",
|
||||||
|
"title": "Show notifications",
|
||||||
|
"options": {
|
||||||
|
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||||
|
"follow_request": "Follow request",
|
||||||
|
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
||||||
|
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
||||||
|
"mention": "$t(screenTabs:me.push.mention.heading)",
|
||||||
|
"poll": "$t(screenTabs:me.push.poll.heading)",
|
||||||
|
"status": "Toot from subscribed users",
|
||||||
|
"update": "Reblog has been edited",
|
||||||
|
"admin.sign_up": "$t(screenTabs:me.push.admin.sign_up.heading)",
|
||||||
|
"admin.report": "$t(screenTabs:me.push.admin.report.heading)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"me": {
|
"me": {
|
||||||
|
@ -15,7 +15,6 @@ import Animated, {
|
|||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import ActionsAltText from './Actions/AltText'
|
import ActionsAltText from './Actions/AltText'
|
||||||
import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
|
||||||
|
|
||||||
const ScreenActions = ({
|
const ScreenActions = ({
|
||||||
route: { params },
|
route: { params },
|
||||||
@ -53,8 +52,6 @@ const ScreenActions = ({
|
|||||||
|
|
||||||
const actions = () => {
|
const actions = () => {
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
case 'notifications_filter':
|
|
||||||
return <ActionsNotificationsFilter />
|
|
||||||
case 'alt_text':
|
case 'alt_text':
|
||||||
return <ActionsAltText text={params.text} />
|
return <ActionsAltText text={params.text} />
|
||||||
}
|
}
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
import Button from '@components/Button'
|
|
||||||
import MenuContainer from '@components/Menu/Container'
|
|
||||||
import MenuHeader from '@components/Menu/Header'
|
|
||||||
import MenuRow from '@components/Menu/Row'
|
|
||||||
import { useNavigation } from '@react-navigation/native'
|
|
||||||
import {
|
|
||||||
checkInstanceFeature,
|
|
||||||
getInstanceNotificationsFilter,
|
|
||||||
updateInstanceNotificationsFilter
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import React, { useMemo } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
|
||||||
import { useAppDispatch } from '@root/store'
|
|
||||||
|
|
||||||
const ActionsNotificationsFilter: React.FC = () => {
|
|
||||||
const navigation = useNavigation()
|
|
||||||
const { t } = useTranslation('screenActions')
|
|
||||||
|
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
|
||||||
const queryClient = useQueryClient()
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
const instanceNotificationsFilter = useSelector(
|
|
||||||
getInstanceNotificationsFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!instanceNotificationsFilter) {
|
|
||||||
navigation.goBack()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasTypeStatus = useSelector(
|
|
||||||
checkInstanceFeature('notification_type_status')
|
|
||||||
)
|
|
||||||
const hasTypeUpdate = useSelector(
|
|
||||||
checkInstanceFeature('notification_type_update')
|
|
||||||
)
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return (
|
|
||||||
instanceNotificationsFilter &&
|
|
||||||
(
|
|
||||||
[
|
|
||||||
'follow',
|
|
||||||
'follow_request',
|
|
||||||
'favourite',
|
|
||||||
'reblog',
|
|
||||||
'mention',
|
|
||||||
'poll',
|
|
||||||
'status',
|
|
||||||
'update'
|
|
||||||
] as [
|
|
||||||
'follow',
|
|
||||||
'follow_request',
|
|
||||||
'favourite',
|
|
||||||
'reblog',
|
|
||||||
'mention',
|
|
||||||
'poll',
|
|
||||||
'status',
|
|
||||||
'update'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
.filter(type => {
|
|
||||||
switch (type) {
|
|
||||||
case 'status':
|
|
||||||
return hasTypeStatus
|
|
||||||
case 'update':
|
|
||||||
return hasTypeUpdate
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(type => (
|
|
||||||
<MenuRow
|
|
||||||
key={type}
|
|
||||||
title={t(`content.notificationsFilter.content.${type}`)}
|
|
||||||
switchValue={instanceNotificationsFilter[type]}
|
|
||||||
switchOnValueChange={() =>
|
|
||||||
dispatch(
|
|
||||||
updateInstanceNotificationsFilter({
|
|
||||||
...instanceNotificationsFilter,
|
|
||||||
[type]: !instanceNotificationsFilter[type]
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}, [instanceNotificationsFilter, hasTypeStatus, hasTypeUpdate])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MenuContainer>
|
|
||||||
<MenuHeader heading={t(`content.notificationsFilter.heading`)} />
|
|
||||||
{options}
|
|
||||||
</MenuContainer>
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
content={t('common:buttons.apply')}
|
|
||||||
onPress={() => {
|
|
||||||
queryClient.resetQueries(queryKey)
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ActionsNotificationsFilter
|
|
@ -3,17 +3,13 @@ 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 {
|
import { PUSH_ADMIN, PUSH_DEFAULT, setChannels } from '@utils/slices/instances/push/utils'
|
||||||
checkPushAdminPermission,
|
|
||||||
PUSH_ADMIN,
|
|
||||||
PUSH_DEFAULT,
|
|
||||||
setChannels
|
|
||||||
} 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'
|
||||||
@ -88,7 +84,6 @@ const TabMePush: React.FC = () => {
|
|||||||
switchOnValueChange={() =>
|
switchOnValueChange={() =>
|
||||||
dispatch(
|
dispatch(
|
||||||
updateInstancePushAlert({
|
updateInstancePushAlert({
|
||||||
changed: alert,
|
|
||||||
alerts: {
|
alerts: {
|
||||||
...instancePush?.alerts,
|
...instancePush?.alerts,
|
||||||
[alert]: instancePush?.alerts[alert]
|
[alert]: instancePush?.alerts[alert]
|
||||||
@ -104,7 +99,7 @@ const TabMePush: React.FC = () => {
|
|||||||
const adminAlerts = () =>
|
const adminAlerts = () =>
|
||||||
profileQuery.data?.role?.permissions
|
profileQuery.data?.role?.permissions
|
||||||
? PUSH_ADMIN.map(({ type, permission }) =>
|
? PUSH_ADMIN.map(({ type, permission }) =>
|
||||||
checkPushAdminPermission(permission, profileQuery.data.role?.permissions) ? (
|
checkPermission(permission, profileQuery.data.role?.permissions) ? (
|
||||||
<MenuRow
|
<MenuRow
|
||||||
key={type}
|
key={type}
|
||||||
title={t(`me.push.${type}.heading`)}
|
title={t(`me.push.${type}.heading`)}
|
||||||
@ -113,7 +108,6 @@ const TabMePush: React.FC = () => {
|
|||||||
switchOnValueChange={() =>
|
switchOnValueChange={() =>
|
||||||
dispatch(
|
dispatch(
|
||||||
updateInstancePushAlert({
|
updateInstancePushAlert({
|
||||||
changed: type,
|
|
||||||
alerts: {
|
alerts: {
|
||||||
...instancePush?.alerts,
|
...instancePush?.alerts,
|
||||||
[type]: instancePush?.alerts[type]
|
[type]: instancePush?.alerts[type]
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
import { HeaderCenter, HeaderRight } from '@components/Header'
|
|
||||||
import Timeline from '@components/Timeline'
|
|
||||||
import TimelineNotifications from '@components/Timeline/Notifications'
|
|
||||||
import navigationRef from '@helpers/navigationRef'
|
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
||||||
import { TabNotificationsStackParamList } from '@utils/navigation/navigators'
|
|
||||||
import usePopToTop from '@utils/navigation/usePopToTop'
|
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
|
||||||
import React, { useCallback, useMemo } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { Platform } from 'react-native'
|
|
||||||
import TabShared from './Shared'
|
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabNotificationsStackParamList>()
|
|
||||||
|
|
||||||
const TabNotifications = React.memo(
|
|
||||||
() => {
|
|
||||||
const { t, i18n } = useTranslation('screenTabs')
|
|
||||||
|
|
||||||
const screenOptionsRoot = useMemo(
|
|
||||||
() => ({
|
|
||||||
title: t('tabs.notifications.name'),
|
|
||||||
...(Platform.OS === 'android' && {
|
|
||||||
headerCenter: () => <HeaderCenter content={t('tabs.notifications.name')} />
|
|
||||||
}),
|
|
||||||
headerRight: () => (
|
|
||||||
<HeaderRight
|
|
||||||
accessibilityLabel={t('notifications.filter.accessibilityLabel')}
|
|
||||||
accessibilityHint={t('notifications.filter.accessibilityHint')}
|
|
||||||
content='Filter'
|
|
||||||
onPress={() =>
|
|
||||||
navigationRef.navigate('Screen-Actions', {
|
|
||||||
type: 'notifications_filter'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
[i18n.language]
|
|
||||||
)
|
|
||||||
|
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
|
||||||
const children = useCallback(
|
|
||||||
() => (
|
|
||||||
<Timeline
|
|
||||||
queryKey={queryKey}
|
|
||||||
customProps={{
|
|
||||||
renderItem: ({ item }) => (
|
|
||||||
<TimelineNotifications notification={item} queryKey={queryKey} />
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
usePopToTop()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
|
||||||
<Stack.Screen
|
|
||||||
name='Tab-Notifications-Root'
|
|
||||||
children={children}
|
|
||||||
options={screenOptionsRoot}
|
|
||||||
/>
|
|
||||||
{TabShared({ Stack })}
|
|
||||||
</Stack.Navigator>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
export default TabNotifications
|
|
138
src/screens/Tabs/Notifications/Filters.tsx
Normal file
138
src/screens/Tabs/Notifications/Filters.tsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
|
import {
|
||||||
|
checkPermission,
|
||||||
|
PERMISSION_MANAGE_REPORTS,
|
||||||
|
PERMISSION_MANAGE_USERS
|
||||||
|
} from '@helpers/permissions'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { TabNotificationsStackScreenProps } from '@utils/navigation/navigators'
|
||||||
|
import { useProfileQuery } from '@utils/queryHooks/profile'
|
||||||
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
import {
|
||||||
|
checkInstanceFeature,
|
||||||
|
getInstanceNotificationsFilter,
|
||||||
|
updateInstanceNotificationsFilter
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
|
import { isEqual } from 'lodash'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Alert } from 'react-native'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
|
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<
|
||||||
|
TabNotificationsStackScreenProps<'Tab-Notifications-Filters'>
|
||||||
|
> = ({ navigation }) => {
|
||||||
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
|
const hasTypeStatus = useSelector(checkInstanceFeature('notification_type_status'))
|
||||||
|
const hasTypeUpdate = useSelector(checkInstanceFeature('notification_type_update'))
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const instanceNotificationsFilter = useSelector(getInstanceNotificationsFilter)
|
||||||
|
const [filters, setFilters] = useState(instanceNotificationsFilter)
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
useEffect(() => {
|
||||||
|
const changed = !isEqual(instanceNotificationsFilter, filters)
|
||||||
|
navigation.setOptions({
|
||||||
|
title: t('notifications.filters.title'),
|
||||||
|
headerLeft: () => (
|
||||||
|
<HeaderLeft
|
||||||
|
content='ChevronDown'
|
||||||
|
onPress={() => {
|
||||||
|
if (changed) {
|
||||||
|
Alert.alert(t('common:discard.title'), t('common:discard.message'), [
|
||||||
|
{
|
||||||
|
text: t('common:buttons.discard'),
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: () => navigation.goBack()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t('common:buttons.cancel'),
|
||||||
|
style: 'default'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
navigation.goBack()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
headerRight: () => (
|
||||||
|
<HeaderRight
|
||||||
|
type='text'
|
||||||
|
content={t('common:buttons.apply')}
|
||||||
|
onPress={() => {
|
||||||
|
if (changed) {
|
||||||
|
dispatch(updateInstanceNotificationsFilter(filters))
|
||||||
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
||||||
|
queryClient.invalidateQueries({ queryKey })
|
||||||
|
}
|
||||||
|
navigation.goBack()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}, [filters])
|
||||||
|
|
||||||
|
const profileQuery = useProfileQuery()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView style={{ flex: 1 }}>
|
||||||
|
<MenuContainer>
|
||||||
|
{NOTIFICATIONS_FILTERS_DEFAULT.filter(type => {
|
||||||
|
switch (type) {
|
||||||
|
case 'status':
|
||||||
|
return hasTypeStatus
|
||||||
|
case 'update':
|
||||||
|
return hasTypeUpdate
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}).map((type, index) => (
|
||||||
|
<MenuRow
|
||||||
|
key={index}
|
||||||
|
title={t(`notifications.filters.options.${type}`)}
|
||||||
|
switchValue={filters[type]}
|
||||||
|
switchOnValueChange={() => setFilters({ ...filters, [type]: !filters[type] })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{NOTIFICATIONS_FILTERS_ADMIN.filter(({ permission }) =>
|
||||||
|
checkPermission(permission, profileQuery.data?.role?.permissions)
|
||||||
|
).map(({ type }) => (
|
||||||
|
<MenuRow
|
||||||
|
key={type}
|
||||||
|
title={t(`notifications.filters.options.${type}`)}
|
||||||
|
switchValue={filters[type]}
|
||||||
|
switchOnValueChange={() => setFilters({ ...filters, [type]: !filters[type] })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</MenuContainer>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabNotificationsFilters
|
61
src/screens/Tabs/Notifications/index.tsx
Normal file
61
src/screens/Tabs/Notifications/index.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { HeaderRight } from '@components/Header'
|
||||||
|
import Timeline from '@components/Timeline'
|
||||||
|
import TimelineNotifications from '@components/Timeline/Notifications'
|
||||||
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||||
|
import { ScreenTabsScreenProps, TabNotificationsStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import usePopToTop from '@utils/navigation/usePopToTop'
|
||||||
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import TabShared from '../Shared'
|
||||||
|
import TabNotificationsFilters from './Filters'
|
||||||
|
|
||||||
|
const Stack = createNativeStackNavigator<TabNotificationsStackParamList>()
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Notifications' }]
|
||||||
|
return (
|
||||||
|
<Timeline
|
||||||
|
queryKey={queryKey}
|
||||||
|
customProps={{
|
||||||
|
renderItem: ({ item }) => <TimelineNotifications notification={item} queryKey={queryKey} />
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const TabNotifications = ({ navigation }: ScreenTabsScreenProps<'Tab-Notifications'>) => {
|
||||||
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
|
usePopToTop()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Tab-Notifications-Root'
|
||||||
|
component={Root}
|
||||||
|
options={{
|
||||||
|
title: t('tabs.notifications.name'),
|
||||||
|
headerRight: () => (
|
||||||
|
<HeaderRight
|
||||||
|
accessibilityLabel={t('notifications.filters.accessibilityLabel')}
|
||||||
|
accessibilityHint={t('notifications.filters.accessibilityHint')}
|
||||||
|
content='Filter'
|
||||||
|
onPress={() =>
|
||||||
|
navigation.navigate('Tab-Notifications', { screen: 'Tab-Notifications-Filters' })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Tab-Notifications-Filters'
|
||||||
|
component={TabNotificationsFilters}
|
||||||
|
options={{ presentation: 'modal', gestureEnabled: false }}
|
||||||
|
/>
|
||||||
|
{TabShared({ Stack })}
|
||||||
|
</Stack.Navigator>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabNotifications
|
@ -6,15 +6,10 @@ import TabSharedHistory from '@screens/Tabs/Shared/History'
|
|||||||
import TabSharedSearch from '@screens/Tabs/Shared/Search'
|
import TabSharedSearch from '@screens/Tabs/Shared/Search'
|
||||||
import TabSharedToot from '@screens/Tabs/Shared/Toot'
|
import TabSharedToot from '@screens/Tabs/Shared/Toot'
|
||||||
import TabSharedUsers from '@screens/Tabs/Shared/Users'
|
import TabSharedUsers from '@screens/Tabs/Shared/Users'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import TabSharedAccountInLists from './AccountInLists'
|
import TabSharedAccountInLists from './AccountInLists'
|
||||||
|
|
||||||
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
||||||
const { colors, mode } = useTheme()
|
|
||||||
const { t } = useTranslation('screenTabs')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack.Group>
|
<Stack.Group>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
@ -136,6 +136,11 @@ const instancesMigration = {
|
|||||||
return {
|
return {
|
||||||
...instance,
|
...instance,
|
||||||
mePage: { ...instance.mePage, followedTags: { shown: false } },
|
mePage: { ...instance.mePage, followedTags: { shown: false } },
|
||||||
|
notifications_filter: {
|
||||||
|
...instance.notifications_filter,
|
||||||
|
'admin.sign_up': true,
|
||||||
|
'admin.report': true
|
||||||
|
},
|
||||||
push: {
|
push: {
|
||||||
...instance.push,
|
...instance.push,
|
||||||
global: instance.push.global.value,
|
global: instance.push.global.value,
|
||||||
|
@ -29,6 +29,8 @@ export type InstanceV11 = {
|
|||||||
poll: boolean
|
poll: boolean
|
||||||
status: boolean
|
status: boolean
|
||||||
update: boolean
|
update: boolean
|
||||||
|
'admin.sign_up': boolean
|
||||||
|
'admin.report': boolean
|
||||||
}
|
}
|
||||||
push: {
|
push: {
|
||||||
global: boolean
|
global: boolean
|
||||||
|
@ -136,7 +136,10 @@ export type TabPublicStackParamList = {
|
|||||||
|
|
||||||
export type TabNotificationsStackParamList = {
|
export type TabNotificationsStackParamList = {
|
||||||
'Tab-Notifications-Root': undefined
|
'Tab-Notifications-Root': undefined
|
||||||
|
'Tab-Notifications-Filters': undefined
|
||||||
} & TabSharedStackParamList
|
} & TabSharedStackParamList
|
||||||
|
export type TabNotificationsStackScreenProps<T extends keyof TabNotificationsStackParamList> =
|
||||||
|
NativeStackScreenProps<TabNotificationsStackParamList, T>
|
||||||
|
|
||||||
export type TabMeStackParamList = {
|
export type TabMeStackParamList = {
|
||||||
'Tab-Me-Root': undefined
|
'Tab-Me-Root': undefined
|
||||||
|
@ -87,7 +87,9 @@ const addInstance = createAsyncThunk(
|
|||||||
mention: true,
|
mention: true,
|
||||||
poll: true,
|
poll: true,
|
||||||
status: true,
|
status: true,
|
||||||
update: true
|
update: true,
|
||||||
|
'admin.sign_up': true,
|
||||||
|
'admin.report': true
|
||||||
},
|
},
|
||||||
push: {
|
push: {
|
||||||
global: false,
|
global: false,
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { PERMISSION_MANAGE_REPORTS, PERMISSION_MANAGE_USERS } from '@helpers/permissions'
|
import {
|
||||||
|
checkPermission,
|
||||||
|
PERMISSION_MANAGE_REPORTS,
|
||||||
|
PERMISSION_MANAGE_USERS
|
||||||
|
} from '@helpers/permissions'
|
||||||
import queryClient from '@helpers/queryClient'
|
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'
|
||||||
@ -20,16 +24,6 @@ export const PUSH_ADMIN: { type: 'admin.sign_up' | 'admin.report'; permission: n
|
|||||||
{ type: 'admin.report', permission: PERMISSION_MANAGE_REPORTS }
|
{ 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) => {
|
export const setChannels = async (instance: InstanceLatest) => {
|
||||||
const account = `@${instance.account.acct}@${instance.uri}`
|
const account = `@${instance.account.acct}@${instance.uri}`
|
||||||
|
|
||||||
@ -68,7 +62,7 @@ export const setChannels = async (instance: InstanceLatest) => {
|
|||||||
await setChannel(push)
|
await setChannel(push)
|
||||||
}
|
}
|
||||||
for (const { type, permission } of PUSH_ADMIN) {
|
for (const { type, permission } of PUSH_ADMIN) {
|
||||||
if (checkPushAdminPermission(permission, profileQuery.role?.permissions)) {
|
if (checkPermission(permission, profileQuery.role?.permissions)) {
|
||||||
await setChannel(type)
|
await setChannel(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ export const updateInstancePushAlert = createAsyncThunk(
|
|||||||
async ({
|
async ({
|
||||||
alerts
|
alerts
|
||||||
}: {
|
}: {
|
||||||
changed: keyof InstanceLatest['push']['alerts']
|
|
||||||
alerts: InstanceLatest['push']['alerts']
|
alerts: InstanceLatest['push']['alerts']
|
||||||
}): Promise<InstanceLatest['push']['alerts']> => {
|
}): Promise<InstanceLatest['push']['alerts']> => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
Loading…
Reference in New Issue
Block a user