index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. 'use client'
  2. import { useRef, useState } from 'react'
  3. import { t } from 'i18next'
  4. import { useParams, usePathname } from 'next/navigation'
  5. import s from './style.module.css'
  6. import Tooltip from '@/app/components/base/tooltip'
  7. import { randomString } from '@/utils'
  8. import { textToAudio } from '@/service/share'
  9. type AudioBtnProps = {
  10. value: string
  11. className?: string
  12. isAudition?: boolean
  13. }
  14. const AudioBtn = ({
  15. value,
  16. className,
  17. isAudition,
  18. }: AudioBtnProps) => {
  19. const audioRef = useRef<HTMLAudioElement | null>(null)
  20. const [isPlaying, setIsPlaying] = useState(false)
  21. const [isPause, setPause] = useState(false)
  22. const [hasEnded, setHasEnded] = useState(false)
  23. const selector = useRef(`play-tooltip-${randomString(4)}`)
  24. const params = useParams()
  25. const pathname = usePathname()
  26. const removeCodeBlocks = (inputText: any) => {
  27. const codeBlockRegex = /```[\s\S]*?```/g
  28. return inputText.replace(codeBlockRegex, '')
  29. }
  30. const playAudio = async () => {
  31. const formData = new FormData()
  32. if (value !== '') {
  33. formData.append('text', removeCodeBlocks(value))
  34. let url = ''
  35. let isPublic = false
  36. if (params.token) {
  37. url = '/text-to-audio'
  38. isPublic = true
  39. }
  40. else if (params.appId) {
  41. if (pathname.search('explore/installed') > -1)
  42. url = `/installed-apps/${params.appId}/text-to-audio`
  43. else
  44. url = `/apps/${params.appId}/text-to-audio`
  45. }
  46. try {
  47. const audioResponse = await textToAudio(url, isPublic, formData)
  48. const blob_bytes = Buffer.from(audioResponse.data, 'latin1')
  49. const blob = new Blob([blob_bytes], { type: 'audio/wav' })
  50. const audioUrl = URL.createObjectURL(blob)
  51. const audio = new Audio(audioUrl)
  52. audioRef.current = audio
  53. audio.play().then(() => {
  54. setIsPlaying(true)
  55. }).catch(() => {
  56. setIsPlaying(false)
  57. URL.revokeObjectURL(audioUrl)
  58. })
  59. audio.onended = () => setHasEnded(true)
  60. }
  61. catch (error) {
  62. setIsPlaying(false)
  63. console.error('Error playing audio:', error)
  64. }
  65. }
  66. }
  67. const togglePlayPause = () => {
  68. if (audioRef.current) {
  69. if (isPlaying) {
  70. setPause(true)
  71. audioRef.current.pause()
  72. }
  73. else if (!hasEnded) {
  74. setPause(false)
  75. audioRef.current.play()
  76. }
  77. else if (!isPlaying) {
  78. playAudio().then()
  79. }
  80. setIsPlaying(prevIsPlaying => !prevIsPlaying)
  81. }
  82. else {
  83. playAudio().then()
  84. }
  85. }
  86. return (
  87. <div className={`${(isPlaying && !hasEnded) ? 'mr-1' : className}`}>
  88. <Tooltip
  89. selector={selector.current}
  90. content={(!isPause ? ((isPlaying && !hasEnded) ? t('appApi.playing') : t('appApi.play')) : t('appApi.pause')) as string}
  91. className='z-10'
  92. >
  93. <div
  94. className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || 'rounded-md bg-white'}`}
  95. style={{ boxShadow: !isAudition ? '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' : '' }}
  96. onClick={togglePlayPause}>
  97. <div className={`w-6 h-6 rounded-md ${!isAudition ? 'hover:bg-gray-200' : 'hover:bg-gray-50'} ${!isPause ? ((isPlaying && !hasEnded) ? s.playIcon : s.stopIcon) : s.pauseIcon}`}></div>
  98. </div>
  99. </Tooltip>
  100. </div>
  101. )
  102. }
  103. export default AudioBtn