-
+
{formatTime(duration)}
@@ -335,5 +262,4 @@ const AudioPlayer: React.FC
= ({ src, srcs }) => {
)
}
-
export default AudioPlayer
diff --git a/web/app/components/base/audio-gallery/__tests__/AudioPlayer.spec.tsx b/web/app/components/base/audio-gallery/__tests__/AudioPlayer.spec.tsx
index 04db89cdedf..a86188f635b 100644
--- a/web/app/components/base/audio-gallery/__tests__/AudioPlayer.spec.tsx
+++ b/web/app/components/base/audio-gallery/__tests__/AudioPlayer.spec.tsx
@@ -1,6 +1,5 @@
-import type { ToastHandle } from '@/app/components/base/toast'
import { act, fireEvent, render, screen } from '@testing-library/react'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import useThemeMock from '@/hooks/use-theme'
import { Theme } from '@/types/app'
import AudioPlayer from '../AudioPlayer'
@@ -263,14 +262,12 @@ describe('AudioPlayer — waveform generation', () => {
it('should show Toast when AudioContext is not available', async () => {
vi.stubGlobal('AudioContext', undefined)
+ const toastSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
render(
)
await advanceWaveformTimer()
- const toastFound = Array.from(document.body.querySelectorAll('div')).some(
- d => d.textContent?.includes('Web Audio API is not supported in this browser'),
- )
- expect(toastFound).toBe(true)
+ expect(toastSpy).toHaveBeenCalledWith('Web Audio API is not supported in this browser')
})
it('should set audio unavailable when URL is not http/https', async () => {
@@ -529,7 +526,7 @@ describe('AudioPlayer — missing coverage', () => {
it('should keep play button disabled when source is unavailable', async () => {
vi.stubGlobal('AudioContext', buildAudioContext(300))
- const toastSpy = vi.spyOn(Toast, 'notify').mockImplementation(() => ({} as unknown as ToastHandle))
+ const toastSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
render(
)
await advanceWaveformTimer() // sets isAudioAvailable to false (invalid protocol)
@@ -545,7 +542,7 @@ describe('AudioPlayer — missing coverage', () => {
})
it('should notify when toggle is invoked while audio is unavailable', async () => {
- const toastSpy = vi.spyOn(Toast, 'notify').mockImplementation(() => ({} as unknown as ToastHandle))
+ const toastSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
render(
)
const audio = document.querySelector('audio') as HTMLAudioElement
await act(async () => {
@@ -559,10 +556,7 @@ describe('AudioPlayer — missing coverage', () => {
props.onClick?.()
})
- expect(toastSpy).toHaveBeenCalledWith(expect.objectContaining({
- type: 'error',
- message: 'Audio element not found',
- }))
+ expect(toastSpy).toHaveBeenCalledWith('Audio element not found')
toastSpy.mockRestore()
})
})
@@ -626,7 +620,7 @@ describe('AudioPlayer — additional branch coverage', () => {
})
it('should ignore toggle click after audio error marks source unavailable', async () => {
- const toastSpy = vi.spyOn(Toast, 'notify').mockImplementation(() => ({} as unknown as ToastHandle))
+ const toastSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
render(
)
const audio = document.querySelector('audio') as HTMLAudioElement
await act(async () => {
diff --git a/web/app/components/base/block-input/__tests__/index.spec.tsx b/web/app/components/base/block-input/__tests__/index.spec.tsx
index 233de1937e0..01dbc7bfb81 100644
--- a/web/app/components/base/block-input/__tests__/index.spec.tsx
+++ b/web/app/components/base/block-input/__tests__/index.spec.tsx
@@ -1,6 +1,6 @@
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import BlockInput, { getInputKeys } from '../index'
vi.mock('@/utils/var', () => ({
@@ -14,7 +14,7 @@ vi.mock('@/utils/var', () => ({
describe('BlockInput', () => {
beforeEach(() => {
vi.clearAllMocks()
- vi.spyOn(Toast, 'notify')
+ vi.spyOn(toast, 'error').mockReturnValue('toast-error')
cleanup()
})
@@ -138,7 +138,7 @@ describe('BlockInput', () => {
fireEvent.change(textarea, { target: { value: '{{invalid}}' } })
await waitFor(() => {
- expect(Toast.notify).toHaveBeenCalled()
+ expect(toast.error).toHaveBeenCalled()
})
expect(onConfirm).not.toHaveBeenCalled()
})
diff --git a/web/app/components/base/block-input/index.tsx b/web/app/components/base/block-input/index.tsx
index 05bb95e10b5..528e4f78875 100644
--- a/web/app/components/base/block-input/index.tsx
+++ b/web/app/components/base/block-input/index.tsx
@@ -1,17 +1,14 @@
'use client'
-
import type { ChangeEvent, FC } from 'react'
import * as React from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { toast } from '@/app/components/base/ui/toast'
import { cn } from '@/utils/classnames'
import { checkKeys } from '@/utils/var'
import VarHighlight from '../../app/configuration/base/var-highlight'
-import Toast from '../toast'
-
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
-
export const getInputKeys = (value: string) => {
const keys = value.match(regex)?.map((item) => {
return item.replace('{{', '').replace('}}', '')
@@ -22,13 +19,11 @@ export const getInputKeys = (value: string) => {
keys.forEach((key) => {
if (keyObj[key])
return
-
keyObj[key] = true
res.push(key)
})
return res
}
-
export type IBlockInputProps = {
value: string
className?: string // wrapper class
@@ -36,20 +31,13 @@ export type IBlockInputProps = {
readonly?: boolean
onConfirm?: (value: string, keys: string[]) => void
}
-
-const BlockInput: FC
= ({
- value = '',
- className,
- readonly = false,
- onConfirm,
-}) => {
+const BlockInput: FC = ({ value = '', className, readonly = false, onConfirm }) => {
const { t } = useTranslation()
// current is used to store the current value of the contentEditable element
const [currentValue, setCurrentValue] = useState(value)
useEffect(() => {
setCurrentValue(value)
}, [value])
-
const contentEditableRef = useRef(null)
const [isEditing, setIsEditing] = useState(false)
useEffect(() => {
@@ -57,57 +45,42 @@ const BlockInput: FC = ({
// TODO: Focus at the click position
if (currentValue)
contentEditableRef.current.setSelectionRange(currentValue.length, currentValue.length)
-
contentEditableRef.current.focus()
}
}, [isEditing])
-
const style = cn({
'block h-full w-full break-all border-0 px-4 py-2 text-sm text-gray-900 outline-0': true,
'block-input--editing': isEditing,
})
-
const renderSafeContent = (value: string) => {
const parts = value.split(/(\{\{[^}]+\}\}|\n)/g)
return parts.map((part, index) => {
const variableMatch = /^\{\{([^}]+)\}\}$/.exec(part)
if (variableMatch) {
- return (
-
- )
+ return ()
}
if (part === '\n')
return
-
return {part}
})
}
-
// Not use useCallback. That will cause out callback get old data.
const handleSubmit = (value: string) => {
if (onConfirm) {
const keys = getInputKeys(value)
const result = checkKeys(keys)
if (!result.isValid) {
- Toast.notify({
- type: 'error',
- message: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }),
- })
+ toast.error(t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }))
return
}
onConfirm(value, keys)
}
}
-
const onValueChange = useCallback((e: ChangeEvent) => {
const value = e.target.value
setCurrentValue(value)
handleSubmit(value)
}, [])
-
// Prevent rerendering caused cursor to jump to the start of the contentEditable element
const TextAreaContentView = () => {
return (
@@ -116,10 +89,8 @@ const BlockInput: FC = ({
)
}
-
const placeholder = ''
const editAreaClassName = 'focus:outline-none bg-transparent text-sm'
-
const textAreaContent = (
!readonly && setIsEditing(true)}>
{isEditing
@@ -134,10 +105,10 @@ const BlockInput: FC = ({
onBlur={() => {
blur()
setIsEditing(false)
- // click confirm also make blur. Then outer value is change. So below code has problem.
- // setTimeout(() => {
- // handleCancel()
- // }, 1000)
+ // click confirm also make blur. Then outer value is change. So below code has problem.
+ // setTimeout(() => {
+ // handleCancel()
+ // }, 1000)
}}
/>
@@ -145,7 +116,6 @@ const BlockInput: FC