Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: improve recording functionality #466

Open
wants to merge 6 commits into
base: milestone/16-improve-recording-functionality
Choose a base branch
from
Open
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
34 changes: 20 additions & 14 deletions apps/cms/src/app/(protected)/content/article/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,33 @@ export default function DocumentTypeDocumentsPage() {
* @constant
* @type {JSX.Element}
*/
const modalActionAudioInput = (
const modalActionAudioInput: JSX.Element = isRecording ? (
<span className="animate-pulse">Recording...</span>
) : (
<div className="flex flex-col sm:flex-row justify-end gap-3">
{/* Start button to close the audio input modal */}
<button
className="flex justify-center rounded border border-stroke px-6 py-2 font-medium text-black hover:shadow-1 dark:border-strokedark dark:text-white sm:w-full sm:text-sm"
onClick={resetAudioInput}
>
<Button onClick={resetAudioInput} style="outline-rounded" className={["text-black dark:text-white"]}>
Close
</button>
</Button>
{/* End button to close the audio input modal */}

{/* Start button to save changes audio input */}
<button
className="flex justify-center rounded bg-primary px-6 py-2 font-medium text-gray hover:bg-opacity-90 sm:w-full sm:text-sm"
onClick={submitAudio}
>
<Button onClick={submitAudio} style="rounded" className={["text-black dark:text-white"]}>
Save
</button>
</Button>
{/* End button to save changes audio input */}
</div>
);

/**
* Modal header right.
* @constant
* @type {JSX.Element}
*/
Comment on lines +81 to +85
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a javascript style comment which is not needed for typescript. Remove it.

const headerRight: JSX.Element = (
<span className="i-ic-baseline-fiber-manual-record text-red animate-pulse w-[22px] h-[22px]" />
);

return (
<>
<Suspense fallback={<Loader />}>
Expand All @@ -100,22 +105,22 @@ export default function DocumentTypeDocumentsPage() {
</Button>

<div
className={`${isDropdownCreateOpen ? "block" : "hidden"} absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none`}
className={`${isDropdownCreateOpen ? "block" : "hidden"} absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white dark:bg-black shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none`}
role="menu"
aria-orientation="vertical"
aria-labelledby="dropdownCreateArticle"
tabIndex={-1}
>
<div className="py-1" role="none">
<button
className="block px-4 py-2 text-sm text-gray-700 hover:bg-black hover:bg-opacity-5 w-full text-left"
className="block px-4 py-2 text-sm text-black dark:text-white bg-white dark:bg-black hover:opacity-70 dark:hover:opacity-70 w-full text-left"
onClick={addNewArticle}
>
<span className="i-ic-create mr-2" />
{`Add New ${documentType.titleSingular.translated}`}
</button>
<button
className="block px-4 py-2 text-sm text-gray-700 hover:bg-black hover:bg-opacity-5 w-full text-left"
className="block px-4 py-2 text-sm text-black dark:text-white bg-white dark:bg-black hover:opacity-70 dark:hover:opacity-70 w-full text-left"
onClick={() => setIsDialogOpen(true)}
>
<span className="i-ic-mic mr-2" />
Expand Down Expand Up @@ -145,6 +150,7 @@ export default function DocumentTypeDocumentsPage() {
disableOverlayClose={true}
onClose={resetAudioInput}
actions={modalActionAudioInput}
headerRight={isRecording ? headerRight : null}
title={"Tell your story"}
>
{status === ProcessingState.Ready ? (
Expand Down
6 changes: 4 additions & 2 deletions apps/cms/src/components/VoiceRecorder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ export default function VoiceRecorder(props: VoiceRecorderProps): JSX.Element {
* It handles starting, stopping, and restarting the speech recognition process.
*/
useEffect(() => {
const SpeechRecognition = new TanamSpeechRecognition();
const SpeechRecognition = new TanamSpeechRecognition({
continuous: true,
});

if (SpeechRecognition) {
recognitionRef.current = SpeechRecognition;
Expand Down Expand Up @@ -272,7 +274,7 @@ export default function VoiceRecorder(props: VoiceRecorderProps): JSX.Element {
<div className="text-center">
<h3 className="text-lg font-medium mb-2">Your Recording:</h3>
<div className="waveform-container">
<div id="overviewContainer" className="w-full h-30"></div>
<div id="overviewContainer" className="w-full h-30 bg-white"></div>
</div>
<audio controls src={audioUrl} id="audio" className="w-full rounded-md shadow-sm"></audio>
<button
Expand Down
27 changes: 22 additions & 5 deletions libs/domain-frontend/src/models/TanamSpeechRecognition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,15 @@ export interface SpeechRecognitionAlternative {
confidence: number;
}

export interface TanamSpeechRecognitionOptions {
lang?: string;
interimResults?: boolean;
maxAlternatives?: number;
continuous?: boolean;
}

export class TanamSpeechRecognition implements SpeechRecognition {
constructor() {
constructor(options: TanamSpeechRecognitionOptions = {}) {
const SpeechRecognitionConstructor = window.SpeechRecognition || window.webkitSpeechRecognition;

if (!SpeechRecognitionConstructor) {
Expand All @@ -93,6 +100,16 @@ export class TanamSpeechRecognition implements SpeechRecognition {

this.recognition = new SpeechRecognitionConstructor();

this.lang = options.lang || "en-US";
this.interimResults = options.interimResults ?? false;
this.maxAlternatives = options.maxAlternatives ?? 1;
this.continuous = options.continuous ?? false;

this.recognition.lang = this.lang;
this.recognition.interimResults = this.interimResults;
this.recognition.maxAlternatives = this.maxAlternatives;
this.recognition.continuous = this.continuous;

// Bind event handlers
this.recognition.onstart = this.handleEvent.bind(this, "onstart");
this.recognition.onend = this.handleEvent.bind(this, "onend");
Expand All @@ -107,10 +124,10 @@ export class TanamSpeechRecognition implements SpeechRecognition {
this.recognition.onnomatch = this.handleEvent.bind(this, "onnomatch");
}

public lang = "en-US";
public interimResults = false;
public maxAlternatives = 1;
public continuous = false;
public lang: string;
public interimResults: boolean;
public maxAlternatives: number;
public continuous: boolean;

public onaudiostart?: (event: Event) => void;
public onsoundstart?: (event: Event) => void;
Expand Down
2 changes: 1 addition & 1 deletion libs/ui-components/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import clsx from "clsx";
import React, {useEffect, useState} from "react";

interface ButtonProps {
title: string;
title?: string;
onClick?: () => Promise<void> | void;
style?: "normal" | "plain-text" | "rounded" | "outline" | "outline-rounded" | "icon" | "icon-rounded";
color?: "primary" | "meta-3" | "black";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export function DocumentTypeGenericList({documents, documentType, isLoading}: Ta
headers={["Title", "Created", "Status"]}
rows={documents.map((document, key) => [
<Link key={`${key}-${document.id}-id`} href={`/content/${document.documentType}/${document.id}`}>
<p className="font-medium text-black dark:text-white">{document.data[documentType.titleField] as string}</p>
<p className="font-medium text-black dark:text-white hover:opacity-70 dark:hover:opacity-70">
{document.data[documentType.titleField] as string}
</p>
</Link>,

<p key={`${key}-${document.id}-date`} className="text-black dark:text-white">
Expand Down
37 changes: 19 additions & 18 deletions libs/ui-components/src/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import {Button} from "./Button";

interface ModalProps {
isOpen: boolean;
Expand All @@ -7,6 +8,8 @@ interface ModalProps {
title: string;
children: React.ReactNode;
actions?: React.ReactNode;
headerRight?: React.ReactNode;
headerLeft?: React.ReactNode;
}

/**
Expand All @@ -20,6 +23,8 @@ export function Modal({
title,
children,
actions,
headerRight,
headerLeft,
onClose,
}: ModalProps): JSX.Element | null {
// If the modal is not open, don't render anything
Expand All @@ -40,38 +45,34 @@ export function Modal({

<div className="flex items-center justify-center min-h-screen px-4 text-center">
{/* Start modal content */}
<div className="inline-block bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full">
<div className="inline-block bg-white dark:bg-black rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full">
{/* Start modal header */}
<div className="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">{title}</h3>
<button
type="button"
className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
data-modal-hide="default-modal"
onClick={onClose}
>
<span className="i-ic-baseline-close w-[22px] h-[22px]" />
<span className="sr-only">Close modal</span>
</button>
{headerLeft ? headerLeft : <h3 className="text-xl font-semibold text-black dark:text-white">{title}</h3>}

{headerRight ? (
headerRight
) : (
<Button onClick={onClose} style="plain-text" className={["text-black dark:text-white !p-0"]}>
<span className="i-ic-baseline-close w-[22px] h-[22px]" />
<span className="sr-only">Close modal</span>
</Button>
)}
</div>
{/* End modal header */}

{/* Start modal body */}
<div className="p-4 md:p-5 space-y-4">{children}</div>
<div className="p-4 md:p-5 space-y-4 text-black dark:text-white">{children}</div>
{/* End modal body */}

{/* Modal footer */}
<div className="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600 sm:flex-row-reverse">
{actions ? (
actions
) : (
<button
type="button"
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
onClick={onClose}
>
<Button onClick={onClose} style="outline-rounded" className={["text-black dark:text-white"]}>
Close
</button>
</Button>
)}
</div>
</div>
Expand Down
Loading