Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions src/components/DataEntry/DataEntryTable/NewEntry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
import SenseDialog from "components/DataEntry/DataEntryTable/NewEntry/SenseDialog";
import VernDialog from "components/DataEntry/DataEntryTable/NewEntry/VernDialog";
import Pronunciations from "components/Pronunciations/PronunciationsComponent";
import PronunciationsFrontend from "components/Pronunciations/PronunciationsFrontend";
import { StoreState } from "types";
import theme from "types/theme";

Expand Down Expand Up @@ -290,12 +290,10 @@ export default function NewEntry(props: NewEntryProps): ReactElement {
)}
</Grid>
<Grid item xs={2} style={gridItemStyle(1)}>
<Pronunciations
wordId={""}
audioInFrontend
<PronunciationsFrontend
pronunciationFiles={newAudioUrls}
deleteAudio={(_, fileName: string) => delNewAudioUrl(fileName)}
uploadAudio={(_, audioFile: File) => addNewAudioUrl(audioFile)}
deleteAudio={delNewAudioUrl}
uploadAudio={addNewAudioUrl}
/>
</Grid>
<Grid item xs={1} style={gridItemStyle(1)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { newWritingSystem } from "types/writingSystem";

jest.mock("@mui/material/Autocomplete", () => "div");

jest.mock("components/Pronunciations/PronunciationsComponent", () => "div");
jest.mock("components/Pronunciations/PronunciationsFrontend", () => "div");
jest.mock("components/Pronunciations/Recorder");

const mockStore = configureMockStore()({ treeViewState: { open: false } });
Expand Down
14 changes: 7 additions & 7 deletions src/components/DataEntry/DataEntryTable/RecentEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
GlossWithSuggestions,
VernWithSuggestions,
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
import Pronunciations from "components/Pronunciations/PronunciationsComponent";
import PronunciationsBackend from "components/Pronunciations/PronunciationsBackend";
import theme from "types/theme";
import { newGloss } from "types/word";
import { firstGlossText } from "utilities/wordUtilities";
Expand Down Expand Up @@ -129,14 +129,14 @@ export default function RecentEntry(props: RecentEntryProps): ReactElement {
}}
>
{!props.disabled && (
<Pronunciations
wordId={props.entry.id}
<PronunciationsBackend
pronunciationFiles={props.entry.audio}
deleteAudio={(wordId: string, fileName: string) => {
props.deleteAudioFromWord(wordId, fileName);
wordId={props.entry.id}
deleteAudio={(fileName: string) => {
props.deleteAudioFromWord(props.entry.id, fileName);
}}
uploadAudio={(wordId: string, audioFile: File) => {
props.addAudioToWord(wordId, audioFile);
uploadAudio={(audioFile: File) => {
props.addAudioToWord(props.entry.id, audioFile);
}}
/>
)}
Expand Down
11 changes: 2 additions & 9 deletions src/components/DataEntry/DataEntryTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { getUserId } from "backend/localStorage";
import NewEntry from "components/DataEntry/DataEntryTable/NewEntry";
import RecentEntry from "components/DataEntry/DataEntryTable/RecentEntry";
import { filterWordsWithSenses } from "components/DataEntry/utilities";
import { getFileNameForWord } from "components/Pronunciations/AudioRecorder";
import { uploadFileFromUrl } from "components/Pronunciations/utilities";
import { StoreState } from "types";
import { Hash } from "types/hash";
import { useAppSelector } from "types/hooks";
Expand Down Expand Up @@ -568,14 +568,7 @@ export default function DataEntryTable(
defunctWord(oldId);
let newId = oldId;
for (const audioURL of audioURLs) {
const audioBlob = await fetch(audioURL).then((result) => result.blob());
const fileName = getFileNameForWord(newId);
const audioFile = new File([audioBlob], fileName, {
type: audioBlob.type,
lastModified: Date.now(),
});
newId = await backend.uploadAudio(newId, audioFile);
URL.revokeObjectURL(audioURL);
newId = await uploadFileFromUrl(newId, audioURL);
}
defunctWord(oldId, newId);
return newId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jest.mock(
"components/DataEntry/DataEntryTable/RecentEntry",
() => MockRecentEntry
);
jest.mock("components/Pronunciations/PronunciationsComponent", () => "div");
jest.mock("components/Pronunciations/PronunciationsFrontend", () => "div");
jest.mock("components/Pronunciations/Recorder");
jest.mock("utilities/utilities");

Expand Down
13 changes: 3 additions & 10 deletions src/components/Pronunciations/AudioPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ import { useAppDispatch, useAppSelector } from "types/hooks";
import { themeColors } from "types/theme";

interface PlayerProps {
pronunciationUrl: string;
wordId: string;
deleteAudio: (fileName: string) => void;
fileName: string;
deleteAudio?: (wordId: string, fileName: string) => void;
isPlaying?: boolean;
pronunciationUrl: string;
}

const useStyles = makeStyles((theme: Theme) =>
Expand Down Expand Up @@ -62,12 +61,6 @@ export default function AudioPlayer(props: PlayerProps): ReactElement {
}
}, [audio, dispatchReset, isPlaying]);

function deleteAudio(): void {
if (props.deleteAudio) {
props.deleteAudio(props.wordId, props.fileName);
}
}

function togglePlay(): void {
if (!isPlaying) {
dispatch(playing(props.fileName));
Expand Down Expand Up @@ -161,7 +154,7 @@ export default function AudioPlayer(props: PlayerProps): ReactElement {
textId="buttons.deletePermanently"
titleId="pronunciations.deleteRecording"
onClose={() => setDeleteConf(false)}
onConfirm={deleteAudio}
onConfirm={() => props.deleteAudio(props.fileName)}
buttonIdClose="audio-delete-cancel"
buttonIdConfirm="audio-delete-confirm"
/>
Expand Down
13 changes: 3 additions & 10 deletions src/components/Pronunciations/AudioRecorder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@ import { toast } from "react-toastify";
import Recorder from "components/Pronunciations/Recorder";
import RecorderContext from "components/Pronunciations/RecorderContext";
import RecorderIcon from "components/Pronunciations/RecorderIcon";
import { getFileNameForWord } from "components/Pronunciations/utilities";

interface RecorderProps {
wordId: string;
uploadAudio: (wordId: string, audioFile: File) => void;
}

export function getFileNameForWord(wordId: string): string {
const fourCharParts = wordId.match(/.{1,6}/g);
const compressed = fourCharParts?.map((i) =>
Number("0x" + i).toString(36)
) ?? ["unknownWord"];
return compressed.join("") + "_" + new Date().getTime().toString(36);
uploadAudio: (audioFile: File) => void;
}

export default function AudioRecorder(props: RecorderProps): ReactElement {
Expand All @@ -38,7 +31,7 @@ export default function AudioRecorder(props: RecorderProps): ReactElement {
lastModified: Date.now(),
type: Recorder.blobType,
};
props.uploadAudio(props.wordId, new File([blob], fileName, options));
props.uploadAudio(new File([blob], fileName, options));
}

return (
Expand Down
64 changes: 64 additions & 0 deletions src/components/Pronunciations/PronunciationsBackend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { memo, ReactElement } from "react";

import { getAudioUrl } from "backend";
import AudioPlayer from "components/Pronunciations/AudioPlayer";
import AudioRecorder from "components/Pronunciations/AudioRecorder";

interface PronunciationsBackendProps {
playerOnly?: boolean;
overrideMemo?: boolean;
pronunciationFiles: string[];
wordId: string;
deleteAudio: (fileName: string) => void;
uploadAudio?: (audioFile: File) => void;
}

/** Audio recording/playing component for backend audio. */
export function PronunciationsBackend(
props: PronunciationsBackendProps
): ReactElement {
if (props.playerOnly && props.uploadAudio) {
console.warn("uploadAudio is defined but unused since playerOnly is true");
}
if (!props.playerOnly && !props.uploadAudio) {
console.warn("uploadAudio undefined; playerOnly should be set to true");
}

const audioButtons: ReactElement[] = props.pronunciationFiles.map(
(fileName) => (
<AudioPlayer
fileName={fileName}
key={fileName}
pronunciationUrl={getAudioUrl(props.wordId, fileName)}
deleteAudio={props.deleteAudio}
/>
)
);

return (
<>
{!props.playerOnly && !!props.uploadAudio && (
<AudioRecorder wordId={props.wordId} uploadAudio={props.uploadAudio} />
)}
{audioButtons}
</>
);
}

// Memoize to decrease unnecessary fetching of audio files.
// https://dmitripavlutin.com/use-react-memo-wisely/#11-custom-equality-check-of-props
function propsAreEqual(
prev: PronunciationsBackendProps,
next: PronunciationsBackendProps
): boolean {
if (next.overrideMemo) {
return false;
}
return (
prev.wordId === next.wordId &&
JSON.stringify(prev.pronunciationFiles) ===
JSON.stringify(next.pronunciationFiles)
);
}

export default memo(PronunciationsBackend, propsAreEqual);
52 changes: 0 additions & 52 deletions src/components/Pronunciations/PronunciationsComponent.tsx

This file was deleted.

35 changes: 35 additions & 0 deletions src/components/Pronunciations/PronunciationsFrontend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ReactElement } from "react";

import AudioPlayer from "components/Pronunciations/AudioPlayer";
import AudioRecorder from "components/Pronunciations/AudioRecorder";

interface PronunciationFrontendProps {
pronunciationFiles: string[];
elemBetweenRecordAndPlay?: ReactElement;
deleteAudio: (fileName: string) => void;
uploadAudio: (audioFile: File) => void;
}

/** Audio recording/playing component for audio being recorded and held in the frontend. */
export default function PronunciationsFrontend(
props: PronunciationFrontendProps
): ReactElement {
const audioButtons: ReactElement[] = props.pronunciationFiles.map(
(fileName) => (
<AudioPlayer
fileName={fileName}
key={fileName}
pronunciationUrl={fileName}
deleteAudio={props.deleteAudio}
/>
)
);

return (
<>
<AudioRecorder wordId={""} uploadAudio={props.uploadAudio} />
{props.elemBetweenRecordAndPlay}
{audioButtons}
</>
);
}
Loading