mirror of https://github.com/tooot-app/app
Refine notifications
https://github.com/tooot-app/app/issues/306 https://github.com/tooot-app/app/issues/305 This one uses the positive filtering that is added since v3.5, that a such a filter won't be shown as there is no way to check if a user is an admin or not and showing a useless option for majority users won't be a good experience.
This commit is contained in:
parent
5a23b73f69
commit
4398e520ed
|
@ -341,6 +341,7 @@ declare namespace Mastodon {
|
||||||
| 'favourite'
|
| 'favourite'
|
||||||
| 'poll'
|
| 'poll'
|
||||||
| 'status'
|
| 'status'
|
||||||
|
| 'update'
|
||||||
created_at: string
|
created_at: string
|
||||||
account: Account
|
account: Account
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { useAppDispatch } from '@root/store'
|
import { useAppDispatch } from '@root/store'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
||||||
import addInstance from '@utils/slices/instances/add'
|
import addInstance from '@utils/slices/instances/add'
|
||||||
import { checkInstanceFeature, Instance } from '@utils/slices/instancesSlice'
|
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
|
@ -12,7 +13,7 @@ export interface Props {
|
||||||
instanceDomain: string
|
instanceDomain: string
|
||||||
// Domain can be different than uri
|
// Domain can be different than uri
|
||||||
instance: Mastodon.Instance
|
instance: Mastodon.Instance
|
||||||
appData: Instance['appData']
|
appData: InstanceLatest['appData']
|
||||||
goBack?: boolean
|
goBack?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ const TimelineActioned = React.memo(
|
||||||
navigation.push('Tab-Shared-Account', { account })
|
navigation.push('Tab-Shared-Account', { account })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = () => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'pinned':
|
case 'pinned':
|
||||||
return (
|
return (
|
||||||
|
@ -48,7 +48,6 @@ const TimelineActioned = React.memo(
|
||||||
{content(t('shared.actioned.pinned'))}
|
{content(t('shared.actioned.pinned'))}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -63,7 +62,6 @@ const TimelineActioned = React.memo(
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'follow':
|
case 'follow':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -78,7 +76,6 @@ const TimelineActioned = React.memo(
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'follow_request':
|
case 'follow_request':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -93,7 +90,6 @@ const TimelineActioned = React.memo(
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'poll':
|
case 'poll':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -106,7 +102,6 @@ const TimelineActioned = React.memo(
|
||||||
{content(t('shared.actioned.poll'))}
|
{content(t('shared.actioned.poll'))}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'reblog':
|
case 'reblog':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -125,7 +120,6 @@ const TimelineActioned = React.memo(
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
|
||||||
case 'status':
|
case 'status':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -140,9 +134,22 @@ const TimelineActioned = React.memo(
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
break
|
case 'update':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
name='BarChart2'
|
||||||
|
size={StyleConstants.Font.Size.S}
|
||||||
|
color={iconColor}
|
||||||
|
style={styles.icon}
|
||||||
|
/>
|
||||||
|
{content(t('shared.actioned.update'))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return <></>
|
||||||
}
|
}
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
@ -153,8 +160,9 @@ const TimelineActioned = React.memo(
|
||||||
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
||||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||||
}}
|
}}
|
||||||
children={children}
|
>
|
||||||
/>
|
{children()}
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
|
|
|
@ -8,5 +8,20 @@
|
||||||
"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"
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"feature": "notification_type_status",
|
||||||
|
"version": 3.3,
|
||||||
|
"reference": "https://docs.joinmastodon.org/entities/notification/#required-attributes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"feature": "notification_type_update",
|
||||||
|
"version": 3.5,
|
||||||
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"feature": "notification_types_positive_filter",
|
||||||
|
"version": 3.5,
|
||||||
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -29,7 +29,8 @@
|
||||||
"reblog": {
|
"reblog": {
|
||||||
"default": "{{name}} boosted",
|
"default": "{{name}} boosted",
|
||||||
"notification": "{{name}} boosted your toot"
|
"notification": "{{name}} boosted your toot"
|
||||||
}
|
},
|
||||||
|
"update": "Reblog has been edited"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"reply": {
|
"reply": {
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
"heading": "Show notification types",
|
"heading": "Show notification types",
|
||||||
"content": {
|
"content": {
|
||||||
"follow": "$t(screenTabs:me.push.follow.heading)",
|
"follow": "$t(screenTabs:me.push.follow.heading)",
|
||||||
|
"follow_request": "Follow request",
|
||||||
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
"favourite": "$t(screenTabs:me.push.favourite.heading)",
|
||||||
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
"reblog": "$t(screenTabs:me.push.reblog.heading)",
|
||||||
"mention": "$t(screenTabs:me.push.mention.heading)",
|
"mention": "$t(screenTabs:me.push.mention.heading)",
|
||||||
"poll": "$t(screenTabs:me.push.poll.heading)",
|
"poll": "$t(screenTabs:me.push.poll.heading)",
|
||||||
"follow_request": "Follow request"
|
"status": "Toot from subscribed users",
|
||||||
|
"update": "Reblog has been edited"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import MenuHeader from '@components/Menu/Header'
|
||||||
import MenuRow from '@components/Menu/Row'
|
import MenuRow from '@components/Menu/Row'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import {
|
import {
|
||||||
|
checkInstanceFeature,
|
||||||
getInstanceNotificationsFilter,
|
getInstanceNotificationsFilter,
|
||||||
updateInstanceNotificationsFilter
|
updateInstanceNotificationsFilter
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
|
@ -33,42 +34,63 @@ const ActionsNotificationsFilter: React.FC = () => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasTypeStatus = useSelector(
|
||||||
|
checkInstanceFeature('notification_type_status')
|
||||||
|
)
|
||||||
|
const hasTypeUpdate = useSelector(
|
||||||
|
checkInstanceFeature('notification_type_update')
|
||||||
|
)
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
instanceNotificationsFilter &&
|
instanceNotificationsFilter &&
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
'follow',
|
'follow',
|
||||||
|
'follow_request',
|
||||||
'favourite',
|
'favourite',
|
||||||
'reblog',
|
'reblog',
|
||||||
'mention',
|
'mention',
|
||||||
'poll',
|
'poll',
|
||||||
'follow_request'
|
'status',
|
||||||
|
'update'
|
||||||
] as [
|
] as [
|
||||||
'follow',
|
'follow',
|
||||||
|
'follow_request',
|
||||||
'favourite',
|
'favourite',
|
||||||
'reblog',
|
'reblog',
|
||||||
'mention',
|
'mention',
|
||||||
'poll',
|
'poll',
|
||||||
'follow_request'
|
'status',
|
||||||
|
'update'
|
||||||
]
|
]
|
||||||
).map(type => (
|
)
|
||||||
<MenuRow
|
.filter(type => {
|
||||||
key={type}
|
switch (type) {
|
||||||
title={t(`content.notificationsFilter.content.${type}`)}
|
case 'status':
|
||||||
switchValue={instanceNotificationsFilter[type]}
|
return hasTypeStatus
|
||||||
switchOnValueChange={() =>
|
case 'update':
|
||||||
dispatch(
|
return hasTypeUpdate
|
||||||
updateInstanceNotificationsFilter({
|
default:
|
||||||
...instanceNotificationsFilter,
|
return true
|
||||||
[type]: !instanceNotificationsFilter[type]
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/>
|
})
|
||||||
))
|
.map(type => (
|
||||||
|
<MenuRow
|
||||||
|
key={type}
|
||||||
|
title={t(`content.notificationsFilter.content.${type}`)}
|
||||||
|
switchValue={instanceNotificationsFilter[type]}
|
||||||
|
switchOnValueChange={() =>
|
||||||
|
dispatch(
|
||||||
|
updateInstanceNotificationsFilter({
|
||||||
|
...instanceNotificationsFilter,
|
||||||
|
[type]: !instanceNotificationsFilter[type]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}, [instanceNotificationsFilter])
|
}, [instanceNotificationsFilter, hasTypeStatus, hasTypeUpdate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -5,11 +5,8 @@ import ComponentInstance from '@components/Instance'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import initQuery from '@utils/initQuery'
|
import initQuery from '@utils/initQuery'
|
||||||
import {
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
getInstanceActive,
|
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||||
getInstances,
|
|
||||||
Instance
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
@ -19,7 +16,7 @@ import { ScrollView } from 'react-native-gesture-handler'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
instance: Instance
|
instance: InstanceLatest
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { isRelease } from '@utils/checkEnvironment'
|
|
||||||
import * as Sentry from 'sentry-expo'
|
import * as Sentry from 'sentry-expo'
|
||||||
import log from './log'
|
import log from './log'
|
||||||
|
|
||||||
|
@ -7,7 +6,7 @@ const sentry = () => {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: 'https://53348b60ff844d52886e90251b3a5f41@o917354.ingest.sentry.io/6410576',
|
dsn: 'https://53348b60ff844d52886e90251b3a5f41@o917354.ingest.sentry.io/6410576',
|
||||||
enableInExpoDevelopment: false,
|
enableInExpoDevelopment: false,
|
||||||
debug: !isRelease,
|
// debug: !isRelease,
|
||||||
autoSessionTracking: true
|
autoSessionTracking: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ const instancesPersistConfig = {
|
||||||
key: 'instances',
|
key: 'instances',
|
||||||
prefix,
|
prefix,
|
||||||
storage: Platform.OS === 'ios' ? secureStorage : AsyncStorage,
|
storage: Platform.OS === 'ios' ? secureStorage : AsyncStorage,
|
||||||
version: 9,
|
version: 10,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
migrate: createMigrate(instancesMigration)
|
migrate: createMigrate(instancesMigration)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
|
import { InstanceLatest } from './migrations/instances/migration'
|
||||||
// import { prefetchTimelineQuery } from './queryHooks/timeline'
|
// import { prefetchTimelineQuery } from './queryHooks/timeline'
|
||||||
import { Instance, updateInstanceActive } from './slices/instancesSlice'
|
import { updateInstanceActive } from './slices/instancesSlice'
|
||||||
|
|
||||||
const initQuery = async ({
|
const initQuery = async ({
|
||||||
instance,
|
instance,
|
||||||
prefetch
|
prefetch
|
||||||
}: {
|
}: {
|
||||||
instance: Instance
|
instance: InstanceLatest
|
||||||
prefetch?: { enabled: boolean; page?: 'Following' | 'LocalPublic' }
|
prefetch?: { enabled: boolean; page?: 'Following' | 'LocalPublic' }
|
||||||
}) => {
|
}) => {
|
||||||
store.dispatch(updateInstanceActive(instance))
|
store.dispatch(updateInstanceActive(instance))
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { InstanceV6 } from './v6'
|
||||||
import { InstanceV7 } from './v7'
|
import { InstanceV7 } from './v7'
|
||||||
import { InstanceV8 } from './v8'
|
import { InstanceV8 } from './v8'
|
||||||
import { InstanceV9 } from './v9'
|
import { InstanceV9 } from './v9'
|
||||||
|
import { InstanceV10 } from './v10'
|
||||||
|
|
||||||
const instancesMigration = {
|
const instancesMigration = {
|
||||||
4: (state: InstanceV3): InstanceV4 => {
|
4: (state: InstanceV3): InstanceV4 => {
|
||||||
|
@ -99,7 +100,23 @@ const instancesMigration = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
10: (state: { instances: InstanceV9[] }): { instances: InstanceV10[] } => {
|
||||||
|
return {
|
||||||
|
instances: state.instances.map(instance => {
|
||||||
|
return {
|
||||||
|
...instance,
|
||||||
|
notifications_filter: {
|
||||||
|
...instance.notifications_filter,
|
||||||
|
status: true,
|
||||||
|
update: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { InstanceV10 as InstanceLatest }
|
||||||
|
|
||||||
export default instancesMigration
|
export default instancesMigration
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
|
|
||||||
|
export type InstanceV10 = {
|
||||||
|
active: boolean
|
||||||
|
appData: {
|
||||||
|
clientId: string
|
||||||
|
clientSecret: string
|
||||||
|
}
|
||||||
|
url: string
|
||||||
|
token: string
|
||||||
|
uri: Mastodon.Instance['uri']
|
||||||
|
urls: Mastodon.Instance['urls']
|
||||||
|
account: {
|
||||||
|
id: Mastodon.Account['id']
|
||||||
|
acct: Mastodon.Account['acct']
|
||||||
|
avatarStatic: Mastodon.Account['avatar_static']
|
||||||
|
preferences: Mastodon.Preferences
|
||||||
|
}
|
||||||
|
version: string
|
||||||
|
configuration?: Mastodon.Instance['configuration']
|
||||||
|
filters: Mastodon.Filter[]
|
||||||
|
notifications_filter: {
|
||||||
|
follow: boolean
|
||||||
|
follow_request: boolean
|
||||||
|
favourite: boolean
|
||||||
|
reblog: boolean
|
||||||
|
mention: boolean
|
||||||
|
poll: boolean
|
||||||
|
status: boolean
|
||||||
|
update: boolean
|
||||||
|
}
|
||||||
|
push: {
|
||||||
|
global: { loading: boolean; value: boolean }
|
||||||
|
decode: { loading: boolean; value: boolean }
|
||||||
|
alerts: {
|
||||||
|
follow: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['follow']
|
||||||
|
}
|
||||||
|
favourite: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['favourite']
|
||||||
|
}
|
||||||
|
reblog: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['reblog']
|
||||||
|
}
|
||||||
|
mention: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['mention']
|
||||||
|
}
|
||||||
|
poll: {
|
||||||
|
loading: boolean
|
||||||
|
value: Mastodon.PushSubscription['alerts']['poll']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys: {
|
||||||
|
auth?: string
|
||||||
|
public?: string // legacy
|
||||||
|
private?: string // legacy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timelinesLookback?: {
|
||||||
|
[key: string]: {
|
||||||
|
queryKey: QueryKeyTimeline
|
||||||
|
ids: Mastodon.Status['id'][]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mePage: {
|
||||||
|
lists: { shown: boolean }
|
||||||
|
announcements: { shown: boolean; unread: number }
|
||||||
|
}
|
||||||
|
drafts: ComposeStateDraft[]
|
||||||
|
frequentEmojis: {
|
||||||
|
emoji: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>
|
||||||
|
score: number
|
||||||
|
count: number
|
||||||
|
lastUsed: number
|
||||||
|
}[]
|
||||||
|
}
|
|
@ -4,7 +4,8 @@ import { displayMessage } from '@components/Message'
|
||||||
import navigationRef from '@helpers/navigationRef'
|
import navigationRef from '@helpers/navigationRef'
|
||||||
import { useAppDispatch } from '@root/store'
|
import { useAppDispatch } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
import { disableAllPushes, Instance } from '@utils/slices/instancesSlice'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
|
import { disableAllPushes } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
@ -12,7 +13,7 @@ import { TFunction } from 'react-i18next'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
t: TFunction<'screens'>
|
t: TFunction<'screens'>
|
||||||
instances: Instance[]
|
instances: InstanceLatest[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushUseConnect = ({ t, instances }: Params) => {
|
const pushUseConnect = ({ t, instances }: Params) => {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import initQuery from '@utils/initQuery'
|
import initQuery from '@utils/initQuery'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { Instance } from '@utils/slices/instancesSlice'
|
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import pushUseNavigate from './useNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
instances: Instance[]
|
instances: InstanceLatest[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushUseReceive = ({ instances }: Params) => {
|
const pushUseReceive = ({ instances }: Params) => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import initQuery from '@utils/initQuery'
|
import initQuery from '@utils/initQuery'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { Instance } from '@utils/slices/instancesSlice'
|
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import pushUseNavigate from './useNavigate'
|
import pushUseNavigate from './useNavigate'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
instances: Instance[]
|
instances: InstanceLatest[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushUseRespond = ({ instances }: Params) => {
|
const pushUseRespond = ({ instances }: Params) => {
|
||||||
|
|
|
@ -2,7 +2,10 @@ import apiInstance, { InstanceResponse } from '@api/instance'
|
||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
import { getInstanceNotificationsFilter } from '@utils/slices/instancesSlice'
|
import {
|
||||||
|
checkInstanceFeature,
|
||||||
|
getInstanceNotificationsFilter
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
import {
|
import {
|
||||||
|
@ -62,16 +65,26 @@ const queryFunction = async ({
|
||||||
case 'Notifications':
|
case 'Notifications':
|
||||||
const rootStore = store.getState()
|
const rootStore = store.getState()
|
||||||
const notificationsFilter = getInstanceNotificationsFilter(rootStore)
|
const notificationsFilter = getInstanceNotificationsFilter(rootStore)
|
||||||
|
const usePositiveFilter = checkInstanceFeature(
|
||||||
|
'notification_types_positive_filter'
|
||||||
|
)(rootStore)
|
||||||
return apiInstance<Mastodon.Notification[]>({
|
return apiInstance<Mastodon.Notification[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: 'notifications',
|
url: 'notifications',
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
...(notificationsFilter && {
|
...(notificationsFilter &&
|
||||||
exclude_types: Object.keys(notificationsFilter)
|
(usePositiveFilter
|
||||||
// @ts-ignore
|
? {
|
||||||
.filter(filter => notificationsFilter[filter] === false)
|
types: Object.keys(notificationsFilter)
|
||||||
})
|
// @ts-ignore
|
||||||
|
.filter(filter => notificationsFilter[filter] === true)
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
exclude_types: Object.keys(notificationsFilter)
|
||||||
|
// @ts-ignore
|
||||||
|
.filter(filter => notificationsFilter[filter] === false)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import apiGeneral from '@api/general'
|
import apiGeneral from '@api/general'
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { Instance } from '../instancesSlice'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
|
|
||||||
const addInstance = createAsyncThunk(
|
const addInstance = createAsyncThunk(
|
||||||
'instances/add',
|
'instances/add',
|
||||||
|
@ -11,11 +11,11 @@ const addInstance = createAsyncThunk(
|
||||||
instance,
|
instance,
|
||||||
appData
|
appData
|
||||||
}: {
|
}: {
|
||||||
domain: Instance['url']
|
domain: InstanceLatest['url']
|
||||||
token: Instance['token']
|
token: InstanceLatest['token']
|
||||||
instance: Mastodon.Instance
|
instance: Mastodon.Instance
|
||||||
appData: Instance['appData']
|
appData: InstanceLatest['appData']
|
||||||
}): Promise<{ type: 'add' | 'overwrite'; data: Instance }> => {
|
}): Promise<{ type: 'add' | 'overwrite'; data: InstanceLatest }> => {
|
||||||
const { store } = require('@root/store')
|
const { store } = require('@root/store')
|
||||||
const instances = (store.getState() as RootState).instances.instances
|
const instances = (store.getState() as RootState).instances.instances
|
||||||
|
|
||||||
|
@ -81,11 +81,13 @@ const addInstance = createAsyncThunk(
|
||||||
filters,
|
filters,
|
||||||
notifications_filter: {
|
notifications_filter: {
|
||||||
follow: true,
|
follow: true,
|
||||||
|
follow_request: true,
|
||||||
favourite: true,
|
favourite: true,
|
||||||
reblog: true,
|
reblog: true,
|
||||||
mention: true,
|
mention: true,
|
||||||
poll: true,
|
poll: true,
|
||||||
follow_request: true
|
status: true,
|
||||||
|
update: true
|
||||||
},
|
},
|
||||||
push: {
|
push: {
|
||||||
global: { loading: false, value: false },
|
global: { loading: false, value: false },
|
||||||
|
|
|
@ -2,7 +2,8 @@ import apiInstance from '@api/instance'
|
||||||
import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot'
|
import apiTooot, { TOOOT_API_DOMAIN } from '@api/tooot'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { getInstance, Instance } from '@utils/slices/instancesSlice'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
|
import { getInstance } from '@utils/slices/instancesSlice'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import * as Random from 'expo-random'
|
import * as Random from 'expo-random'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
|
@ -34,7 +35,7 @@ const subscribe = async ({
|
||||||
const pushRegister = async (
|
const pushRegister = async (
|
||||||
state: RootState,
|
state: RootState,
|
||||||
expoToken: string
|
expoToken: string
|
||||||
): Promise<Instance['push']['keys']['auth']> => {
|
): Promise<InstanceLatest['push']['keys']['auth']> => {
|
||||||
const instance = getInstance(state)
|
const instance = getInstance(state)
|
||||||
const instanceUrl = instance?.url
|
const instanceUrl = instance?.url
|
||||||
const instanceUri = instance?.uri
|
const instanceUri = instance?.uri
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import { Instance } from '../instancesSlice'
|
|
||||||
import { updateInstancePush } from './updatePush'
|
import { updateInstancePush } from './updatePush'
|
||||||
|
|
||||||
const removeInstance = createAsyncThunk(
|
const removeInstance = createAsyncThunk(
|
||||||
'instances/remove',
|
'instances/remove',
|
||||||
async (instance: Instance, { dispatch }): Promise<Instance> => {
|
async (instance: InstanceLatest, { dispatch }): Promise<InstanceLatest> => {
|
||||||
if (instance.push.global.value) {
|
if (instance.push.global.value) {
|
||||||
dispatch(updateInstancePush(false))
|
dispatch(updateInstancePush(false))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { Instance } from '../instancesSlice'
|
|
||||||
import pushRegister from './push/register'
|
import pushRegister from './push/register'
|
||||||
import pushUnregister from './push/unregister'
|
import pushUnregister from './push/unregister'
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export const updateInstancePush = createAsyncThunk(
|
||||||
async (
|
async (
|
||||||
disable: boolean,
|
disable: boolean,
|
||||||
{ getState }
|
{ getState }
|
||||||
): Promise<Instance['push']['keys']['auth'] | undefined> => {
|
): Promise<InstanceLatest['push']['keys']['auth'] | undefined> => {
|
||||||
const state = getState() as RootState
|
const state = getState() as RootState
|
||||||
const expoToken = isDevelopment
|
const expoToken = isDevelopment
|
||||||
? 'DEVELOPMENT_TOKEN_1'
|
? 'DEVELOPMENT_TOKEN_1'
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import apiInstance from '@api/instance'
|
import apiInstance from '@api/instance'
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import { Instance } from '../instancesSlice'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
|
|
||||||
export const updateInstancePushAlert = createAsyncThunk(
|
export const updateInstancePushAlert = createAsyncThunk(
|
||||||
'instances/updatePushAlert',
|
'instances/updatePushAlert',
|
||||||
async ({
|
async ({
|
||||||
alerts
|
alerts
|
||||||
}: {
|
}: {
|
||||||
changed: keyof Instance['push']['alerts']
|
changed: keyof InstanceLatest['push']['alerts']
|
||||||
alerts: Instance['push']['alerts']
|
alerts: InstanceLatest['push']['alerts']
|
||||||
}): Promise<Instance['push']['alerts']> => {
|
}): Promise<InstanceLatest['push']['alerts']> => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
Object.keys(alerts).map(alert =>
|
Object.keys(alerts).map(alert =>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { getInstance, Instance } from '../instancesSlice'
|
import { getInstance } from '../instancesSlice'
|
||||||
import androidDefaults from './push/androidDefaults'
|
import androidDefaults from './push/androidDefaults'
|
||||||
|
|
||||||
export const updateInstancePushDecode = createAsyncThunk(
|
export const updateInstancePushDecode = createAsyncThunk(
|
||||||
|
@ -13,7 +14,7 @@ export const updateInstancePushDecode = createAsyncThunk(
|
||||||
async (
|
async (
|
||||||
disable: boolean,
|
disable: boolean,
|
||||||
{ getState }
|
{ getState }
|
||||||
): Promise<{ disable: Instance['push']['decode']['value'] }> => {
|
): Promise<{ disable: InstanceLatest['push']['decode']['value'] }> => {
|
||||||
const state = getState() as RootState
|
const state = getState() as RootState
|
||||||
const instance = getInstance(state)
|
const instance = getInstance(state)
|
||||||
if (!instance?.url || !instance.account.id || !instance.push.keys) {
|
if (!instance?.url || !instance.account.id || !instance.push.keys) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import features from '@helpers/features'
|
||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
import { InstanceV9 } from '@utils/migrations/instances/v9'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import addInstance from './instances/add'
|
import addInstance from './instances/add'
|
||||||
import { checkEmojis } from './instances/checkEmojis'
|
import { checkEmojis } from './instances/checkEmojis'
|
||||||
import removeInstance from './instances/remove'
|
import removeInstance from './instances/remove'
|
||||||
|
@ -14,24 +14,25 @@ import { updateInstancePush } from './instances/updatePush'
|
||||||
import { updateInstancePushAlert } from './instances/updatePushAlert'
|
import { updateInstancePushAlert } from './instances/updatePushAlert'
|
||||||
import { updateInstancePushDecode } from './instances/updatePushDecode'
|
import { updateInstancePushDecode } from './instances/updatePushDecode'
|
||||||
|
|
||||||
export type Instance = InstanceV9
|
|
||||||
|
|
||||||
export type InstancesState = {
|
export type InstancesState = {
|
||||||
instances: Instance[]
|
instances: InstanceLatest[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const instancesInitialState: InstancesState = {
|
export const instancesInitialState: InstancesState = {
|
||||||
instances: []
|
instances: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const findInstanceActive = (instances: Instance[]) =>
|
const findInstanceActive = (instances: InstanceLatest[]) =>
|
||||||
instances.findIndex(instance => instance.active)
|
instances.findIndex(instance => instance.active)
|
||||||
|
|
||||||
const instancesSlice = createSlice({
|
const instancesSlice = createSlice({
|
||||||
name: 'instances',
|
name: 'instances',
|
||||||
initialState: instancesInitialState,
|
initialState: instancesInitialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateInstanceActive: ({ instances }, action: PayloadAction<Instance>) => {
|
updateInstanceActive: (
|
||||||
|
{ instances },
|
||||||
|
action: PayloadAction<InstanceLatest>
|
||||||
|
) => {
|
||||||
instances = instances.map(instance => {
|
instances = instances.map(instance => {
|
||||||
instance.active =
|
instance.active =
|
||||||
instance.url === action.payload.url &&
|
instance.url === action.payload.url &&
|
||||||
|
@ -42,7 +43,9 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
updateInstanceAccount: (
|
updateInstanceAccount: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<Pick<Instance['account'], 'acct' & 'avatarStatic'>>
|
action: PayloadAction<
|
||||||
|
Pick<InstanceLatest['account'], 'acct' & 'avatarStatic'>
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].account = {
|
instances[activeIndex].account = {
|
||||||
|
@ -52,7 +55,7 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
updateInstanceNotificationsFilter: (
|
updateInstanceNotificationsFilter: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<Instance['notifications_filter']>
|
action: PayloadAction<InstanceLatest['notifications_filter']>
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].notifications_filter = action.payload
|
instances[activeIndex].notifications_filter = action.payload
|
||||||
|
@ -99,7 +102,7 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
updateInstanceTimelineLookback: (
|
updateInstanceTimelineLookback: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<Instance['timelinesLookback']>
|
action: PayloadAction<InstanceLatest['timelinesLookback']>
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex] &&
|
instances[activeIndex] &&
|
||||||
|
@ -110,7 +113,7 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
updateInstanceMePage: (
|
updateInstanceMePage: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<Partial<Instance['mePage']>>
|
action: PayloadAction<Partial<InstanceLatest['mePage']>>
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].mePage = {
|
instances[activeIndex].mePage = {
|
||||||
|
@ -120,10 +123,12 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
countInstanceEmoji: (
|
countInstanceEmoji: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<Instance['frequentEmojis'][0]['emoji']>
|
action: PayloadAction<InstanceLatest['frequentEmojis'][0]['emoji']>
|
||||||
) => {
|
) => {
|
||||||
const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week
|
const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week
|
||||||
const calculateScore = (emoji: Instance['frequentEmojis'][0]): number => {
|
const calculateScore = (
|
||||||
|
emoji: InstanceLatest['frequentEmojis'][0]
|
||||||
|
): number => {
|
||||||
var seconds = (new Date().getTime() - emoji.lastUsed) / 1000
|
var seconds = (new Date().getTime() - emoji.lastUsed) / 1000
|
||||||
var score = emoji.count + 1
|
var score = emoji.count + 1
|
||||||
var order = Math.log(Math.max(score, 1)) / Math.LN10
|
var order = Math.log(Math.max(score, 1)) / Math.LN10
|
||||||
|
@ -136,7 +141,7 @@ const instancesSlice = createSlice({
|
||||||
e.emoji.shortcode === action.payload.shortcode &&
|
e.emoji.shortcode === action.payload.shortcode &&
|
||||||
e.emoji.url === action.payload.url
|
e.emoji.url === action.payload.url
|
||||||
)
|
)
|
||||||
let newEmojisSort: Instance['frequentEmojis']
|
let newEmojisSort: InstanceLatest['frequentEmojis']
|
||||||
if (foundEmojiIndex > -1) {
|
if (foundEmojiIndex > -1) {
|
||||||
newEmojisSort = instances[activeIndex].frequentEmojis
|
newEmojisSort = instances[activeIndex].frequentEmojis
|
||||||
.map((e, i) =>
|
.map((e, i) =>
|
||||||
|
|
Loading…
Reference in New Issue