about summary refs log tree commit diff
path: root/src/screens/Messages/Conversation/index.tsx
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-05-17 14:21:15 -0700
committerGitHub <noreply@github.com>2024-05-17 16:21:15 -0500
commitd02e0884c40adebe3799254395d933205b104a86 (patch)
tree00cd164d727072ad04179662a2d7bfe914691ba9 /src/screens/Messages/Conversation/index.tsx
parent1b47ea7367c7d0f37557d8f07329c3b6f97a5e03 (diff)
downloadvoidsky-d02e0884c40adebe3799254395d933205b104a86.tar.zst
[🐴] Block Info (#4068)
* get the damn thing in there 😮‍💨

* more cleanup and little fixes

another nit

nit

small annoyance

add a comment

only use `scrollTo` when necessary

remove now unnecessary styles

* move padding out

* add unblock function

* rm need for moderationpts

* ?

* ??

* extract leaveconvoprompt

* move `setHasScrolled` to `onContentSizeChanged`

* account for block footer

* wrap up

nit

make sure recipient is loaded before showing

refactor to hide chat input

typo squigglie

add report dialog

finalize delete

implement custom animation

add configurable replace animation

add leave convo to block options

* correct functionality for report

* moev component to another file

* maybe...

* fix chat item

* improve

* remove unused gtmobile

* nit

* more cleanup

* more cleanup

* fix merge

* fix header

* few more changes

* nit

* remove old
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}
+            />
+          }
         />
       )}
     </>