about summary refs log tree commit diff
path: root/src/view/com/notifications/NotificationFeedItem.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-04-18 21:15:32 -0500
committerGitHub <noreply@github.com>2025-04-18 19:15:32 -0700
commit0ac15920a477a5c8090fd2b929b36ac0b6e02c34 (patch)
treedebd067ccc0f3f5f814d8ec10082e41034d47c7c /src/view/com/notifications/NotificationFeedItem.tsx
parentf1e44ee12e0ccde71e616121708e70462351f068 (diff)
downloadvoidsky-0ac15920a477a5c8090fd2b929b36ac0b6e02c34.tar.zst
Verification (#8226)
* WIP

* Alignment with icon

* Add create/remove prompts

* Fill out check dialog a bit

* Reorg

* Handle was verified state

* Add warning to edit profile

* Add warning to handle dialog

* Decent alignment in posts on all platforms

* Refactor alignment for posts, chatlist, hover card

* Disable on profile

* Convo header

* Compute simple verification state

* Add other icon, rename, integrate

* Swap in simple state for profile edits

* Clean up utility hooks

* Add verifications UI to dialog

* Add edu nux

* Revert change

* Fix wrapping of check on profile

* Rename

* Fix gap under PostMeta

* Update check dialogs

* Handle takendown verifiers in check dialog

* alf composer reply to

* Refactor verification state

* Add create/remove mutations, non-functional for now

* Fix up post-rebase

* Add check to first author noty

* Do cache updates after mutations

* DRY up hook, add to profile updates too

* Add to drawer

* Update account list

* Adapt to new types

* Hook up mutations

* Use profile shadow in feeds

* Add to settings

* Shadow currentAccountProfile

* Add invalid state to verifications

* Fix alignment and overflow in Settings and Drawer

* Re-integrate post rebase

* Remove debug code

* Update copy

* Add unverified notification support

* Remove link

* Make sure dialog closes

* Update URL

* Add settings screen

* Integrate new setting into verification states

* Add metrics, bump package, fix bad import

* NUX fixes

* Update copy

* Fixes

* Update types

* fix search autocomplete

* fix lint

* add display name warning to new dialog

* update default prefs

* Add parsing support for notifications

* Bump pkg

* Tweak noty styles

* Adjust check alignment

* Tweak check alignment

* Fix badge for verifier

* Modify copy

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Diffstat (limited to 'src/view/com/notifications/NotificationFeedItem.tsx')
-rw-r--r--src/view/com/notifications/NotificationFeedItem.tsx204
1 files changed, 150 insertions, 54 deletions
diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx
index 8875ec02e..1de0b67b3 100644
--- a/src/view/com/notifications/NotificationFeedItem.tsx
+++ b/src/view/com/notifications/NotificationFeedItem.tsx
@@ -49,7 +49,7 @@ import {Post} from '#/view/com/post/Post'
 import {formatCount} from '#/view/com/util/numeric/format'
 import {TimeElapsed} from '#/view/com/util/TimeElapsed'
 import {PreviewableUserAvatar, UserAvatar} from '#/view/com/util/UserAvatar'
-import {atoms as a, useTheme} from '#/alf'
+import {atoms as a, platform, useTheme} from '#/alf'
 import {Button, ButtonText} from '#/components/Button'
 import {
   ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
@@ -59,12 +59,15 @@ import {Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled} from '#/compon
 import {PersonPlus_Filled_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
 import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost'
 import {StarterPack} from '#/components/icons/StarterPack'
+import {VerifiedCheck} from '#/components/icons/VerifiedCheck'
 import {InlineLinkText, Link} from '#/components/Link'
 import * as MediaPreview from '#/components/MediaPreview'
 import {ProfileHoverCard} from '#/components/ProfileHoverCard'
 import {Notification as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
 import {SubtleWebHover} from '#/components/SubtleWebHover'
 import {Text} from '#/components/Typography'
+import {useSimpleVerificationState} from '#/components/verification'
+import {VerificationCheck} from '#/components/verification/VerificationCheck'
 import * as bsky from '#/types/bsky'
 
 const MAX_AUTHORS = 5
@@ -145,6 +148,9 @@ let NotificationFeedItem = ({
 
   const niceTimestamp = niceDate(i18n, item.notification.indexedAt)
   const firstAuthor = authors[0]
+  const firstAuthorVerification = useSimpleVerificationState({
+    profile: firstAuthor.profile,
+  })
   const firstAuthorName = sanitizeDisplayName(
     firstAuthor.profile.displayName || firstAuthor.profile.handle,
   )
@@ -186,6 +192,24 @@ let NotificationFeedItem = ({
       emoji
       label={_(msg`Go to ${firstAuthorName}'s profile`)}>
       {forceLTR(firstAuthorName)}
+      {firstAuthorVerification.showBadge && (
+        <View
+          style={[
+            a.relative,
+            {
+              paddingTop: platform({android: 2}),
+              marginBottom: platform({ios: -7}),
+              top: platform({web: 1}),
+              paddingLeft: 3,
+              paddingRight: 2,
+            },
+          ]}>
+          <VerificationCheck
+            width={14}
+            verifier={firstAuthorVerification.role === 'verifier'}
+          />
+        </View>
+      )}
     </InlineLinkText>
   )
   const additionalAuthorsCount = authors.length - 1
@@ -366,6 +390,60 @@ let NotificationFeedItem = ({
         <StarterPack width={30} gradient="sky" />
       </View>
     )
+    // @ts-ignore TODO
+  } else if (item.type === 'verified') {
+    a11yLabel = hasMultipleAuthors
+      ? _(
+          msg`${firstAuthorName} and ${plural(additionalAuthorsCount, {
+            one: `${formattedAuthorsCount} other`,
+            other: `${formattedAuthorsCount} others`,
+          })} verified you`,
+        )
+      : _(msg`${firstAuthorName} verified you`)
+    notificationContent = hasMultipleAuthors ? (
+      <Trans>
+        {firstAuthorLink} and{' '}
+        <Text style={[pal.text, s.bold]}>
+          <Plural
+            value={additionalAuthorsCount}
+            one={`${formattedAuthorsCount} other`}
+            other={`${formattedAuthorsCount} others`}
+          />
+        </Text>{' '}
+        verified you
+      </Trans>
+    ) : (
+      <Trans>{firstAuthorLink} verified you</Trans>
+    )
+    icon = <VerifiedCheck size="xl" />
+    // @ts-ignore TODO
+  } else if (item.type === 'unverified') {
+    a11yLabel = hasMultipleAuthors
+      ? _(
+          msg`${firstAuthorName} and ${plural(additionalAuthorsCount, {
+            one: `${formattedAuthorsCount} other`,
+            other: `${formattedAuthorsCount} others`,
+          })} removed their verifications from your account`,
+        )
+      : _(msg`${firstAuthorName} removed their verification from your account`)
+    notificationContent = hasMultipleAuthors ? (
+      <Trans>
+        {firstAuthorLink} and{' '}
+        <Text style={[pal.text, s.bold]}>
+          <Plural
+            value={additionalAuthorsCount}
+            one={`${formattedAuthorsCount} other`}
+            other={`${formattedAuthorsCount} others`}
+          />
+        </Text>{' '}
+        removed their verifications from your account
+      </Trans>
+    ) : (
+      <Trans>
+        {firstAuthorLink} removed their verification from your account
+      </Trans>
+    )
+    icon = <VerifiedCheck size="xl" fill={t.palette.contrast_500} />
   } else {
     return null
   }
@@ -447,7 +525,6 @@ let NotificationFeedItem = ({
                 style={[
                   a.flex_row,
                   a.flex_wrap,
-                  a.pb_2xs,
                   {paddingTop: 6},
                   a.self_start,
                   a.text_md,
@@ -475,7 +552,9 @@ let NotificationFeedItem = ({
               </Text>
             </ExpandListPressable>
             {item.type === 'post-like' || item.type === 'repost' ? (
-              <AdditionalPostText post={item.subject} />
+              <View style={[a.pt_2xs]}>
+                <AdditionalPostText post={item.subject} />
+              </View>
             ) : null}
             {item.type === 'feedgen-like' && item.subjectUri ? (
               <FeedSourceCard
@@ -672,8 +751,6 @@ function ExpandedAuthorsList({
   visible: boolean
   authors: Author[]
 }) {
-  const {_} = useLingui()
-  const t = useTheme()
   const heightInterp = useAnimatedValue(visible ? 1 : 0)
   const targetHeight =
     authors.length * (EXPANDED_AUTHOR_EL_HEIGHT + 10) /*10=margin*/
@@ -692,59 +769,78 @@ function ExpandedAuthorsList({
     <Animated.View style={[a.overflow_hidden, heightStyle]}>
       {visible &&
         authors.map(author => (
-          <Link
-            key={author.profile.did}
-            label={author.profile.displayName || author.profile.handle}
-            accessibilityHint={_(msg`Opens this profile`)}
-            to={makeProfileLink({
-              did: author.profile.did,
-              handle: author.profile.handle,
-            })}
-            style={styles.expandedAuthor}>
-            <View style={[a.mr_sm]}>
-              <ProfileHoverCard did={author.profile.did}>
-                <UserAvatar
-                  size={35}
-                  avatar={author.profile.avatar}
-                  moderation={author.moderation.ui('avatar')}
-                  type={author.profile.associated?.labeler ? 'labeler' : 'user'}
-                />
-              </ProfileHoverCard>
-            </View>
-            <View style={[a.flex_1]}>
-              <View style={[a.flex_row, a.align_end]}>
-                <Text
-                  numberOfLines={1}
-                  emoji
-                  style={[
-                    a.text_md,
-                    a.font_bold,
-                    a.leading_tight,
-                    {maxWidth: '70%'},
-                  ]}>
-                  {sanitizeDisplayName(
-                    author.profile.displayName || author.profile.handle,
-                  )}
-                </Text>
-                <Text
-                  numberOfLines={1}
-                  style={[
-                    a.pl_xs,
-                    a.text_md,
-                    a.leading_tight,
-                    a.flex_shrink,
-                    t.atoms.text_contrast_medium,
-                  ]}>
-                  {sanitizeHandle(author.profile.handle, '@')}
-                </Text>
-              </View>
-            </View>
-          </Link>
+          <ExpandedAuthorCard key={author.profile.did} author={author} />
         ))}
     </Animated.View>
   )
 }
 
+function ExpandedAuthorCard({author}: {author: Author}) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const verification = useSimpleVerificationState({
+    profile: author.profile,
+  })
+  return (
+    <Link
+      key={author.profile.did}
+      label={author.profile.displayName || author.profile.handle}
+      accessibilityHint={_(msg`Opens this profile`)}
+      to={makeProfileLink({
+        did: author.profile.did,
+        handle: author.profile.handle,
+      })}
+      style={styles.expandedAuthor}>
+      <View style={[a.mr_sm]}>
+        <ProfileHoverCard did={author.profile.did}>
+          <UserAvatar
+            size={35}
+            avatar={author.profile.avatar}
+            moderation={author.moderation.ui('avatar')}
+            type={author.profile.associated?.labeler ? 'labeler' : 'user'}
+          />
+        </ProfileHoverCard>
+      </View>
+      <View style={[a.flex_1]}>
+        <View style={[a.flex_row, a.align_end]}>
+          <Text
+            numberOfLines={1}
+            emoji
+            style={[
+              a.text_md,
+              a.font_bold,
+              a.leading_tight,
+              {maxWidth: '70%'},
+            ]}>
+            {sanitizeDisplayName(
+              author.profile.displayName || author.profile.handle,
+            )}
+          </Text>
+          {verification.showBadge && (
+            <View style={[a.pl_xs, a.self_center]}>
+              <VerificationCheck
+                width={14}
+                verifier={verification.role === 'verifier'}
+              />
+            </View>
+          )}
+          <Text
+            numberOfLines={1}
+            style={[
+              a.pl_xs,
+              a.text_md,
+              a.leading_tight,
+              a.flex_shrink,
+              t.atoms.text_contrast_medium,
+            ]}>
+            {sanitizeHandle(author.profile.handle, '@')}
+          </Text>
+        </View>
+      </View>
+    </Link>
+  )
+}
+
 function AdditionalPostText({post}: {post?: AppBskyFeedDefs.PostView}) {
   const t = useTheme()
   if (
@@ -761,7 +857,7 @@ function AdditionalPostText({post}: {post?: AppBskyFeedDefs.PostView}) {
         {text?.length > 0 && (
           <Text
             emoji
-            style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
+            style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
             {text}
           </Text>
         )}