From aad42020bb1d142b279ba2204ab37f76ebb6e092 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 5 Jun 2026 17:57:54 +0300 Subject: [PATCH] extract resolveAllowGroupInvites helper, fix optimistic cache Address review feedback: dedupe the allowGroupInvites fallback chain into a single helper next to canBeAddedToGroup, used by the settings display, the save path, and onMutate. The optimistic cache now stores the same resolved value the server receives, so cache and persisted record stay aligned before the next profile refetch. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/dms/util.ts | 16 ++++++++++ src/screens/Messages/Settings.tsx | 11 ++----- .../queries/messages/actor-declaration.ts | 29 ++++++++++++++----- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/components/dms/util.ts b/src/components/dms/util.ts index 89b9c5b01f..7fe5f91dc3 100644 --- a/src/components/dms/util.ts +++ b/src/components/dms/util.ts @@ -47,6 +47,22 @@ export function canBeAddedToGroup(profile: bsky.profile.AnyProfileView) { } } +/** + * Resolves the effective `allowGroupInvites` value for a chat declaration. + * When unset, group invites follow the general DM preference + * (`allowIncoming`), which itself defaults to `following`. This mirrors the + * `undefined` fallthrough in canBeAddedToGroup, and is the single source of + * truth for both displaying and persisting the setting. + */ +export function resolveAllowGroupInvites( + chat: {allowIncoming?: string; allowGroupInvites?: string} | undefined, +): 'all' | 'none' | 'following' { + return (chat?.allowGroupInvites ?? chat?.allowIncoming ?? 'following') as + | 'all' + | 'none' + | 'following' +} + export function localDateString(date: Date) { // can't use toISOString because it should be in local time const mm = date.getMonth() diff --git a/src/screens/Messages/Settings.tsx b/src/screens/Messages/Settings.tsx index f392d1c65c..91d9f879d5 100644 --- a/src/screens/Messages/Settings.tsx +++ b/src/screens/Messages/Settings.tsx @@ -13,6 +13,7 @@ import {AgeRestrictedScreen} from '#/components/ageAssurance/AgeRestrictedScreen import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' import * as Dialog from '#/components/Dialog' import {Divider} from '#/components/Divider' +import {resolveAllowGroupInvites} from '#/components/dms/util' import * as Toggle from '#/components/forms/Toggle' import {Bell_Stroke2_Corner0_Rounded as BellIcon} from '#/components/icons/Bell' import {Car_Stroke2_Corner2_Rounded as CarIcon} from '#/components/icons/Car' @@ -199,15 +200,7 @@ export function MessagesSettingsScreenInner({}: Props) { {allowGroupInvitesFromOptions.map(option => ( diff --git a/src/state/queries/messages/actor-declaration.ts b/src/state/queries/messages/actor-declaration.ts index 7261f96eb7..f6cd2d51d5 100644 --- a/src/state/queries/messages/actor-declaration.ts +++ b/src/state/queries/messages/actor-declaration.ts @@ -7,6 +7,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query' import {logger} from '#/logger' import {useAgent, useSession} from '#/state/session' +import {resolveAllowGroupInvites} from '#/components/dms/util' import {RQKEY as PROFILE_RKEY} from '../profile' export function useUpdateActorDeclaration({ @@ -34,12 +35,12 @@ export function useUpdateActorDeclaration({ update.allowIncoming ?? current?.associated?.chat?.allowIncoming ?? 'following' - const allowGroupInvites = - update.allowGroupInvites ?? - current?.associated?.chat?.allowGroupInvites ?? - // when unset, group invites follow the general DM preference - // (see canBeAddedToGroup), so mirror that when persisting - allowIncoming + const allowGroupInvites = resolveAllowGroupInvites({ + allowIncoming, + allowGroupInvites: + update.allowGroupInvites ?? + current?.associated?.chat?.allowGroupInvites, + }) const result = await agent.com.atproto.repo.putRecord({ repo: currentAccount.did, collection: 'chat.bsky.actor.declaration', @@ -58,14 +59,26 @@ export function useUpdateActorDeclaration({ PROFILE_RKEY(currentAccount?.did), (old?: AppBskyActorDefs.ProfileViewDetailed) => { if (!old) return old + const allowIncoming = + update.allowIncoming ?? + old.associated?.chat?.allowIncoming ?? + 'following' + // resolve the same concrete value the server will receive, so + // optimistic cache and persisted record stay aligned + const allowGroupInvites = resolveAllowGroupInvites({ + allowIncoming, + allowGroupInvites: + update.allowGroupInvites ?? + old.associated?.chat?.allowGroupInvites, + }) return { ...old, associated: { ...old.associated, chat: { - allowIncoming: 'following', ...old.associated?.chat, - ...update, + allowIncoming, + allowGroupInvites, }, }, } satisfies AppBskyActorDefs.ProfileViewDetailed