functionality for handling male and female recordings of words

This commit is contained in:
adueck 2024-09-10 19:07:16 -04:00
parent a91ae1b1f3
commit d2a1d03640
5 changed files with 69 additions and 21 deletions

View File

@ -9,6 +9,7 @@ import ExtraEntryInfo from "../components/ExtraEntryInfo";
import classNames from "classnames"; import classNames from "classnames";
import { Types as T, InlinePs } from "@lingdocs/ps-react"; import { Types as T, InlinePs } from "@lingdocs/ps-react";
import playStorageAudio from "./PlayStorageAudio"; import playStorageAudio from "./PlayStorageAudio";
import { getRecordedGenders } from "../lib/recorded-genders";
function Entry({ function Entry({
entry, entry,
@ -22,11 +23,12 @@ function Entry({
isolateEntry?: (ts: number) => void; isolateEntry?: (ts: number) => void;
admin: boolean; admin: boolean;
}) { }) {
function handlePlayStorageAudio( const gendersRecorded = getRecordedGenders(entry);
e: React.MouseEvent<HTMLElement, MouseEvent> function handlePlayStorageAudio(gender: T.Gender) {
) { return (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation(); e.stopPropagation();
playStorageAudio(entry.ts, entry.p, () => null); playStorageAudio(entry.ts, gender, entry.p, () => null);
};
} }
return ( return (
<div <div
@ -43,13 +45,14 @@ function Entry({
{` `} {` `}
{/* Can't figure out why but the <em> here can't be empty */} {/* Can't figure out why but the <em> here can't be empty */}
<em>{entry.c || "\u00A0"}</em> <em>{entry.c || "\u00A0"}</em>
{entry.a && !nonClickable && ( {!nonClickable &&
<i gendersRecorded.map((gender) => (
onClick={handlePlayStorageAudio} <i
className="clickable ml-2 fas fa-volume-down px-1" onClick={handlePlayStorageAudio(gender)}
title="play audio" className="clickable ml-2 fas fa-volume-down px-1"
/> title="play audio"
)} />
))}
</div> </div>
<div> <div>
<ExtraEntryInfo entry={entry} textOptions={textOptions} /> <ExtraEntryInfo entry={entry} textOptions={textOptions} />

View File

@ -1,6 +1,7 @@
import { Types as T, InlinePs } from "@lingdocs/ps-react"; import { Types as T, InlinePs } from "@lingdocs/ps-react";
import { getAudioPath } from "./PlayStorageAudio"; import { getAudioPath } from "./PlayStorageAudio";
import ReactGA from "react-ga4"; import ReactGA from "react-ga4";
import { getRecordedGenders } from "../lib/recorded-genders";
export function EntryAudioDisplay({ export function EntryAudioDisplay({
entry, entry,
@ -9,23 +10,46 @@ export function EntryAudioDisplay({
entry: T.DictionaryEntry; entry: T.DictionaryEntry;
opts: T.TextOptions; opts: T.TextOptions;
}) { }) {
const audioPath = getAudioPath(entry.ts); if (!entry.a) {
return null;
}
return (
<div className="mb-4">
{getRecordedGenders(entry).map((gender) => (
<EntryRecording entry={entry} opts={opts} gender={gender} />
))}
</div>
);
}
function EntryRecording({
entry,
opts,
gender,
}: {
entry: T.DictionaryEntry;
opts: T.TextOptions;
gender: T.Gender;
}) {
const audioPath = getAudioPath(entry.ts, gender);
if (!entry.a) { if (!entry.a) {
return null; return null;
} }
function handlePlay() { function handlePlay() {
ReactGA.event({ ReactGA.event({
category: "sounds", category: "sounds",
action: `play ${entry.p} - ${entry.ts}`, action: `play ${entry.p} - ${entry.ts} ${gender}`,
}); });
} }
function handleDownload() { function handleDownload() {
ReactGA.event({ ReactGA.event({
category: "sounds", category: "sounds",
action: `download ${entry.p} - ${entry.ts}`, action: `download ${entry.p} - ${entry.ts} ${gender}`,
}); });
const documentName = `${entry.p}-${entry.ts}.mp3`; const documentName = `${entry.p}-${entry.ts}-${
gender === "masc" ? "m" : "f"
}.mp3`;
fetch(audioPath) fetch(audioPath)
.then((res) => { .then((res) => {
@ -45,10 +69,15 @@ export function EntryAudioDisplay({
<figure> <figure>
<figcaption className="mb-2 pl-2"> <figcaption className="mb-2 pl-2">
<div style={{ display: "none" }}> <div style={{ display: "none" }}>
Listen to <InlinePs opts={opts} ps={{ p: entry.p, f: entry.f }} /> Listen to <InlinePs opts={opts} ps={{ p: entry.p, f: entry.f }} />:
{` `}
{gender === "masc" ? "Male" : "Female"} recording
</div> </div>
</figcaption> </figcaption>
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<div className="mr-2" style={{ width: "1rem" }}>
{gender === "masc" ? "M" : "F"}
</div>
<audio <audio
controls controls
controlsList="nofullscreen" controlsList="nofullscreen"

View File

@ -1,11 +1,15 @@
import ReactGA from "react-ga4"; import ReactGA from "react-ga4";
import { Types as T } from "@lingdocs/ps-react";
export function getAudioPath(ts: number): string { export function getAudioPath(ts: number, gender: T.Gender): string {
return `https://storage.lingdocs.com/audio/${ts}.mp3`; return `https://storage.lingdocs.com/audio/${ts}${
gender === "fem" ? "f" : ""
}.mp3`;
} }
export default function playStorageAudio( export default function playStorageAudio(
ts: number, ts: number,
gender: T.Gender,
p: string, p: string,
callback: () => void callback: () => void
) { ) {
@ -14,7 +18,7 @@ export default function playStorageAudio(
category: "sounds", category: "sounds",
action: `quick play ${p} - ${ts}`, action: `quick play ${p} - ${ts}`,
}); });
let audio = new Audio(getAudioPath(ts)); let audio = new Audio(getAudioPath(ts, gender));
audio.addEventListener("ended", () => { audio.addEventListener("ended", () => {
callback(); callback();
audio.remove(); audio.remove();

View File

@ -0,0 +1,11 @@
import { Types as T } from "@lingdocs/ps-react";
export function getRecordedGenders(entry: T.DictionaryEntry): T.Gender[] {
return entry.a === 1
? ["masc"]
: entry.a === 2
? ["fem"]
: entry.a === 3
? ["masc", "fem"]
: [];
}

View File

@ -58,7 +58,8 @@ registerRoute(
registerRoute( registerRoute(
// Add in any other file extensions or routing criteria as needed. // Add in any other file extensions or routing criteria as needed.
({ url }) => ({ url }) =>
url.origin === self.location.origin && ([".png", ".woff2", "woff"].some(ending => url.pathname.endsWith(ending))) url.origin === self.location.origin &&
[".png", ".woff2", "woff"].some((ending) => url.pathname.endsWith(ending)),
// Customize this strategy as needed, e.g., by changing to CacheFirst. // Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({ new StaleWhileRevalidate({
cacheName: "images", cacheName: "images",