Replace Add User to Lists modal with modern Dialog component

Migrate from deprecated modal system to modern Dialog system using the efficient getListsWithMembership() API instead of the inefficient useDangerousListMembershipsQuery(). Adds optimistic updates for faster UI feedback on add/remove operations.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-01-10 13:39:13 +02:00
parent a19d652a39
commit ff23ebb58e
9 changed files with 514 additions and 183 deletions
+18 -9
View File
@@ -13,7 +13,6 @@ import {Trans} from '@lingui/react/macro'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {useModalControls} from '#/state/modals'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useListMembersQuery} from '#/state/queries/list-members'
import {useSession} from '#/state/session'
@@ -23,6 +22,8 @@ import {ProfileCardFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlacehol
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {UserAddRemoveListsDialog} from '#/components/dialogs/lists/UserAddRemoveListsDialog'
import {ListFooter} from '#/components/Lists'
import * as ProfileCard from '#/components/ProfileCard'
import type * as bsky from '#/types/bsky'
@@ -58,9 +59,12 @@ export function ListMembers({
const t = useTheme()
const {_} = useLingui()
const [isRefreshing, setIsRefreshing] = useState(false)
const {openModal} = useModalControls()
const {currentAccount} = useSession()
const moderationOpts = useModerationOpts()
const editListsDialogControl = useDialogControl()
const [selectedProfile, setSelectedProfile] = React.useState<
bsky.profile.AnyProfileView | undefined
>()
const {
data,
@@ -128,14 +132,10 @@ export function ListMembers({
const onPressEditMembership = useCallback(
(e: GestureResponderEvent, profile: bsky.profile.AnyProfileView) => {
e.preventDefault()
openModal({
name: 'user-add-remove-lists',
subject: profile.did,
displayName: profile.displayName || profile.handle,
handle: profile.handle,
})
setSelectedProfile(profile)
editListsDialogControl.open()
},
[openModal],
[editListsDialogControl],
)
// rendering
@@ -263,6 +263,15 @@ export function ListMembers({
removeClippedSubviews={true}
desktopFixedHeight={desktopFixedHeightOffset || true}
/>
{selectedProfile && (
<UserAddRemoveListsDialog
control={editListsDialogControl}
subjectDid={selectedProfile.did}
displayName={selectedProfile.displayName || selectedProfile.handle}
handle={selectedProfile.handle}
/>
)}
</View>
)
}
+1 -5
View File
@@ -7,7 +7,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
import {useModalControls, useModals} from '#/state/modals'
import {FullWindowOverlay} from '#/components/FullWindowOverlay'
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
import * as UserAddRemoveListsModal from './UserAddRemoveLists'
const DEFAULT_SNAPPOINTS = ['90%']
const HANDLE_HEIGHT = 24
@@ -40,10 +39,7 @@ export function ModalsContainer() {
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
let element
if (activeModal?.name === 'user-add-remove-lists') {
snapPoints = UserAddRemoveListsModal.snapPoints
element = <UserAddRemoveListsModal.Component {...activeModal} />
} else {
{
return null
}
+1 -4
View File
@@ -6,7 +6,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {type Modal as ModalIface} from '#/state/modals'
import {useModalControls, useModals} from '#/state/modals'
import * as UserAddRemoveLists from './UserAddRemoveLists'
export function ModalsContainer() {
const {isModalActive, activeModals} = useModals()
@@ -44,9 +43,7 @@ function Modal({modal}: {modal: ModalIface}) {
}
let element
if (modal.name === 'user-add-remove-lists') {
element = <UserAddRemoveLists.Component {...modal} />
} else {
{
return null
}
+13 -11
View File
@@ -12,7 +12,6 @@ import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers'
import {type Shadow} from '#/state/cache/types'
import {useModalControls} from '#/state/modals'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
import {
RQKEY as profileQueryKey,
@@ -25,6 +24,7 @@ import {EventStopper} from '#/view/com/util/EventStopper'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {UserAddRemoveListsDialog} from '#/components/dialogs/lists/UserAddRemoveListsDialog'
import {StarterPackDialog} from '#/components/dialogs/StarterPackDialog'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox'
import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
@@ -74,7 +74,6 @@ let ProfileMenu = ({
const ax = useAnalytics()
const {_} = useLingui()
const {currentAccount, hasSession} = useSession()
const {openModal} = useModalControls()
const reportDialogControl = useReportDialogControl()
const queryClient = useQueryClient()
const navigation = useNavigation<NavigationProp>()
@@ -107,6 +106,7 @@ let ProfileMenu = ({
const goLiveDialogControl = useDialogControl()
const goLiveDisabledDialogControl = useDialogControl()
const addToStarterPacksDialogControl = useDialogControl()
const addToListsDialogControl = useDialogControl()
const showLoggedOutWarning = useMemo(() => {
return (
@@ -131,15 +131,8 @@ let ProfileMenu = ({
}, [profile])
const onPressAddRemoveLists = useCallback(() => {
openModal({
name: 'user-add-remove-lists',
subject: profile.did,
handle: profile.handle,
displayName: profile.displayName || profile.handle,
onAdd: invalidateProfileQuery,
onRemove: invalidateProfileQuery,
})
}, [profile, openModal, invalidateProfileQuery])
addToListsDialogControl.open()
}, [addToListsDialogControl])
const onPressMuteAccount = useCallback(async () => {
if (profile.viewer?.muted) {
@@ -530,6 +523,15 @@ let ProfileMenu = ({
targetDid={profile.did}
/>
<UserAddRemoveListsDialog
control={addToListsDialogControl}
subjectDid={profile.did}
displayName={profile.displayName || profile.handle}
handle={profile.handle}
onAdd={invalidateProfileQuery}
onRemove={invalidateProfileQuery}
/>
<ReportDialog
control={reportDialogControl}
subject={{