feat(web): redesign full-screen media viewer UX
All checks were successful
CI / test (push) Successful in 27s
All checks were successful
CI / test (push) Successful in 27s
This commit is contained in:
@@ -15,6 +15,7 @@ import { useAudioPlayerStore } from "../store/audioPlayerStore";
|
||||
import { useUiStore } from "../store/uiStore";
|
||||
import { formatTime } from "../utils/format";
|
||||
import { formatMessageHtml } from "../utils/formatMessage";
|
||||
import { MediaViewer } from "./MediaViewer";
|
||||
|
||||
type ContextMenuState = {
|
||||
x: number;
|
||||
@@ -724,72 +725,14 @@ export function MessageList() {
|
||||
) : null}
|
||||
|
||||
{mediaViewer ? (
|
||||
<div className="fixed inset-0 z-[170] flex items-center justify-center bg-slate-950/90 p-2 md:p-4" onClick={() => setMediaViewer(null)}>
|
||||
<div className="relative flex h-full w-full max-w-5xl items-center justify-center" onClick={(event) => event.stopPropagation()}>
|
||||
<button
|
||||
className="absolute left-1 top-1 z-10 rounded-full bg-slate-900/80 px-3 py-2 text-xs text-slate-200 hover:bg-slate-800 md:left-3 md:top-3"
|
||||
onClick={() => setMediaViewer(null)}
|
||||
type="button"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
{mediaViewer.items.length > 1 ? (
|
||||
<>
|
||||
<button
|
||||
className="absolute left-1/2 top-3 z-10 -translate-x-1/2 rounded-full bg-slate-900/80 px-2 py-1 text-xs text-slate-200 md:top-4"
|
||||
type="button"
|
||||
>
|
||||
{mediaViewer.index + 1} / {mediaViewer.items.length}
|
||||
</button>
|
||||
<button
|
||||
className="absolute left-1 top-1/2 z-10 -translate-y-1/2 rounded-full bg-slate-900/80 px-3 py-3 text-lg text-slate-200 hover:bg-slate-800 md:left-3"
|
||||
onClick={() =>
|
||||
setMediaViewer((prev) =>
|
||||
prev ? { ...prev, index: prev.index <= 0 ? prev.items.length - 1 : prev.index - 1 } : prev
|
||||
)
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<button
|
||||
className="absolute right-1 top-1/2 z-10 -translate-y-1/2 rounded-full bg-slate-900/80 px-3 py-3 text-lg text-slate-200 hover:bg-slate-800 md:right-3"
|
||||
onClick={() =>
|
||||
setMediaViewer((prev) =>
|
||||
prev ? { ...prev, index: prev.index >= prev.items.length - 1 ? 0 : prev.index + 1 } : prev
|
||||
)
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
className="absolute right-1 top-1 z-10 rounded-full bg-slate-900/80 px-3 py-2 text-xs text-slate-200 hover:bg-slate-800 md:right-3 md:top-3"
|
||||
onClick={async () => {
|
||||
const current = mediaViewer.items[mediaViewer.index];
|
||||
try {
|
||||
await downloadFileFromUrl(current.url);
|
||||
showToast("File downloaded");
|
||||
} catch {
|
||||
showToast("Download failed");
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
|
||||
{mediaViewer.items[mediaViewer.index]?.type === "image" ? (
|
||||
<img className="max-h-full max-w-full rounded-xl object-contain" src={mediaViewer.items[mediaViewer.index].url} alt="media" />
|
||||
) : (
|
||||
<video className="max-h-full max-w-full rounded-xl" controls src={mediaViewer.items[mediaViewer.index].url} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<MediaViewer
|
||||
index={mediaViewer.index}
|
||||
items={mediaViewer.items}
|
||||
onClose={() => setMediaViewer(null)}
|
||||
onIndexChange={(nextIndex) => setMediaViewer((prev) => (prev ? { ...prev, index: nextIndex } : prev))}
|
||||
onToast={showToast}
|
||||
open
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{deleteMessageId ? (
|
||||
|
||||
Reference in New Issue
Block a user