about summary refs log tree commit diff
path: root/src/screens/Messages/Conversation/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/Messages/Conversation/index.tsx')
-rw-r--r--src/screens/Messages/Conversation/index.tsx228
1 files changed, 61 insertions, 167 deletions
diff --git a/src/screens/Messages/Conversation/index.tsx b/src/screens/Messages/Conversation/index.tsx
index 2c42ed16d..0fe4138bb 100644
--- a/src/screens/Messages/Conversation/index.tsx
+++ b/src/screens/Messages/Conversation/index.tsx
@@ -1,35 +1,28 @@
 import React, {useCallback} from 'react'
-import {TouchableOpacity, View} from 'react-native'
+import {View} from 'react-native'
 import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useFocusEffect, useNavigation} from '@react-navigation/native'
+import {useFocusEffect} from '@react-navigation/native'
 import {NativeStackScreenProps} from '@react-navigation/native-stack'
 
-import {makeProfileLink} from '#/lib/routes/links'
-import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
+import {CommonNavigatorParams} from '#/lib/routes/types'
 import {useGate} from '#/lib/statsig/statsig'
-import {useProfileShadow} from '#/state/cache/profile-shadow'
 import {useCurrentConvoId} from '#/state/messages/current-convo-id'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {useProfileQuery} from '#/state/queries/profile'
-import {BACK_HITSLOP} from 'lib/constants'
-import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {isWeb} from 'platform/detection'
+import {useProfileShadow} from 'state/cache/profile-shadow'
 import {ConvoProvider, isConvoActive, useConvo} from 'state/messages/convo'
 import {ConvoStatus} from 'state/messages/convo/types'
 import {useSetMinimalShellMode} from 'state/shell'
-import {PreviewableUserAvatar} from 'view/com/util/UserAvatar'
 import {CenteredView} from 'view/com/util/Views'
 import {MessagesList} from '#/screens/Messages/Conversation/MessagesList'
-import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
-import {ConvoMenu} from '#/components/dms/ConvoMenu'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import {MessagesListBlockedFooter} from '#/components/dms/MessagesListBlockedFooter'
+import {MessagesListHeader} from '#/components/dms/MessagesListHeader'
 import {Error} from '#/components/Error'
-import {Link} from '#/components/Link'
-import {ListMaybePlaceholder} from '#/components/Lists'
 import {Loader} from '#/components/Loader'
-import {Text} from '#/components/Typography'
 import {ClipClopGate} from '../gate'
 
 type Props = NativeStackScreenProps<
