From fbed7d4bb8f670c895aeeb685a96f06d2f55338f Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 2 Sep 2026 14:57:15 -0400 Subject: [PATCH] Let starter pack owners remove opted-out users --- .../StarterPack/Main/ProfilesList.tsx | 85 ++++++++++++++++--- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/src/components/StarterPack/Main/ProfilesList.tsx b/src/components/StarterPack/Main/ProfilesList.tsx index 0bbe2ea080..475ef3d31a 100644 --- a/src/components/StarterPack/Main/ProfilesList.tsx +++ b/src/components/StarterPack/Main/ProfilesList.tsx @@ -2,18 +2,25 @@ import {forwardRef, useCallback, useImperativeHandle, useState} from 'react' import {type ListRenderItemInfo, View} from 'react-native' import {AtUri} from '@atproto/syntax' import {type ModerationOpts} from '@bsky/sdk/moderation' +import {msg} from '@lingui/core/macro' +import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' +import {cleanError} from '#/lib/strings/errors' import {useAllListMembersQuery} from '#/state/queries/list-members' +import {useListMembershipRemoveMutation} from '#/state/queries/list-memberships' import {useSession} from '#/state/session' import {List, type ListRef} from '#/view/com/util/List' import {type SectionRef} from '#/screens/Profile/Sections/types' import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' 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 {Text} from '#/components/Typography' import {IS_NATIVE, IS_WEB} from '#/env' import {type app} from '#/lexicons' @@ -53,18 +60,17 @@ export const ProfilesList = forwardRef( const getSortedProfiles = () => { if (!listItems) return - if (!isOwn) return listItems - const myIndex = listItems.findIndex( - item => item.subject.did === currentAccount?.did, - ) - return myIndex !== -1 - ? [ - listItems[myIndex], - ...listItems.slice(0, myIndex), - ...listItems.slice(myIndex + 1), - ] - : listItems + return [...listItems].sort((a, b) => { + if (a.subjectOptedOut !== b.subjectOptedOut) { + return a.subjectOptedOut ? 1 : -1 + } + if (isOwn) { + if (a.subject.did === currentAccount?.did) return -1 + if (b.subject.did === currentAccount?.did) return 1 + } + return 0 + }) } const onScrollToTop = useCallback(() => { scrollElRef.current?.scrollToOffset({ @@ -94,9 +100,7 @@ export const ProfilesList = forwardRef( logContext="StarterPackProfilesList" /> {item.subjectOptedOut ? ( - - Opted out of this starter pack - + ) : null} ) @@ -144,3 +148,56 @@ export const ProfilesList = forwardRef( ) }, ) + +function OptedOutControls({ + item, + listUri, + canRemove, +}: { + item: app.bsky.graph.defs.ListItemView + listUri: string + canRemove: boolean +}) { + const {_} = useLingui() + const t = useTheme() + const {mutate: removeMembership, isPending} = useListMembershipRemoveMutation( + { + onSuccess: () => Toast.show(_(msg`Removed from starter pack`)), + onError: error => + Toast.show(cleanError(error), { + type: 'error', + }), + }, + ) + + return ( + + + Opted out of this starter pack + + {canRemove ? ( + + ) : null} + + ) +}