feat(web): redesign full-screen media viewer UX
All checks were successful
CI / test (push) Successful in 27s

This commit is contained in:
2026-03-08 13:14:18 +03:00
parent 10d4e0386a
commit eda84d4d82
3 changed files with 324 additions and 103 deletions

View File

@@ -19,6 +19,7 @@ import type { AuthUser, ChatAttachment, ChatDetail, ChatMember, Message, UserSea
import { useAuthStore } from "../store/authStore";
import { useChatStore } from "../store/chatStore";
import { useUiStore } from "../store/uiStore";
import { MediaViewer } from "./MediaViewer";
interface Props {
chatId: number | null;
@@ -745,43 +746,15 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
</div>
) : null}
{mediaViewer ? (
<div className="fixed inset-0 z-[140] 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-2 top-2 z-10 rounded bg-slate-900/80 px-2 py-1 text-xs" onClick={() => setMediaViewer(null)} type="button">
Close
</button>
{mediaViewer.items.length > 1 ? (
<>
<button
className="absolute left-2 top-1/2 z-10 -translate-y-1/2 rounded-full bg-slate-900/80 px-3 py-2 text-lg"
onClick={() => setMediaViewer((prev) => (prev ? { ...prev, index: prev.index <= 0 ? prev.items.length - 1 : prev.index - 1 } : prev))}
type="button"
>
</button>
<button
className="absolute right-2 top-1/2 z-10 -translate-y-1/2 rounded-full bg-slate-900/80 px-3 py-2 text-lg"
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-2 top-2 z-10 rounded bg-slate-900/80 px-2 py-1 text-xs"
onClick={() => jumpToMessage(mediaViewer.items[mediaViewer.index].messageId)}
type="button"
>
Jump
</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))}
onJumpToMessage={(messageId) => jumpToMessage(messageId)}
onToast={showToast}
open
/>
) : null}
</div>,
document.body