feat(web): inline chat search and global audio bar
Some checks failed
CI / test (push) Failing after 20s
Some checks failed
CI / test (push) Failing after 20s
- replace modal message search with header inline search controls - add global top audio bar linked to active inline audio player - improve chat info header variants and light theme readability
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
import type { Message, MessageReaction } from "../chat/types";
|
||||
import { useAuthStore } from "../store/authStore";
|
||||
import { useChatStore } from "../store/chatStore";
|
||||
import { useAudioPlayerStore } from "../store/audioPlayerStore";
|
||||
import { useUiStore } from "../store/uiStore";
|
||||
import { formatTime } from "../utils/format";
|
||||
import { formatMessageHtml } from "../utils/formatMessage";
|
||||
@@ -796,7 +797,7 @@ function renderMessageContent(
|
||||
<span className="inline-flex h-7 w-7 items-center justify-center rounded-full bg-slate-700/80">🎤</span>
|
||||
<span className="font-semibold">Voice message</span>
|
||||
</div>
|
||||
<AudioInlinePlayer src={text} />
|
||||
<AudioInlinePlayer src={text} title="Voice message" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -814,7 +815,7 @@ function renderMessageContent(
|
||||
<p className="text-[11px] text-slate-400">Audio file</p>
|
||||
</div>
|
||||
</div>
|
||||
<AudioInlinePlayer src={text} />
|
||||
<AudioInlinePlayer src={text} title={extractFileName(text)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -915,8 +916,12 @@ async function downloadFileFromUrl(url: string): Promise<void> {
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
}
|
||||
|
||||
function AudioInlinePlayer({ src }: { src: string }) {
|
||||
function AudioInlinePlayer({ src, title }: { src: string; title: string }) {
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||
const activate = useAudioPlayerStore((s) => s.activate);
|
||||
const detach = useAudioPlayerStore((s) => s.detach);
|
||||
const setPlayingState = useAudioPlayerStore((s) => s.setPlaying);
|
||||
const setVolumeState = useAudioPlayerStore((s) => s.setVolume);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [position, setPosition] = useState(0);
|
||||
@@ -934,17 +939,30 @@ function AudioInlinePlayer({ src }: { src: string }) {
|
||||
};
|
||||
const onEnded = () => {
|
||||
setIsPlaying(false);
|
||||
setPlayingState(audio, false);
|
||||
};
|
||||
const onPlay = () => {
|
||||
activate(audio, { src, title });
|
||||
setPlayingState(audio, true);
|
||||
};
|
||||
const onPause = () => {
|
||||
setPlayingState(audio, false);
|
||||
};
|
||||
|
||||
audio.addEventListener("loadedmetadata", onLoaded);
|
||||
audio.addEventListener("timeupdate", onTime);
|
||||
audio.addEventListener("ended", onEnded);
|
||||
audio.addEventListener("play", onPlay);
|
||||
audio.addEventListener("pause", onPause);
|
||||
return () => {
|
||||
audio.removeEventListener("loadedmetadata", onLoaded);
|
||||
audio.removeEventListener("timeupdate", onTime);
|
||||
audio.removeEventListener("ended", onEnded);
|
||||
audio.removeEventListener("play", onPlay);
|
||||
audio.removeEventListener("pause", onPause);
|
||||
detach(audio);
|
||||
};
|
||||
}, []);
|
||||
}, [activate, detach, setPlayingState, src, title]);
|
||||
|
||||
async function togglePlay() {
|
||||
const audio = audioRef.current;
|
||||
@@ -955,6 +973,7 @@ function AudioInlinePlayer({ src }: { src: string }) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
activate(audio, { src, title });
|
||||
await audio.play();
|
||||
setIsPlaying(true);
|
||||
} catch {
|
||||
@@ -974,6 +993,7 @@ function AudioInlinePlayer({ src }: { src: string }) {
|
||||
if (!audio) return;
|
||||
audio.volume = nextValue;
|
||||
setVolume(nextValue);
|
||||
setVolumeState(audio, nextValue);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user