about summary refs log tree commit diff
path: root/src/view/screens/Search/Search.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/screens/Search/Search.tsx')
-rw-r--r--src/view/screens/Search/Search.tsx221
1 files changed, 110 insertions, 111 deletions
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index 3e711ab56..f16b4fff2 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useLayoutEffect} from 'react'
+import React, {useCallback, useLayoutEffect, useMemo} from 'react'
 import {
   ActivityIndicator,
   Image,
@@ -10,7 +10,6 @@ import {
   View,
 } from 'react-native'
 import {ScrollView as RNGHScrollView} from 'react-native-gesture-handler'
-import RNPickerSelect from 'react-native-picker-select'
 import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api'
 import {
   FontAwesomeIcon,
@@ -57,12 +56,25 @@ import {Text} from '#/view/com/util/text/Text'
 import {Explore} from '#/view/screens/Search/Explore'
 import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
 import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
-import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
+import {
+  atoms as a,
+  native,
+  platform,
+  tokens,
+  useBreakpoints,
+  useTheme,
+  web,
+} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import * as FeedCard from '#/components/FeedCard'
 import {SearchInput} from '#/components/forms/SearchInput'
-import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
+import {
+  ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
+  ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon,
+} from '#/components/icons/Chevron'
+import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe'
 import * as Layout from '#/components/Layout'
+import * as Menu from '#/components/Menu'
 import {account, useStorage} from '#/storage'
 
 function Loader() {
@@ -315,103 +327,95 @@ function SearchLanguageDropdown({
   value: string
   onChange(value: string): void
 }) {
-  const t = useTheme()
   const {_} = useLingui()
   const {appLanguage, contentLanguages} = useLanguagePrefs()
 
-  const items = React.useMemo(() => {
-    return [
-      {
-        label: _(msg`Any language`),
-        inputLabel: _(msg`Any language`),
-        value: '',
-        key: '*',
-      },
-    ].concat(
-      LANGUAGES.filter(
-        (lang, index, self) =>
-          Boolean(lang.code2) && // reduce to the code2 varieties
-          index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
-      )
-        .map(l => ({
-          label: languageName(l, appLanguage),
-          inputLabel: languageName(l, appLanguage),
-          value: l.code2,
-          key: l.code2 + l.code3,
-        }))
-        .sort((a, b) => {
-          // prioritize user's languages
-          const aIsUser = contentLanguages.includes(a.value)
-          const bIsUser = contentLanguages.includes(b.value)
-          if (aIsUser && !bIsUser) return -1
-          if (bIsUser && !aIsUser) return 1
-          // prioritize "common" langs in the network
-          const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value)
-          const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value)
-          if (aIsCommon && !bIsCommon) return -1
-          if (bIsCommon && !aIsCommon) return 1
-          // fall back to alphabetical
-          return a.label.localeCompare(b.label)
-        }),
+  const languages = useMemo(() => {
+    return LANGUAGES.filter(
+      (lang, index, self) =>
+        Boolean(lang.code2) && // reduce to the code2 varieties
+        index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
     )
-  }, [_, appLanguage, contentLanguages])
-
-  const style = {
-    backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-    color: t.atoms.text.color,
-    fontSize: a.text_xs.fontSize,
-    fontFamily: 'inherit',
-    fontWeight: a.font_bold.fontWeight,
-    paddingHorizontal: 14,
-    paddingRight: 32,
-    paddingVertical: 8,
-    borderRadius: a.rounded_full.borderRadius,
-    borderWidth: a.border.borderWidth,
-    borderColor: t.atoms.border_contrast_low.borderColor,
-  }
+      .map(l => ({
+        label: languageName(l, appLanguage),
+        value: l.code2,
+        key: l.code2 + l.code3,
+      }))
+      .sort((a, b) => {
+        // prioritize user's languages
+        const aIsUser = contentLanguages.includes(a.value)
+        const bIsUser = contentLanguages.includes(b.value)
+        if (aIsUser && !bIsUser) return -1
+        if (bIsUser && !aIsUser) return 1
+        // prioritize "common" langs in the network
+        const aIsCommon = !!APP_LANGUAGES.find(al => al.code2 === a.value)
+        const bIsCommon = !!APP_LANGUAGES.find(al => al.code2 === b.value)
+        if (aIsCommon && !bIsCommon) return -1
+        if (bIsCommon && !aIsCommon) return 1
+        // fall back to alphabetical
+        return a.label.localeCompare(b.label)
+      })
+  }, [appLanguage, contentLanguages])
+
+  const currentLanguageLabel =
+    languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`)
 
   return (
-    <RNPickerSelect
-      darkTheme={t.scheme === 'dark'}
-      placeholder={{}}
-      value={value}
-      onValueChange={onChange}
-      items={items}
-      Icon={() => (
-        <ChevronDown fill={t.atoms.text_contrast_low.color} size="sm" />
-      )}
-      useNativeAndroidPickerStyle={false}
-      style={{
-        iconContainer: {
-          pointerEvents: 'none',
-          right: a.px_sm.paddingRight,
-          top: 0,
-          bottom: 0,
-          display: 'flex',
-          justifyContent: 'center',
-        },
-        inputAndroid: {
-          ...style,
-          paddingVertical: 2,
-        },
-        inputIOS: {
-          ...style,
-        },
-        inputWeb: web({
-          ...style,
-          cursor: 'pointer',
-          // @ts-ignore web only
-          '-moz-appearance': 'none',
-          '-webkit-appearance': 'none',
-          appearance: 'none',
-          outline: 0,
-          borderWidth: 0,
-          overflow: 'hidden',
-          whiteSpace: 'nowrap',
-          textOverflow: 'ellipsis',
-        }),
-      }}
-    />
+    <Menu.Root>
+      <Menu.Trigger
+        label={_(
+          msg`Filter search by language (currently: ${currentLanguageLabel})`,
+        )}>
+        {({props}) => (
+          <Button
+            {...props}
+            label={props.accessibilityLabel}
+            size="small"
+            color={platform({native: 'primary', default: 'secondary'})}
+            variant={platform({native: 'ghost', default: 'solid'})}
+            style={native([
+              a.py_sm,
+              a.px_sm,
+              {marginRight: tokens.space.sm * -1},
+            ])}>
+            <ButtonIcon icon={EarthIcon} />
+            <ButtonText>{currentLanguageLabel}</ButtonText>
+            <ButtonIcon
+              icon={platform({
+                native: ChevronUpDownIcon,
+                default: ChevronDownIcon,
+              })}
+            />
+          </Button>
+        )}
+      </Menu.Trigger>
+      <Menu.Outer
+        // HACKFIX: Currently there is no height limit for Radix dropdowns,
+        // so if it's too tall it just goes off screen. TODO: fix internally -sfn
+        style={web({maxHeight: '70vh'})}>
+        <Menu.LabelText>
+          <Trans>Filter search by language</Trans>
+        </Menu.LabelText>
+        <Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}>
+          <Menu.ItemText>
+            <Trans>All languages</Trans>
+          </Menu.ItemText>
+          <Menu.ItemRadio selected={value === ''} />
+        </Menu.Item>
+        <Menu.Divider />
+        <Menu.Group>
+          {languages.map(lang => (
+            <Menu.Item
+              key={lang.key}
+              label={lang.label}
+              onPress={() => onChange(lang.value)}>
+              <Menu.ItemText>{lang.label}</Menu.ItemText>
+              <Menu.ItemRadio selected={value === lang.value} />
+            </Menu.Item>
+          ))}
+        </Menu.Group>
+      </Menu.Outer>
+    </Menu.Root>
   )
 }
 
@@ -795,22 +799,19 @@ export function SearchScreen(
               // HACK: shift up search input. we can't remove the top padding
               // on the search input because it messes up the layout animation
               // if we add it only when the header is hidden
-              style={{marginBottom: tokens.space.sm * -1}}>
+              style={{marginBottom: tokens.space.xs * -1}}>
               <Layout.Header.Outer noBottomBorder>
                 <Layout.Header.MenuButton />
-                <Layout.Header.Content
-                  align={showFilters ? 'left' : 'platform'}>
+                <Layout.Header.Content align="left">
                   <Layout.Header.TitleText>
                     <Trans>Search</Trans>
                   </Layout.Header.TitleText>
                 </Layout.Header.Content>
                 {showFilters ? (
-                  <View style={[{minWidth: 140}]}>
-                    <SearchLanguageDropdown
-                      value={params.lang}
-                      onChange={params.setLang}
-                    />
-                  </View>
+                  <SearchLanguageDropdown
+                    value={params.lang}
+                    onChange={params.setLang}
+                  />
                 ) : (
                   <Layout.Header.Slot />
                 )}
@@ -856,12 +857,10 @@ export function SearchScreen(
                     a.justify_between,
                     a.gap_sm,
                   ]}>
-                  <View style={[{width: 140}]}>
-                    <SearchLanguageDropdown
-                      value={params.lang}
-                      onChange={params.setLang}
-                    />
-                  </View>
+                  <SearchLanguageDropdown
+                    value={params.lang}
+                    onChange={params.setLang}
+                  />
                 </View>
               )}
             </View>