Address starter pack opt-out review feedback

This commit is contained in:
vineyardbovines
2026-09-02 19:47:24 -04:00
parent d993cc4163
commit 9eaee783e9
7 changed files with 46 additions and 6 deletions
+1
View File
@@ -703,6 +703,7 @@ export type Events = {
}
'starterPack:removeUser': {
starterPack?: string
context?: 'opt-out'
}
'starterPack:share': {
starterPack: string
@@ -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 (
<Admonition.Outer type="info" style={[a.mt_sm]}>
<Admonition.Row style={[a.align_center]}>
@@ -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 ? (
<ButtonIcon icon={Loader} />
) : (
@@ -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}
</Text>
{subjectOptedOut ? (
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
<Trans>Opted out</Trans>
</Text>
) : null}
</View>
{btnType === 'checkbox' ? (
<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}
/>
)
@@ -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', {
+2 -1
View File
@@ -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',
@@ -24,8 +24,10 @@ function keyExtractor(item: bsky.profile.AnyProfileView) {
export function StepProfiles({
moderationOpts,
optedOutDids,
}: {
moderationOpts: ModerationOpts
optedOutDids: Set<string>
}) {
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)}
/>
)
}
+10 -1
View File
@@ -341,7 +341,16 @@ function WizardInner({
{state.currentStep === 'Details' ? (
<StepDetails />
) : state.currentStep === 'Profiles' ? (
<StepProfiles moderationOpts={moderationOpts} />
<StepProfiles
moderationOpts={moderationOpts}
optedOutDids={
new Set(
currentListItems
?.filter(item => item.subjectOptedOut)
.map(item => item.subject.did),
)
}
/>
) : state.currentStep === 'Feeds' ? (
<StepFeeds moderationOpts={moderationOpts} />
) : null}