Skip to content

Commit

Permalink
Adiciona antdm, transforma as vozes em lista e resolve carregamento d…
Browse files Browse the repository at this point in the history
…as vozes
  • Loading branch information
geraldohomero committed Aug 11, 2024
1 parent 8ae9bb7 commit b0d0f78
Show file tree
Hide file tree
Showing 10 changed files with 1,135 additions and 762 deletions.
76 changes: 28 additions & 48 deletions app/components/listaVozes.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"use client";

import React, { useEffect, useState } from 'react';
import { CssBaseline, Container, Grid, Button, ThemeProvider, CircularProgress } from '@mui/material';
import TextInput from './textInput';
import VoiceSelect from './voiceSelect';
import VoiceDetails from './voiceDetails';
import darkTheme from './theme';
import { Layout, Row, Col, Spin, Input, Typography } from 'antd';
import { Voice } from '../types/voice';
import VoiceSelect from './voiceSelect';

const { Header, Content } = Layout;
const { TextArea } = Input;
const { Title } = Typography;

const fetchVoices = async (setVozes: React.Dispatch<React.SetStateAction<Voice[]>>, setLoadingVoiceSelect: React.Dispatch<React.SetStateAction<boolean>>) => {
setLoadingVoiceSelect(true);
Expand Down Expand Up @@ -89,61 +90,40 @@ const ListaVozes: React.FC = () => {
}, []);

return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<Container>
<Grid container justifyContent="center" alignItems="center" style={{ minHeight: '20vh', paddingTop: '20px' }}>
<Grid item xs={12} md={6}>
<TextInput
<Layout>
<Header style={{ textAlign: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Title style={{ color: 'white' }}>App Masters</Title>
</Header>
<Content style={{ padding: '20px' }}>
<Row justify="center" align="middle" style={{ minHeight: '20vh' }}>
<Col span={12}>
<TextArea
value={texto}
onChange={(e) => setTexto(e.target.value)}
placeholder="Digite o texto aqui"
placeholder="Digite o texto aqui e depois selecione uma voz"
rows={3}
/>
{loadingVoiceSelect ? (
<CircularProgress />
<Spin />
) : (
<VoiceSelect
vozes={vozes}
selectedVoice={selectedVoice}
onChange={(voiceId) => {
const selected = vozes.find(voice => voice.voice_id === voiceId);
setSelectedVoice(selected || null);
const voice = vozes.find(v => v.voice_id === voiceId) || null;
setSelectedVoice(voice);
}}
texto={texto}
handlePlay={handlePlay}
handleDownload={handleDownload}
setVoiceLoadingState={setVoiceLoadingState}
voiceLoadingState={voiceLoadingState}
/>
)}
{selectedVoice && (
<Grid container spacing={2} style={{ marginTop: '20px' }}>
<Grid item xs={12}>
<VoiceDetails selectedVoice={selectedVoice} />
</Grid>
{texto && (
<>
<Button
variant="contained"
color="primary"
onClick={() => handlePlay(selectedVoice.voice_id, texto, setVoiceLoadingState)}
disabled={voiceLoadingState === `play-${selectedVoice.voice_id}`}
style={{ marginTop: '10px', marginLeft: '18px' }}
>
{voiceLoadingState === `play-${selectedVoice.voice_id}` ? <CircularProgress size={24} /> : 'Play'}
</Button>
<Button
variant="contained"
color="secondary"
onClick={() => handleDownload(selectedVoice.voice_id, texto, setVoiceLoadingState)}
disabled={voiceLoadingState === `download-${selectedVoice.voice_id}`}
style={{ marginTop: '10px', marginLeft: '10px' }}
>
{voiceLoadingState === `download-${selectedVoice.voice_id}` ? <CircularProgress size={24} /> : 'Download'}
</Button>
</>
)}
</Grid>
)}
</Grid>
</Grid>
</Container>
</ThemeProvider>
</Col>
</Row>
</Content>
</Layout>
);
};

Expand Down
4 changes: 3 additions & 1 deletion app/components/textInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React from 'react';
import { Input } from 'antd';

interface TextInputProps {
value: string;
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
placeholder: string;
}

