import clsx from "clsx"; import { t } from "i18next"; import { FC, useEffect, useRef, useState } from "react"; import { feedbackToast } from "@/utils/common"; import { IMessageItemProps } from "."; import styles from "./message-item.module.scss"; const PLAYED_KEY = "chaglian_played_sounds"; const getPlayedMap = (): Record => { try { return JSON.parse(localStorage.getItem(PLAYED_KEY) ?? "{}"); } catch (error) { return {}; } }; const markPlayed = (clientMsgID: string) => { const map = getPlayedMap(); map[clientMsgID] = true; localStorage.setItem(PLAYED_KEY, JSON.stringify(map)); }; const SoundIcon = ({ className }: { className?: string }) => ( ); const SoundMessageRender: FC = ({ message, isSender }) => { const soundElem = message.soundElem; const duration = Math.max(1, Math.round(soundElem?.duration ?? 0)); const [playing, setPlaying] = useState(false); const [played, setPlayed] = useState( () => Boolean(getPlayedMap()[message.clientMsgID]), ); const audioRef = useRef(); useEffect(() => { return () => { audioRef.current?.pause(); audioRef.current = undefined; }; }, []); // 宽度随时长微变:60~180px const width = Math.min(180, 60 + duration * 6); const play = () => { const url = soundElem?.sourceUrl || soundElem?.soundPath; if (!url || playing) return; const audio = new Audio(url); audioRef.current = audio; setPlaying(true); audio.onended = () => setPlaying(false); audio.onerror = () => { setPlaying(false); feedbackToast({ msg: t("toast.accessFailed"), error: new Error("audio error") }); }; audio.play().catch(() => setPlaying(false)); markPlayed(message.clientMsgID); setPlayed(true); }; return (
{duration}″
{!isSender && !played && ( )}
); }; export default SoundMessageRender;