about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-08-29 03:03:12 +0300
committerGitHub <noreply@github.com>2025-08-28 17:03:12 -0700
commit8a4398608acac5f44e8d3c0ea8f6976b8ae1119a (patch)
tree7e7f75a11d12403efd4069ba00c4e362254a5d1a
parent27c105856868da9c25a0e8732ff625a602967287 (diff)
downloadvoidsky-8a4398608acac5f44e8d3c0ea8f6976b8ae1119a.tar.zst
Close web mention suggestions popup on `Escape` (#8605)
* alf web typeahead

* fix type error

* fix escape behaviour

* change selection on hover

* rm React.

* undo random change
-rw-r--r--src/view/com/composer/Composer.tsx10
-rw-r--r--src/view/com/composer/text-input/TextInput.tsx57
-rw-r--r--src/view/com/composer/text-input/TextInput.types.ts42
-rw-r--r--src/view/com/composer/text-input/TextInput.web.tsx104
-rw-r--r--src/view/com/composer/text-input/web/Autocomplete.tsx313
5 files changed, 253 insertions, 273 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index b533510ec..20f2549ad 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -114,10 +114,7 @@ import {SelectPostLanguagesBtn} from '#/view/com/composer/select-language/Select
 import {SuggestedLanguage} from '#/view/com/composer/select-language/SuggestedLanguage'
 // TODO: Prevent naming components that coincide with RN primitives
 // due to linting false positives
-import {
-  TextInput,
-  type TextInputRef,
-} from '#/view/com/composer/text-input/TextInput'
+import {TextInput} from '#/view/com/composer/text-input/TextInput'
 import {ThreadgateBtn} from '#/view/com/composer/threadgate/ThreadgateBtn'
 import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog'
 import {VideoPreview} from '#/view/com/composer/videos/VideoPreview'
@@ -155,6 +152,7 @@ import {
   processVideo,
   type VideoState,
 } from './state/video'
+import {type TextInputRef} from './text-input/TextInput.types'
 import {getVideoMetadata} from './videos/pickVideo'
 import {clearThumbnailCache} from './videos/VideoTranscodeBackdrop'
 
@@ -306,7 +304,9 @@ export const ComposePost = ({
   )
 
   const onPressCancel = useCallback(() => {
-    if (
+    if (textInput.current?.maybeClosePopup()) {
+      return
+    } else if (
       thread.posts.some(
         post =>
           post.shortenedGraphemeLength > 0 ||
diff --git a/src/view/com/composer/text-input/TextInput.tsx b/src/view/com/composer/text-input/TextInput.tsx
index ea92d0b91..8b3e61b0e 100644
--- a/src/view/com/composer/text-input/TextInput.tsx
+++ b/src/view/com/composer/text-input/TextInput.tsx
@@ -1,7 +1,6 @@
-import React, {
-  type ComponentProps,
-  forwardRef,
+import {
   useCallback,
+  useImperativeHandle,
   useMemo,
   useRef,
   useState,
@@ -9,7 +8,6 @@ import React, {
 import {
   type NativeSyntheticEvent,
   Text as RNText,
-  type TextInput as RNTextInput,
   type TextInputSelectionChangeEventData,
   View,
 } from 'react-native'
@@ -33,57 +31,38 @@ import {
 import {atoms as a, useAlf} from '#/alf'
 import {normalizeTextStyles} from '#/alf/typography'
 import {Autocomplete} from './mobile/Autocomplete'
-
-export interface TextInputRef {
-  focus: () => void
-  blur: () => void
-  getCursorPosition: () => DOMRect | undefined
-}
-
-interface TextInputProps extends ComponentProps<typeof RNTextInput> {
-  richtext: RichText
-  placeholder: string
-  webForceMinHeight: boolean
-  hasRightPadding: boolean
-  isActive: boolean
-  setRichText: (v: RichText) => void
-  onPhotoPasted: (uri: string) => void
-  onPressPublish: (richtext: RichText) => void
-  onNewLink: (uri: string) => void
-  onError: (err: string) => void
-}
+import {type TextInputProps} from './TextInput.types'
 
 interface Selection {
   start: number
   end: number
 }
 
-export const TextInput = forwardRef(function TextInputImpl(
-  {
-    richtext,
-    placeholder,
-    hasRightPadding,
-    setRichText,
-    onPhotoPasted,
-    onNewLink,
-    onError,
-    ...props
-  }: TextInputProps,
+export function TextInput({
   ref,
-) {
+  richtext,
+  placeholder,
+  hasRightPadding,
+  setRichText,
+  onPhotoPasted,
+  onNewLink,
+  onError,
+  ...props
+}: TextInputProps) {
   const {theme: t, fonts} = useAlf()
   const textInput = useRef<PasteInputRef>(null)
   const textInputSelection = useRef<Selection>({start: 0, end: 0})
   const theme = useTheme()
   const [autocompletePrefix, setAutocompletePrefix] = useState('')
-  const prevLength = React.useRef(richtext.length)
+  const prevLength = useRef(richtext.length)
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     focus: () => textInput.current?.focus(),
     blur: () => {
       textInput.current?.blur()
     },
     getCursorPosition: () => undefined, // Not implemented on native
+    maybeClosePopup: () => false, // Not needed on native
   }))
 
   const pastSuggestedUris = useRef(new Set<string>())
@@ -185,7 +164,7 @@ export const TextInput = forwardRef(function TextInputImpl(
     [onChangeText, richtext, setAutocompletePrefix],
   )
 
-  const inputTextStyle = React.useMemo(() => {
+  const inputTextStyle = useMemo(() => {
     const style = normalizeTextStyles(
       [a.text_lg, a.leading_snug, t.atoms.text],
       {
@@ -277,4 +256,4 @@ export const TextInput = forwardRef(function TextInputImpl(
       />
     </View>
   )
-})
+}
diff --git a/src/view/com/composer/text-input/TextInput.types.ts b/src/view/com/composer/text-input/TextInput.types.ts
new file mode 100644
index 000000000..fab2bc32f
--- /dev/null
+++ b/src/view/com/composer/text-input/TextInput.types.ts
@@ -0,0 +1,42 @@
+import {type TextInput} from 'react-native'
+import {type RichText} from '@atproto/api'
+
+export type TextInputRef = {
+  focus: () => void
+  blur: () => void
+  /**
+   * @platform web
+   */
+  getCursorPosition: () =>
+    | {left: number; right: number; top: number; bottom: number}
+    | undefined
+  /**
+   * Closes the autocomplete popup if it is open.
+   * Returns `true` if the popup was closed, `false` otherwise.
+   *
+   * @platform web
+   */
+  maybeClosePopup: () => boolean
+}
+
+export type TextInputProps = {
+  ref: React.Ref<TextInputRef>
+  richtext: RichText
+  webForceMinHeight: boolean
+  hasRightPadding: boolean
+  isActive: boolean
+  setRichText: (v: RichText) => void
+  onPhotoPasted: (uri: string) => void
+  onPressPublish: (richtext: RichText) => void
+  onNewLink: (uri: string) => void
+  onError: (err: string) => void
+  onFocus: () => void
+} & Pick<
+  React.ComponentProps<typeof TextInput>,
+  | 'placeholder'
+  | 'autoFocus'
+  | 'style'
+  | 'accessible'
+  | 'accessibilityLabel'
+  | 'accessibilityHint'
+>
diff --git a/src/view/com/composer/text-input/TextInput.web.tsx b/src/view/com/composer/text-input/TextInput.web.tsx
index cb7ed194a..9f6cc6ae2 100644
--- a/src/view/com/composer/text-input/TextInput.web.tsx
+++ b/src/view/com/composer/text-input/TextInput.web.tsx
@@ -1,4 +1,11 @@
-import React, {useRef} from 'react'
+import {
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
 import {StyleSheet, View} from 'react-native'
 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
 import {AppBskyRichtextFacet, RichText} from '@atproto/api'
@@ -16,7 +23,6 @@ import {EditorContent, type JSONContent, useEditor} from '@tiptap/react'
 import Graphemer from 'graphemer'
 
 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
-import {usePalette} from '#/lib/hooks/usePalette'
 import {blobToDataUri, isUriImage} from '#/lib/media/util'
 import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
 import {
@@ -27,57 +33,34 @@ import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEm
 import {atoms as a, useAlf} from '#/alf'
 import {normalizeTextStyles} from '#/alf/typography'
 import {Portal} from '#/components/Portal'
-import {Text} from '../../util/text/Text'
-import {createSuggestion} from './web/Autocomplete'
+import {Text} from '#/components/Typography'
+import {type TextInputProps} from './TextInput.types'
+import {type AutocompleteRef, createSuggestion} from './web/Autocomplete'
 import {type Emoji} from './web/EmojiPicker'
 import {LinkDecorator} from './web/LinkDecorator'
 import {TagDecorator} from './web/TagDecorator'
 
-export interface TextInputRef {
-  focus: () => void
-  blur: () => void
-  getCursorPosition: () => DOMRect | undefined
-}
-
-interface TextInputProps {
-  richtext: RichText
-  placeholder: string
-  suggestedLinks: Set<string>
-  webForceMinHeight: boolean
-  hasRightPadding: boolean
-  isActive: boolean
-  setRichText: (v: RichText | ((v: RichText) => RichText)) => void
-  onPhotoPasted: (uri: string) => void
-  onPressPublish: (richtext: RichText) => void
-  onNewLink: (uri: string) => void
-  onError: (err: string) => void
-  onFocus: () => void
-}
-
-export const TextInput = React.forwardRef(function TextInputImpl(
-  {
-    richtext,
-    placeholder,
-    webForceMinHeight,
-    hasRightPadding,
-    isActive,
-    setRichText,
-    onPhotoPasted,
-    onPressPublish,
-    onNewLink,
-    onFocus,
-  }: // onError, TODO
-  TextInputProps,
+export function TextInput({
   ref,
-) {
+  richtext,
+  placeholder,
+  webForceMinHeight,
+  hasRightPadding,
+  isActive,
+  setRichText,
+  onPhotoPasted,
+  onPressPublish,
+  onNewLink,
+  onFocus,
+}: TextInputProps) {
   const {theme: t, fonts} = useAlf()
   const autocomplete = useActorAutocompleteFn()
-  const pal = usePalette('default')
   const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
 
-  const [isDropping, setIsDropping] = React.useState(false)
+  const [isDropping, setIsDropping] = useState(false)
+  const autocompleteRef = useRef<AutocompleteRef>(null)
 
-  const extensions = React.useMemo(
+  const extensions = useMemo(
     () => [
       Document,
       LinkDecorator,
@@ -86,7 +69,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
         HTMLAttributes: {
           class: 'mention',
         },
-        suggestion: createSuggestion({autocomplete}),
+        suggestion: createSuggestion({autocomplete, autocompleteRef}),
       }),
       Paragraph,
       Placeholder.configure({
@@ -99,7 +82,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
     [autocomplete, placeholder],
   )
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (!isActive) {
       return
     }
@@ -109,7 +92,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
     }
   }, [onPressPublish, isActive])
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (!isActive) {
       return
     }
@@ -119,7 +102,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
     }
   }, [isActive, onPhotoPasted])
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (!isActive) {
       return
     }
@@ -296,13 +279,13 @@ export const TextInput = React.forwardRef(function TextInputImpl(
     [modeClass],
   )
 
-  const onEmojiInserted = React.useCallback(
+  const onEmojiInserted = useCallback(
     (emoji: Emoji) => {
       editor?.chain().focus().insertContent(emoji.native).run()
     },
     [editor],
   )
-  React.useEffect(() => {
+  useEffect(() => {
     if (!isActive) {
       return
     }
@@ -312,7 +295,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
     }
   }, [onEmojiInserted, isActive])
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     focus: () => {
       editor?.chain().focus()
     },
@@ -323,9 +306,10 @@ export const TextInput = React.forwardRef(function TextInputImpl(
       const pos = editor?.state.selection.$anchor.pos
       return pos ? editor?.view.coordsAtPos(pos) : undefined
     },
+    maybeClosePopup: () => autocompleteRef.current?.maybeClose() ?? false,
   }))
 
-  const inputStyle = React.useMemo(() => {
+  const inputStyle = useMemo(() => {
     const style = normalizeTextStyles(
       [a.text_lg, a.leading_snug, t.atoms.text],
       {
@@ -360,10 +344,20 @@ export const TextInput = React.forwardRef(function TextInputImpl(
             style={styles.dropContainer}
             entering={FadeIn.duration(80)}
             exiting={FadeOut.duration(80)}>
-            <View style={[pal.view, pal.border, styles.dropModal]}>
+            <View
+              style={[
+                t.atoms.bg,
+                t.atoms.border_contrast_low,
+                styles.dropModal,
+              ]}>
               <Text
-                type="lg"
-                style={[pal.text, pal.borderDark, styles.dropText]}>
+                style={[
+                  a.text_lg,
+                  a.font_bold,
+                  t.atoms.text_contrast_medium,
+                  t.atoms.border_contrast_high,
+                  styles.dropText,
+                ]}>
                 <Trans>Drop to add images</Trans>
               </Text>
             </View>
@@ -372,7 +366,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
       )}
     </>
   )
-})
+}
 
 function editorJsonToText(
   json: JSONContent,
diff --git a/src/view/com/composer/text-input/web/Autocomplete.tsx b/src/view/com/composer/text-input/web/Autocomplete.tsx
index 94ecb53cc..1a95736c3 100644
--- a/src/view/com/composer/text-input/web/Autocomplete.tsx
+++ b/src/view/com/composer/text-input/web/Autocomplete.tsx
@@ -1,6 +1,6 @@
 import {forwardRef, useEffect, useImperativeHandle, useState} from 'react'
-import {Pressable, StyleSheet, View} from 'react-native'
-import {type AppBskyActorDefs} from '@atproto/api'
+import {Pressable, View} from 'react-native'
+import {type AppBskyActorDefs, type ModerationOpts} from '@atproto/api'
 import {Trans} from '@lingui/macro'
 import {ReactRenderer} from '@tiptap/react'
 import {
@@ -10,25 +10,26 @@ import {
 } from '@tiptap/suggestion'
 import tippy, {type Instance as TippyInstance} from 'tippy.js'
 
-import {usePalette} from '#/lib/hooks/usePalette'
-import {sanitizeDisplayName} from '#/lib/strings/display-names'
-import {sanitizeHandle} from '#/lib/strings/handles'
+import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {type ActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
-import {Text} from '#/view/com/util/text/Text'
-import {UserAvatar} from '#/view/com/util/UserAvatar'
-import {atoms as a} from '#/alf'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
-import {useGrapheme} from '../hooks/useGrapheme'
+import {atoms as a, useTheme} from '#/alf'
+import * as ProfileCard from '#/components/ProfileCard'
+import {Text} from '#/components/Typography'
 
 interface MentionListRef {
   onKeyDown: (props: SuggestionKeyDownProps) => boolean
 }
 
+export interface AutocompleteRef {
+  maybeClose: () => boolean
+}
+
 export function createSuggestion({
   autocomplete,
+  autocompleteRef,
 }: {
   autocomplete: ActorAutocompleteFn
+  autocompleteRef: React.Ref<AutocompleteRef>
 }): Omit<SuggestionOptions, 'editor'> {
   return {
     async items({query}) {
@@ -40,10 +41,15 @@ export function createSuggestion({
       let component: ReactRenderer<MentionListRef> | undefined
       let popup: TippyInstance[] | undefined
 
+      const hide = () => {
+        popup?.[0]?.destroy()
+        component?.destroy()
+      }
+
       return {
         onStart: props => {
           component = new ReactRenderer(MentionList, {
-            props,
+            props: {...props, autocompleteRef, hide},
             editor: props.editor,
           })
 
@@ -78,204 +84,163 @@ export function createSuggestion({
 
         onKeyDown(props) {
           if (props.event.key === 'Escape') {
-            popup?.[0]?.hide()
-
-            return true
+            return false
           }
 
           return component?.ref?.onKeyDown(props) || false
         },
 
         onExit() {
-          popup?.[0]?.destroy()
-          component?.destroy()
+          hide()
         },
       }
     },
   }
 }
 
-const MentionList = forwardRef<MentionListRef, SuggestionProps>(
-  function MentionListImpl(props: SuggestionProps, ref) {
-    const [selectedIndex, setSelectedIndex] = useState(0)
-    const pal = usePalette('default')
+const MentionList = forwardRef<
+  MentionListRef,
+  SuggestionProps & {
+    autocompleteRef: React.Ref<AutocompleteRef>
+    hide: () => void
+  }
+>(function MentionListImpl({items, command, hide, autocompleteRef}, ref) {
+  const [selectedIndex, setSelectedIndex] = useState(0)
+  const t = useTheme()
+  const moderationOpts = useModerationOpts()
 
-    const selectItem = (index: number) => {
-      const item = props.items[index]
+  const selectItem = (index: number) => {
+    const item = items[index]
 
-      if (item) {
-        props.command({id: item.handle})
-      }
+    if (item) {
+      command({id: item.handle})
     }
+  }
 
-    const upHandler = () => {
-      setSelectedIndex(
-        (selectedIndex + props.items.length - 1) % props.items.length,
-      )
-    }
+  const upHandler = () => {
+    setSelectedIndex((selectedIndex + items.length - 1) % items.length)
+  }
 
-    const downHandler = () => {
-      setSelectedIndex((selectedIndex + 1) % props.items.length)
-    }
+  const downHandler = () => {
+    setSelectedIndex((selectedIndex + 1) % items.length)
+  }
 
-    const enterHandler = () => {
-      selectItem(selectedIndex)
-    }
+  const enterHandler = () => {
+    selectItem(selectedIndex)
+  }
+
+  useEffect(() => setSelectedIndex(0), [items])
+
+  useImperativeHandle(autocompleteRef, () => ({
+    maybeClose: () => {
+      hide()
+      return true
+    },
+  }))
+
+  useImperativeHandle(ref, () => ({
+    onKeyDown: ({event}) => {
+      if (event.key === 'ArrowUp') {
+        upHandler()
+        return true
+      }
+
+      if (event.key === 'ArrowDown') {
+        downHandler()
+        return true
+      }
+
+      if (event.key === 'Enter' || event.key === 'Tab') {
+        enterHandler()
+        return true
+      }
+
+      return false
+    },
+  }))
 
-    useEffect(() => setSelectedIndex(0), [props.items])
-
-    useImperativeHandle(ref, () => ({
-      onKeyDown: ({event}) => {
-        if (event.key === 'ArrowUp') {
-          upHandler()
-          return true
-        }
-
-        if (event.key === 'ArrowDown') {
-          downHandler()
-          return true
-        }
-
-        if (event.key === 'Enter' || event.key === 'Tab') {
-          enterHandler()
-          return true
-        }
-
-        return false
-      },
-    }))
-
-    const {items} = props
-
-    return (
-      <div className="items">
-        <View style={[pal.borderDark, pal.view, styles.container]}>
-          {items.length > 0 ? (
-            items.map((item, index) => {
-              const isSelected = selectedIndex === index
-
-              return (
-                <AutocompleteProfileCard
-                  key={item.handle}
-                  profile={item}
-                  isSelected={isSelected}
-                  itemIndex={index}
-                  totalItems={items.length}
-                  onPress={() => {
-                    selectItem(index)
-                  }}
-                />
-              )
-            })
-          ) : (
-            <Text type="sm" style={[pal.text, styles.noResult]}>
-              <Trans>No result</Trans>
-            </Text>
-          )}
-        </View>
-      </div>
-    )
-  },
-)
+  if (!moderationOpts) return null
+
+  return (
+    <div className="items">
+      <View
+        style={[
+          t.atoms.border_contrast_low,
+          t.atoms.bg,
+          a.rounded_sm,
+          a.border,
+          a.p_xs,
+          {width: 300},
+        ]}>
+        {items.length > 0 ? (
+          items.map((item, index) => {
+            const isSelected = selectedIndex === index
+
+            return (
+              <AutocompleteProfileCard
+                key={item.handle}
+                profile={item}
+                isSelected={isSelected}
+                onPress={() => selectItem(index)}
+                onHover={() => setSelectedIndex(index)}
+                moderationOpts={moderationOpts}
+              />
+            )
+          })
+        ) : (
+          <Text style={[a.text_sm, a.px_md, a.py_md]}>
+            <Trans>No result</Trans>
+          </Text>
+        )}
+      </View>
+    </div>
+  )
+})
 
 function AutocompleteProfileCard({
   profile,
   isSelected,
-  itemIndex,
-  totalItems,
   onPress,
+  onHover,
+  moderationOpts,
 }: {
   profile: AppBskyActorDefs.ProfileViewBasic
   isSelected: boolean
-  itemIndex: number
-  totalItems: number
   onPress: () => void
+  onHover: () => void
+  moderationOpts: ModerationOpts
 }) {
-  const pal = usePalette('default')
-  const {getGraphemeString} = useGrapheme()
-  const {name: displayName} = getGraphemeString(
-    sanitizeDisplayName(profile.displayName || sanitizeHandle(profile.handle)),
-    30, // Heuristic value; can be modified
-  )
-  const state = useSimpleVerificationState({
-    profile,
-  })
+  const t = useTheme()
+
   return (
     <Pressable
       style={[
-        isSelected ? pal.viewLight : undefined,
-        pal.borderDark,
-        styles.mentionContainer,
-        itemIndex === 0
-          ? styles.firstMention
-          : itemIndex === totalItems - 1
-            ? styles.lastMention
-            : undefined,
+        isSelected && t.atoms.bg_contrast_25,
+        a.align_center,
+        a.justify_between,
+        a.flex_row,
+        a.px_md,
+        a.py_sm,
+        a.gap_2xl,
+        a.rounded_xs,
+        a.transition_color,
       ]}
       onPress={onPress}
+      onPointerEnter={onHover}
       accessibilityRole="button">
-      <View style={[styles.avatarAndDisplayName, a.flex_1]}>
-        <UserAvatar
-          avatar={profile.avatar ?? null}
-          size={26}
-          type={profile.associated?.labeler ? 'labeler' : 'user'}
-        />
-        <View style={[a.flex_row, a.align_center, a.gap_xs, a.flex_1]}>
-          <Text emoji style={[pal.text]} numberOfLines={1}>
-            {displayName}
-          </Text>
-          {state.isVerified && (
-            <View>
-              <VerificationCheck
-                width={12}
-                verifier={state.role === 'verifier'}
-              />
-            </View>
-          )}
-        </View>
-      </View>
-      <View>
-        <Text type="xs" style={pal.textLight} numberOfLines={1}>
-          {sanitizeHandle(profile.handle, '@')}
-        </Text>
+      <View style={[a.flex_1]}>
+        <ProfileCard.Header>
+          <ProfileCard.Avatar
+            profile={profile}
+            moderationOpts={moderationOpts}
+            disabledPreview
+          />
+          <ProfileCard.NameAndHandle
+            profile={profile}
+            moderationOpts={moderationOpts}
+          />
+        </ProfileCard.Header>
       </View>
     </Pressable>
   )
 }
-
-const styles = StyleSheet.create({
-  container: {
-    width: 500,
-    borderRadius: 6,
-    borderWidth: 1,
-    borderStyle: 'solid',
-    padding: 4,
-  },
-  mentionContainer: {
-    display: 'flex',
-    alignItems: 'center',
-    justifyContent: 'space-between',
-    flexDirection: 'row',
-    paddingHorizontal: 12,
-    paddingVertical: 8,
-    gap: 16,
-  },
-  firstMention: {
-    borderTopLeftRadius: 2,
-    borderTopRightRadius: 2,
-  },
-  lastMention: {
-    borderBottomLeftRadius: 2,
-    borderBottomRightRadius: 2,
-  },
-  avatarAndDisplayName: {
-    display: 'flex',
-    flexDirection: 'row',
-    alignItems: 'center',
-    gap: 6,
-  },
-  noResult: {
-    paddingHorizontal: 12,
-    paddingVertical: 8,
-  },
-})