const TextInput: React.FC<TextInputProps> = ({ value, onChange, placeholder }) => {
return (
<textarea
<Input.TextArea
value={value}
onChange={onChange}
placeholder={placeholder}
Expand Down
9 changes: 0 additions & 9 deletions app/components/theme.tsx

This file was deleted.

81 changes: 0 additions & 81 deletions app/components/voiceDetails.tsx

This file was deleted.

134 changes: 117 additions & 17 deletions app/components/voiceSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,132 @@
import React, { useState } from 'react';
import { Select, MenuItem } from '@mui/material';
import { List, Card, Typography, Badge, Space, Button } from 'antd';
import { PlayCircleOutlined, PauseCircleOutlined, LoadingOutlined } from '@ant-design/icons';
import { Voice } from '../types/voice';

interface VoiceSelectProps {
vozes: Voice[];
selectedVoice: Voice | null;
onChange: (voiceId: string) => void;
texto: string;
handlePlay: (voiceId: string, texto: string, setVoiceLoadingState: React.Dispatch<React.SetStateAction<string | null>>) => void;
handleDownload: (voiceId: string, texto: string, setVoiceLoadingState: React.Dispatch<React.SetStateAction<string | null>>) => void;
setVoiceLoadingState: React.Dispatch<React.SetStateAction<string | null>>;
voiceLoadingState: string | null;
}

const VoiceSelect: React.FC<VoiceSelectProps> = ({ vozes, selectedVoice, onChange }) => {
const VoiceSelect: React.FC<VoiceSelectProps> = ({ vozes, selectedVoice, onChange, texto, handlePlay, handleDownload, setVoiceLoadingState, voiceLoadingState }) => {
const [audio, setAudio] = useState<HTMLAudioElement | null>(null);
const [isPlaying, setIsPlaying] = useState<string | null>(null);
const sortedVozes = [...vozes].sort((a, b) => a.name.localeCompare(b.name));

const handlePlayPause = (voice: Voice) => {
if (isPlaying === voice.voice_id) {
audio?.pause();
setIsPlaying(null);
} else {
if (audio) {
audio.pause();
}
const newAudio = new Audio(voice.preview_url);
newAudio.play();
setAudio(newAudio);
setIsPlaying(voice.voice_id);
newAudio.onended = () => setIsPlaying(null);
}
};

const handleCardClick = (voiceId: string) => {
if (selectedVoice?.voice_id === voiceId) {
onChange('');
} else {
onChange(voiceId);
}
};

return (
<Select
value={selectedVoice ? selectedVoice.voice_id : ''}
onChange={(e) => onChange(e.target.value)}
displayEmpty
fullWidth
>
<MenuItem value="">
<em>Selecione uma voz</em>
</MenuItem>
{sortedVozes.map(voice => (
<MenuItem key={voice.voice_id} value={voice.voice_id}>
{voice.name} - {voice.labels.description}
</MenuItem>
))}
</Select>
<div style={{ overflow: 'auto' }}>
<List
dataSource={sortedVozes}
renderItem={voice => (
<List.Item>
<Card
hoverable
onClick={() => handleCardClick(voice.voice_id)}
style={{
borderColor: selectedVoice?.voice_id === voice.voice_id ? '#1890ff' : '#f0f0f0',
borderWidth: '2px',
width: '100%',
height: '100%',
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Card.Meta
title={<Typography.Text className="responsive-title">
{voice.name}
</Typography.Text>}
/>
<div className="voice-card-buttons">
<Button
type="text"
onClick={(e) => {
e.stopPropagation();
handlePlayPause(voice);
}}
style={{ display: 'flex', alignItems: 'center' }}
>
{isPlaying === voice.voice_id ? (
<PauseCircleOutlined />
) : (
<PlayCircleOutlined />
)}
<Typography.Text style={{ marginLeft: '5px', fontSize: '10px' }}>Preview</Typography.Text>
</Button>
{selectedVoice?.voice_id === voice.voice_id && texto && (
<>
{voiceLoadingState === `play-${voice.voice_id}` ? (
<LoadingOutlined style={{ marginLeft: '10px' }} />
) : (
<Button
type="primary"
onClick={(e) => {
e.stopPropagation();
handlePlay(voice.voice_id, texto, setVoiceLoadingState);
}}
style={{ marginLeft: '10px' }}
>
Play
</Button>
)}
{voiceLoadingState === `download-${voice.voice_id}` ? (
<LoadingOutlined style={{ marginLeft: '10px' }} />
) : (
<Button
type="default"
onClick={(e) => {
e.stopPropagation();
handleDownload(voice.voice_id, texto, setVoiceLoadingState);
}}
style={{ marginLeft: '10px' }}
>
Download
</Button>
)}
</>
)}
</div>
</div>
<Space direction="horizontal" style={{ marginTop: '10px' }} wrap>
<Badge count={voice.category} style={{ backgroundColor: '#52c41a' }} />
<Badge count={voice.description} style={{ backgroundColor: '#52c41a' }} />
{voice.labels && Object.values(voice.labels).map((value, index) => (
<Badge key={index} count={value} style={{ backgroundColor: '#52c41a' }} />
))}
</Space>
</Card>
</List.Item>
)}
/>
</div>
);
};

Expand Down
Loading

0 comments on commit b0d0f78

Please sign in to comment.