Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add language filtering UI to search #5459

Merged
merged 14 commits into from
Sep 25, 2024
3 changes: 3 additions & 0 deletions src/alf/themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ export function createThemes({
} as const

const light: Theme = {
scheme: 'light',
name: 'light',
palette: lightPalette,
atoms: {
Expand Down Expand Up @@ -390,6 +391,7 @@ export function createThemes({
}

const dark: Theme = {
scheme: 'dark',
name: 'dark',
palette: darkPalette,
atoms: {
Expand Down Expand Up @@ -479,6 +481,7 @@ export function createThemes({

const dim: Theme = {
...dark,
scheme: 'dark',
name: 'dim',
palette: dimPalette,
atoms: {
Expand Down
1 change: 1 addition & 0 deletions src/alf/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export type ThemedAtoms = {
}
}
export type Theme = {
scheme: 'light' | 'dark' // for library support
name: ThemeName
palette: Palette
atoms: ThemedAtoms
Expand Down
23 changes: 16 additions & 7 deletions src/components/forms/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ export function createInput(Component: typeof TextInput) {
placeholder,
value,
onChangeText,
onFocus,
onBlur,
isInvalid,
inputRef,
style,
Expand Down Expand Up @@ -173,8 +175,14 @@ export function createInput(Component: typeof TextInput) {
ref={refs}
value={value}
onChangeText={onChangeText}
onFocus={ctx.onFocus}
onBlur={ctx.onBlur}
onFocus={e => {
ctx.onFocus()
onFocus?.(e)
}}
onBlur={e => {
ctx.onBlur()
onBlur?.(e)
}}
placeholder={placeholder || label}
placeholderTextColor={t.palette.contrast_500}
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
Expand All @@ -188,22 +196,23 @@ export function createInput(Component: typeof TextInput) {
a.px_xs,
{
// paddingVertical doesn't work w/multiline - esb
paddingTop: 14,
paddingBottom: 14,
paddingTop: 12,
paddingBottom: 13,
lineHeight: a.text_md.fontSize * 1.1875,
textAlignVertical: rest.multiline ? 'top' : undefined,
minHeight: rest.multiline ? 80 : undefined,
minWidth: 0,
},
// fix for autofill styles covering border
web({
paddingTop: 12,
paddingBottom: 12,
paddingTop: 10,
paddingBottom: 11,
marginTop: 2,
marginBottom: 2,
}),
android({
paddingBottom: 16,
paddingTop: 8,
paddingBottom: 8,
}),
style,
]}
Expand Down
43 changes: 43 additions & 0 deletions src/screens/Search/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {describe, expect, it} from '@jest/globals'

import {parseSearchQuery} from '#/screens/Search/utils'

describe(`parseSearchQuery`, () => {
const tests = [
{
input: `bluesky`,
output: {query: `bluesky`, params: {}},
},
{
input: `bluesky from:esb.lol`,
output: {query: `bluesky`, params: {from: `esb.lol`}},
},
{
input: `bluesky "from:esb.lol"`,
output: {query: `bluesky "from:esb.lol"`, params: {}},
},
{
input: `bluesky mentions:@esb.lol`,
output: {query: `bluesky`, params: {mentions: `@esb.lol`}},
},
{
input: `bluesky since:2021-01-01:00:00:00`,
output: {query: `bluesky`, params: {since: `2021-01-01:00:00:00`}},
},
{
input: `bluesky lang:"en"`,
output: {query: `bluesky`, params: {lang: `en`}},
},
{
input: `bluesky "literal" lang:en "from:invalid"`,
output: {query: `bluesky "literal" "from:invalid"`, params: {lang: `en`}},
},
]

it.each(tests)(
`$input -> $output.query $output.params`,
({input, output}) => {
expect(parseSearchQuery(input)).toEqual(output)
},
)
})
43 changes: 43 additions & 0 deletions src/screens/Search/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export type Params = Record<string, string>

export function parseSearchQuery(rawQuery: string) {
let base = rawQuery
const rawLiterals = rawQuery.match(/[^:\w\d]".+?"/gi) || []

// remove literals from base
for (const literal of rawLiterals) {
base = base.replace(literal.trim(), '')
}

// find remaining params in base
const rawParams = base.match(/[a-z]+:[a-z-\.@\d:"]+/gi) || []

for (const param of rawParams) {
base = base.replace(param, '')
}

base = base.trim()

const params = rawParams.reduce((params, param) => {
const [name, ...value] = param.split(/:/)
params[name] = value.join(':').replace(/"/g, '') // dates can contain additional colons
return params
}, {} as Params)
const literals = rawLiterals.map(l => String(l).trim())

return {
query: [base, literals.join(' ')].filter(Boolean).join(' '),
params,
}
}

export function makeSearchQuery(query: string, params: Params) {
return [
query,
Object.entries(params)
.map(([name, value]) => `${name}:${value}`)
.join(' '),
]
.filter(Boolean)
.join(' ')
}
Loading
Loading