From 9eaee783e97dab5750c96ebc61555bdd942ce35a Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 2 Sep 2026 19:47:24 -0400 Subject: [PATCH] Address starter pack opt-out review feedback --- src/analytics/metrics/types.ts | 1 + src/components/StarterPack/Main/ProfilesList.tsx | 15 ++++++++++++--- .../StarterPack/Wizard/WizardListCard.tsx | 14 +++++++++++++- src/screens/StarterPack/StarterPackScreen.tsx | 5 +++++ src/screens/StarterPack/Wizard/State.tsx | 3 ++- src/screens/StarterPack/Wizard/StepProfiles.tsx | 3 +++ src/screens/StarterPack/Wizard/index.tsx | 11 ++++++++++- 7 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts index 1639c2a43d..a20350338d 100644 --- a/src/analytics/metrics/types.ts +++ b/src/analytics/metrics/types.ts @@ -703,6 +703,7 @@ export type Events = { } 'starterPack:removeUser': { starterPack?: string + context?: 'opt-out' } 'starterPack:share': { starterPack: string diff --git a/src/components/StarterPack/Main/ProfilesList.tsx b/src/components/StarterPack/Main/ProfilesList.tsx index 05c0227ae5..3b9cfbb89a 100644 --- a/src/components/StarterPack/Main/ProfilesList.tsx +++ b/src/components/StarterPack/Main/ProfilesList.tsx @@ -22,6 +22,7 @@ import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {Loader} from '#/components/Loader' import {Default as ProfileCard} from '#/components/ProfileCard' import * as Toast from '#/components/Toast' +import {useAnalytics} from '#/analytics' import {IS_NATIVE, IS_WEB} from '#/env' import {type app} from '#/lexicons' @@ -159,9 +160,14 @@ function OptedOutControls({ canRemove: boolean }) { const {_} = useLingui() + const ax = useAnalytics() + const [isRemoved, setIsRemoved] = useState(false) const {mutate: removeMembership, isPending} = useListMembershipRemoveMutation( { - onSuccess: () => Toast.show(_(msg`Removed from starter pack`)), + onSuccess: () => { + setIsRemoved(true) + Toast.show(_(msg`Removed from starter pack`)) + }, onError: error => Toast.show(cleanError(error), { type: 'error', @@ -169,6 +175,8 @@ function OptedOutControls({ }, ) + if (isRemoved) return null + return ( @@ -183,13 +191,14 @@ function OptedOutControls({ label={_(msg`Remove user from starter pack`)} color="secondary" disabled={isPending} - onPress={() => + onPress={() => { + ax.metric('starterPack:removeUser', {context: 'opt-out'}) removeMembership({ listUri, actorDid: item.subject.did, membershipUri: item.uri, }) - }> + }}> {isPending ? ( ) : ( diff --git a/src/components/StarterPack/Wizard/WizardListCard.tsx b/src/components/StarterPack/Wizard/WizardListCard.tsx index 39397f38e3..eba7398754 100644 --- a/src/components/StarterPack/Wizard/WizardListCard.tsx +++ b/src/components/StarterPack/Wizard/WizardListCard.tsx @@ -36,6 +36,7 @@ function WizardListCard({ avatar, included, disabled, + subjectOptedOut, moderationUi, }: { type: 'user' | 'algo' @@ -48,6 +49,7 @@ function WizardListCard({ avatar?: string included?: boolean disabled?: boolean + subjectOptedOut?: boolean moderationUi: ModerationUI }) { const t = useTheme() @@ -97,6 +99,11 @@ function WizardListCard({ numberOfLines={1}> {subtitle} + {subjectOptedOut ? ( + + Opted out + + ) : null} {btnType === 'checkbox' ? ( @@ -123,12 +130,14 @@ export function WizardProfileCard({ dispatch, profile, moderationOpts, + subjectOptedOut = false, }: { btnType: 'checkbox' | 'remove' state: WizardState dispatch: (action: WizardAction) => void profile: bsky.profile.AnyProfileView moderationOpts: ModerationOpts + subjectOptedOut?: boolean }) { const ax = useAnalytics() const {currentAccount} = useSession() @@ -138,7 +147,9 @@ export function WizardProfileCard({ const isTarget = profile.did === targetProfileDid const included = isTarget || state.profiles.some(p => p.did === profile.did) const disabled = - isTarget || (!included && state.profiles.length >= STARTER_PACK_MAX_SIZE) + subjectOptedOut || + isTarget || + (!included && state.profiles.length >= STARTER_PACK_MAX_SIZE) const moderationUi = moderateProfile(profile, moderationOpts).ui('avatar') const displayName = profile.displayName ? sanitizeDisplayName(profile.displayName) @@ -169,6 +180,7 @@ export function WizardProfileCard({ avatar={profile.avatar} included={included} disabled={disabled} + subjectOptedOut={subjectOptedOut} moderationUi={moderationUi} /> ) diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index 1a6271c63a..c30bd3bb58 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -557,6 +557,11 @@ function OverflowMenu({ starterPack: starterPack.uri, action, }) + Toast.show( + action === 'optOut' + ? _(msg`Opted out of starter pack`) + : _(msg`Opt-out undone`), + ) }, onError: error => { logger.error('Failed to update starter pack opt-out', { diff --git a/src/screens/StarterPack/Wizard/State.tsx b/src/screens/StarterPack/Wizard/State.tsx index b28a0bd728..b12f069aca 100644 --- a/src/screens/StarterPack/Wizard/State.tsx +++ b/src/screens/StarterPack/Wizard/State.tsx @@ -138,7 +138,8 @@ export function Provider({ currentStep: 'Details', name: starterPack.record.name, description: starterPack.record.description, - profiles: listItems?.map(i => i.subject) ?? [], + profiles: + listItems?.filter(i => !i.subjectOptedOut).map(i => i.subject) ?? [], feeds: starterPack.feeds ?? [], processing: false, transitionDirection: 'Forward', diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx index 3ba167e856..2761fc1f0d 100644 --- a/src/screens/StarterPack/Wizard/StepProfiles.tsx +++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx @@ -24,8 +24,10 @@ function keyExtractor(item: bsky.profile.AnyProfileView) { export function StepProfiles({ moderationOpts, + optedOutDids, }: { moderationOpts: ModerationOpts + optedOutDids: Set }) { const t = useTheme() const [state, dispatch] = useWizardState() @@ -59,6 +61,7 @@ export function StepProfiles({ state={state} dispatch={dispatch} moderationOpts={moderationOpts} + subjectOptedOut={optedOutDids.has(item.did)} /> ) } diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index d1abb10900..96a6b3ce28 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -341,7 +341,16 @@ function WizardInner({ {state.currentStep === 'Details' ? ( ) : state.currentStep === 'Profiles' ? ( - + item.subjectOptedOut) + .map(item => item.subject.did), + ) + } + /> ) : state.currentStep === 'Feeds' ? ( ) : null}