added audio download button and tooltips for various buttons etc

This commit is contained in:
adueck 2024-05-04 13:49:22 +04:00
parent 3f919f609a
commit cfeaec9550
5 changed files with 126 additions and 59 deletions

View File

@ -47,6 +47,7 @@ function Entry({
<i
onClick={handlePlayStorageAudio}
className="clickable ml-2 fas fa-volume-down px-1"
title="play audio"
/>
)}
</div>

View File

@ -12,6 +12,7 @@ export function EntryAudioDisplay({
opts: T.TextOptions;
user: LingdocsUser | undefined;
}) {
const audioPath = getAudioPath(entry.ts);
if (!entry.a) {
return null;
}
@ -24,23 +25,54 @@ export function EntryAudioDisplay({
action: `play ${entry.p} - ${entry.ts}`,
});
}
function handleDownload() {
const documentName = `${entry.p}-${entry.ts}.mp3`;
fetch(audioPath)
.then((res) => {
return res.blob();
})
.then((blob) => {
const href = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.download = documentName;
a.href = href;
a.click();
a.href = "";
})
.catch((err) => console.error(err));
}
return (
<figure>
<figcaption className="mb-2 pl-2">
Listen to <InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
<div>
Listen to{" "}
<InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
</div>
</figcaption>
<audio
controls
controlsList="nofullscreen"
src={getAudioPath(entry.ts)}
preload="auto"
onPlay={handlePlay}
>
<a href={getAudioPath(entry.ts)}>
<div className="d-flex align-items-center">
<audio
controls
controlsList="nofullscreen"
src={audioPath}
preload="auto"
onPlay={handlePlay}
>
{/* <a href={getAudioPath(entry.ts)}>
Download audio for{" "}
<InlinePs opts={opts}>{{ p: entry.p, f: entry.f }}</InlinePs>
</a>
</audio>
</a > */}
</audio>
<button
type="button"
onClick={handleDownload}
className="ml-2 btn btn-light"
title="download audio"
>
<i className="fas fa-download"></i>
</button>
</div>
</figure>
);
}

View File

