about summary refs log tree commit diff
path: root/src/components/dms/ConvoMenu.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/dms/ConvoMenu.tsx')
-rw-r--r--src/components/dms/ConvoMenu.tsx94
1 files changed, 83 insertions, 11 deletions
diff --git a/src/components/dms/ConvoMenu.tsx b/src/components/dms/ConvoMenu.tsx
index 0a1d3f01c..16e8d98c6 100644
--- a/src/components/dms/ConvoMenu.tsx
+++ b/src/components/dms/ConvoMenu.tsx
@@ -1,19 +1,27 @@
 import React, {useCallback} from 'react'
 import {Keyboard, Pressable, View} from 'react-native'
-import {AppBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api'
+import {
+  AppBskyActorDefs,
+  ChatBskyConvoDefs,
+  ModerationDecision,
+} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/native'
 
 import {NavigationProp} from '#/lib/routes/types'
+import {listUriToHref} from '#/lib/strings/url-helpers'
+import {Shadow} from '#/state/cache/types'
 import {
   useConvoQuery,
   useMarkAsReadMutation,
 } from '#/state/queries/messages/conversation'
 import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
 import {useMuteConvo} from '#/state/queries/messages/mute-conversation'
+import {useProfileBlockMutationQueue} from '#/state/queries/profile'
 import * as Toast from '#/view/com/util/Toast'
 import {atoms as a, useTheme} from '#/alf'
+import * as Dialog from '#/components/Dialog'
 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
 import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
 import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
@@ -22,8 +30,10 @@ import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Perso
 import {PersonCheck_Stroke2_Corner0_Rounded as PersonCheck} from '#/components/icons/PersonCheck'
 import {PersonX_Stroke2_Corner0_Rounded as PersonX} from '#/components/icons/PersonX'
 import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
+import {InlineLinkText} from '#/components/Link'
 import * as Menu from '#/components/Menu'
 import * as Prompt from '#/components/Prompt'
+import {Text} from '#/components/Typography'
 import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '../icons/Bubble'
 
 let ConvoMenu = ({
@@ -34,22 +44,35 @@ let ConvoMenu = ({
   showMarkAsRead,
   hideTrigger,
   triggerOpacity,
+  moderation,
 }: {
   convo: ChatBskyConvoDefs.ConvoView
-  profile: AppBskyActorDefs.ProfileViewBasic
-  onUpdateConvo?: (convo: ChatBskyConvoDefs.ConvoView) => void
+  profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
   control?: Menu.MenuControlProps
   currentScreen: 'list' | 'conversation'
   showMarkAsRead?: boolean
   hideTrigger?: boolean
   triggerOpacity?: number
+  moderation: ModerationDecision
 }): React.ReactNode => {
   const navigation = useNavigation<NavigationProp>()
   const {_} = useLingui()
   const t = useTheme()
   const leaveConvoControl = Prompt.usePromptControl()
   const reportControl = Prompt.usePromptControl()
+  const blockedByListControl = Prompt.usePromptControl()
   const {mutate: markAsRead} = useMarkAsReadMutation()
+  const modui = moderation.ui('profileView')
+  const {listBlocks, userBlock} = React.useMemo(() => {
+    const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
+    const listBlocks = blocks.filter(alert => alert.source.type === 'list')
+    const userBlock = blocks.find(alert => alert.source.type === 'user')
+    return {
+      listBlocks,
+      userBlock,
+    }
+  }, [modui])
+  const isBlocking = !!userBlock || !!listBlocks.length
 
   const {data: convo} = useConvoQuery(initialConvo)
 
@@ -70,6 +93,21 @@ let ConvoMenu = ({
     },
   })
 
+  const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
+
+  const toggleBlock = React.useCallback(() => {
+    if (listBlocks.length) {
+      blockedByListControl.open()
+      return
+    }
+
+    if (userBlock) {
+      queueUnblock()
+    } else {
+      queueBlock()
+    }
+  }, [userBlock, listBlocks, blockedByListControl, queueBlock, queueUnblock])
+
   const {mutate: leaveConvo} = useLeaveConvo(convo?.id, {
     onSuccess: () => {
       if (currentScreen === 'conversation') {
@@ -146,18 +184,16 @@ let ConvoMenu = ({
             </Menu.Item>
           </Menu.Group>
           <Menu.Divider />
-          {/* TODO(samuel): implement this */}
           <Menu.Group>
             <Menu.Item
-              label={_(msg`Block account`)}
-              onPress={() => {}}
-              disabled>
+              label={
+                isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)
+              }
+              onPress={toggleBlock}>
               <Menu.ItemText>
-                <Trans>Block account</Trans>
+                {isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)}
               </Menu.ItemText>
-              <Menu.ItemIcon
-                icon={profile.viewer?.blocking ? PersonCheck : PersonX}
-              />
+              <Menu.ItemIcon icon={isBlocking ? PersonX : PersonCheck} />
             </Menu.Item>
             <Menu.Item
               label={_(msg`Report conversation`)}
@@ -202,6 +238,42 @@ let ConvoMenu = ({
         confirmButtonCta={_(msg`I understand`)}
         onConfirm={noop}
       />
+
+      <Prompt.Outer control={blockedByListControl} testID="blockedByListDialog">
+        <Prompt.TitleText>{_(msg`User blocked by list`)}</Prompt.TitleText>
+
+        <View style={[a.gap_sm, a.pb_lg]}>
+          <Text
+            selectable
+            style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
+            {_(
+              msg`This account is blocked by one or more of your moderation lists. To unblock, please visit the lists directly and remove this user.`,
+            )}{' '}
+          </Text>
+
+          <Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]}>
+            {_(msg`Lists blocking this user:`)}{' '}
+            {listBlocks.map((block, i) =>
+              block.source.type === 'list' ? (
+                <React.Fragment key={block.source.list.uri}>
+                  {i === 0 ? null : ', '}
+                  <InlineLinkText
+                    to={listUriToHref(block.source.list.uri)}
+                    style={[a.text_md, a.leading_snug]}>
+                    {block.source.list.name}
+                  </InlineLinkText>
+                </React.Fragment>
+              ) : null,
+            )}
+          </Text>
+        </View>
+
+        <Prompt.Actions>
+          <Prompt.Cancel cta={_(msg`I understand`)} />
+        </Prompt.Actions>
+
+        <Dialog.Close />
+      </Prompt.Outer>
     </>
   )
 }