forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useMemo} from 'react'
2import {msg, Trans} from '@lingui/macro'
3import {useLingui} from '@lingui/react'
4
5import {languageName} from '#/locale/helpers'
6import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages'
7import {useLanguagePrefs} from '#/state/preferences'
8import {atoms as a, native, platform, tokens} from '#/alf'
9import {Button, ButtonIcon, ButtonText} from '#/components/Button'
10import {
11 ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
12 ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon,
13} from '#/components/icons/Chevron'
14import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe'
15import * as Menu from '#/components/Menu'
16
17export function SearchLanguageDropdown({
18 value,
19 onChange,
20}: {
21 value: string
22 onChange(value: string): void
23}) {
24 const {_} = useLingui()
25 const {appLanguage, contentLanguages} = useLanguagePrefs()
26
27 const languages = useMemo(() => {
28 return LANGUAGES.filter(
29 (lang, index, self) =>
30 Boolean(lang.code2) && // reduce to the code2 varieties
31 index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
32 )
33 .map(l => ({
34 label: languageName(l, appLanguage),
35 value: l.code2,
36 key: l.code2 + l.code3,
37 }))
38 .sort((a, b) => {
39 // prioritize user's languages
40 const aIsUser = contentLanguages.includes(a.value)
41 const bIsUser = contentLanguages.includes(b.value)
42 if (aIsUser && !bIsUser) return -1
43 if (bIsUser && !aIsUser) return 1
44 // prioritize "common" langs in the network
45 const aIsCommon = !!APP_LANGUAGES.find(
46 al =>
47 // skip `ast`, because it uses a 3-letter code which conflicts with `as`
48 // it begins with `a` anyway so still is top of the list
49 al.code2 !== 'ast' && al.code2.startsWith(a.value),
50 )
51 const bIsCommon = !!APP_LANGUAGES.find(
52 al =>
53 // ditto
54 al.code2 !== 'ast' && al.code2.startsWith(b.value),
55 )
56 if (aIsCommon && !bIsCommon) return -1
57 if (bIsCommon && !aIsCommon) return 1
58 // fall back to alphabetical
59 return a.label.localeCompare(b.label)
60 })
61 }, [appLanguage, contentLanguages])
62
63 const currentLanguageLabel =
64 languages.find(lang => lang.value === value)?.label ?? _(msg`All languages`)
65
66 return (
67 <Menu.Root>
68 <Menu.Trigger
69 label={_(
70 msg`Filter search by language (currently: ${currentLanguageLabel})`,
71 )}>
72 {({props}) => (
73 <Button
74 {...props}
75 label={props.accessibilityLabel}
76 size="small"
77 color={platform({native: 'primary', default: 'secondary'})}
78 variant={platform({native: 'ghost', default: 'solid'})}
79 style={native([
80 a.py_sm,
81 a.px_sm,
82 {marginRight: tokens.space.sm * -1},
83 ])}>
84 <ButtonIcon icon={EarthIcon} />
85 <ButtonText>{currentLanguageLabel}</ButtonText>
86 <ButtonIcon
87 icon={platform({
88 native: ChevronUpDownIcon,
89 default: ChevronDownIcon,
90 })}
91 />
92 </Button>
93 )}
94 </Menu.Trigger>
95 <Menu.Outer>
96 <Menu.LabelText>
97 <Trans>Filter search by language</Trans>
98 </Menu.LabelText>
99 <Menu.Item label={_(msg`All languages`)} onPress={() => onChange('')}>
100 <Menu.ItemText>
101 <Trans>All languages</Trans>
102 </Menu.ItemText>
103 <Menu.ItemRadio selected={value === ''} />
104 </Menu.Item>
105 <Menu.Divider />
106 <Menu.Group>
107 {languages.map(lang => (
108 <Menu.Item
109 key={lang.key}
110 label={lang.label}
111 onPress={() => onChange(lang.value)}>
112 <Menu.ItemText>{lang.label}</Menu.ItemText>
113 <Menu.ItemRadio selected={value === lang.value} />
114 </Menu.Item>
115 ))}
116 </Menu.Group>
117 </Menu.Outer>
118 </Menu.Root>
119 )
120}