show the exact inflection results seperate from the fuzzy inflection results
This commit is contained in:
parent
ffef1dd4bb
commit
229599860a
|
@ -115,8 +115,7 @@ class App extends Component<RouteComponentProps, State> {
|
|||
language: "Pashto",
|
||||
searchType: "fuzzy",
|
||||
searchBarStickyFocus: false,
|
||||
theme: /* istanbul ignore next */ (window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light",
|
||||
theme: (window.matchMedia?.("(prefers-color-scheme: dark)").matches) ? "dark" : "light",
|
||||
textOptionsRecord: {
|
||||
lastModified: Date.now() as AT.TimeStamp,
|
||||
textOptions: defaultTextOptions,
|
||||
|
@ -224,32 +223,50 @@ class App extends Component<RouteComponentProps, State> {
|
|||
// shortcuts to isolote word in search results
|
||||
Mousetrap.bind(["1", "2", "3", "4", "5", "6", "7", "8", "9"], (e) => {
|
||||
e.preventDefault();
|
||||
if (e.repeat) return;
|
||||
if (this.props.location.pathname !== "/search") return;
|
||||
if (e.repeat) {
|
||||
return;
|
||||
}
|
||||
if (this.props.location.pathname !== "/search") {
|
||||
return;
|
||||
}
|
||||
const toIsolate = this.state.results[Number(e.key) - 1];
|
||||
if (!toIsolate) return;
|
||||
if (!toIsolate) {
|
||||
return;
|
||||
}
|
||||
this.handleIsolateEntry(toIsolate.ts);
|
||||
})
|
||||
Mousetrap.bind(["ctrl+down", "ctrl+up", "command+down", "command+up"], (e) => {
|
||||
e.preventDefault();
|
||||
if (e.repeat) return;
|
||||
if (e.repeat) {
|
||||
return;
|
||||
}
|
||||
this.handleOptionsUpdate({ type: "toggleLanguage" });
|
||||
});
|
||||
Mousetrap.bind(["ctrl+b", "command+b"], (e) => {
|
||||
e.preventDefault();
|
||||
if (e.repeat) return;
|
||||
if (e.repeat) {
|
||||
return;
|
||||
}
|
||||
this.handleSearchValueChange("");
|
||||
});
|
||||
Mousetrap.bind(["ctrl+i", "command+i"], (e) => {
|
||||
e.preventDefault();
|
||||
if (e.repeat) return;
|
||||
if (!this.state.searchValue) return;
|
||||
if (e.repeat) {
|
||||
return;
|
||||
}
|
||||
if (!this.state.searchValue) {
|
||||
return;
|
||||
}
|
||||
this.handleInflectionSearch();
|
||||
});
|
||||
Mousetrap.bind(["ctrl+s", "command+s"], (e) => {
|
||||
if (this.state.user?.level === "basic") return;
|
||||
if (this.state.user?.level === "basic") {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
if (!this.state.isolatedEntry) return;
|
||||
if (!this.state.isolatedEntry) {
|
||||
return;
|
||||
}
|
||||
const toAdd = {
|
||||
entry: this.state.isolatedEntry,
|
||||
notes: "",
|
||||
|
@ -258,8 +275,12 @@ class App extends Component<RouteComponentProps, State> {
|
|||
});
|
||||
Mousetrap.bind(["ctrl+\\", "command+\\"], (e) => {
|
||||
e.preventDefault();
|
||||
if (e.repeat) return;
|
||||
if (this.state.user?.level === "basic") return;
|
||||
if (e.repeat) {
|
||||
return;
|
||||
}
|
||||
if (this.state.user?.level === "basic") {
|
||||
return;
|
||||
}
|
||||
if (this.props.location.pathname !== "/wordlist") {
|
||||
this.props.history.push("/wordlist");
|
||||
} else {
|
||||
|
@ -319,8 +340,12 @@ class App extends Component<RouteComponentProps, State> {
|
|||
try {
|
||||
const prevUser = this.state.user;
|
||||
const user = await getUser();
|
||||
if (user === "offline") return;
|
||||
if (user) sendSubmissions();
|
||||
if (user === "offline") {
|
||||
return;
|
||||
}
|
||||
if (user) {
|
||||
sendSubmissions();
|
||||
}
|
||||
if (!user) {
|
||||
if (this.state.user) {
|
||||
console.log("setting state user because user is newly undefined");
|
||||
|
@ -652,7 +677,7 @@ class App extends Component<RouteComponentProps, State> {
|
|||
state={this.state}
|
||||
optionsDispatch={this.handleOptionsUpdate}
|
||||
handleSearchValueChange={this.handleSearchValueChange}
|
||||
onBottom
|
||||
onBottom={true}
|
||||
/>}
|
||||
</footer>
|
||||
</div>;
|
||||
|
|
|
@ -46,7 +46,7 @@ function InflectionFormMatchDisplay(
|
|||
<div className="mb-2"><strong>{displayFormResult(form.path)}</strong></div>
|
||||
{form.matches.map((match, i) => <div className="ml-2" key={i}>
|
||||
<InlinePs opts={textOptions}>{match.ps}</InlinePs>
|
||||
<div className="ml-3 my-2">
|
||||
<div className="ml-4 my-2">
|
||||
<em>
|
||||
{(transitivity === "grammatically transitive" && isPast)
|
||||
? "Always 3rd pers. masc. plur."
|
||||
|
|
|
@ -51,7 +51,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
|
|||
data-testid="languageToggle"
|
||||
>
|
||||
<div aria-label={`language-choice-${language === "Pashto" ? "ps-to-en" : "en-to-ps"}`}>
|
||||
Ps <span className={`fa fa-arrow-${arrowDirection}`} ></span> En
|
||||
Ps <span className={`fa fa-arrow-${arrowDirection}`} /> En
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
@ -64,7 +64,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
|
|||
onClick={() => optionsDispatch({ type: "toggleSearchType" })}
|
||||
data-testid="searchTypeToggle"
|
||||
>
|
||||
<span className={`fa fa-${icon}`} ></span>
|
||||
<span className={`fa fa-${icon}`} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@ -73,7 +73,7 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
|
|||
? "Browse alphabetically"
|
||||
: `Search ${state.options.language === "Pashto" ? "Pashto" : "English"}`;
|
||||
return (
|
||||
<nav className={`navbar bg-light${!onBottom ? " fixed-top" : ""}`} style={{ zIndex: 50, width: "100%" }}>
|
||||
<nav className={`navbar bg-light${onBottom ? "" : " fixed-top"}`} style={{ zIndex: 50, width: "100%" }}>
|
||||
<div className="form-inline my-1 my-lg-1">
|
||||
<div className="input-group">
|
||||
<input
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { searchPile } from "../lib/search-pile";
|
||||
import {
|
||||
isNounAdjOrVerb, removeAccents,
|
||||
isNounAdjOrVerb, removeAccents, standardizePashto,
|
||||
} from "@lingdocs/ps-react";
|
||||
import { dictionary } from "../lib/dictionary";
|
||||
import {
|
||||
|
@ -26,7 +26,65 @@ const relevancySorter = new relevancy.Sorter();
|
|||
// That's so much better I'm removing the option of skipping compounds
|
||||
// ~4th iteration:~ ignore perfective or imperfective if wasn't present in verb info (not worth it - scrapped)
|
||||
|
||||
export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): InflectionSearchResult[] {
|
||||
export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue: string): {
|
||||
exact: InflectionSearchResult[],
|
||||
fuzzy: InflectionSearchResult[],
|
||||
} {
|
||||
// pretty ugly function to seperate the exact and fuzzy inflection result matches
|
||||
function getEntryMatchIndex(r: InflectionSearchResult[], entry: T.DictionaryEntry): number {
|
||||
return r.findIndex(rs => rs.entry.ts === entry.ts);
|
||||
}
|
||||
function getFormIndex(r: InflectionSearchResult, path: string[]): number {
|
||||
const joinedPath = path.join("");
|
||||
return r.forms.findIndex(rs => rs.path.join("") === joinedPath);
|
||||
}
|
||||
const v = standardizePashto(searchValue);
|
||||
const allResults = searchAllInflectionsCore(allDocs, searchValue);
|
||||
const results: { exact: InflectionSearchResult[], fuzzy: InflectionSearchResult[] } = {
|
||||
exact: [],
|
||||
fuzzy: [],
|
||||
};
|
||||
allResults.forEach((result) => {
|
||||
const entryMatches: { exact: InflectionSearchResult[], fuzzy: InflectionSearchResult[] } = {
|
||||
exact: [],
|
||||
fuzzy: [],
|
||||
};
|
||||
result.forms.forEach((form) => {
|
||||
form.matches.forEach((match) => {
|
||||
if (match.ps.p === v || match.ps.f === v) {
|
||||
addToEntryMatches("exact");
|
||||
} else {
|
||||
addToEntryMatches("fuzzy");
|
||||
}
|
||||
function addToEntryMatches(t: "exact" | "fuzzy") {
|
||||
let entryMatchIndex = getEntryMatchIndex(entryMatches[t], result.entry);
|
||||
if (entryMatchIndex === -1) {
|
||||
entryMatches[t].push({
|
||||
entry: result.entry,
|
||||
forms: [],
|
||||
});
|
||||
entryMatchIndex = entryMatches[t].length - 1;
|
||||
}
|
||||
let formIndex = getFormIndex(entryMatches[t][entryMatchIndex], form.path);
|
||||
if (formIndex === -1) {
|
||||
entryMatches[t][entryMatchIndex].forms.push({
|
||||
path: form.path,
|
||||
matches: [match],
|
||||
});
|
||||
formIndex = entryMatches[t][entryMatchIndex].forms.length - 1;
|
||||
} else {
|
||||
entryMatches[t][entryMatchIndex].forms[formIndex].matches.push(match);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
results.exact.push(...entryMatches.exact);
|
||||
results.fuzzy.push(...entryMatches.fuzzy);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
export function searchAllInflectionsCore(allDocs: T.DictionaryEntry[], searchValue: string): InflectionSearchResult[] {
|
||||
const index = isPashtoScript(searchValue) ? "p" : "f"
|
||||
function sortResultsByRelevancy(arr: InflectionSearchResult[]): InflectionSearchResult[] {
|
||||
return relevancySorter.sort(arr, searchValue, (obj: InflectionSearchResult, calc: any) => (
|
||||
|
@ -65,7 +123,7 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
|
|||
);
|
||||
const preSearchFun = (ps: T.PsString) => !!ps[script].match(begRegex);
|
||||
const searchRegex = new RegExp(
|
||||
makeAWeeBitFuzzy(searchValue, script, true) + "$",
|
||||
`${makeAWeeBitFuzzy(searchValue, script, true)}$`,
|
||||
"i",
|
||||
);
|
||||
// add little bit fuzzy
|
||||
|
@ -110,21 +168,18 @@ export function searchAllInflections(allDocs: T.DictionaryEntry[], searchValue:
|
|||
}, []);
|
||||
// console.timeEnd(timerLabel);
|
||||
// TODO!!: Sorting on this as well
|
||||
if (["را", "ور", "در"].includes(searchValue.slice(0, 2))) {
|
||||
return [
|
||||
const allResults = (["را", "ور", "در"].includes(searchValue.slice(0, 2)))
|
||||
? [
|
||||
...results,
|
||||
// also search without the directionary pronoun
|
||||
...searchAllInflections(allDocs, searchValue.slice(2)),
|
||||
];
|
||||
}
|
||||
if (["raa", "war", "dar", "wăr", "dăr"].includes(searchValue.slice(0, 3))) {
|
||||
return [
|
||||
// also search without the directional pronoun
|
||||
...searchAllInflectionsCore(allDocs, searchValue.slice(2)),
|
||||
] : (["raa", "war", "dar", "wăr", "dăr"].includes(searchValue.slice(0, 3)))
|
||||
? [
|
||||
...results,
|
||||
// also search without the directionary pronoun
|
||||
...searchAllInflections(allDocs, searchValue.slice(3)),
|
||||
];
|
||||
}
|
||||
// also search without the directional pronoun
|
||||
...searchAllInflectionsCore(allDocs, searchValue.slice(3)),
|
||||
] : results;
|
||||
// because we used a bit of a fuzzy search, sort the results by relevancy
|
||||
// this is a bit complicated...
|
||||
return sortResultsByRelevancy(results.map(sortMatchesByRelevancy));
|
||||
return sortResultsByRelevancy(allResults.map(sortMatchesByRelevancy));
|
||||
}
|
|
@ -88,31 +88,36 @@ function Results({ state, isolateEntry, handleInflectionSearch }: {
|
|||
{inflectionResults === "searching" && <div>
|
||||
<p className="lead mt-1">Searching conjugations/inflections... <i className="fas fa-hourglass-half" /></p>
|
||||
</div>}
|
||||
{Array.isArray(inflectionResults) && <div>
|
||||
{inflectionResults && inflectionResults !== "searching" && <div>
|
||||
<h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4>
|
||||
{inflectionResults.length === 0 && <div className="mt-4">
|
||||
{inflectionResults.exact.length === 0 && inflectionResults.fuzzy.length === 0 && <div className="mt-4">
|
||||
<div>No conjugation/inflection matches found for <strong>{state.searchValue}</strong></div>
|
||||
</div>}
|
||||
{inflectionResults.map((p) => (
|
||||
<div key={p.entry.ts}>
|
||||
<Entry
|
||||
key={p.entry.i}
|
||||
entry={p.entry}
|
||||
textOptions={textOptions}
|
||||
isolateEntry={isolateEntry}
|
||||
/>
|
||||
<div className="mb-3 ml-2">
|
||||
{p.forms.map((form, i) => (
|
||||
<InflectionFormMatchDisplay
|
||||
key={"inf-form" + i}
|
||||
textOptions={textOptions}
|
||||
form={form}
|
||||
{(["exact", "fuzzy"] as ("exact" | "fuzzy")[]).map((t) => {
|
||||
return (inflectionResults[t].length !== 0) ? <>
|
||||
<h5 className="mb-3">{t === "exact" ? "Exact" : "Approximate"} Matches</h5>
|
||||
{inflectionResults[t].map((p) => (
|
||||
<div key={p.entry.ts}>
|
||||
<Entry
|
||||
key={p.entry.i}
|
||||
entry={p.entry}
|
||||
textOptions={textOptions}
|
||||
isolateEntry={isolateEntry}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="mb-3 ml-2">
|
||||
{p.forms.map((form, i) => (
|
||||
<InflectionFormMatchDisplay
|
||||
key={`inf-form${i}`}
|
||||
textOptions={textOptions}
|
||||
form={form}
|
||||
entry={p.entry}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</> : null;
|
||||
})}
|
||||
</div>}
|
||||
{inflectionResults === undefined && suggestionState === "none" && state.results.map((entry) => (
|
||||
<Entry
|
||||
|
|
|
@ -11,7 +11,10 @@ export type State = {
|
|||
reviewTasks: import("./functions-types").ReviewTask[],
|
||||
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo | undefined,
|
||||
user: undefined | import("./account-types").LingdocsUser,
|
||||
inflectionSearchResults: undefined | "searching" | InflectionSearchResult[],
|
||||
inflectionSearchResults: undefined | "searching" | {
|
||||
exact: InflectionSearchResult[],
|
||||
fuzzy: InflectionSearchResult[],
|
||||
},
|
||||
}
|
||||
|
||||
export type DictionaryAPI = {
|
||||
|
|
Loading…
Reference in New Issue