feat: voice message
This commit is contained in:
@@ -22,6 +22,11 @@ const renderContent = (data: MessagePayload) => {
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case ContentTypes.audio:
|
||||
res = (
|
||||
<span className="text-gray-400 italic">[Voice Message]</span>
|
||||
);
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
res = (
|
||||
<div className="max-h-[152px] overflow-hidden dark:text-gray-100">
|
||||
|
||||
@@ -7,7 +7,7 @@ import MarkdownRender from "../MarkdownRender";
|
||||
import FileMessage from "../FileMessage";
|
||||
import { ContentType } from "../../../types/message";
|
||||
import LinkifyText from '../LinkifyText';
|
||||
import VoiceMessage, { VoiceMessageProps } from "../VoiceMessage";
|
||||
import VoiceMessage from "../VoiceMessage";
|
||||
|
||||
type Props = {
|
||||
context: "user" | "channel";
|
||||
@@ -55,7 +55,7 @@ const renderContent = ({
|
||||
case ContentTypes.audio:
|
||||
{
|
||||
// const { url, secure_url } = properties; todo
|
||||
ctn = <VoiceMessage data={content as VoiceMessageProps} />;
|
||||
ctn = <VoiceMessage file_path={content} />;
|
||||
}
|
||||
break;
|
||||
case ContentTypes.file:
|
||||
|
||||
@@ -20,6 +20,13 @@ const renderContent = (data: MessagePayload) => {
|
||||
// });
|
||||
break;
|
||||
|
||||
case ContentTypes.audio:
|
||||
res = (
|
||||
<div className="text-sm">
|
||||
<span className="text-gray-400 italic">[Voice Message]</span>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
res = (
|
||||
<div className="max-h-[100px] overflow-auto">
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import IconPause from '../../assets/icons/pause.svg';
|
||||
import IconPlay from '../../assets/icons/play.circle.svg';
|
||||
import BASE_URL from '../../app/config';
|
||||
|
||||
export type VoiceMessageProps = {
|
||||
type: string,
|
||||
@@ -6,19 +10,70 @@ export type VoiceMessageProps = {
|
||||
secure_url: string,
|
||||
}
|
||||
|
||||
const VoiceMessage = ({ data }: { data: VoiceMessageProps }) => {
|
||||
const [audio, setAudio] = useState(new Audio(data.url));
|
||||
// useEffect(() => {
|
||||
// first
|
||||
|
||||
// return () => {
|
||||
// second
|
||||
// }
|
||||
// }, [url])
|
||||
const VoiceMessage = ({ file_path }: { file_path: string }) => {
|
||||
const waveRef = useRef<WaveSurfer | null>(null);
|
||||
const containerRef = useRef(null);
|
||||
const [playing, setPlaying] = useState(false);
|
||||
const [duration, setDuration] = useState(0);
|
||||
// const [audio, setAudio] = useState<HTMLAudioElement>(new Audio(`${BASE_URL}/resource/file?file_path=${encodeURIComponent(
|
||||
// file_path
|
||||
// )}`));
|
||||
useEffect(() => {
|
||||
let wave: WaveSurfer | null = null;
|
||||
if (containerRef.current && file_path) {
|
||||
const audioSrc = `${BASE_URL}/resource/file?file_path=${encodeURIComponent(
|
||||
file_path
|
||||
)}`;
|
||||
wave = WaveSurfer.create({
|
||||
container: containerRef.current,
|
||||
maxCanvasWidth: 200,
|
||||
height: 32,
|
||||
waveColor: '#0BA5EC',
|
||||
cursorColor: '#0BA5AA',
|
||||
progressColor: "#0BA5AA",
|
||||
hideScrollbar: true,
|
||||
// mediaControls: true,
|
||||
normalize: true
|
||||
});
|
||||
wave.load(audioSrc);
|
||||
|
||||
wave.on('play', function () {
|
||||
setPlaying(true);
|
||||
});
|
||||
wave.on('pause', function () {
|
||||
setPlaying(false);
|
||||
});
|
||||
wave.on('ready', function () {
|
||||
if (waveRef.current) {
|
||||
const dur = waveRef.current.getDuration();
|
||||
setDuration(Math.ceil(dur));
|
||||
}
|
||||
});
|
||||
waveRef.current = wave;
|
||||
}
|
||||
return () => {
|
||||
if (waveRef.current) {
|
||||
waveRef.current.destroy();
|
||||
}
|
||||
};
|
||||
}, [file_path]);
|
||||
const handleClick = () => {
|
||||
if (waveRef.current) {
|
||||
if (waveRef.current.isPlaying()) {
|
||||
waveRef.current.pause();
|
||||
} else {
|
||||
waveRef.current.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className='bg-green-800'>
|
||||
audio message
|
||||
<div className="select-none flex items-center gap-2 p-2 rounded-lg bg-primary-100 dark:bg-primary-900 max-w-sm">
|
||||
<button onClick={handleClick}>
|
||||
{playing ? <IconPause className="stroke-primary-500" /> : <IconPlay className="stroke-primary-500" />}
|
||||
</button>
|
||||
<div ref={containerRef} className='flex-1 h-8' >
|
||||
</div>
|
||||
<time className='text-primary-500 text-xs whitespace-nowrap text-left'>{duration}'</time>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -59,6 +59,11 @@ export const renderPreviewMessage = (message = null) => {
|
||||
res = <LinkifyText text={content} url={false} mentionTextOnly={true} />;
|
||||
}
|
||||
break;
|
||||
case ContentTypes.audio:
|
||||
{
|
||||
res = `[${i18n.t("voice_message", { ns: "chat" })}]`;
|
||||
}
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
{
|
||||
res = `[markdown]`;
|
||||
|
||||
Reference in New Issue
Block a user