better SEO
This commit is contained in:
parent
c67c00ed4a
commit
5b38546043
|
@ -13,7 +13,7 @@ export function EntryAudioDisplay({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<figure>
|
<figure>
|
||||||
<figcaption className="mb-1">
|
<figcaption className="mb-2 pl-2">
|
||||||
Listen to <InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
|
Listen to <InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
|
||||||
</figcaption>
|
</figcaption>
|
||||||
<audio
|
<audio
|
||||||
|
|
|
@ -155,13 +155,13 @@ function IsolatedEntry({
|
||||||
return (
|
return (
|
||||||
<div className="wide-width-limiter">
|
<div className="wide-width-limiter">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{entry.p} - LingDocs Pashto Dictionary</title>
|
<title>{entry.p} | LingDocs Pashto Dictionary</title>
|
||||||
<link
|
<link
|
||||||
rel="canonical"
|
rel="canonical"
|
||||||
href={`https://dictionary.lingdocs.com/word?id=${entry.ts}`}
|
href={`https://dictionary.lingdocs.com/word?id=${entry.ts}`}
|
||||||
/>
|
/>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="row">
|
<dl className="row mb-1">
|
||||||
<div className="col-8">
|
<div className="col-8">
|
||||||
<Entry
|
<Entry
|
||||||
nonClickable
|
nonClickable
|
||||||
|
@ -226,7 +226,7 @@ function IsolatedEntry({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</dl>
|
||||||
<EntryAudioDisplay entry={entry} opts={textOptions} />
|
<EntryAudioDisplay entry={entry} opts={textOptions} />
|
||||||
{wordlistWord && (
|
{wordlistWord && (
|
||||||
<>
|
<>
|
||||||
|
@ -358,6 +358,7 @@ function IsolatedEntry({
|
||||||
state={{ ...state, results: relatedEntries }}
|
state={{ ...state, results: relatedEntries }}
|
||||||
isolateEntry={isolateEntry}
|
isolateEntry={isolateEntry}
|
||||||
handleInflectionSearch={() => null}
|
handleInflectionSearch={() => null}
|
||||||
|
relatedResults
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -8,212 +8,278 @@
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as FT from "../types/functions-types";
|
import * as FT from "../types/functions-types";
|
||||||
import {
|
import { submissionBase, addSubmission } from "../lib/submissions";
|
||||||
submissionBase,
|
|
||||||
addSubmission,
|
|
||||||
} from "../lib/submissions";
|
|
||||||
import { isPashtoScript } from "../lib/is-pashto";
|
import { isPashtoScript } from "../lib/is-pashto";
|
||||||
import Entry from "../components/Entry";
|
import Entry from "../components/Entry";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import InflectionFormMatchDisplay from "../components/InflectionFormMatchDisplay";
|
import InflectionFormMatchDisplay from "../components/InflectionFormMatchDisplay";
|
||||||
import { getTextOptions } from "../lib/get-text-options";
|
import { getTextOptions } from "../lib/get-text-options";
|
||||||
import {
|
import { State } from "../types/dictionary-types";
|
||||||
State,
|
|
||||||
} from "../types/dictionary-types";
|
|
||||||
|
|
||||||
export const inflectionSearchIcon = "fas fa-search-plus";
|
export const inflectionSearchIcon = "fas fa-search-plus";
|
||||||
|
|
||||||
// TODO: put power results in a prop so we can do it from outside with the keyboard shortcut
|
// TODO: put power results in a prop so we can do it from outside with the keyboard shortcut
|
||||||
function Results({ state, isolateEntry, handleInflectionSearch }: {
|
function Results({
|
||||||
state: State,
|
state,
|
||||||
isolateEntry: (ts: number) => void,
|
isolateEntry,
|
||||||
handleInflectionSearch: () => void,
|
handleInflectionSearch,
|
||||||
|
relatedResults,
|
||||||
|
}: {
|
||||||
|
state: State;
|
||||||
|
isolateEntry: (ts: number) => void;
|
||||||
|
handleInflectionSearch: () => void;
|
||||||
|
relatedResults?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [suggestionState, setSuggestionState] = useState<"none" | "editing" | "received">("none");
|
const [suggestionState, setSuggestionState] = useState<
|
||||||
const [comment, setComment] = useState<string>("");
|
"none" | "editing" | "received"
|
||||||
const [pashto, setPashto] = useState<string>("");
|
>("none");
|
||||||
const [phonetics, setPhonetics] = useState<string>("");
|
const [comment, setComment] = useState<string>("");
|
||||||
const [english, setEnglish] = useState<string>("");
|
const [pashto, setPashto] = useState<string>("");
|
||||||
const textOptions = getTextOptions(state);
|
const [phonetics, setPhonetics] = useState<string>("");
|
||||||
function startSuggestion() {
|
const [english, setEnglish] = useState<string>("");
|
||||||
const toStart = state.searchValue;
|
const textOptions = getTextOptions(state);
|
||||||
if (isPashtoScript(toStart)) {
|
function startSuggestion() {
|
||||||
setPashto(toStart);
|
const toStart = state.searchValue;
|
||||||
setPhonetics("");
|
if (isPashtoScript(toStart)) {
|
||||||
} else {
|
setPashto(toStart);
|
||||||
setPashto("");
|
setPhonetics("");
|
||||||
setPhonetics(toStart);
|
} else {
|
||||||
}
|
setPashto("");
|
||||||
setSuggestionState("editing");
|
setPhonetics(toStart);
|
||||||
}
|
}
|
||||||
function cancelSuggestion() {
|
setSuggestionState("editing");
|
||||||
setPashto("");
|
}
|
||||||
setPhonetics("");
|
function cancelSuggestion() {
|
||||||
setSuggestionState("none");
|
setPashto("");
|
||||||
}
|
setPhonetics("");
|
||||||
function submitSuggestion(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
setSuggestionState("none");
|
||||||
event.preventDefault();
|
}
|
||||||
if (!state.user) return;
|
function submitSuggestion(
|
||||||
const p = pashto;
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||||
const f = phonetics;
|
) {
|
||||||
const e = english;
|
event.preventDefault();
|
||||||
const newEntry: FT.EntrySuggestion = {
|
if (!state.user) return;
|
||||||
...submissionBase(state.user),
|
const p = pashto;
|
||||||
type: "entry suggestion",
|
const f = phonetics;
|
||||||
entry: { ts: 0, i: 0, p, f, g: "", e },
|
const e = english;
|
||||||
comment,
|
const newEntry: FT.EntrySuggestion = {
|
||||||
};
|
...submissionBase(state.user),
|
||||||
addSubmission(newEntry, state.user);
|
type: "entry suggestion",
|
||||||
setSuggestionState("received");
|
entry: { ts: 0, i: 0, p, f, g: "", e },
|
||||||
}
|
comment,
|
||||||
const inflectionResults = state.inflectionSearchResults;
|
};
|
||||||
return <div className="width-limiter">
|
addSubmission(newEntry, state.user);
|
||||||
|
setSuggestionState("received");
|
||||||
|
}
|
||||||
|
const inflectionResults = state.inflectionSearchResults;
|
||||||
|
return (
|
||||||
|
<div className="width-limiter">
|
||||||
|
{!relatedResults && (
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>LingDocs Pashto Dictionary</title>
|
<title>LingDocs Pashto Dictionary</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{(state.user && (window.location.pathname !== "/word") && suggestionState === "none" && inflectionResults === undefined) && <button
|
)}
|
||||||
|
{state.user &&
|
||||||
|
window.location.pathname !== "/word" &&
|
||||||
|
suggestionState === "none" &&
|
||||||
|
inflectionResults === undefined && (
|
||||||
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn btn-outline-secondary bg-white entry-suggestion-button${state.options.searchBarPosition === "bottom" ? " entry-suggestion-button-with-bottom-searchbar" : ""}`}
|
className={`btn btn-outline-secondary bg-white entry-suggestion-button${
|
||||||
|
state.options.searchBarPosition === "bottom"
|
||||||
|
? " entry-suggestion-button-with-bottom-searchbar"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
onClick={startSuggestion}
|
onClick={startSuggestion}
|
||||||
>
|
>
|
||||||
<i className="fas fa-plus" style={{ padding: "3px" }} />
|
<i className="fas fa-plus" style={{ padding: "3px" }} />
|
||||||
</button>}
|
</button>
|
||||||
{(inflectionResults === undefined && suggestionState === "none" && window.location.pathname === "/search") && <button
|
)}
|
||||||
|
{inflectionResults === undefined &&
|
||||||
|
suggestionState === "none" &&
|
||||||
|
window.location.pathname === "/search" && (
|
||||||
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn btn-outline-secondary bg-white conjugation-search-button${state.options.searchBarPosition === "bottom" ? " conjugation-search-button-with-bottom-searchbar" : ""}`}
|
className={`btn btn-outline-secondary bg-white conjugation-search-button${
|
||||||
|
state.options.searchBarPosition === "bottom"
|
||||||
|
? " conjugation-search-button-with-bottom-searchbar"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
onClick={handleInflectionSearch}
|
onClick={handleInflectionSearch}
|
||||||
>
|
>
|
||||||
<i className={inflectionSearchIcon} style={{ padding: "3px" }} />
|
<i className={inflectionSearchIcon} style={{ padding: "3px" }} />
|
||||||
</button>}
|
</button>
|
||||||
{inflectionResults === "searching" && <div>
|
)}
|
||||||
<p className="lead mt-1">Searching conjugations/inflections... <i className="fas fa-hourglass-half" /></p>
|
{inflectionResults === "searching" && (
|
||||||
</div>}
|
<div>
|
||||||
{inflectionResults && inflectionResults !== "searching" && <div>
|
<p className="lead mt-1">
|
||||||
<h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4>
|
Searching conjugations/inflections...{" "}
|
||||||
{inflectionResults.exact.length === 0 && inflectionResults.fuzzy.length === 0 && <div className="mt-4">
|
<i className="fas fa-hourglass-half" />
|
||||||
<div>No conjugation/inflection matches found for <strong>{state.searchValue}</strong></div>
|
</p>
|
||||||
</div>}
|
</div>
|
||||||
{(["exact", "fuzzy"] as ("exact" | "fuzzy")[]).map((t) => {
|
)}
|
||||||
return (inflectionResults[t].length !== 0) ? <>
|
{inflectionResults && inflectionResults !== "searching" && (
|
||||||
<h5 className="mb-3">{t === "exact" ? "Exact" : "Approximate"} Matches</h5>
|
<div>
|
||||||
{inflectionResults[t].map((p) => (
|
<h4 className="mt-1 mb-3">Conjugation/Inflection Results</h4>
|
||||||
<div key={p.entry.ts}>
|
{inflectionResults.exact.length === 0 &&
|
||||||
<Entry
|
inflectionResults.fuzzy.length === 0 && (
|
||||||
key={p.entry.i}
|
<div className="mt-4">
|
||||||
entry={p.entry}
|
<div>
|
||||||
textOptions={textOptions}
|
No conjugation/inflection matches found for{" "}
|
||||||
isolateEntry={isolateEntry}
|
<strong>{state.searchValue}</strong>
|
||||||
/>
|
|
||||||
<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
|
|
||||||
key={entry.i}
|
|
||||||
entry={entry}
|
|
||||||
textOptions={textOptions}
|
|
||||||
isolateEntry={isolateEntry}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{(state.user && (suggestionState === "editing")) && <div className="my-3">
|
|
||||||
<h5 className="mb-3">Suggest an entry for the dictionary:</h5>
|
|
||||||
<div className="form-group mt-4" style={{ maxWidth: "500px" }}>
|
|
||||||
<div className="row mb-2">
|
|
||||||
<div className="col">
|
|
||||||
<label htmlFor="suggestionPashto">Pashto:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
dir="rtl"
|
|
||||||
id="suggestionPashto"
|
|
||||||
data-lpignore="true"
|
|
||||||
value={pashto}
|
|
||||||
autoComplete="off"
|
|
||||||
autoCorrect="off"
|
|
||||||
autoCapitalize="off"
|
|
||||||
onChange={(e) => setPashto(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col">
|
|
||||||
<label htmlFor="suggestionPhonetics">Phonetics:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
dir="ltr"
|
|
||||||
id="suggestionPhonetics"
|
|
||||||
autoComplete="off"
|
|
||||||
autoCorrect="off"
|
|
||||||
autoCapitalize="off"
|
|
||||||
data-lpignore="true"
|
|
||||||
value={phonetics}
|
|
||||||
onChange={(e) => setPhonetics(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<label htmlFor="suggestionEnglish">English:</label>
|
</div>
|
||||||
|
)}
|
||||||
|
{(["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 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" && (
|
||||||
|
<dl>
|
||||||
|
{state.results.map((entry) => (
|
||||||
|
<Entry
|
||||||
|
key={entry.i}
|
||||||
|
entry={entry}
|
||||||
|
textOptions={textOptions}
|
||||||
|
isolateEntry={isolateEntry}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
)}
|
||||||
|
{state.user && suggestionState === "editing" && (
|
||||||
|
<div className="my-3">
|
||||||
|
<h5 className="mb-3">Suggest an entry for the dictionary:</h5>
|
||||||
|
<div className="form-group mt-4" style={{ maxWidth: "500px" }}>
|
||||||
|
<div className="row mb-2">
|
||||||
|
<div className="col">
|
||||||
|
<label htmlFor="suggestionPashto">Pashto:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control mb-2"
|
className="form-control"
|
||||||
id="suggestionEnglish"
|
dir="rtl"
|
||||||
data-lpignore="true"
|
id="suggestionPashto"
|
||||||
value={english}
|
data-lpignore="true"
|
||||||
autoComplete="off"
|
value={pashto}
|
||||||
onChange={(e) => setEnglish(e.target.value)}
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
onChange={(e) => setPashto(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="editSuggestionForm">Comments:</label>
|
</div>
|
||||||
|
<div className="col">
|
||||||
|
<label htmlFor="suggestionPhonetics">Phonetics:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="editSuggestionForm"
|
dir="ltr"
|
||||||
data-lpignore="true"
|
id="suggestionPhonetics"
|
||||||
value={comment}
|
autoComplete="off"
|
||||||
onChange={(e) => setComment(e.target.value)}
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
data-lpignore="true"
|
||||||
|
value={phonetics}
|
||||||
|
onChange={(e) => setPhonetics(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<label htmlFor="suggestionEnglish">English:</label>
|
||||||
type="button"
|
<input
|
||||||
className="btn btn-secondary mr-3"
|
type="text"
|
||||||
onClick={submitSuggestion}
|
className="form-control mb-2"
|
||||||
data-testid="editWordSubmitButton"
|
id="suggestionEnglish"
|
||||||
>
|
data-lpignore="true"
|
||||||
Submit
|
value={english}
|
||||||
</button>
|
autoComplete="off"
|
||||||
<button
|
onChange={(e) => setEnglish(e.target.value)}
|
||||||
type="button"
|
/>
|
||||||
className="btn btn-outline-secondary"
|
<label htmlFor="editSuggestionForm">Comments:</label>
|
||||||
onClick={cancelSuggestion}
|
<input
|
||||||
data-testid="editWordCancelButton"
|
type="text"
|
||||||
>
|
className="form-control"
|
||||||
Cancel
|
id="editSuggestionForm"
|
||||||
</button>
|
data-lpignore="true"
|
||||||
</div>}
|
value={comment}
|
||||||
{suggestionState === "received" && <div className="my-3">
|
onChange={(e) => setComment(e.target.value)}
|
||||||
Thanks for the help!
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
<button
|
||||||
{(((inflectionResults === undefined) && suggestionState === "none" && state.searchValue && (!state.results.length))) && <div>
|
type="button"
|
||||||
<h5 className="mt-2">No Results Found in {state.options.language}</h5>
|
className="btn btn-secondary mr-3"
|
||||||
{state.options.language === "Pashto" && isPashtoScript(state.searchValue) && <p className="mt-3">
|
onClick={submitSuggestion}
|
||||||
Click on the <i className={inflectionSearchIcon} /> to search inflections and conjugations
|
data-testid="editWordSubmitButton"
|
||||||
</p>}
|
>
|
||||||
{state.options.searchType === "alphabetical" && <div className="mt-4 font-weight-light">
|
Submit
|
||||||
<div className="mb-3">You are using alphabetical browsing mode</div>
|
</button>
|
||||||
<div>Click on the <span className="fa fa-book" ></span> icon above for smart search <span className="fa fa-bolt" ></span></div>
|
<button
|
||||||
</div>}
|
type="button"
|
||||||
</div>}
|
className="btn btn-outline-secondary"
|
||||||
</div>;
|
onClick={cancelSuggestion}
|
||||||
|
data-testid="editWordCancelButton"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{suggestionState === "received" && (
|
||||||
|
<div className="my-3">Thanks for the help!</div>
|
||||||
|
)}
|
||||||
|
{inflectionResults === undefined &&
|
||||||
|
suggestionState === "none" &&
|
||||||
|
state.searchValue &&
|
||||||
|
!state.results.length && (
|
||||||
|
<div>
|
||||||
|
<h5 className="mt-2">
|
||||||
|
No Results Found in {state.options.language}
|
||||||
|
</h5>
|
||||||
|
{state.options.language === "Pashto" &&
|
||||||
|
isPashtoScript(state.searchValue) && (
|
||||||
|
<p className="mt-3">
|
||||||
|
Click on the <i className={inflectionSearchIcon} /> to search
|
||||||
|
inflections and conjugations
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{state.options.searchType === "alphabetical" && (
|
||||||
|
<div className="mt-4 font-weight-light">
|
||||||
|
<div className="mb-3">
|
||||||
|
You are using alphabetical browsing mode
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Click on the <span className="fa fa-book"></span> icon above
|
||||||
|
for smart search <span className="fa fa-bolt"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Results;
|
export default Results;
|
Loading…
Reference in New Issue