diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx
index a2cc637450..a15e3d7c1e 100644
--- a/src/state/modals/index.tsx
+++ b/src/state/modals/index.tsx
@@ -10,15 +10,6 @@ export interface CreateOrEditListModal {
onSave?: (uri: string) => void
}
-export interface UserAddRemoveListsModal {
- name: 'user-add-remove-lists'
- subject: string
- handle: string
- displayName: string
- onAdd?: (listUri: string) => void
- onRemove?: (listUri: string) => void
-}
-
export interface DeleteAccountModal {
name: 'delete-account'
}
@@ -54,7 +45,6 @@ export type Modal =
// Lists
| CreateOrEditListModal
- | UserAddRemoveListsModal
// Bluesky access
| WaitlistModal
diff --git a/src/view/com/lists/ListMembers.tsx b/src/view/com/lists/ListMembers.tsx
index dc18cbe5db..c933ddc522 100644
--- a/src/view/com/lists/ListMembers.tsx
+++ b/src/view/com/lists/ListMembers.tsx
@@ -1,12 +1,11 @@
import React, {useCallback} from 'react'
import {Dimensions, type StyleProp, View, type ViewStyle} from 'react-native'
-import {type AppBskyGraphDefs} from '@atproto/api'
+import {type AppBskyGraphDefs, type ModerationOpts} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
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'
@@ -16,6 +15,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'
@@ -48,10 +49,8 @@ export function ListMembers({
headerOffset?: number
desktopFixedHeightOffset?: number
}) {
- const t = useTheme()
const {_} = useLingui()
const [isRefreshing, setIsRefreshing] = React.useState(false)
- const {openModal} = useModalControls()
const {currentAccount} = useSession()
const moderationOpts = useModerationOpts()
@@ -118,18 +117,6 @@ export function ListMembers({
fetchNextPage()
}, [fetchNextPage])
- const onPressEditMembership = React.useCallback(
- (profile: bsky.profile.AnyProfileView) => {
- openModal({
- name: 'user-add-remove-lists',
- subject: profile.did,
- displayName: profile.displayName || profile.handle,
- handle: profile.handle,
- })
- },
- [openModal],
- )
-
// rendering
// =
@@ -161,43 +148,12 @@ export function ListMembers({
if (!moderationOpts) return null
return (
-
-
-
-
-
-
- {isOwner && (
-
- )}
-
-
-
-
-
-
-
-
+
)
},
[
@@ -207,9 +163,8 @@ export function ListMembers({
onPressRetryLoadMore,
moderationOpts,
isOwner,
- onPressEditMembership,
_,
- t,
+ list,
],
)
@@ -258,3 +213,68 @@ export function ListMembers({
)
}
+
+function ListMember({
+ profile,
+ moderationOpts,
+ isOwner,
+ list,
+}: {
+ profile: bsky.profile.AnyProfileView
+ moderationOpts: ModerationOpts
+ isOwner?: boolean
+ list: string
+}) {
+ const t = useTheme()
+ const {_} = useLingui()
+ const editMembershipDialogControl = useDialogControl()
+
+ return (
+
+
+
+
+
+
+ {isOwner && (
+
+ )}
+
+
+
+
+
+
+
+
+ {
+ if (type === 'remove' && uri === list) {
+ editMembershipDialogControl.close()
+ }
+ }}
+ />
+
+ )
+}
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index f9afd183e6..1e3e5ce2d0 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -13,7 +13,6 @@ import * as DeleteAccountModal from './DeleteAccount'
import * as InviteCodesModal from './InviteCodes'
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
-import * as UserAddRemoveListsModal from './UserAddRemoveLists'
const DEFAULT_SNAPPOINTS = ['90%']
const HANDLE_HEIGHT = 24
@@ -49,9 +48,6 @@ export function ModalsContainer() {
if (activeModal?.name === 'create-or-edit-list') {
snapPoints = CreateOrEditListModal.snapPoints
element =
- } else if (activeModal?.name === 'user-add-remove-lists') {
- snapPoints = UserAddRemoveListsModal.snapPoints
- element =
} else if (activeModal?.name === 'delete-account') {
snapPoints = DeleteAccountModal.snapPoints
element =
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 3eb7443802..1954fe50c1 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -12,7 +12,6 @@ import * as DeleteAccountModal from './DeleteAccount'
import * as InviteCodesModal from './InviteCodes'
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
-import * as UserAddRemoveLists from './UserAddRemoveLists'
export function ModalsContainer() {
const {isModalActive, activeModals} = useModals()
@@ -52,8 +51,6 @@ function Modal({modal}: {modal: ModalIface}) {
let element
if (modal.name === 'create-or-edit-list') {
element =
- } else if (modal.name === 'user-add-remove-lists') {
- element =
} else if (modal.name === 'delete-account') {
element =
} else if (modal.name === 'invite-codes') {
diff --git a/src/view/com/modals/UserAddRemoveLists.tsx b/src/view/com/modals/UserAddRemoveLists.tsx
deleted file mode 100644
index b0b76644f0..0000000000
--- a/src/view/com/modals/UserAddRemoveLists.tsx
+++ /dev/null
@@ -1,298 +0,0 @@
-import React, {useCallback} from 'react'
-import {
- ActivityIndicator,
- StyleSheet,
- useWindowDimensions,
- View,
-} from 'react-native'
-import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-import {sanitizeDisplayName} from '#/lib/strings/display-names'
-import {cleanError} from '#/lib/strings/errors'
-import {sanitizeHandle} from '#/lib/strings/handles'
-import {s} from '#/lib/styles'
-import {isAndroid, isMobileWeb, isWeb} from '#/platform/detection'
-import {useModalControls} from '#/state/modals'
-import {
- getMembership,
- ListMembersip,
- useDangerousListMembershipsQuery,
- useListMembershipAddMutation,
- useListMembershipRemoveMutation,
-} from '#/state/queries/list-memberships'
-import {useSession} from '#/state/session'
-import {MyLists} from '../lists/MyLists'
-import {Button} from '../util/forms/Button'
-import {Text} from '../util/text/Text'
-import * as Toast from '../util/Toast'
-import {UserAvatar} from '../util/UserAvatar'
-
-export const snapPoints = ['fullscreen']
-
-export function Component({
- subject,
- handle,
- displayName,
- onAdd,
- onRemove,
-}: {
- subject: string
- handle: string
- displayName: string
- onAdd?: (listUri: string) => void
- onRemove?: (listUri: string) => void
-}) {
- const {closeModal} = useModalControls()
- const pal = usePalette('default')
- const {height: screenHeight} = useWindowDimensions()
- const {_} = useLingui()
- const {data: memberships} = useDangerousListMembershipsQuery()
-
- const onPressDone = useCallback(() => {
- closeModal()
- }, [closeModal])
-
- const listStyle = React.useMemo(() => {
- if (isMobileWeb) {
- return [pal.border, {height: screenHeight / 2}]
- } else if (isWeb) {
- return [pal.border, {height: screenHeight / 1.5}]
- }
-
- return [pal.border, {flex: 1, borderTopWidth: StyleSheet.hairlineWidth}]
- }, [pal.border, screenHeight])
-
- const headerStyles = [
- {
- textAlign: 'center',
- fontWeight: '600',
- fontSize: 20,
- marginBottom: 12,
- paddingHorizontal: 12,
- } as const,
- pal.text,
- ]
-
- return (
-
-
-
- Update{' '}
-
- {displayName}
- {' '}
- in Lists
-
-
- (
-
- )}
- style={listStyle}
- />
-
-
-
-
- )
-}
-
-function ListItem({
- index,
- list,
- memberships,
- subject,
- handle,
- onAdd,
- onRemove,
-}: {
- index: number
- list: GraphDefs.ListView
- memberships: ListMembersip[] | undefined
- subject: string
- handle: string
- onAdd?: (listUri: string) => void
- onRemove?: (listUri: string) => void
-}) {
- const pal = usePalette('default')
- const {_} = useLingui()
- const {currentAccount} = useSession()
- const [isProcessing, setIsProcessing] = React.useState(false)
- const membership = React.useMemo(
- () => getMembership(memberships, list.uri, subject),
- [memberships, list.uri, subject],
- )
- const listMembershipAddMutation = useListMembershipAddMutation()
- const listMembershipRemoveMutation = useListMembershipRemoveMutation()
-
- const onToggleMembership = useCallback(async () => {
- if (typeof membership === 'undefined') {
- return
- }
- setIsProcessing(true)
- try {
- if (membership === false) {
- await listMembershipAddMutation.mutateAsync({
- listUri: list.uri,
- actorDid: subject,
- })
- Toast.show(_(msg`Added to list`))
- onAdd?.(list.uri)
- } else {
- await listMembershipRemoveMutation.mutateAsync({
- listUri: list.uri,
- actorDid: subject,
- membershipUri: membership,
- })
- Toast.show(_(msg`Removed from list`))
- onRemove?.(list.uri)
- }
- } catch (e) {
- Toast.show(cleanError(e), 'xmark')
- } finally {
- setIsProcessing(false)
- }
- }, [
- _,
- list,
- subject,
- membership,
- setIsProcessing,
- onAdd,
- onRemove,
- listMembershipAddMutation,
- listMembershipRemoveMutation,
- ])
-
- return (
-
-
-
-
-
-
- {sanitizeDisplayName(list.name)}
-
-
- {list.purpose === 'app.bsky.graph.defs#curatelist' &&
- (list.creator.did === currentAccount?.did ? (
- User list by you
- ) : (
-
- User list by {sanitizeHandle(list.creator.handle, '@')}
-
- ))}
- {list.purpose === 'app.bsky.graph.defs#modlist' &&
- (list.creator.did === currentAccount?.did ? (
- Moderation list by you
- ) : (
-
- Moderation list by {sanitizeHandle(list.creator.handle, '@')}
-
- ))}
-
-
-
- {isProcessing || typeof membership === 'undefined' ? (
-
- ) : (
-
- )}
-
-
- )
-}
-
-const styles = StyleSheet.create({
- container: {
- paddingHorizontal: isWeb ? 0 : 16,
- },
- btns: {
- position: 'relative',
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- gap: 10,
- paddingTop: 10,
- paddingBottom: isAndroid ? 10 : 0,
- borderTopWidth: StyleSheet.hairlineWidth,
- },
- footerBtn: {
- paddingHorizontal: 24,
- paddingVertical: 12,
- },
-
- listItem: {
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 14,
- paddingVertical: 10,
- },
- listItemAvi: {
- width: 54,
- paddingLeft: 4,
- paddingTop: 8,
- paddingBottom: 10,
- },
- listItemContent: {
- flex: 1,
- paddingRight: 10,
- paddingTop: 10,
- paddingBottom: 10,
- },
- checkbox: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- borderWidth: 1,
- width: 24,
- height: 24,
- borderRadius: 6,
- marginRight: 8,
- },
- loadingContainer: {
- position: 'absolute',
- top: 10,
- right: 0,
- bottom: 0,
- justifyContent: 'center',
- },
-})