@@ -73,6 +66,11 @@ function Inner() {
   const convoState = useConvo()
   const {_} = useLingui()
 
+  const moderationOpts = useModerationOpts()
+  const {data: recipient} = useProfileQuery({
+    did: convoState.recipients?.[0].did,
+  })
+
   // Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user,
   // we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be
   // empty. So, we also check for that possible state as well and render once we can.
@@ -86,7 +84,7 @@ function Inner() {
   if (convoState.status === ConvoStatus.Error) {
     return (
       <CenteredView style={a.flex_1} sideBorders>
-        <Header />
+        <MessagesListHeader />
         <Error
           title={_(msg`Something went wrong`)}
           message={_(msg`We couldn't load this conversation`)}
@@ -96,20 +94,21 @@ function Inner() {
     )
   }
 
-  /*
-   * Any other convo states (atm) are "ready" states
-   */
   return (
     <CenteredView style={[a.flex_1]} sideBorders>
-      <Header profile={convoState.recipients?.[0]} />
+      {!readyToShow && <MessagesListHeader />}
       <View style={[a.flex_1]}>
-        {isConvoActive(convoState) ? (
-          <MessagesList
+        {moderationOpts && recipient ? (
+          <InnerReady
+            moderationOpts={moderationOpts}
+            recipient={recipient}
             hasScrolled={hasScrolled}
             setHasScrolled={setHasScrolled}
           />
         ) : (
-          <ListMaybePlaceholder isLoading />
+          <>
+            <View style={[a.align_center, a.gap_sm, a.flex_1]} />
+          </>
         )}
         {!readyToShow && (
           <View
@@ -132,160 +131,55 @@ function Inner() {
   )
 }
 
-const PFP_SIZE = isWeb ? 40 : 34
-
-let Header = ({
-  profile: initialProfile,
-}: {
-  profile?: AppBskyActorDefs.ProfileViewBasic
-}): React.ReactNode => {
-  const t = useTheme()
-  const {_} = useLingui()
-  const {gtTablet} = useBreakpoints()
-  const navigation = useNavigation<NavigationProp>()
-  const moderationOpts = useModerationOpts()
-  const {data: profile} = useProfileQuery({did: initialProfile?.did})
-
-  const onPressBack = useCallback(() => {
-    if (isWeb) {
-      navigation.replace('Messages')
-    } else {
-      navigation.goBack()
-    }
-  }, [navigation])
-
-  return (
-    <View
-      style={[
-        t.atoms.bg,
-        t.atoms.border_contrast_low,
-        a.border_b,
-        a.flex_row,
-        a.align_center,
-        a.gap_sm,
-        gtTablet ? a.pl_lg : a.pl_xl,
-        a.pr_lg,
-        a.py_sm,
-      ]}>
-      {!gtTablet && (
-        <TouchableOpacity
-          testID="conversationHeaderBackBtn"
-          onPress={onPressBack}
-          hitSlop={BACK_HITSLOP}
-          style={{width: 30, height: 30}}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Back`)}
-          accessibilityHint="">
-          <FontAwesomeIcon
-            size={18}
-            icon="angle-left"
-            style={{
-              marginTop: 6,
-            }}
-            color={t.atoms.text.color}
-          />
-        </TouchableOpacity>
-      )}
-
-      {profile && moderationOpts ? (
-        <HeaderReady profile={profile} moderationOpts={moderationOpts} />
-      ) : (
-        <>
-          <View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
-            <View
-              style={[
-                {width: PFP_SIZE, height: PFP_SIZE},
-                a.rounded_full,
-                t.atoms.bg_contrast_25,
-              ]}
-            />
-            <View style={a.gap_xs}>
-              <View
-                style={[
-                  {width: 120, height: 16},
-                  a.rounded_xs,
-                  t.atoms.bg_contrast_25,
-                  a.mt_xs,
-                ]}
-              />
-              <View
-                style={[
-                  {width: 175, height: 12},
-                  a.rounded_xs,
-                  t.atoms.bg_contrast_25,
-                ]}
-              />
-            </View>
-          </View>
-
-          <View style={{width: 30}} />
-        </>
-      )}
-    </View>
-  )
-}
-Header = React.memo(Header)
-
-function HeaderReady({
-  profile: profileUnshadowed,
+function InnerReady({
   moderationOpts,
+  recipient: recipientUnshadowed,
+  hasScrolled,
+  setHasScrolled,
 }: {
-  profile: AppBskyActorDefs.ProfileViewBasic
   moderationOpts: ModerationOpts
+  recipient: AppBskyActorDefs.ProfileViewBasic
+  hasScrolled: boolean
+  setHasScrolled: React.Dispatch<React.SetStateAction<boolean>>
 }) {
-  const t = useTheme()
   const convoState = useConvo()
-  const profile = useProfileShadow(profileUnshadowed)
-  const moderation = React.useMemo(
-    () => moderateProfile(profile, moderationOpts),
-    [profile, moderationOpts],
-  )
-
-  const isDeletedAccount = profile?.handle === 'missing.invalid'
-  const displayName = isDeletedAccount
-    ? 'Deleted Account'
-    : sanitizeDisplayName(
-        profile.displayName || profile.handle,
-        moderation.ui('displayName'),
-      )
+  const recipient = useProfileShadow(recipientUnshadowed)
+
+  const moderation = React.useMemo(() => {
+    return moderateProfile(recipient, moderationOpts)
+  }, [recipient, moderationOpts])
+
+  const blockInfo = React.useMemo(() => {
+    const modui = moderation.ui('profileView')
+    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,
+    }
+  }, [moderation])
 
   return (
     <>
-      <Link
-        style={[a.flex_row, a.align_center, a.gap_md, a.flex_1, a.pr_md]}
-        to={makeProfileLink(profile)}>
-        <PreviewableUserAvatar
-          size={PFP_SIZE}
-          profile={profile}
-          moderation={moderation.ui('avatar')}
-          disableHoverCard={moderation.blocked}
-        />
-        <View style={a.flex_1}>
-          <Text
-            style={[a.text_md, a.font_bold, web(a.leading_normal)]}
-            numberOfLines={1}>
-            {displayName}
-          </Text>
-          {!isDeletedAccount && (
-            <Text
-              style={[
-                t.atoms.text_contrast_medium,
-                a.text_sm,
-                web([a.leading_normal, {marginTop: -2}]),
-              ]}
-              numberOfLines={1}>
-              @{profile.handle}
-            </Text>
-          )}
-        </View>
-      </Link>
-
+      <MessagesListHeader
+        profile={recipient}
+        moderation={moderation}
+        blockInfo={blockInfo}
+      />
       {isConvoActive(convoState) && (
-        <ConvoMenu
-          convo={convoState.convo}
-          profile={profile}
-          currentScreen="conversation"
-          moderation={moderation}
+        <MessagesList
+          hasScrolled={hasScrolled}
+          setHasScrolled={setHasScrolled}
+          blocked={moderation?.blocked}
+          footer={
+            <MessagesListBlockedFooter
+              recipient={recipient}
+              convoId={convoState.convo.id}
+              hasMessages={convoState.items.length > 0}
+              blockInfo={blockInfo}
+            />
+          }
         />
       )}
     </>