fix: sync and update the evaluation result

This commit is contained in:
jialin
2025-06-18 11:48:09 +08:00
parent 9d1e6df8f0
commit 9295f9cef3
11 changed files with 196 additions and 136 deletions
+16 -8
View File
@@ -1,7 +1,7 @@
import { formatTime } from '@/utils/index'; import { formatTime } from '@/utils/index';
import { DeleteOutlined, DownloadOutlined } from '@ant-design/icons'; import { DeleteOutlined, DownloadOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Dropdown, Slider, type MenuProps } from 'antd'; import { Button, Dropdown, Slider, Tooltip, type MenuProps } from 'antd';
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
import { round } from 'lodash'; import { round } from 'lodash';
import React, { import React, {
@@ -20,7 +20,7 @@ interface AudioPlayerProps {
url: string; url: string;
speed?: number; speed?: number;
ref?: any; ref?: any;
name?: string; name: string;
height?: number; height?: number;
width?: number; width?: number;
duration?: number; duration?: number;
@@ -107,6 +107,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
autoplay = false, autoplay = false,
speed: defaultSpeed = 1, speed: defaultSpeed = 1,
actions = ['delete'], actions = ['delete'],
name,
onDelete onDelete
} = props; } = props;
const audioRef = React.useRef<HTMLAudioElement>(null); const audioRef = React.useRef<HTMLAudioElement>(null);
@@ -117,6 +118,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
currentTime: 0, currentTime: 0,
duration: 0 duration: 0
}); });
console.log('audioState', name);
const [playOn, setPlayOn] = React.useState<boolean>(false); const [playOn, setPlayOn] = React.useState<boolean>(false);
const [speakerOn, setSpeakerOn] = React.useState<boolean>(false); const [speakerOn, setSpeakerOn] = React.useState<boolean>(false);
const [volume, setVolume] = React.useState<number>(1); const [volume, setVolume] = React.useState<number>(1);
@@ -246,7 +248,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = 'audio.mp3'; // Default filename link.download = filename || 'audio.mp3'; // Default filename
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
link.remove(); link.remove();
@@ -304,7 +306,11 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
return ( return (
<div <div
className={styles.wrapper} className={styles.wrapper}
style={{ width: props.width || '100%', height: props.height || '54px' }} style={{
width: props.width || '100%',
height: props.height || '60px',
position: 'relative'
}}
> >
<div className="inner"> <div className="inner">
<Button <Button
@@ -327,10 +333,12 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
) )
} }
></Button> ></Button>
<span className="time current"> <Tooltip title={name}>
{formatTime(audioState.currentTime)} /{' '} <span className="time current">
{formatTime(audioState.duration)} {formatTime(audioState.currentTime)} /{' '}
</span> {formatTime(audioState.duration)}
</span>
</Tooltip>
<div className="slider"> <div className="slider">
<Slider <Slider
tooltip={{ open: false }} tooltip={{ open: false }}
@@ -9,11 +9,22 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { baseColorMap } from '../../config'; import { baseColorMap } from '../../config';
const DownloadButton = styled(Button)` const DownloadButton = styled(Button).attrs({
className: 'download-button'
})`
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; right: 10px;
z-index: 10; z-index: 10;
display: none;
`;
const CardWrapperBox = styled.div`
&:hover {
.download-button {
display: flex;
}
}
`; `;
interface RequestTokenInnerProps { interface RequestTokenInnerProps {
@@ -66,29 +77,31 @@ const RequestTokenInner: React.FC<RequestTokenInnerProps> = (props) => {
const intl = useIntl(); const intl = useIntl();
return ( return (
<CardWrapper style={{ width: '100%', position: 'relative' }}> <CardWrapperBox>
<DownloadButton <CardWrapper style={{ width: '100%', position: 'relative' }}>
type="link" <DownloadButton
icon={<ExportOutlined />} type="link"
size="small" icon={<ExportOutlined />}
onClick={onExport} size="small"
> onClick={onExport}
{intl.formatMessage({ id: 'common.button.export' })} >
</DownloadButton> {intl.formatMessage({ id: 'common.button.export' })}
<SimpleCard dataList={dataList} height={80}></SimpleCard> </DownloadButton>
<MixLineBar <SimpleCard dataList={dataList} height={80}></SimpleCard>
chartData={{ <MixLineBar
line: requestData, chartData={{
bar: tokenData line: requestData,
}} bar: tokenData
seriesData={[]} }}
xAxisData={xAxisData} seriesData={[]}
height={360} xAxisData={xAxisData}
smooth={true} height={360}
legendData={legendData} smooth={true}
labelFormatter={labelFormatter} legendData={legendData}
></MixLineBar> labelFormatter={labelFormatter}
</CardWrapper> ></MixLineBar>
</CardWrapper>
</CardWrapperBox>
); );
}; };
+67 -60
View File
@@ -112,6 +112,7 @@ const AddModal: FC<AddModalProps> = (props) => {
unlockWarningStatus, unlockWarningStatus,
handleOnValuesChange: handleOnValuesChangeBefore, handleOnValuesChange: handleOnValuesChangeBefore,
handleEvaluateOnChange, handleEvaluateOnChange,
clearCahceFormValues,
warningStatus, warningStatus,
submitAnyway submitAnyway
} = useCheckCompatibility(); } = useCheckCompatibility();
@@ -123,13 +124,6 @@ const AddModal: FC<AddModalProps> = (props) => {
const [collapsed, setCollapsed] = useState<boolean>(false); const [collapsed, setCollapsed] = useState<boolean>(false);
const [isGGUF, setIsGGUF] = useState<boolean>(false); const [isGGUF, setIsGGUF] = useState<boolean>(false);
const modelFileRef = useRef<any>(null); const modelFileRef = useRef<any>(null);
const isHolderRef = useRef<{
model: boolean;
file: boolean;
}>({
model: false,
file: false
});
const evaluateStateRef = useRef<{ state: EvaluateProccessType }>({ const evaluateStateRef = useRef<{ state: EvaluateProccessType }>({
state: 'form' state: 'form'
}); });
@@ -141,6 +135,7 @@ const AddModal: FC<AddModalProps> = (props) => {
*/ */
const updateRequestModelId = () => { const updateRequestModelId = () => {
requestModelIdRef.current += 1; requestModelIdRef.current += 1;
return requestModelIdRef.current;
}; };
/** /**
@@ -152,17 +147,6 @@ const AddModal: FC<AddModalProps> = (props) => {
evaluateStateRef.current.state = state; evaluateStateRef.current.state = state;
}; };
/**
*
* @param flag set the evaluate status of the model or file
*/
const setIsHolderRef = (flag: Record<string, boolean>) => {
isHolderRef.current = {
...isHolderRef.current,
...flag
};
};
const handleOnValuesChange = (data: { const handleOnValuesChange = (data: {
changedValues: any; changedValues: any;
allValues: any; allValues: any;
@@ -188,29 +172,30 @@ const AddModal: FC<AddModalProps> = (props) => {
return categories || null; return categories || null;
}; };
const handleSelectModelFile = async (item: any, evaluate?: boolean) => { const handleSelectModelFile = async (item: any) => {
form.current?.form?.resetFields(resetFieldsByFile); form.current?.form?.resetFields(resetFieldsByFile);
const modelInfo = onSelectModel(selectedModel, props.source); const modelInfo = onSelectModel(selectedModel, props.source);
/** display the selected model file information, but not
* unitl the evaluate result is ready
*/
form.current?.setFieldsValue?.({ form.current?.setFieldsValue?.({
...modelInfo, ...modelInfo,
file_name: item.fakeName, file_name: item.fakeName,
categories: getCategory(item) categories: getCategory(item)
}); });
console.log('handleSelectModelFile', item);
await new Promise((resolve) => { await new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve(true); resolve(true);
}, 0); }, 0);
}); });
// evaluate the form data when select a model file
if (item.fakeName) { if (item.fakeName) {
unlockWarningStatus(); unlockWarningStatus();
const currentModelId = requestModelIdRef.current; const currentModelId = updateRequestModelId();
setEvaluteState(EvaluateProccess.file); setEvaluteState(EvaluateProccess.file);
const evaluateRes = await handleEvaluateOnChange?.({ const evaluateRes = await handleEvaluateOnChange?.({
changedValues: {}, changedValues: {},
allValues: form.current?.form?.getFieldsValue?.(), allValues: form.current?.form?.getFieldsValue?.(),
@@ -245,41 +230,68 @@ const AddModal: FC<AddModalProps> = (props) => {
} }
}; };
const handleOnSelectModel = (item: any, evaluate?: boolean) => { const handleOnSelectModel = async (item: any) => {
/** // If the item is empty or the same as the selected model, do nothing
* evaluate: false means select a new model console.log('handleOnSelectModel', item, selectedModel);
* evaluate: true means select a model file from the evaluate result if (
*/ _.isEmpty(item) ||
updateRequestModelId(); (item.isGGUF === selectedModel.isGGUF && item.name === selectedModel.name)
) {
return;
}
setIsGGUF(item.isGGUF);
clearCahceFormValues();
unlockWarningStatus();
setEvaluteState(EvaluateProccess.model);
setSelectedModel(item);
// If the evaluate is false, it means that the user selects a new model or the first time to open the modal. form.current?.form?.resetFields(resetFieldsByModel);
if (!evaluate) { const modelInfo = onSelectModel(item, props.source);
unlockWarningStatus(); form.current?.setFieldsValue?.({
setEvaluteState(EvaluateProccess.model); ...modelInfo,
setSelectedModel(item); categories: getCategory(item)
form.current?.form?.resetFields(resetFieldsByModel); });
const modelInfo = onSelectModel(item, props.source);
setWarningStatus(
{
show: true,
title: '',
type: 'transition',
message: intl.formatMessage({ id: 'models.form.evaluating' })
},
{
override: true
}
);
await new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 0);
});
if (item.isGGUF) {
modelFileRef.current?.fetchModelFiles?.();
}
};
const handleOnSelectModelAfterEvaluate = (item: any) => {
console.log('handleOnSelectModelAfterEvaluate', item);
if (item.isGGUF) {
return;
}
const modelInfo = onSelectModel(item, props.source);
if (
evaluateStateRef.current.state === EvaluateProccess.model &&
item.evaluateResult
) {
handleShowCompatibleAlert(item.evaluateResult);
form.current?.setFieldsValue?.({ form.current?.setFieldsValue?.({
...getDefaultSpec(item),
...modelInfo, ...modelInfo,
categories: getCategory(item) categories: getCategory(item)
}); });
} }
if (!item.isGGUF) {
setIsGGUF(false);
const modelInfo = onSelectModel(item, props.source);
if (
evaluateStateRef.current.state === EvaluateProccess.model &&
item.evaluateResult
) {
handleShowCompatibleAlert(item.evaluateResult);
form.current?.setFieldsValue?.({
...getDefaultSpec(item),
...modelInfo,
categories: getCategory(item)
});
}
}
}; };
const handleOnOk = async (allValues: FormData) => { const handleOnOk = async (allValues: FormData) => {
@@ -298,14 +310,6 @@ const AddModal: FC<AddModalProps> = (props) => {
const handleSetIsGGUF = async (flag: boolean) => { const handleSetIsGGUF = async (flag: boolean) => {
setIsGGUF(flag); setIsGGUF(flag);
await new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 0);
});
if (flag) {
modelFileRef.current?.fetchModelFiles?.();
}
}; };
const handleBackendChange = async (backend: string) => { const handleBackendChange = async (backend: string) => {
@@ -446,6 +450,9 @@ const AddModal: FC<AddModalProps> = (props) => {
hasLinuxWorker={hasLinuxWorker} hasLinuxWorker={hasLinuxWorker}
modelSource={props.source} modelSource={props.source}
onSelectModel={handleOnSelectModel} onSelectModel={handleOnSelectModel}
onSelectModelAfterEvaluate={
handleOnSelectModelAfterEvaluate
}
displayEvaluateStatus={displayEvaluateStatus} displayEvaluateStatus={displayEvaluateStatus}
unlockWarningStatus={unlockWarningStatus} unlockWarningStatus={unlockWarningStatus}
gpuOptions={props.gpuOptions} gpuOptions={props.gpuOptions}
@@ -0,0 +1,23 @@
import { Flex, Skeleton, Space } from 'antd';
import styled from 'styled-components';
const Wrapper = styled(Flex)`
padding: 12px 14px;
border: 1px solid var(--ant-color-border);
border-radius: var(--border-radius-base);
background-color: var(--ant-color-bg-container);
`;
const FileSkeleton = () => {
return (
<Wrapper vertical justify={'space-between'}>
<Skeleton paragraph={{ rows: 1, width: '100%' }} title={false}></Skeleton>
<Space>
<Skeleton.Node style={{ width: 60, height: 22 }}></Skeleton.Node>
<Skeleton.Node style={{ width: 60, height: 22 }}></Skeleton.Node>
</Space>
</Wrapper>
);
};
export default FileSkeleton;
+22 -16
View File
@@ -21,6 +21,7 @@ import {
} from '../apis'; } from '../apis';
import { backendOptionsMap, modelSourceMap } from '../config'; import { backendOptionsMap, modelSourceMap } from '../config';
import '../style/hf-model-file.less'; import '../style/hf-model-file.less';
import FileSkeleton from './file-skeleton';
import ModelFileItem from './model-file-item'; import ModelFileItem from './model-file-item';
import TitleWrapper from './title-wrapper'; import TitleWrapper from './title-wrapper';
@@ -40,7 +41,8 @@ interface HFModelFileProps {
modelSource: string; modelSource: string;
ref: any; ref: any;
gpuOptions?: any[]; gpuOptions?: any[];
onSelectFile?: (file: any, evaluate?: boolean) => void; onSelectFile?: (file: any) => void;
onSelectFileAfterEvaluate?: (file: any) => void;
} }
const pattern = /^(.*)-(\d+)-of-(\d+)\.(.*)$/; const pattern = /^(.*)-(\d+)-of-(\d+)\.(.*)$/;
@@ -50,7 +52,8 @@ const includeReg = /\.(safetensors|gguf)$/i;
const filterRegGGUF = /\.(gguf)$/i; const filterRegGGUF = /\.(gguf)$/i;
const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => { const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
const { collapsed, modelSource, isDownload } = props; const { collapsed, modelSource, isDownload, onSelectFileAfterEvaluate } =
props;
const intl = useIntl(); const intl = useIntl();
const [isEvaluating, setIsEvaluating] = useState(false); const [isEvaluating, setIsEvaluating] = useState(false);
const [dataSource, setDataSource] = useState<any>({ const [dataSource, setDataSource] = useState<any>({
@@ -74,8 +77,8 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
const checkTokenRef = useRef<any>(null); const checkTokenRef = useRef<any>(null);
const timer = useRef<any>(null); const timer = useRef<any>(null);
const handleSelectModelFile = (item: any, evaluate?: boolean) => { const handleSelectModelFile = (item: any) => {
props.onSelectFile?.(item, evaluate); props.onSelectFile?.(item);
setCurrent(item.path); setCurrent(item.path);
currentPathRef.current = item.path; currentPathRef.current = item.path;
}; };
@@ -258,7 +261,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
); );
if (currentItem) { if (currentItem) {
handleSelectModelFile(currentItem, true); onSelectFileAfterEvaluate?.(currentItem);
} }
setDataSource({ fileList: resultList, loading: false }); setDataSource({ fileList: resultList, loading: false });
setIsEvaluating(false); setIsEvaluating(false);
@@ -361,7 +364,13 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
)} )}
<SimpleOverlay height={collapsed ? 'max-content' : 'calc(100vh - 300px)'}> <SimpleOverlay height={collapsed ? 'max-content' : 'calc(100vh - 300px)'}>
<div style={{ padding: '16px 24px' }}> <div style={{ padding: '16px 24px' }}>
{dataSource.fileList.length ? ( {dataSource.loading ? (
<ItemFileWrapper>
{_.times(5, (index: number) => {
return <FileSkeleton key={index}></FileSkeleton>;
})}
</ItemFileWrapper>
) : dataSource.fileList.length ? (
<ItemFileWrapper> <ItemFileWrapper>
{_.map(dataSource.fileList, (item: any) => { {_.map(dataSource.fileList, (item: any) => {
return ( return (
@@ -376,16 +385,13 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
})} })}
</ItemFileWrapper> </ItemFileWrapper>
) : ( ) : (
!dataSource.loading && <Empty
!dataSource.fileList.length && ( imageStyle={{ height: 'auto', marginTop: '20px' }}
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}
imageStyle={{ height: 'auto', marginTop: '20px' }} description={intl.formatMessage({
image={Empty.PRESENTED_IMAGE_SIMPLE} id: 'models.search.nofiles'
description={intl.formatMessage({ })}
id: 'models.search.nofiles' />
})}
/>
)
)} )}
</div> </div>
</SimpleOverlay> </SimpleOverlay>
+9 -1
View File
@@ -292,8 +292,16 @@ const ModelCard: React.FC<{
); );
useEffect(() => { useEffect(() => {
if (!props.selectedModel) return;
getModelCardData(); getModelCardData();
}, [props.selectedModel?.name]); setIsGGUFModel(props.selectedModel.isGGUF);
setModelData({
id: props.selectedModel.name,
name: props.selectedModel.name,
isGGUF: props.selectedModel.isGGUF
});
}, [props.selectedModel?.name, props.selectedModel?.isGGUF]);
useEffect(() => { useEffect(() => {
return () => { return () => {
+11 -27
View File
@@ -46,7 +46,8 @@ interface SearchInputProps {
gpuOptions?: any[]; gpuOptions?: any[];
setLoadingModel?: (flag: boolean) => void; setLoadingModel?: (flag: boolean) => void;
onSourceChange?: (source: string) => void; onSourceChange?: (source: string) => void;
onSelectModel: (model: any, evaluate?: boolean) => void; onSelectModel: (model: any) => void;
onSelectModelAfterEvaluate: (model: any) => void;
unlockWarningStatus?: () => void; unlockWarningStatus?: () => void;
displayEvaluateStatus?: ( displayEvaluateStatus?: (
data: MessageStatus, data: MessageStatus,
@@ -63,6 +64,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
gpuOptions, gpuOptions,
setLoadingModel, setLoadingModel,
onSelectModel, onSelectModel,
onSelectModelAfterEvaluate,
displayEvaluateStatus, displayEvaluateStatus,
unlockWarningStatus unlockWarningStatus
} = props; } = props;
@@ -138,9 +140,13 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
return isGGUF || isGGUFFromMs; return isGGUF || isGGUFFromMs;
}; };
const handleOnSelectModel = (model: any, evaluate?: boolean) => { const handleOnSelectModel = (model: any) => {
const item = model || {}; const item = model || {};
onSelectModel(item, evaluate); if (item.evaluateResult && !item.isGGUF) {
onSelectModelAfterEvaluate(item);
} else {
onSelectModel(item);
}
setCurrent(item.id); setCurrent(item.id);
currentRef.current = item.id; currentRef.current = item.id;
}; };
@@ -332,12 +338,13 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
repoOptions: resultList repoOptions: resultList
}; };
}); });
// current selected item
const currentItem = resultList.find( const currentItem = resultList.find(
(item) => item.id === currentRef.current (item) => item.id === currentRef.current
); );
if (currentItem) { if (currentItem) {
handleOnSelectModel(currentItem, true); onSelectModelAfterEvaluate(currentItem);
} }
} catch (error) { } catch (error) {
if (requestIdRef.current === currentRequestId) { if (requestIdRef.current === currentRequestId) {
@@ -406,17 +413,6 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
sortType: sort sortType: sort
}); });
// It's a new request, so we need to reset the state
unlockWarningStatus?.();
displayEvaluateStatus?.(
{
show: list?.length > 0,
message: ''
},
{
override: true
}
);
handleOnSelectModel(list[0]); handleOnSelectModel(list[0]);
setLoadingModel?.(false); setLoadingModel?.(false);
@@ -498,18 +494,6 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
repoOptions: currentList repoOptions: currentList
}; };
}); });
unlockWarningStatus?.();
// reset evaluate status
displayEvaluateStatus?.(
{
show: true,
message: ''
},
{
override: true
}
);
console.log('isEvaluating:', isEvaluating);
handleOnSelectModel(currentList[0]); handleOnSelectModel(currentList[0]);
handleEvaluate(currentList); handleEvaluate(currentList);
} else if (modelSource === modelSourceMap.modelscope_value) { } else if (modelSource === modelSourceMap.modelscope_value) {
+6
View File
@@ -530,6 +530,10 @@ export const useCheckCompatibility = () => {
return noLocalValue || noOllamaValue; return noLocalValue || noOllamaValue;
}; };
const clearCahceFormValues = () => {
cacheFormValuesRef.current = {};
};
const handleOnValuesChange = async (params: { const handleOnValuesChange = async (params: {
changedValues: any; changedValues: any;
allValues: any; allValues: any;
@@ -537,6 +541,7 @@ export const useCheckCompatibility = () => {
}) => { }) => {
const { allValues, source } = params; const { allValues, source } = params;
if (_.isEqual(cacheFormValuesRef.current, allValues)) { if (_.isEqual(cacheFormValuesRef.current, allValues)) {
console.log('No changes detected, skipping evaluation.');
return; return;
} }
@@ -604,6 +609,7 @@ export const useCheckCompatibility = () => {
handleBackendChangeBefore, handleBackendChangeBefore,
handleOnValuesChange: debounceHandleValuesChange, handleOnValuesChange: debounceHandleValuesChange,
handleEvaluateOnChange: handleOnValuesChange, handleEvaluateOnChange: handleOnValuesChange,
clearCahceFormValues,
warningStatus, warningStatus,
checkTokenRef, checkTokenRef,
submitAnyway submitAnyway
@@ -305,6 +305,7 @@ const MessageInput: React.FC<MessageInputProps> = forwardRef(
uid: updateUidCount(), uid: updateUidCount(),
format: audioTypeMap[data.file.type] as AudioFormat, format: audioTypeMap[data.file.type] as AudioFormat,
base64: base64Audio.split(',')[1], base64: base64Audio.split(',')[1],
name: audioData.name,
data: _.pick(audioData, ['url', 'name', 'duration']) data: _.pick(audioData, ['url', 'name', 'duration'])
} }
] ]
@@ -518,6 +519,7 @@ const MessageInput: React.FC<MessageInputProps> = forwardRef(
<AudioWrapper> <AudioWrapper>
<SimpleAudio <SimpleAudio
url={message.audio?.[0].data?.url} url={message.audio?.[0].data?.url}
name={message.audio?.[0].name}
height={44} height={44}
onDelete={handleDeleteAudio} onDelete={handleDeleteAudio}
></SimpleAudio> ></SimpleAudio>
@@ -217,6 +217,7 @@ const MessageBody: React.FC<MessageBodyProps> = forwardRef(
<AudioWrapper> <AudioWrapper>
<SimpleAudio <SimpleAudio
url={data.audio?.[0]?.data.url} url={data.audio?.[0]?.data.url}
name={data.audio?.[0]?.data.name}
actions={[]} actions={[]}
height={44} height={44}
></SimpleAudio> ></SimpleAudio>
@@ -247,6 +248,7 @@ const MessageBody: React.FC<MessageBodyProps> = forwardRef(
<AudioWrapper> <AudioWrapper>
<SimpleAudio <SimpleAudio
url={data.audio?.[0]?.data.url} url={data.audio?.[0]?.data.url}
name={data.audio?.[0]?.data.name}
onDelete={handleDeleteAudio} onDelete={handleDeleteAudio}
height={44} height={44}
></SimpleAudio> ></SimpleAudio>
+1
View File
@@ -19,6 +19,7 @@ export interface AudioData {
uid: string | number; uid: string | number;
base64: string; base64: string;
format: AudioFormat; format: AudioFormat;
name?: string;
data: { data: {
url: string; url: string;
name: string; name: string;