about summary refs log tree commit diff
path: root/src/view/com/profile
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/profile')
-rw-r--r--src/view/com/profile/ProfileCard.tsx93
-rw-r--r--src/view/com/profile/ProfileHeader.tsx74
2 files changed, 74 insertions, 93 deletions
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 946e0f2ab..ba0c59def 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -1,7 +1,11 @@
 import * as React from 'react'
 import {StyleSheet, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
-import {AppBskyActorDefs} from '@atproto/api'
+import {
+  AppBskyActorDefs,
+  moderateProfile,
+  ProfileModeration,
+} from '@atproto/api'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
 import {UserAvatar} from '../util/UserAvatar'
@@ -11,12 +15,11 @@ import {useStores} from 'state/index'
 import {FollowButton} from './FollowButton'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {sanitizeHandle} from 'lib/strings/handles'
-import {
-  getProfileViewBasicLabelInfo,
-  getProfileModeration,
-} from 'lib/labeling/helpers'
-import {ModerationBehaviorCode} from 'lib/labeling/types'
 import {makeProfileLink} from 'lib/routes/links'
+import {
+  describeModerationCause,
+  getProfileModerationCauses,
+} from 'lib/moderation'
 
 export const ProfileCard = observer(
   ({
@@ -25,7 +28,6 @@ export const ProfileCard = observer(
     noBg,
     noBorder,
     followers,
-    overrideModeration,
     renderButton,
   }: {
     testID?: string
@@ -33,7 +35,6 @@ export const ProfileCard = observer(
     noBg?: boolean
     noBorder?: boolean
     followers?: AppBskyActorDefs.ProfileView[] | undefined
-    overrideModeration?: boolean
     renderButton?: (
       profile: AppBskyActorDefs.ProfileViewBasic,
     ) => React.ReactNode
@@ -41,18 +42,11 @@ export const ProfileCard = observer(
     const store = useStores()
     const pal = usePalette('default')
 
-    const moderation = getProfileModeration(
-      store,
-      getProfileViewBasicLabelInfo(profile),
+    const moderation = moderateProfile(
+      profile,
+      store.preferences.moderationOpts,
     )
 
-    if (
-      moderation.list.behavior === ModerationBehaviorCode.Hide &&
-      !overrideModeration
-    ) {
-      return null
-    }
-
     return (
       <Link
         testID={testID}
@@ -82,20 +76,17 @@ export const ProfileCard = observer(
               lineHeight={1.2}>
               {sanitizeDisplayName(
                 profile.displayName || sanitizeHandle(profile.handle),
+                moderation.profile,
               )}
             </Text>
             <Text type="md" style={[pal.textLight]} numberOfLines={1}>
               {sanitizeHandle(profile.handle, '@')}
             </Text>
-            {!!profile.viewer?.followedBy && (
-              <View style={s.flexRow}>
-                <View style={[s.mt5, pal.btn, styles.pill]}>
-                  <Text type="xs" style={pal.text}>
-                    Follows You
-                  </Text>
-                </View>
-              </View>
-            )}
+            <ProfileCardPills
+              followedBy={!!profile.viewer?.followedBy}
+              moderation={moderation}
+            />
+            {!!profile.viewer?.followedBy && <View style={s.flexRow} />}
           </View>
           {renderButton ? (
             <View style={styles.layoutButton}>{renderButton(profile)}</View>
@@ -114,6 +105,44 @@ export const ProfileCard = observer(
   },
 )
 
+function ProfileCardPills({
+  followedBy,
+  moderation,
+}: {
+  followedBy: boolean
+  moderation: ProfileModeration
+}) {
+  const pal = usePalette('default')
+
+  const causes = getProfileModerationCauses(moderation)
+  if (!followedBy && !causes.length) {
+    return null
+  }
+
+  return (
+    <View style={styles.pills}>
+      {followedBy && (
+        <View style={[s.mt5, pal.btn, styles.pill]}>
+          <Text type="xs" style={pal.text}>
+            Follows You
+          </Text>
+        </View>
+      )}
+      {causes.map(cause => {
+        const desc = describeModerationCause(cause, 'account')
+        return (
+          <View style={[s.mt5, pal.btn, styles.pill]}>
+            <Text type="xs" style={pal.text}>
+              {cause?.type === 'label' ? '⚠' : ''}
+              {desc.name}
+            </Text>
+          </View>
+        )
+      })}
+    </View>
+  )
+}
+
 const FollowersList = observer(
   ({followers}: {followers?: AppBskyActorDefs.ProfileView[] | undefined}) => {
     const store = useStores()
@@ -125,9 +154,9 @@ const FollowersList = observer(
     const followersWithMods = followers
       .map(f => ({
         f,
-        mod: getProfileModeration(store, getProfileViewBasicLabelInfo(f)),
+        mod: moderateProfile(f, store.preferences.moderationOpts),
       }))
-      .filter(({mod}) => mod.list.behavior !== ModerationBehaviorCode.Hide)
+      .filter(({mod}) => !mod.account.filter)
 
     return (
       <View style={styles.followedBy}>
@@ -218,6 +247,12 @@ const styles = StyleSheet.create({
     paddingRight: 10,
     paddingBottom: 10,
   },
+  pills: {
+    flexDirection: 'row',
+    flexWrap: 'wrap',
+    columnGap: 6,
+    rowGap: 2,
+  },
   pill: {
     borderRadius: 4,
     paddingHorizontal: 6,
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index a372f0d81..f8531d76c 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -21,15 +21,13 @@ import * as Toast from '../util/Toast'
 import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {Text} from '../util/text/Text'
 import {ThemedText} from '../util/text/ThemedText'
-import {TextLink} from '../util/Link'
 import {RichText} from '../util/text/RichText'
 import {UserAvatar} from '../util/UserAvatar'
 import {UserBanner} from '../util/UserBanner'
-import {ProfileHeaderWarnings} from '../util/moderation/ProfileHeaderWarnings'
+import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useAnalytics} from 'lib/analytics/analytics'
 import {NavigationProp} from 'lib/routes/types'
-import {listUriToHref} from 'lib/strings/url-helpers'
 import {isDesktopWeb, isNative} from 'platform/detection'
 import {FollowState} from 'state/models/cache/my-follows'
 import {shareUrl} from 'lib/sharing'
@@ -116,7 +114,10 @@ const ProfileHeaderLoaded = observer(
     }, [navigation])
 
     const onPressAvi = React.useCallback(() => {
-      if (view.avatar) {
+      if (
+        view.avatar &&
+        !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
+      ) {
         store.shell.openLightbox(new ProfileImageLightbox(view))
       }
     }, [store, view])
@@ -434,6 +435,7 @@ const ProfileHeaderLoaded = observer(
               style={[pal.text, styles.title]}>
               {sanitizeDisplayName(
                 view.displayName || sanitizeHandle(view.handle),
+                view.moderation.profile,
               )}
             </Text>
           </View>
@@ -494,7 +496,9 @@ const ProfileHeaderLoaded = observer(
                   </Text>
                 </Text>
               </View>
-              {view.descriptionRichText ? (
+              {view.description &&
+              view.descriptionRichText &&
+              !view.moderation.profile.blur ? (
                 <RichText
                   testID="profileHeaderDescription"
                   style={[styles.description, pal.text]}
@@ -504,52 +508,7 @@ const ProfileHeaderLoaded = observer(
               ) : undefined}
             </>
           )}
-          <ProfileHeaderWarnings moderation={view.moderation.view} />
-          <View style={styles.moderationLines}>
-            {view.viewer.blocking ? (
-              <View
-                testID="profileHeaderBlockedNotice"
-                style={[styles.moderationNotice, pal.viewLight]}>
-                <FontAwesomeIcon icon="ban" style={[pal.text]} />
-                <Text type="lg-medium" style={pal.text}>
-                  Account blocked
-                </Text>
-              </View>
-            ) : view.viewer.muted ? (
-              <View
-                testID="profileHeaderMutedNotice"
-                style={[styles.moderationNotice, pal.viewLight]}>
-                <FontAwesomeIcon
-                  icon={['far', 'eye-slash']}
-                  style={[pal.text]}
-                />
-                <Text type="lg-medium" style={pal.text}>
-                  Account muted{' '}
-                  {view.viewer.mutedByList && (
-                    <Text type="lg-medium" style={pal.text}>
-                      by{' '}
-                      <TextLink
-                        type="lg-medium"
-                        style={pal.link}
-                        href={listUriToHref(view.viewer.mutedByList.uri)}
-                        text={view.viewer.mutedByList.name}
-                      />
-                    </Text>
-                  )}
-                </Text>
-              </View>
-            ) : undefined}
-            {view.viewer.blockedBy && (
-              <View
-                testID="profileHeaderBlockedNotice"
-                style={[styles.moderationNotice, pal.viewLight]}>
-                <FontAwesomeIcon icon="ban" style={[pal.text]} />
-                <Text type="lg-medium" style={pal.text}>
-                  This account has blocked you
-                </Text>
-              </View>
-            )}
-          </View>
+          <ProfileHeaderAlerts moderation={view.moderation} />
         </View>
         {!isDesktopWeb && !hideBackButton && (
           <TouchableWithoutFeedback
@@ -693,19 +652,6 @@ const styles = StyleSheet.create({
     paddingVertical: 2,
   },
 
-  moderationLines: {
-    gap: 6,
-  },
-
-  moderationNotice: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    borderRadius: 8,
-    paddingHorizontal: 16,
-    paddingVertical: 14,
-    gap: 8,
-  },
-
   br40: {borderRadius: 40},
   br50: {borderRadius: 50},
 })