add activity notifs to shadow

This commit is contained in:
Samuel Newman
2025-06-27 17:42:38 +03:00
parent 040ab90b45
commit 83e423f78a
3 changed files with 37 additions and 18 deletions
@@ -1,15 +1,20 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyNotificationDefs, type ModerationOpts} from '@atproto/api' import {
type AppBskyNotificationDefs,
type ModerationOpts,
type Un$Typed,
} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useMutation} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {updateProfileShadow} from '#/state/cache/profile-shadow'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {platform, useTheme, web} from '#/alf' import {platform, useTheme, web} from '#/alf'
@@ -51,11 +56,6 @@ export function SubscribeProfileDialog({
) )
} }
const defaultState = {
post: false,
reply: false,
} satisfies AppBskyNotificationDefs.ActivitySubscription
function DialogInner({ function DialogInner({
profile, profile,
moderationOpts, moderationOpts,
@@ -69,7 +69,10 @@ function DialogInner({
const t = useTheme() const t = useTheme()
const agent = useAgent() const agent = useAgent()
const control = Dialog.useDialogContext() const control = Dialog.useDialogContext()
const initialState = profile.viewer?.activitySubscription || defaultState const queryClient = useQueryClient()
const initialState = parseActivitySubscription(
profile.viewer?.activitySubscription,
)
const [state, setState] = useState(initialState) const [state, setState] = useState(initialState)
const values = useMemo(() => { const values = useMemo(() => {
@@ -110,7 +113,7 @@ function DialogInner({
error, error,
} = useMutation({ } = useMutation({
mutationFn: async ( mutationFn: async (
activitySubscription: AppBskyNotificationDefs.ActivitySubscription, activitySubscription: Un$Typed<AppBskyNotificationDefs.ActivitySubscription>,
) => { ) => {
await new Promise(resolve => setTimeout(resolve, 1000)) await new Promise(resolve => setTimeout(resolve, 1000))
await agent.app.bsky.notification.putActivitySubscription({ await agent.app.bsky.notification.putActivitySubscription({
@@ -118,9 +121,13 @@ function DialogInner({
activitySubscription, activitySubscription,
}) })
}, },
onSuccess: (_data, variables) => { onSuccess: (_data, activitySubscription) => {
control.close() control.close(() =>
if (!variables.post && !variables.reply) { updateProfileShadow(queryClient, profile.did, {
activitySubscription,
}),
)
if (!activitySubscription.post && !activitySubscription.reply) {
Toast.show( Toast.show(
_( _(
msg`You will no longer receive notifications for ${sanitizeHandle(profile.handle, '@')}`, msg`You will no longer receive notifications for ${sanitizeHandle(profile.handle, '@')}`,
@@ -138,8 +145,8 @@ function DialogInner({
Toast.show(_(msg`Changes saved`), 'check') Toast.show(_(msg`Changes saved`), 'check')
} }
}, },
onError: error => { onError: err => {
logger.error('Could not save activity subscription', {message: error}) logger.error('Could not save activity subscription', {message: err})
}, },
}) })
@@ -261,3 +268,11 @@ function DialogInner({
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
) )
} }
function parseActivitySubscription(
sub?: AppBskyNotificationDefs.ActivitySubscription,
): Un$Typed<AppBskyNotificationDefs.ActivitySubscription> {
if (!sub) return {post: false, reply: false}
const {post, reply} = sub
return {post, reply}
}
@@ -15,7 +15,6 @@ import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection' import {isIOS} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {type Shadow} from '#/state/cache/types'
import { import {
useProfileBlockMutationQueue, useProfileBlockMutationQueue,
useProfileFollowMutationQueue, useProfileFollowMutationQueue,
@@ -59,8 +58,8 @@ let ProfileHeaderStandard = ({
}: Props): React.ReactNode => { }: Props): React.ReactNode => {
const t = useTheme() const t = useTheme()
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> = const profile =
useProfileShadow(profileUnshadowed) useProfileShadow<AppBskyActorDefs.ProfileViewDetailed>(profileUnshadowed)
const {currentAccount, hasSession} = useSession() const {currentAccount, hasSession} = useSession()
const {_} = useLingui() const {_} = useLingui()
const moderation = useMemo( const moderation = useMemo(
+6 -1
View File
@@ -1,5 +1,5 @@
import {useEffect, useMemo, useState} from 'react' import {useEffect, useMemo, useState} from 'react'
import {type AppBskyActorDefs} from '@atproto/api' import {type AppBskyActorDefs, type AppBskyNotificationDefs} from '@atproto/api'
import {type QueryClient} from '@tanstack/react-query' import {type QueryClient} from '@tanstack/react-query'
import EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
@@ -33,6 +33,7 @@ export interface ProfileShadow {
blockingUri: string | undefined blockingUri: string | undefined
verification: AppBskyActorDefs.VerificationState verification: AppBskyActorDefs.VerificationState
status: AppBskyActorDefs.StatusView | undefined status: AppBskyActorDefs.StatusView | undefined
activitySubscription: AppBskyNotificationDefs.ActivitySubscription | undefined
} }
const shadows: WeakMap< const shadows: WeakMap<
@@ -137,6 +138,10 @@ function mergeShadow<TProfileView extends bsky.profile.AnyProfileView>(
muted: 'muted' in shadow ? shadow.muted : profile.viewer?.muted, muted: 'muted' in shadow ? shadow.muted : profile.viewer?.muted,
blocking: blocking:
'blockingUri' in shadow ? shadow.blockingUri : profile.viewer?.blocking, 'blockingUri' in shadow ? shadow.blockingUri : profile.viewer?.blocking,
activitySubscription:
'activitySubscription' in shadow
? shadow.activitySubscription
: profile.viewer?.activitySubscription,
}, },
verification: verification:
'verification' in shadow ? shadow.verification : profile.verification, 'verification' in shadow ? shadow.verification : profile.verification,