about summary refs log tree commit diff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/LikesDialog.tsx129
-rw-r--r--src/components/ProfileCard.tsx53
2 files changed, 39 insertions, 143 deletions
diff --git a/src/components/LikesDialog.tsx b/src/components/LikesDialog.tsx
deleted file mode 100644
index cb000b433..000000000
--- a/src/components/LikesDialog.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import {useCallback, useMemo} from 'react'
-import {ActivityIndicator, FlatList, View} from 'react-native'
-import {AppBskyFeedGetLikes as GetLikes} 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 {useLikedByQuery} from '#/state/queries/post-liked-by'
-import {useResolveUriQuery} from '#/state/queries/resolve-uri'
-import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
-import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
-import {atoms as a, useTheme} from '#/alf'
-import * as Dialog from '#/components/Dialog'
-import {Loader} from '#/components/Loader'
-import {Text} from '#/components/Typography'
-
-interface LikesDialogProps {
-  control: Dialog.DialogOuterProps['control']
-  uri: string
-}
-
-export function LikesDialog(props: LikesDialogProps) {
-  return (
-    <Dialog.Outer control={props.control}>
-      <Dialog.Handle />
-      <LikesDialogInner {...props} />
-    </Dialog.Outer>
-  )
-}
-
-export function LikesDialogInner({control, uri}: LikesDialogProps) {
-  const {_} = useLingui()
-  const t = useTheme()
-
-  const {
-    data: resolvedUri,
-    error: resolveError,
-    isFetched: hasFetchedResolvedUri,
-  } = useResolveUriQuery(uri)
-  const {
-    data,
-    isFetching: isFetchingLikedBy,
-    isFetched: hasFetchedLikedBy,
-    isFetchingNextPage,
-    hasNextPage,
-    fetchNextPage,
-    isError,
-    error: likedByError,
-  } = useLikedByQuery(resolvedUri?.uri)
-
-  const isLoading = !hasFetchedResolvedUri || !hasFetchedLikedBy
-  const likes = useMemo(() => {
-    if (data?.pages) {
-      return data.pages.flatMap(page => page.likes)
-    }
-    return []
-  }, [data])
-
-  const onEndReached = useCallback(async () => {
-    if (isFetchingLikedBy || !hasNextPage || isError) return
-    try {
-      await fetchNextPage()
-    } catch (err) {
-      logger.error('Failed to load more likes', {message: err})
-    }
-  }, [isFetchingLikedBy, hasNextPage, isError, fetchNextPage])
-
-  const renderItem = useCallback(
-    ({item}: {item: GetLikes.Like}) => {
-      return (
-        <ProfileCardWithFollowBtn
-          key={item.actor.did}
-          profile={item.actor}
-          onPress={() => control.close()}
-        />
-      )
-    },
-    [control],
-  )
-
-  return (
-    <Dialog.Inner label={_(msg`Users that have liked this content or profile`)}>
-      <Text style={[a.text_2xl, a.font_bold, a.leading_tight, a.pb_lg]}>
-        <Trans>Liked by</Trans>
-      </Text>
-
-      {isLoading ? (
-        <View style={{minHeight: 300}}>
-          <Loader size="xl" />
-        </View>
-      ) : resolveError || likedByError || !data ? (
-        <ErrorMessage message={cleanError(resolveError || likedByError)} />
-      ) : likes.length === 0 ? (
-        <View style={[t.atoms.bg_contrast_50, a.px_md, a.py_xl, a.rounded_md]}>
-          <Text style={[a.text_center]}>
-            <Trans>
-              Nobody has liked this yet. Maybe you should be the first!
-            </Trans>
-          </Text>
-        </View>
-      ) : (
-        <FlatList
-          data={likes}
-          keyExtractor={item => item.actor.did}
-          onEndReached={onEndReached}
-          renderItem={renderItem}
-          initialNumToRender={15}
-          ListFooterComponent={
-            <ListFooterComponent isFetching={isFetchingNextPage} />
-          }
-        />
-      )}
-
-      <Dialog.Close />
-    </Dialog.Inner>
-  )
-}
-
-function ListFooterComponent({isFetching}: {isFetching: boolean}) {
-  if (isFetching) {
-    return (
-      <View style={a.pt_lg}>
-        <ActivityIndicator />
-      </View>
-    )
-  }
-  return null
-}
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index beb09cdc7..394ff9946 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -8,15 +8,15 @@ import {
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
+import {getModerationCauseKey} from '#/lib/moderation'
 import {type LogEvents} from '#/lib/statsig/statsig'
 import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
 import {useProfileShadow} from '#/state/cache/profile-shadow'
 import {useProfileFollowMutationQueue} from '#/state/queries/profile'
 import {useSession} from '#/state/session'
-import {ProfileCardPills} from '#/view/com/profile/ProfileCard'
 import * as Toast from '#/view/com/util/Toast'
-import {UserAvatar} from '#/view/com/util/UserAvatar'
+import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
 import {atoms as a, useTheme} from '#/alf'
 import {
   Button,
@@ -27,6 +27,7 @@ import {
 import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
 import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
 import {Link as InternalLink, type LinkProps} from '#/components/Link'
+import * as Pills from '#/components/Pills'
 import {RichText} from '#/components/RichText'
 import {Text} from '#/components/Typography'
 import type * as bsky from '#/types/bsky'
@@ -35,13 +36,15 @@ export function Default({
   profile,
   moderationOpts,
   logContext = 'ProfileCard',
+  testID,
 }: {
   profile: bsky.profile.AnyProfileView
   moderationOpts: ModerationOpts
   logContext?: 'ProfileCard' | 'StarterPackProfilesList'
+  testID?: string
 }) {
   return (
-    <Link profile={profile}>
+    <Link testID={testID} profile={profile}>
       <Card
         profile={profile}
         moderationOpts={moderationOpts}
@@ -60,8 +63,6 @@ export function Card({
   moderationOpts: ModerationOpts
   logContext?: 'ProfileCard' | 'StarterPackProfilesList'
 }) {
-  const moderation = moderateProfile(profile, moderationOpts)
-
   return (
     <Outer>
       <Header>
@@ -74,10 +75,7 @@ export function Card({
         />
       </Header>
 
-      <ProfileCardPills
-        followedBy={Boolean(profile.viewer?.followedBy)}
-        moderation={moderation}
-      />
+      <Labels profile={profile} moderationOpts={moderationOpts} />
 
       <Description profile={profile} />
     </Outer>
@@ -87,7 +85,7 @@ export function Card({
 export function Outer({
   children,
 }: {
-  children: React.ReactElement | React.ReactElement[]
+  children: React.ReactNode | React.ReactNode[]
 }) {
   return <View style={[a.w_full, a.flex_1, a.gap_xs]}>{children}</View>
 }
@@ -95,7 +93,7 @@ export function Outer({
 export function Header({
   children,
 }: {
-  children: React.ReactElement | React.ReactElement[]
+  children: React.ReactNode | React.ReactNode[]
 }) {
   return <View style={[a.flex_row, a.align_center, a.gap_sm]}>{children}</View>
 }
@@ -137,10 +135,9 @@ export function Avatar({
   const moderation = moderateProfile(profile, moderationOpts)
 
   return (
-    <UserAvatar
+    <PreviewableUserAvatar
       size={40}
-      avatar={profile.avatar}
-      type={profile.associated?.labeler ? 'labeler' : 'user'}
+      profile={profile}
       moderation={moderation.ui('avatar')}
     />
   )
@@ -415,3 +412,31 @@ export function FollowButtonInner({
     </View>
   )
 }
+
+export function Labels({
+  profile,
+  moderationOpts,
+}: {
+  profile: bsky.profile.AnyProfileView
+  moderationOpts: ModerationOpts
+}) {
+  const moderation = moderateProfile(profile, moderationOpts)
+  const modui = moderation.ui('profileList')
+  const followedBy = profile.viewer?.followedBy
+
+  if (!followedBy && !modui.inform && !modui.alert) {
+    return null
+  }
+
+  return (
+    <Pills.Row style={[a.pt_xs]}>
+      {followedBy && <Pills.FollowsYou />}
+      {modui.alerts.map(alert => (
+        <Pills.Label key={getModerationCauseKey(alert)} cause={alert} />
+      ))}
+      {modui.informs.map(inform => (
+        <Pills.Label key={getModerationCauseKey(inform)} cause={inform} />
+      ))}
+    </Pills.Row>
+  )
+}