Merge pull request #5485 from urbit/lf/recency-improvements

landscape: better leap searching
This commit is contained in:
Liam Fitzgerald 2022-01-10 11:10:51 -06:00 committed by GitHub
commit f1f57abec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 511 additions and 31980 deletions

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
"css-loader": "^3.6.0",
"file-saver": "^2.0.5",
"formik": "^2.1.5",
"fuzzy": "^0.1.3",
"immer": "^9.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.1",

View File

@ -1,6 +1,7 @@
import { Box, Row, Text } from '@tlon/indigo-react';
import { omit } from 'lodash';
import Mousetrap from 'mousetrap';
import fuzzy from 'fuzzy';
import _ from 'lodash';
import f from 'lodash/fp';
import React, {
@ -44,6 +45,19 @@ const SEARCHED_CATEGORIES = [
const settingsSel = (s: SettingsState) => s.leap;
const CAT_LIMIT = 6;
/**
* Flatten `catMap` according to ordering in `cats`
*/
function flattenCattegoryMap(cats: string[], catMap: Map<string, OmniboxItem[]>) {
let res = [] as OmniboxItem[];
cats.forEach(cat => {
res = res.concat(_.take(catMap.get(cat), CAT_LIMIT));
});
return res;
}
export function Omnibox(props: OmniboxProps): ReactElement {
const location = useLocation();
const history = useHistory();
@ -124,29 +138,28 @@ export function Omnibox(props: OmniboxProps): ReactElement {
);
}, [index]);
const results = useMemo(() => {
const [results, categoryOrder] = useMemo(
(): [Map<string, OmniboxItem[]>, string[]] => {
if (query.length <= 1) {
return initialResults;
return [initialResults, ['other']];
}
const q = query.toLowerCase();
const resultsMap = new Map<string, OmniboxItem[]>();
let categoryMaxes: Record<string, number> = {};
SEARCHED_CATEGORIES.map((category) => {
const categoryIndex = index.get(category);
resultsMap.set(
category,
categoryIndex.filter((result) => {
return (
result.title.toLowerCase().includes(q) ||
result.link.toLowerCase().includes(q) ||
result.app.toLowerCase().includes(q) ||
(result.host !== null
? result.host.toLowerCase().includes(q)
: false)
);
})
);
const fuzzied = fuzzy
.filter(q, categoryIndex, { extract: res => res.title });
categoryMaxes[category] = fuzzied
.map(a => a.score)
.reduce((a,b) => Math.max(a,b), 0);
resultsMap.set(category, fuzzied.map(a => a.original));
});
return resultsMap;
let order = Object.entries(categoryMaxes)
.sort(([,a],[,b]) => b - a)
.map(([id]) => id);
return [resultsMap, order];
}, [query, index]);
const navigate = useCallback(
@ -181,7 +194,7 @@ export function Omnibox(props: OmniboxProps): ReactElement {
);
const setPreviousSelected = useCallback(() => {
const flattenedResults = Array.from(results.values()).map(f.take(CAT_LIMIT)).flat();
const flattenedResults = flattenCattegoryMap(categoryOrder, results);
const totalLength = flattenedResults.length;
if (selected.length) {
const currentIndex = flattenedResults.indexOf(
@ -201,10 +214,10 @@ export function Omnibox(props: OmniboxProps): ReactElement {
const { app, link } = flattenedResults[totalLength - 1];
setSelected([app, link]);
}
}, [results, selected]);
}, [results, categoryOrder, selected]);
const setNextSelected = useCallback(() => {
const flattenedResults = Array.from(results.values()).map(f.take(CAT_LIMIT)).flat();
const flattenedResults = flattenCattegoryMap(categoryOrder, results);
if (selected.length) {
const currentIndex = flattenedResults.indexOf(
// @ts-ignore unclear how to give this spread a return signature
@ -223,7 +236,7 @@ export function Omnibox(props: OmniboxProps): ReactElement {
const { app, link } = flattenedResults[0];
setSelected([app, link]);
}
}, [selected, results]);
}, [results, categoryOrder, selected]);
const setSelection = (app, link) => {
setLeapCursor('pointer');
@ -255,14 +268,15 @@ export function Omnibox(props: OmniboxProps): ReactElement {
}
if (evt.key === 'Enter') {
evt.preventDefault();
let values = flattenCattegoryMap(categoryOrder, results);
if (selected.length) {
navigate(selected[0], selected[1], evt.shiftKey);
} else if (Array.from(results.values()).flat().length === 0) {
} else if (values.length === 0) {
return;
} else {
navigate(
Array.from(results.values()).flat()[0].app,
Array.from(results.values()).flat()[0].link,
values[0].app,
values[0].link,
evt.shiftKey
);
}
@ -275,15 +289,16 @@ export function Omnibox(props: OmniboxProps): ReactElement {
query,
props.show,
results,
categoryOrder,
setPreviousSelected,
setNextSelected
]
);
useEffect(() => {
const flattenedResultLinks: [string, string][] = Array.from(results.values())
.flat()
.map(result => [result.app, result.link]);
const flattenedResultLinks: [string, string][] =
flattenCattegoryMap(categoryOrder, results)
.map(result => [result.app, result.link]);
if (!flattenedResultLinks.includes(selected as [string, string])) {
setSelected(flattenedResultLinks[0] || []);
}
@ -319,10 +334,10 @@ export function Omnibox(props: OmniboxProps): ReactElement {
borderBottomLeftRadius={2}
borderBottomRightRadius={2}
>
{SEARCHED_CATEGORIES.map(category =>
{categoryOrder.map(category =>
({
category,
categoryResults: _.take(results.get(category).sort(sortResults), CAT_LIMIT)
categoryResults: _.take(results.get(category), CAT_LIMIT)
})
)
.filter(category => category.categoryResults.length > 0)