Granular notification settings (#8484)

* add mockup screen

* add notification index screen

* add redirect screen

* upgrade sdk

* new icons

* add new screens

* make router typesafe, finish adding screens

* add routes to go server

* load settings

* push notif settings

* improve web

* fix lockfile lint

* no $type on preferences

* prompt to enable push notifications

* fix reply prefs

* space out options

* fix copy error

* Update RepostsOnRepostsNotificationSettings.tsx

* only send minimal diff to putPrefs

* fix yarn.lock

* Update Navigation.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* Update src/screens/Settings/NotificationSettings/index.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* add description to `syncOthers`

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
This commit is contained in:
Samuel Newman
2025-06-17 12:37:14 +03:00
committed by GitHub
parent 7dc6bb57a6
commit 21989b558b
34 changed files with 1434 additions and 287 deletions
+45 -54
View File
@@ -1,72 +1,63 @@
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {type AppBskyNotificationDefs} from '@atproto/api'
import {t} from '@lingui/macro'
import {
type QueryClient,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import {until} from '#/lib/async/until'
import {logger} from '#/logger'
import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed'
import {invalidateCachedUnreadPage} from '#/state/queries/notifications/unread'
import {useAgent} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
export function useNotificationSettingsMutation() {
const {_} = useLingui()
const RQKEY_ROOT = 'notification-settings'
const RQKEY = [RQKEY_ROOT]
export function useNotificationSettingsQuery() {
const agent = useAgent()
return useQuery({
queryKey: RQKEY,
queryFn: async () => {
const response = await agent.app.bsky.notification.getPreferences()
return response.data.preferences
},
})
}
export function useNotificationSettingsUpdateMutation() {
const agent = useAgent()
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (keys: string[]) => {
const enabled = keys[0] === 'enabled'
await agent.api.app.bsky.notification.putPreferences({
priority: enabled,
})
await until(
5, // 5 tries
1e3, // 1s delay between tries
res => res.data.priority === enabled,
() => agent.api.app.bsky.notification.listNotifications({limit: 1}),
mutationFn: async (
update: Partial<AppBskyNotificationDefs.Preferences>,
) => {
const response = await agent.app.bsky.notification.putPreferencesV2(
update,
)
eagerlySetCachedPriority(queryClient, enabled)
return response.data.preferences
},
onError: err => {
logger.error('Failed to save notification preferences', {
safeMessage: err,
})
Toast.show(
_(msg`Failed to save notification preferences, please try again`),
'xmark',
)
onMutate: update => {
optimisticUpdateNotificationSettings(queryClient, update)
},
onSuccess: () => {
Toast.show(_(msg({message: 'Preference saved', context: 'toast'})))
},
onSettled: () => {
invalidateCachedUnreadPage()
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS('all')})
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS('mentions')})
onError: e => {
logger.error('Could not update notification settings', {message: e})
queryClient.invalidateQueries({queryKey: RQKEY})
Toast.show(t`Could not update notification settings`, 'xmark')
},
})
}
function eagerlySetCachedPriority(
queryClient: ReturnType<typeof useQueryClient>,
enabled: boolean,
function optimisticUpdateNotificationSettings(
queryClient: QueryClient,
update: Partial<AppBskyNotificationDefs.Preferences>,
) {
function updateData(old: any) {
if (!old) return old
return {
...old,
pages: old.pages.map((page: any) => {
return {
...page,
priority: enabled,
}
}),
}
}
queryClient.setQueryData(RQKEY_NOTIFS('all'), updateData)
queryClient.setQueryData(RQKEY_NOTIFS('mentions'), updateData)
queryClient.setQueryData(
RQKEY,
(old?: AppBskyNotificationDefs.Preferences) => {
if (!old) return old
return {...old, ...update}
},
)
}