@ -9,17 +9,18 @@
import Mousetrap from "mousetrap";
import { useEffect, useRef } from "react";
import { State } from "../types/dictionary-types";
import {
OptionsAction,
Language,
SearchType,
} from "../types/dictionary-types";
import { OptionsAction, Language, SearchType } from "../types/dictionary-types";
const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }: {
state: State
optionsDispatch: (action: OptionsAction) => void,
handleSearchValueChange: (searchValue: string) => void,
onBottom?: boolean,
const SearchBar = ({
state,
optionsDispatch,
handleSearchValueChange,
onBottom,
}: {
state: State;
optionsDispatch: (action: OptionsAction) => void;
handleSearchValueChange: (searchValue: string) => void;
onBottom?: boolean;
}) => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
@ -34,8 +35,8 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
return () => {
window.removeEventListener("focus", onFocus);
Mousetrap.unbind(["shift+space"]);
}
// eslint-disable-next-line
};
// eslint-disable-next-line
}, []);
function onFocus() {
if (["/", "/search"].includes(window.location.pathname)) {
@ -50,43 +51,56 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
onClick={() => optionsDispatch({ type: "toggleLanguage" })}
data-testid="languageToggle"
>
<div aria-label={`language-choice-${language === "Pashto" ? "ps-to-en" : "en-to-ps"}`}>
<div
aria-label={`language-choice-${
language === "Pashto" ? "ps-to-en" : "en-to-ps"
}`}
>
Ps <span className={`fa fa-arrow-${arrowDirection}`} /> En
</div>
</button>
);
}
};
const SearchTypeToggle = ({ searchType }: { searchType: SearchType }) => {
const icon = (searchType === "alphabetical") ? "book" : "bolt";
const icon = searchType === "alphabetical" ? "book" : "bolt";
return (
<button
className="btn btn-outline-secondary"
onClick={() => optionsDispatch({ type: "toggleSearchType" })}
data-testid="searchTypeToggle"
>
<span className={`fa fa-${icon}`} />
</button>
<button
className="btn btn-outline-secondary"
onClick={() => optionsDispatch({ type: "toggleSearchType" })}
data-testid="searchTypeToggle"
title="toggle alphabetical/smart search"
>
<span className={`fa fa-${icon}`} />
</button>
);
};
const placeholder = (state.options.searchType === "alphabetical" && state.options.language === "Pashto")
? "Browse alphabetically"
: `Search ${state.options.language === "Pashto" ? "Pashto" : "English"}`;
const placeholder =
state.options.searchType === "alphabetical" &&
state.options.language === "Pashto"
? "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
type="text"
style={{ borderRight: "0px", zIndex: 200 }}
style={{ borderRight: "0px", zIndex: 200 }}
placeholder={placeholder}
value={state.searchValue}
onChange={(e) => {
handleSearchValueChange(e.target.value);
}}
onBlur={e => {
onBlur={(e) => {
// don't loose focus/cursor if clicking on a word/star etc if searchBarStickyFocus is enabled
if (state.options.searchBarStickyFocus && e.relatedTarget === null) {
if (
state.options.searchBarStickyFocus &&
e.relatedTarget === null
) {
e.target.focus();
}
}}
@ -104,27 +118,33 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
/>
<span className="input-group-append">
<span
className={`btn btn-outline-secondary${!state.searchValue ? " unclickable" : ""} clear-search-button border-left-0 border`}
className={`btn btn-outline-secondary${
!state.searchValue ? " unclickable" : ""
} clear-search-button border-left-0 border`}
style={{ borderRadius: 0 }}
onClick={state.searchValue ? () => {
handleSearchValueChange("");
// keep the focus on the input after pressing the X
inputRef.current && inputRef.current.focus();
} : undefined}
onClick={
state.searchValue
? () => {
handleSearchValueChange("");
// keep the focus on the input after pressing the X
inputRef.current && inputRef.current.focus();
}
: undefined
}
data-testid="clearButton"
title="clear search"
>
<i className="fa fa-times" style={!state.searchValue ? { visibility: "hidden" } : {}}></i>
<i
className="fa fa-times"
style={!state.searchValue ? { visibility: "hidden" } : {}}
></i>
</span>
</span>
<div className="input-group-append">
{state.options.language === "Pashto" &&
<SearchTypeToggle
searchType={state.options.searchType}
/>
}
{<LanguageToggle
language={state.options.language}
/>}
<div className="input-group-append" title="toggle search language">
{state.options.language === "Pashto" && (
<SearchTypeToggle searchType={state.options.searchType} />
)}
{<LanguageToggle language={state.options.language} />}
</div>
</div>
</div>
@ -132,4 +152,4 @@ const SearchBar = ({ state, optionsDispatch, handleSearchValueChange, onBottom }
);
};
export default SearchBar;
export default SearchBar;

View File

@ -176,21 +176,31 @@ function IsolatedEntry({
<div
className="clickable mr-3"
onClick={() => setExploded((os) => !os)}
title="separate letters"
>
<i className={`fas fa-${exploded ? "compress" : "expand"}-alt`} />
</div>
<div className="clickable mr-3" onClick={handleClipId}>
<div
className="clickable mr-3"
onClick={handleClipId}
title="copy word id"
>
<i className="fas fa-tag"></i>
</div>
{state.user && state.user.level === "editor" && (
<>
<div className="clickable mr-3" onClick={handleClipEntry}>
<div
className="clickable mr-3"
onClick={handleClipEntry}
title="copy entry data"
>
<i className="fas fa-code"></i>
</div>
<Link to={`/edit?id=${entry.ts}`} className="plain-link">
<div
className="clickable mr-3"
data-testid="finalEditEntryButton"
title="edit entry"
>
<i className="fa fa-gavel" />
</div>
@ -203,12 +213,14 @@ function IsolatedEntry({
className="clickable mr-3"
data-testid="editEntryButton"
onClick={() => setEditing((os) => !os)}
title="suggest edit"
>
<i className="fa fa-pen" />
</div>
{wordlistEnabled(state.user) && (
<div
className="clickable"
title="add to wordlist"
data-testid={
wordlistWord ? "fullStarButton" : "emptyStarButton"
}

View File

@ -92,6 +92,7 @@ function Results({
: ""
}`}
onClick={startSuggestion}
title="create entry suggestion"
>
<i className="fas fa-plus" style={{ padding: "3px" }} />
</button>
@ -107,6 +108,7 @@ function Results({
: ""
}`}
onClick={handleInflectionSearch}
title="search in inflections/conjugations"
>
<i className={inflectionSearchIcon} style={{ padding: "3px" }} />
</button>