import React, { useState, Dispatch, SetStateAction, useEffect } from 'react' import { Button, Input, KeyValuePair, Modal, Text } from 'aws-northstar/components' import { Container, Grid, Stack } from 'aws-northstar/layouts' import ColumnLayout, { Column } from 'aws-northstar/layouts/ColumnLayout' import type { FlashbarMessage } from 'aws-northstar/components/Flashbar' import { Cancel, Translate } from '@material-ui/icons' import { useTranslation } from 'react-i18next' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import Evaporate from 'evaporate' import Md5 from 'js-md5' import { sha256 as Sha256 } from 'js-sha256' import { v4 as uuidv4 } from 'uuid' import RepositoryFactory from '../repositories/RepositoryFactory' import type { Language, Transcribe } from '../repositories/TranscribeRepository' import SelectBox from './SelectBox' import FileUpload from './FileUpload' type DictationInput = { file: File | null lang: Language num: number addr: string } type DictationInputError = { file: string addr: string } type DictationProps = { handleNotification: (message: FlashbarMessage) => void handleIsLoading: Dispatch> handleProgress: Dispatch> } const Dictation: React.FC = ({ handleNotification, handleIsLoading, handleProgress }: DictationProps) => { const { t: transCommon } = useTranslation('common') const { t: transDictation } = useTranslation('dictation') const [isMounted, setIsMounted] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) const [input, setInput] = useState({ file: null, lang: 'ja-JP', num: 2, addr: '' }) const [inputError, setInputError] = useState({ file: '', addr: '' }) const transcribe = RepositoryFactory.get('transcribe') const evaporate = new Evaporate({ awsRegion: process.env.REACT_APP_AWS_REGION, signerUrl: `${process.env.REACT_APP_API_ENDPOINT}/sign`, bucket: process.env.REACT_APP_S3_BUCKET!, aws_key: process.env.REACT_APP_AWS_ACCESS_KEY, cloudfront: true, sendCanonicalRequestToSignerUrl: true, logging: false, computeContentMd5: true, cryptoHexEncodedHash256: (data) => { const crypto = Sha256.create() crypto.update(data!) return crypto.hex() }, cryptoMd5Method: (data) => { const crypto = Md5.create() crypto.update(data) return crypto.base64() } }) useEffect(() => { setIsMounted(true) }, []) useEffect(() => { if (isMounted) validateEmailInput(input['addr']) }, [input['addr']]) const validateEmailInput = (addr: string): boolean => { let error = '' if (addr.length > 255) { error = transDictation('invalidEmailOver255Chars') } else if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(addr) === false) { error = transDictation('invalidEmailFormat') } setInputError({ ...inputError, addr: error }) return error === '' } useEffect(() => { if (isMounted) validateFileInput(input['file']) }, [input['file']]) const validateFileInput = (file: File | null): boolean => { let error = '' if (file === null) { error = transDictation('invalidFileEmpty') } else if (file.size > 2147483648) { error = transDictation('invalidFileSize') } setInputError({ ...inputError, file: error }) return error === '' } const handleChangeEmailInput = (addr: string) => { setInput({ ...input, addr }) } const handleClickModalOpenButton = () => { const isValid = validateEmailInput(input['addr']) && validateFileInput(input['file']) if (isValid) { setIsModalOpen(true) } } const handleClickDictationButton = () => { handleIsLoading(true) const fileName = uuidv4() evaporate.add({ name: fileName, file: input['file']!, progress: (progress: any) => { handleProgress(Math.floor(progress * 90)) }, complete: async () => { const payload: Transcribe = { ...input, file: fileName } try { await transcribe.start(payload) handleNotification({ header: transDictation('dictationRequestSucceeded'), content: transDictation('dictationRequestDescription'), type: 'success', dismissible: true }) } catch (err) { handleNotification({ header: transDictation('dictationRequestFailed'), content: transDictation('dictationRequestFailedDescription'), type: 'error', dismissible: true }) } finally { handleProgress(null) handleIsLoading(false) setIsModalOpen(false) } } }) } const languages = [ { label: transCommon('japanese'), value: 'ja-JP' }, { label: transCommon('usEnglish'), value: 'en-US' }, { label: transCommon('ukEnglish'), value: 'en-GB' } ] const language = languages.find((lng) => lng.value === input['lang']) const langValue = language ? language['label'] : '' const speakerNumbers = [...Array(10)].map((_, i) => ({ label: String(i + 1), value: String(i + 1) })) const acceptMimeTypes = [ 'audio/mpeg', 'audio/ogg', 'audio/wav', 'audio/x-flac', 'audio/amr', 'audio/webm', 'video/mp4', 'video/webm' ] return ( setIsModalOpen(false)} > {transDictation('startDictationConfirm')} setInput({ ...input, file })} error={inputError['file']} /> {transDictation('language')} setInput({ ...input, lang: lang as Language }) } options={languages} value={String(input['lang'])} /> {transDictation('numberOfSpeakers')} setInput({ ...input, num: Number(num) })} options={speakerNumbers} value={String(input['num'])} /> {transDictation('receiptAddress')} {inputError['addr']} ) } export default Dictation