about summary refs log tree commit diff
path: root/src/screens/Search/components/SearchHistory.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/screens/Search/components/SearchHistory.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/screens/Search/components/SearchHistory.tsx')
-rw-r--r--src/screens/Search/components/SearchHistory.tsx136
1 files changed, 94 insertions, 42 deletions
diff --git a/src/screens/Search/components/SearchHistory.tsx b/src/screens/Search/components/SearchHistory.tsx
index 5e62f2cd0..048203ed8 100644
--- a/src/screens/Search/components/SearchHistory.tsx
+++ b/src/screens/Search/components/SearchHistory.tsx
@@ -1,18 +1,23 @@
 import {Pressable, ScrollView, StyleSheet, View} from 'react-native'
+import {moderateProfile, type ModerationOpts} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {createHitslop, HITSLOP_10} from '#/lib/constants'
 import {makeProfileLink} from '#/lib/routes/links'
 import {sanitizeDisplayName} from '#/lib/strings/display-names'
+import {sanitizeHandle} from '#/lib/strings/handles'
+import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {Link} from '#/view/com/util/Link'
 import {UserAvatar} from '#/view/com/util/UserAvatar'
 import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
-import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf'
+import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf'
 import {Button, ButtonIcon} from '#/components/Button'
 import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
 import * as Layout from '#/components/Layout'
 import {Text} from '#/components/Typography'
+import {useSimpleVerificationState} from '#/components/verification'
+import {VerificationCheck} from '#/components/verification/VerificationCheck'
 import type * as bsky from '#/types/bsky'
 
 export function SearchHistory({
@@ -31,8 +36,8 @@ export function SearchHistory({
   onRemoveProfileClick: (profile: bsky.profile.AnyProfileView) => void
 }) {
   const {gtMobile} = useBreakpoints()
-  const t = useTheme()
   const {_} = useLingui()
+  const moderationOpts = useModerationOpts()
 
   return (
     <Layout.Content
@@ -54,53 +59,25 @@ export function SearchHistory({
               <ScrollView
                 horizontal
                 keyboardShouldPersistTaps="handled"
+                showsHorizontalScrollIndicator={false}
                 style={[
                   a.flex_row,
                   a.flex_nowrap,
                   {marginHorizontal: tokens.space._2xl * -1},
                 ]}
                 contentContainerStyle={[a.px_2xl, a.border_0]}>
-                {selectedProfiles.slice(0, 5).map((profile, index) => (
-                  <View
-                    key={index}
-                    style={[
-                      styles.profileItem,
-                      !gtMobile && styles.profileItemMobile,
-                    ]}>
-                    <Link
-                      href={makeProfileLink(profile)}
-                      title={profile.handle}
-                      asAnchor
-                      anchorNoUnderline
-                      onBeforePress={() => onProfileClick(profile)}
-                      style={[a.align_center, a.w_full]}>
-                      <UserAvatar
-                        avatar={profile.avatar}
-                        type={profile.associated?.labeler ? 'labeler' : 'user'}
-                        size={60}
+                {moderationOpts &&
+                  selectedProfiles
+                    .slice(0, 5)
+                    .map(profile => (
+                      <RecentProfileItem
+                        key={profile.did}
+                        profile={profile}
+                        moderationOpts={moderationOpts}
+                        onPress={() => onProfileClick(profile)}
+                        onRemove={() => onRemoveProfileClick(profile)}
                       />
-                      <Text
-                        emoji
-                        style={[a.text_xs, a.text_center, styles.profileName]}
-                        numberOfLines={1}>
-                        {sanitizeDisplayName(
-                          profile.displayName || profile.handle,
-                        )}
-                      </Text>
-                    </Link>
-                    <Pressable
-                      accessibilityRole="button"
-                      accessibilityLabel={_(msg`Remove profile`)}
-                      accessibilityHint={_(
-                        msg`Removes profile from search history`,
-                      )}
-                      onPress={() => onRemoveProfileClick(profile)}
-                      hitSlop={createHitslop(6)}
-                      style={styles.profileRemoveBtn}>
-                      <XIcon size="xs" style={t.atoms.text_contrast_low} />
-                    </Pressable>
-                  </View>
-                ))}
+                    ))}
               </ScrollView>
             </BlockDrawerGesture>
           </View>
@@ -134,6 +111,81 @@ export function SearchHistory({
   )
 }
 
+function RecentProfileItem({
+  profile,
+  moderationOpts,
+  onPress,
+  onRemove,
+}: {
+  profile: bsky.profile.AnyProfileView
+  moderationOpts: ModerationOpts
+  onPress: () => void
+  onRemove: () => void
+}) {
+  const {_} = useLingui()
+  const {gtMobile} = useBreakpoints()
+  const t = useTheme()
+
+  const moderation = moderateProfile(profile, moderationOpts)
+  const name = sanitizeDisplayName(
+    profile.displayName || sanitizeHandle(profile.handle),
+    moderation.ui('displayName'),
+  )
+  const verification = useSimpleVerificationState({profile})
+
+  return (
+    <View style={[styles.profileItem, !gtMobile && styles.profileItemMobile]}>
+      <Link
+        href={makeProfileLink(profile)}
+        title={profile.handle}
+        asAnchor
+        anchorNoUnderline
+        onBeforePress={onPress}
+        style={[a.align_center, a.w_full]}>
+        <UserAvatar
+          avatar={profile.avatar}
+          type={profile.associated?.labeler ? 'labeler' : 'user'}
+          size={60}
+          moderation={moderation.ui('avatar')}
+        />
+        <View style={styles.profileName}>
+          <View
+            style={[
+              a.flex_row,
+              a.align_center,
+              a.justify_center,
+              web([a.flex_1]),
+            ]}>
+            <Text
+              emoji
+              style={[a.text_xs, a.leading_snug, a.self_start]}
+              numberOfLines={1}>
+              {name}
+            </Text>
+            {verification.showBadge && (
+              <View style={[a.pl_xs]}>
+                <VerificationCheck
+                  width={12}
+                  verifier={verification.role === 'verifier'}
+                />
+              </View>
+            )}
+          </View>
+        </View>
+      </Link>
+      <Pressable
+        accessibilityRole="button"
+        accessibilityLabel={_(msg`Remove profile`)}
+        accessibilityHint={_(msg`Removes profile from search history`)}
+        hitSlop={createHitslop(6)}
+        style={styles.profileRemoveBtn}
+        onPress={onRemove}>
+        <XIcon size="xs" style={t.atoms.text_contrast_low} />
+      </Pressable>
+    </View>
+  )
+}
+
 const styles = StyleSheet.create({
   selectedProfilesContainer: {
     marginTop: 10,