build mobile fix
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { X, Minimize2, Trash2, Edit2, Check, Layers, FolderOpen, Inbox, ScanText, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { X, Minimize2, Trash2, Edit2, Check, Layers, FolderOpen, Inbox, ScanText, ChevronDown, ChevronUp, ExternalLink, Image, Info } from 'lucide-react';
|
||||
import { useMeme, useDeleteMeme, useUpdateMeme, useMoveMeme, useCollections } from '../hooks/useMemes';
|
||||
import { useAuth } from '../hooks/useAuth';
|
||||
import { SharePanel } from './SharePanel';
|
||||
@@ -39,6 +39,7 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
const [editTags, setEditTags] = useState('');
|
||||
const [showRescale, setShowRescale] = useState(false);
|
||||
const [activeChild, setActiveChild] = useState<Meme | null>(null);
|
||||
const [mobileTab, setMobileTab] = useState<'image' | 'details'>('image');
|
||||
|
||||
const meme = data;
|
||||
const displayMeme = activeChild ?? meme;
|
||||
@@ -79,16 +80,18 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
|
||||
if (!meme) return null;
|
||||
|
||||
const isVideo = meme.mime_type.startsWith('video/');
|
||||
const fullSizeUrl = displayMeme ? api.imageUrl(displayMeme.file_path) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 z-40 bg-black/80 animate-fade-in"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="fixed inset-0 z-40 bg-black/80 animate-fade-in" onClick={onClose} />
|
||||
|
||||
{/* Modal — full screen on mobile, inset on desktop */}
|
||||
<div className="fixed inset-0 md:inset-4 lg:inset-8 z-50 flex flex-col bg-zinc-900 md:rounded-2xl shadow-2xl border-0 md:border border-zinc-800 overflow-hidden animate-scale-in">
|
||||
|
||||
<div className="fixed inset-4 md:inset-8 z-50 flex flex-col bg-zinc-900 rounded-2xl shadow-2xl border border-zinc-800 overflow-hidden animate-scale-in">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-zinc-800 flex-shrink-0">
|
||||
<div className="flex items-center justify-between px-4 py-3 md:px-5 md:py-4 border-b border-zinc-800 flex-shrink-0">
|
||||
{editing ? (
|
||||
<input
|
||||
autoFocus
|
||||
@@ -97,9 +100,9 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
className="flex-1 bg-zinc-800 border border-zinc-700 rounded-lg px-3 py-1.5 text-sm font-semibold focus:outline-none focus:border-accent mr-3"
|
||||
/>
|
||||
) : (
|
||||
<h2 className="text-lg font-semibold truncate flex-1 mr-3">{meme.title}</h2>
|
||||
<h2 className="text-base md:text-lg font-semibold truncate flex-1 mr-3">{meme.title}</h2>
|
||||
)}
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5 flex-shrink-0">
|
||||
{isAdmin && (
|
||||
editing ? (
|
||||
<button
|
||||
@@ -110,47 +113,84 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
<Check size={14} /> Save
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={startEdit}
|
||||
className="text-zinc-500 hover:text-zinc-300 transition-colors p-1"
|
||||
title="Edit"
|
||||
>
|
||||
<button onClick={startEdit} className="text-zinc-500 hover:text-zinc-300 transition-colors p-1.5" title="Edit">
|
||||
<Edit2 size={16} />
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
{isAdmin && !meme.parent_id && !meme.mime_type.startsWith('video/') && (
|
||||
{isAdmin && !meme.parent_id && !isVideo && (
|
||||
<button
|
||||
onClick={() => setShowRescale(true)}
|
||||
className="flex items-center gap-1 text-sm px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-300 transition-colors"
|
||||
className="flex items-center gap-1 text-sm px-2.5 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-300 transition-colors"
|
||||
title="Create rescaled copy"
|
||||
>
|
||||
<Minimize2 size={14} />
|
||||
<span className="hidden sm:inline">Rescale</span>
|
||||
</button>
|
||||
)}
|
||||
{/* Full size link — always visible */}
|
||||
{fullSizeUrl && (
|
||||
<a
|
||||
href={fullSizeUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex items-center gap-1 text-sm px-2.5 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-300 transition-colors"
|
||||
title="Open full size"
|
||||
>
|
||||
<ExternalLink size={14} />
|
||||
<span className="hidden sm:inline">Full size</span>
|
||||
</a>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
disabled={deleteMeme.isPending}
|
||||
className="text-zinc-500 hover:text-red-400 transition-colors p-1"
|
||||
className="text-zinc-500 hover:text-red-400 transition-colors p-1.5"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
)}
|
||||
<button onClick={onClose} className="text-zinc-500 hover:text-zinc-300 transition-colors p-1">
|
||||
<button onClick={onClose} className="text-zinc-500 hover:text-zinc-300 transition-colors p-1.5">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile tab bar */}
|
||||
<div className="flex md:hidden border-b border-zinc-800 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => setMobileTab('image')}
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 py-2.5 text-sm font-medium transition-colors border-b-2 ${
|
||||
mobileTab === 'image'
|
||||
? 'border-accent text-accent'
|
||||
: 'border-transparent text-zinc-500 hover:text-zinc-300'
|
||||
}`}
|
||||
>
|
||||
<Image size={14} /> Media
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setMobileTab('details')}
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 py-2.5 text-sm font-medium transition-colors border-b-2 ${
|
||||
mobileTab === 'details'
|
||||
? 'border-accent text-accent'
|
||||
: 'border-transparent text-zinc-500 hover:text-zinc-300'
|
||||
}`}
|
||||
>
|
||||
<Info size={14} /> Details
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className="flex flex-col md:flex-row flex-1 overflow-hidden">
|
||||
{/* Image / video panel */}
|
||||
<div className="flex-1 flex items-center justify-center bg-zinc-950 p-4 overflow-hidden">
|
||||
<div className="flex flex-col md:flex-row flex-1 min-h-0 overflow-hidden">
|
||||
|
||||
{/* Image / video panel — full height on mobile when image tab active */}
|
||||
<div className={`flex-1 flex items-center justify-center bg-zinc-950 p-4 min-h-0 overflow-hidden ${
|
||||
mobileTab === 'details' ? 'hidden md:flex' : 'flex'
|
||||
}`}>
|
||||
{displayMeme && (
|
||||
displayMeme.mime_type.startsWith('video/') ? (
|
||||
isVideo ? (
|
||||
<video
|
||||
key={displayMeme.id}
|
||||
src={api.imageUrl(displayMeme.file_path)}
|
||||
@@ -172,7 +212,9 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="md:w-80 border-t md:border-t-0 md:border-l border-zinc-800 flex flex-col overflow-y-auto">
|
||||
<div className={`md:w-80 md:border-l border-zinc-800 flex flex-col overflow-y-auto ${
|
||||
mobileTab === 'image' ? 'hidden md:flex' : 'flex'
|
||||
}`}>
|
||||
<div className="p-5 space-y-5 flex-1">
|
||||
|
||||
{/* Share */}
|
||||
@@ -289,7 +331,7 @@ export function MemeDetail({ memeId, onClose }: Props) {
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-zinc-500">Type</dt>
|
||||
<dd className="text-zinc-300">{meme.mime_type.replace('image/', '')}</dd>
|
||||
<dd className="text-zinc-300">{meme.mime_type.replace('image/', '').replace('video/', '')}</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-zinc-500">Uploaded</dt>
|
||||
|
||||
Reference in New Issue
Block a user