Ajusta player de audio e permite seek
All checks were successful
All checks were successful
This commit is contained in:
parent
d125930cf6
commit
bc67dc01ef
4 changed files with 80 additions and 33 deletions
|
|
@ -133,7 +133,7 @@ function AudioWaveform({
|
|||
}) {
|
||||
const playedBars = Math.round(progress * peaks.length)
|
||||
return (
|
||||
<div className="flex h-8 items-center gap-[2px]">
|
||||
<div className="flex h-8 items-center gap-[2px] overflow-hidden">
|
||||
{peaks.map((value, index) => {
|
||||
const height = Math.max(4, Math.round(value * 24))
|
||||
const played = index <= playedBars
|
||||
|
|
@ -191,7 +191,7 @@ function AudioAttachmentPlayer({
|
|||
const decoded = await audioContext.decodeAudioData(buffer)
|
||||
await audioContext.close()
|
||||
if (!cancelled) {
|
||||
setPeaks(extractPeaks(decoded))
|
||||
setPeaks(extractPeaks(decoded, 36))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Falha ao gerar waveform:", error)
|
||||
|
|
@ -246,10 +246,11 @@ function AudioAttachmentPlayer({
|
|||
|
||||
const progress = duration > 0 ? currentTime / duration : 0
|
||||
const sizeLabel = formatAttachmentSize(size)
|
||||
const canSeek = duration > 0
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center gap-3 rounded-2xl border px-3 py-2 ${
|
||||
className={`flex w-full max-w-[280px] items-center gap-3 rounded-2xl border px-3 py-2 ${
|
||||
isAgent ? "border-white/10 bg-white/10 text-white" : "border-slate-200 bg-white text-slate-900"
|
||||
}`}
|
||||
>
|
||||
|
|
@ -264,14 +265,34 @@ function AudioAttachmentPlayer({
|
|||
{isPlaying ? <Pause className="size-4" /> : <Play className="size-4" />}
|
||||
</button>
|
||||
|
||||
<div className="flex-1">
|
||||
{isLoadingWaveform ? (
|
||||
<div className={`h-8 rounded-lg ${isAgent ? "bg-white/10" : "bg-slate-100"}`} />
|
||||
) : peaks.length > 0 ? (
|
||||
<AudioWaveform peaks={peaks} progress={progress} isAgent={isAgent} />
|
||||
) : (
|
||||
<div className={`h-8 rounded-lg ${isAgent ? "bg-white/10" : "bg-slate-100"}`} />
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<button
|
||||
type="button"
|
||||
onPointerDown={(event) => {
|
||||
if (!audioRef.current || duration <= 0) return
|
||||
const rect = event.currentTarget.getBoundingClientRect()
|
||||
if (rect.width <= 0) return
|
||||
const ratio = (event.clientX - rect.left) / rect.width
|
||||
const clamped = Math.min(1, Math.max(0, ratio))
|
||||
const targetTime = clamped * duration
|
||||
audioRef.current.currentTime = targetTime
|
||||
setCurrentTime(targetTime)
|
||||
}}
|
||||
aria-label="Buscar posição do áudio"
|
||||
className={[
|
||||
"w-full rounded-lg bg-transparent p-0 text-left transition-colors focus-visible:outline-none",
|
||||
canSeek ? "cursor-pointer" : "cursor-default",
|
||||
canSeek ? (isAgent ? "hover:bg-white/10" : "hover:bg-slate-100") : "",
|
||||
].join(" ")}
|
||||
>
|
||||
{isLoadingWaveform ? (
|
||||
<div className={`h-8 rounded-lg ${isAgent ? "bg-white/10" : "bg-slate-100"}`} />
|
||||
) : peaks.length > 0 ? (
|
||||
<AudioWaveform peaks={peaks} progress={progress} isAgent={isAgent} />
|
||||
) : (
|
||||
<div className={`h-8 rounded-lg ${isAgent ? "bg-white/10" : "bg-slate-100"}`} />
|
||||
)}
|
||||
</button>
|
||||
<div className={`mt-1 flex items-center justify-between text-[10px] ${isAgent ? "text-white/60" : "text-slate-400"}`}>
|
||||
<span>{formatDuration(currentTime)}</span>
|
||||
<span className="truncate">{sizeLabel ?? formatDuration(duration)}</span>
|
||||
|
|
@ -282,7 +303,9 @@ function AudioAttachmentPlayer({
|
|||
type="button"
|
||||
onClick={handleDownload}
|
||||
className={`flex size-8 items-center justify-center rounded-md ${
|
||||
isAgent ? "text-white/70 hover:text-white" : "text-slate-500 hover:text-slate-700"
|
||||
isAgent
|
||||
? "text-white/70 hover:bg-white/10 hover:text-white"
|
||||
: "text-slate-500 hover:bg-slate-100 hover:text-slate-700"
|
||||
}`}
|
||||
aria-label="Baixar áudio"
|
||||
>
|
||||
|
|
@ -1147,12 +1170,12 @@ export function ChatWidget({ ticketId, ticketRef }: ChatWidgetProps) {
|
|||
return (
|
||||
<div
|
||||
key={att.storageId}
|
||||
className="flex items-center gap-2 rounded-lg bg-slate-100 px-2 py-1 text-xs"
|
||||
className="flex w-full items-center gap-2 rounded-lg bg-slate-100 px-2 py-1 text-xs"
|
||||
>
|
||||
<audio controls src={att.previewUrl} className="h-7 w-40" />
|
||||
<audio controls src={att.previewUrl} className="h-8 w-full min-w-0" />
|
||||
<button
|
||||
onClick={() => handleRemoveAttachment(att.storageId)}
|
||||
className="flex size-6 items-center justify-center rounded-full text-slate-500 hover:bg-slate-200"
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-full text-slate-500 hover:bg-slate-200"
|
||||
aria-label="Remover áudio"
|
||||
>
|
||||
<X className="size-3" />
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ function AudioWaveform({
|
|||
}) {
|
||||
const playedBars = Math.round(progress * peaks.length)
|
||||
return (
|
||||
<div className="flex h-9 items-center gap-[2px]">
|
||||
<div className="flex h-9 items-center gap-[2px] overflow-hidden">
|
||||
{peaks.map((value, index) => {
|
||||
const height = Math.max(4, Math.round(value * 28))
|
||||
const played = index <= playedBars
|
||||
|
|
@ -100,7 +100,7 @@ function AudioAttachmentPlayer({
|
|||
const decoded = await audioContext.decodeAudioData(buffer)
|
||||
await audioContext.close()
|
||||
if (!cancelled) {
|
||||
setPeaks(extractPeaks(decoded))
|
||||
setPeaks(extractPeaks(decoded, 36))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Falha ao gerar waveform:", error)
|
||||
|
|
@ -155,11 +155,22 @@ function AudioAttachmentPlayer({
|
|||
|
||||
const progress = duration > 0 ? currentTime / duration : 0
|
||||
const sizeLabel = formatAttachmentSize(size)
|
||||
const canSeek = duration > 0
|
||||
const handleSeek = (event: { currentTarget: HTMLButtonElement; clientX: number }) => {
|
||||
if (!audioRef.current || duration <= 0) return
|
||||
const rect = event.currentTarget.getBoundingClientRect()
|
||||
if (rect.width <= 0) return
|
||||
const ratio = (event.clientX - rect.left) / rect.width
|
||||
const clamped = Math.min(1, Math.max(0, ratio))
|
||||
const targetTime = clamped * duration
|
||||
audioRef.current.currentTime = targetTime
|
||||
setCurrentTime(targetTime)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-2xl border px-3 py-2",
|
||||
"flex w-full max-w-[280px] items-center gap-3 rounded-2xl border px-3 py-2",
|
||||
tone === "dark"
|
||||
? "border-white/10 bg-white/10 text-white"
|
||||
: "border-slate-200 bg-white text-slate-900"
|
||||
|
|
@ -179,14 +190,25 @@ function AudioAttachmentPlayer({
|
|||
{isPlaying ? <Pause className="size-4" /> : <Play className="size-4" />}
|
||||
</Button>
|
||||
|
||||
<div className="flex-1">
|
||||
{isLoadingWaveform ? (
|
||||
<div className={cn("h-9 rounded-lg", tone === "dark" ? "bg-white/10" : "bg-slate-100")} />
|
||||
) : peaks.length > 0 ? (
|
||||
<AudioWaveform peaks={peaks} progress={progress} tone={tone} />
|
||||
) : (
|
||||
<div className={cn("h-9 rounded-lg", tone === "dark" ? "bg-white/10" : "bg-slate-100")} />
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<button
|
||||
type="button"
|
||||
onPointerDown={handleSeek}
|
||||
aria-label="Buscar posição do áudio"
|
||||
className={cn(
|
||||
"w-full rounded-lg bg-transparent p-0 text-left transition-colors focus-visible:outline-none",
|
||||
canSeek ? "cursor-pointer" : "cursor-default",
|
||||
canSeek && (tone === "dark" ? "hover:bg-white/10" : "hover:bg-slate-100")
|
||||
)}
|
||||
>
|
||||
{isLoadingWaveform ? (
|
||||
<div className={cn("h-9 rounded-lg", tone === "dark" ? "bg-white/10" : "bg-slate-100")} />
|
||||
) : peaks.length > 0 ? (
|
||||
<AudioWaveform peaks={peaks} progress={progress} tone={tone} />
|
||||
) : (
|
||||
<div className={cn("h-9 rounded-lg", tone === "dark" ? "bg-white/10" : "bg-slate-100")} />
|
||||
)}
|
||||
</button>
|
||||
<div
|
||||
className={cn(
|
||||
"mt-1 flex items-center justify-between text-[10px]",
|
||||
|
|
@ -204,7 +226,9 @@ function AudioAttachmentPlayer({
|
|||
size="icon"
|
||||
className={cn(
|
||||
"size-8",
|
||||
tone === "dark" ? "text-white/70 hover:text-white" : "text-slate-500 hover:text-slate-700"
|
||||
tone === "dark"
|
||||
? "text-white/70 hover:bg-white/10 hover:text-white"
|
||||
: "text-slate-500 hover:bg-slate-100 hover:text-slate-700"
|
||||
)}
|
||||
onClick={handleDownload}
|
||||
aria-label="Baixar áudio"
|
||||
|
|
|
|||
|
|
@ -826,12 +826,12 @@ export function ChatWidget() {
|
|||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-2 py-1"
|
||||
className="flex w-full items-center gap-2 rounded-lg border border-slate-200 bg-white px-2 py-1"
|
||||
>
|
||||
<audio controls src={file.previewUrl} className="h-8 w-40" />
|
||||
<audio controls src={file.previewUrl} className="h-9 w-full min-w-0" />
|
||||
<button
|
||||
onClick={() => removeAttachment(index)}
|
||||
className="flex size-6 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100"
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100"
|
||||
aria-label="Remover áudio"
|
||||
>
|
||||
<X className="size-3" />
|
||||
|
|
|
|||
|
|
@ -547,8 +547,8 @@ export function TicketChatPanel({ ticketId }: TicketChatPanelProps) {
|
|||
)}
|
||||
|
||||
{pendingAudio && (
|
||||
<div className="mb-2 flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-2 py-1">
|
||||
<audio controls src={pendingAudio.previewUrl} className="h-8 w-44" />
|
||||
<div className="mb-2 flex w-full items-center gap-2 rounded-lg border border-slate-200 bg-white px-2 py-1">
|
||||
<audio controls src={pendingAudio.previewUrl} className="h-9 w-full min-w-0" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
|
|
@ -557,7 +557,7 @@ export function TicketChatPanel({ ticketId }: TicketChatPanelProps) {
|
|||
}
|
||||
setPendingAudio(null)
|
||||
}}
|
||||
className="flex size-7 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100"
|
||||
aria-label="Remover áudio"
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue