From 83e423f78afb677aac7322323923f4530b197bc0 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 27 Jun 2025 17:42:38 +0300 Subject: [PATCH] add activity notifs to shadow --- .../SubscribeProfileDialog.tsx | 43 +++++++++++++------ .../Profile/Header/ProfileHeaderStandard.tsx | 5 +-- src/state/cache/profile-shadow.ts | 7 ++- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/components/activity-notifications/SubscribeProfileDialog.tsx b/src/components/activity-notifications/SubscribeProfileDialog.tsx index 21bc05ce77..7d87ea42ab 100644 --- a/src/components/activity-notifications/SubscribeProfileDialog.tsx +++ b/src/components/activity-notifications/SubscribeProfileDialog.tsx @@ -1,15 +1,20 @@ import {useMemo, useState} from 'react' 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 {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 {cleanError} from '#/lib/strings/errors' import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' +import {updateProfileShadow} from '#/state/cache/profile-shadow' import {useAgent} from '#/state/session' import * as Toast from '#/view/com/util/Toast' import {platform, useTheme, web} from '#/alf' @@ -51,11 +56,6 @@ export function SubscribeProfileDialog({ ) } -const defaultState = { - post: false, - reply: false, -} satisfies AppBskyNotificationDefs.ActivitySubscription - function DialogInner({ profile, moderationOpts, @@ -69,7 +69,10 @@ function DialogInner({ const t = useTheme() const agent = useAgent() 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 values = useMemo(() => { @@ -110,7 +113,7 @@ function DialogInner({ error, } = useMutation({ mutationFn: async ( - activitySubscription: AppBskyNotificationDefs.ActivitySubscription, + activitySubscription: Un$Typed, ) => { await new Promise(resolve => setTimeout(resolve, 1000)) await agent.app.bsky.notification.putActivitySubscription({ @@ -118,9 +121,13 @@ function DialogInner({ activitySubscription, }) }, - onSuccess: (_data, variables) => { - control.close() - if (!variables.post && !variables.reply) { + onSuccess: (_data, activitySubscription) => { + control.close(() => + updateProfileShadow(queryClient, profile.did, { + activitySubscription, + }), + ) + if (!activitySubscription.post && !activitySubscription.reply) { Toast.show( _( msg`You will no longer receive notifications for ${sanitizeHandle(profile.handle, '@')}`, @@ -138,8 +145,8 @@ function DialogInner({ Toast.show(_(msg`Changes saved`), 'check') } }, - onError: error => { - logger.error('Could not save activity subscription', {message: error}) + onError: err => { + logger.error('Could not save activity subscription', {message: err}) }, }) @@ -261,3 +268,11 @@ function DialogInner({ ) } + +function parseActivitySubscription( + sub?: AppBskyNotificationDefs.ActivitySubscription, +): Un$Typed { + if (!sub) return {post: false, reply: false} + const {post, reply} = sub + return {post, reply} +} diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index 90dcf2306b..5dbf32c57f 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -15,7 +15,6 @@ import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' -import {type Shadow} from '#/state/cache/types' import { useProfileBlockMutationQueue, useProfileFollowMutationQueue, @@ -59,8 +58,8 @@ let ProfileHeaderStandard = ({ }: Props): React.ReactNode => { const t = useTheme() const {gtMobile} = useBreakpoints() - const profile: Shadow = - useProfileShadow(profileUnshadowed) + const profile = + useProfileShadow(profileUnshadowed) const {currentAccount, hasSession} = useSession() const {_} = useLingui() const moderation = useMemo( diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 0d3bb1b17d..77c7aea62c 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -1,5 +1,5 @@ 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 EventEmitter from 'eventemitter3' @@ -33,6 +33,7 @@ export interface ProfileShadow { blockingUri: string | undefined verification: AppBskyActorDefs.VerificationState status: AppBskyActorDefs.StatusView | undefined + activitySubscription: AppBskyNotificationDefs.ActivitySubscription | undefined } const shadows: WeakMap< @@ -137,6 +138,10 @@ function mergeShadow( muted: 'muted' in shadow ? shadow.muted : profile.viewer?.muted, blocking: 'blockingUri' in shadow ? shadow.blockingUri : profile.viewer?.blocking, + activitySubscription: + 'activitySubscription' in shadow + ? shadow.activitySubscription + : profile.viewer?.activitySubscription, }, verification: 'verification' in shadow ? shadow.verification : profile.verification,