fix: sửa UI photoview hiển thị ở public

This commit is contained in:
2026-06-28 14:55:25 +07:00
parent 41c8e67229
commit 2f5ef424c8
4 changed files with 405 additions and 417 deletions
+151
View File
@@ -0,0 +1,151 @@
# To AI Agent: Restructure Mobile Photo Viewer Layout with Fixed Viewport, Overlay Metadata, and Scrollable Comments
## 1. Context & Architectural UI Refactor
We are redesigning the full-screen mobile photo inspector interface based on the visual layout annotated in `image.png`. The current structure is fragmented, causing layout instability when switching between images.
### Key Refactor Requirements:
1. **Fixed Image Viewport (Blue Zone):** Secure the photo viewport inside a strict, unyielding aspect-locked square container. Regardless of whether the active image is Landscape or Portrait, the container dimension must NOT snap, resize, or cause screen layout jumps.
2. **Geospatial Overlay (Red Arrow Destination):** Completely remove the static location metadata box from the bottom white panel. Relocate and render this location string as a clean, translucent text overlay pinned directly to the **bottom-left corner inside the image viewport**.
3. **Title Transformation (Green Arrow Destination):** Remove the text header segment "Lịch sử ảnh tại vị trí này (...)". In its place, dynamically render the active **Title of the Photo** (Tiêu đề của ảnh), serving as the primary text separator.
4. **Isolated Scrollable Comment Feed:** The entire lower comment zone must be configured to scroll dynamically. When users swipe up to read multiple comments, the layout thread must slide seamlessly underneath the fixed sticky image frame container (`z-20`).
---
## 2. Structural Layer Elevation (`z-index`) Matrix
┌────────────────────────────────────────────────────────┐
│ Tier 4: System Action Row & Closes (z-50) │ ➔ Native buttons (X, Close)
├────────────────────────────────────────────────────────┤
│ Tier 3: Fixed Photo Frame Viewport (z-20 / sticky) │ ➔ Aspect-Square Image Box
│ └─► Sub-Layer: Location & Time Overlay (z-30) │ ➔ Pinned Bottom-Left on Image
├────────────────────────────────────────────────────────┤
│ Tier 2: Scrollable Comments Panel (z-10) │ ➔ Slides BEHIND Tier 3 when swiped
└────────────────────────────────────────────────────────┘
---
## 3. Code Refactoring Blueprint
### Step 1: Overhaul Layout Architecture (`MobilePhotoViewerModal.tsx`)
Update or create the mobile component layout to enforce the absolute layer boundaries and fixed dimensions:
```typescript
import React, { useState } from 'react';
import { X, MapPin, Calendar, Heart, Send } from 'lucide-react';
interface PhotoDetail {
id: string;
url: string;
title: string;
description?: string;
latitude: number;
longitude: number;
locationName: string;
capturedAt: string;
likesCount: number;
}
export const MobilePhotoViewerModal: React.FC<{ photo: PhotoDetail; onClose: () => void }> = ({ photo, onClose }) => {
const [commentInput, setCommentInput] = useState('');
return (
<div className="fixed inset-0 bg-slate-950 z-[999999] flex flex-col overflow-hidden select-none text-white antialiased">
{/* TIER 4: FIXED SYSTEM ACTIONS CONTROL ROW (z-50) */}
<div className="absolute top-4 right-4 z-50">
<button
onClick={onClose}
className="p-2.5 bg-black/60 hover:bg-black/80 rounded-full border border-slate-800 backdrop-blur-md active:scale-95 transition-transform"
>
<X className="w-5 h-5 text-white" />
</button>
</div>
{/* TIER 3: FIXED BLUE ZONE - ASPECT LOCKED PHOTO FRAME (z-20 / sticky) */}
<div className="w-full aspect-square bg-slate-950 flex items-center justify-center sticky top-0 z-20 border-b border-slate-900/60 shadow-2xl shrink-0">
<img
src={photo.url}
alt={photo.title}
className="w-full h-full object-contain select-none pointer-events-none"
/>
{/* TIER 3.1: RELOCATED DYNAMIC LOCATION & TIMESTAMP OVERLAY (z-30) */}
<div className="absolute bottom-4 left-4 right-16 z-30 flex flex-col gap-1 p-2.5 bg-black/50 backdrop-blur-sm rounded-xl border border-white/10 text-[10px] text-slate-200 max-w-[75vw] pointer-events-none">
<div className="flex items-center gap-1.5 font-semibold text-white">
<MapPin className="w-3.5 h-3.5 text-blue-400 shrink-0" />
<span className="truncate">{photo.locationName || "Vị trí không xác định"}</span>
</div>
<div className="flex items-center gap-1.5 text-slate-300 pl-5">
<Calendar className="w-3 h-3 text-slate-400 shrink-0" />
<span>Ngày chụp: {new Date(photo.capturedAt).toLocaleDateString('vi-VN')}</span>
</div>
</div>
{/* Floating Like Badge Counter */}
<div className="absolute bottom-4 right-4 z-30 bg-black/50 backdrop-blur-sm border border-white/10 rounded-xl px-2.5 py-1.5 flex items-center gap-1 text-[11px] font-bold text-rose-500">
<Heart className="w-3.5 h-3.5 fill-rose-500" />
<span>{photo.likesCount}</span>
</div>
</div>
{/* TIER 2: SCROLLABLE CORE CONTENTS & COMMENTS PANEL (z-10) */}
{/* This entire workspace container slides underneath the photo layout on swipe-up */}
<div className="flex-1 overflow-y-auto bg-slate-900/30 relative z-10 flex flex-col min-h-0">
{/* REPLACED HEADER ZONE: Title replaces the old History text strip */}
<div className="px-5 py-4 border-b border-slate-900/80 bg-slate-900/90 backdrop-blur-md sticky top-0 z-10 space-y-1">
<h2 className="text-sm font-bold text-slate-100 tracking-wide">
{photo.title || "Chưa có tiêu đề"}
</h2>
{photo.description && (
<p className="text-[11px] text-slate-400 leading-relaxed font-medium">
{photo.description}
</p>
)}
</div>
{/* DYNAMIC COMMENTS FEED BLOCK */}
<div className="p-4 space-y-3.5 flex-1 overflow-y-visible">
{/* Mock iteration loop representing user chat bubbles */}
{[...Array(5)].map((_, index) => (
<div key={index} className="flex gap-3 items-start text-xs text-slate-300 animate-fade-in">
<div className="w-7 h-7 rounded-full bg-slate-800 font-bold text-[9px] flex items-center justify-center shrink-0 border border-slate-700">U</div>
<div className="flex-1 bg-slate-900/50 border border-slate-800/60 rounded-xl p-3 space-y-1 shadow-sm">
<div className="flex justify-between items-center text-[10px] font-semibold">
<span className="text-slate-200">Thành viên YoTrip</span>
<span className="text-slate-500 font-normal">Vừa xong</span>
</div>
<p className="text-slate-400 leading-relaxed text-[11px]">Góc chụp đẹp quá, bối cảnh nhìn rất thoáng và đầy đủ ánh sáng tự nhiên!</p>
</div>
</div>
))}
</div>
{/* STICKY BOTTOM INPUT SEND TRAY BAR */}
<div className="p-3 bg-slate-950 border-t border-slate-900 flex items-center gap-2 sticky bottom-0 z-20">
<input
type="text"
value={commentInput}
onChange={(e) => setCommentInput(e.target.value)}
placeholder="Viết bình luận công khai..."
className="flex-1 bg-slate-900 border border-slate-800 text-white rounded-xl p-3 text-xs outline-none focus:border-blue-500 transition-colors placeholder-slate-500"
/>
<button className="p-3 bg-blue-600 hover:bg-blue-500 text-white rounded-xl transition-colors active:scale-95 shadow-lg">
<Send className="w-4 h-4" />
</button>
</div>
</div>
</div>
);
};
## 4. Quality Verification & Acceptance Criteria for AI Agent
[ ] Dimension Stability Test: Switch back and forth between an ultra-wide panoramic photo and a 4:3 vertical shot. The parent blue container (aspect-square) must remain exactly static on the viewport layout, blocking size shifts.
[ ] Overlay Check Validation: Verify the location marker coordinates box is completely wiped from the lower white profile zone and draws successfully on top of the image canvas at the bottom-left edge.
[ ] Text Swap Alignment: Ensure the string text "Lịch sử ảnh tại vị trí này" is replaced completely by the active image title asset hook.
[ ] Scroll Pass Inspection: Swipe up to read the text inside the comments panel. Verify the message rows pass behind the bottom border line of the image canvas container (z-20), while the close button at the top remains fully accessible.