fix: cho phép người đã tải ảnh lên được quyền chỉnh sửa
This commit is contained in:
@@ -61,6 +61,7 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
const [isSavingEdit, setIsSavingEdit] = useState(false);
|
||||
const [isMapOpen, setIsMapOpen] = useState(false);
|
||||
const [resolvedAddress, setResolvedAddress] = useState<string>('');
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const lat = photo?.metadata?.lat;
|
||||
@@ -89,7 +90,7 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
}, [photo?.id, photo?.metadata?.lat, photo?.metadata?.lng]);
|
||||
|
||||
const checkCurrentUser = () => {
|
||||
const userStr = localStorage.getItem('user');
|
||||
const userStr = localStorage.getItem('user') || localStorage.getItem('guest_user');
|
||||
if (userStr) {
|
||||
try {
|
||||
setCurrentUser(JSON.parse(userStr));
|
||||
@@ -125,7 +126,7 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
|
||||
setIsSavingEdit(true);
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('token') || localStorage.getItem('guest_token');
|
||||
const res = await fetch(`/api/v1/photos/${photo.id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
@@ -165,7 +166,7 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
const likeCount = likedUserIds.length;
|
||||
|
||||
const handleToggleLike = async () => {
|
||||
let token = localStorage.getItem('token');
|
||||
let token = localStorage.getItem('token') || localStorage.getItem('guest_token');
|
||||
let userObj = currentUser;
|
||||
|
||||
if (!token) {
|
||||
@@ -362,18 +363,18 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
/>
|
||||
|
||||
{/* Container */}
|
||||
<div className="relative w-full max-w-5xl h-[85vh] bg-slate-900 border border-slate-800 rounded-[32px] shadow-2xl overflow-hidden flex flex-col md:flex-row animate-in zoom-in-95 duration-300 text-slate-100">
|
||||
<div className="relative w-full max-w-5xl h-[90vh] md:h-[85vh] bg-slate-900 border border-slate-800 rounded-[32px] shadow-2xl overflow-y-auto md:overflow-hidden flex flex-col md:flex-row animate-in zoom-in-95 duration-300 text-slate-100">
|
||||
|
||||
{/* Close Button Mobile/Desktop */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 z-50 p-2 bg-slate-950/60 hover:bg-slate-800/80 border border-slate-700/50 rounded-full text-slate-300 hover:text-white transition-colors"
|
||||
className="fixed md:absolute top-6 right-6 md:top-4 md:right-4 z-50 p-2 bg-slate-950/60 hover:bg-slate-800/80 border border-slate-700/50 rounded-full text-slate-300 hover:text-white transition-colors"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Left Side: Photo Detail */}
|
||||
<div className="relative w-full md:w-3/5 h-2/5 md:h-full bg-slate-950 flex items-center justify-center overflow-hidden group">
|
||||
<div className="relative w-full md:w-3/5 h-[45vh] md:h-full bg-slate-950 flex items-center justify-center overflow-hidden group shrink-0">
|
||||
{/* Like Button Overlay */}
|
||||
<button
|
||||
onClick={handleToggleLike}
|
||||
@@ -389,10 +390,11 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
<img
|
||||
src={photo.imageUrl}
|
||||
alt="Public Map Upload"
|
||||
className="w-full h-full object-contain"
|
||||
className="w-full h-full object-contain cursor-zoom-in"
|
||||
onClick={() => setIsFullscreen(true)}
|
||||
/>
|
||||
{/* Info & Timeline overlay inside photo panel */}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-slate-950 via-slate-950/90 to-transparent flex flex-col gap-4">
|
||||
<div className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black/60 via-black/20 to-transparent flex flex-col gap-4">
|
||||
|
||||
{/* Timeline scroll */}
|
||||
{photoGroup && photoGroup.length > 1 && (
|
||||
@@ -559,18 +561,18 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
<MapPin className="w-3.5 h-3.5 text-rose-500" />
|
||||
Địa điểm: {resolvedAddress}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 font-semibold text-emerald-400">
|
||||
<div className="hidden md:flex items-center gap-1.5 font-semibold text-emerald-400">
|
||||
<User className="w-3.5 h-3.5" />
|
||||
Người đăng: {photo.uploader?.name || 'Ẩn danh'}
|
||||
</div>
|
||||
</div>
|
||||
{photo.originalUrl && (
|
||||
{isAuthorized && photo.originalUrl && (
|
||||
<a
|
||||
href={photo.originalUrl}
|
||||
download
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 bg-slate-800/80 hover:bg-slate-700/80 border border-slate-700/50 text-slate-200 hover:text-white font-bold py-1 px-3 rounded-full transition-all active:scale-95 text-[10px]"
|
||||
className="hidden md:flex items-center gap-1.5 bg-slate-800/80 hover:bg-slate-700/80 border border-slate-700/50 text-slate-200 hover:text-white font-bold py-1 px-3 rounded-full transition-all active:scale-95 text-[10px]"
|
||||
>
|
||||
<Download className="w-3.5 h-3.5 text-emerald-500" />
|
||||
Tải ảnh gốc
|
||||
@@ -592,8 +594,36 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Only: Uploader details & divider */}
|
||||
<div className="block md:hidden p-6 bg-slate-900 border-t border-slate-800/60 space-y-4 shrink-0">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2.5 text-left">
|
||||
<div className="p-2.5 bg-emerald-500/10 rounded-xl text-emerald-400">
|
||||
<User className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-[10px] text-slate-450 block uppercase font-bold tracking-wider">Người đăng ảnh</span>
|
||||
<span className="text-sm font-semibold text-white">{photo.uploader?.name || 'Ẩn danh'}</span>
|
||||
</div>
|
||||
</div>
|
||||
{isAuthorized && photo.originalUrl && (
|
||||
<a
|
||||
href={photo.originalUrl}
|
||||
download
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 bg-slate-850 hover:bg-slate-800 border border-slate-700 text-slate-200 hover:text-white font-bold py-2 px-4 rounded-xl transition-all text-xs"
|
||||
>
|
||||
<Download className="w-4 h-4 text-emerald-500" />
|
||||
Tải ảnh gốc
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="border-t border-slate-800/60 pt-2" />
|
||||
</div>
|
||||
|
||||
{/* Right Side: Comments */}
|
||||
<div className="w-full md:w-2/5 h-3/5 md:h-full flex flex-col bg-slate-900 border-t md:border-t-0 md:border-l border-slate-800">
|
||||
<div className="w-full md:w-2/5 h-auto md:h-full flex flex-col bg-slate-900 border-t md:border-t-0 md:border-l border-slate-800">
|
||||
|
||||
{/* Comments Header */}
|
||||
<div className="p-6 border-b border-slate-800 flex items-center justify-between">
|
||||
@@ -685,6 +715,25 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
setEditLng(lng);
|
||||
}}
|
||||
/>
|
||||
|
||||
{isFullscreen && (
|
||||
<div
|
||||
className="fixed inset-0 z-[9999] bg-black/95 flex items-center justify-center cursor-zoom-out animate-in fade-in duration-200"
|
||||
onClick={() => setIsFullscreen(false)}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsFullscreen(false)}
|
||||
className="fixed top-6 right-6 p-3 bg-black/50 hover:bg-black/75 border border-white/10 rounded-full text-white transition-colors z-[10000]"
|
||||
>
|
||||
<X className="w-6 h-6" />
|
||||
</button>
|
||||
<img
|
||||
src={photo.imageUrl}
|
||||
alt="Fullscreen photo"
|
||||
className="max-w-full max-h-full object-contain select-none animate-in zoom-in-95 duration-200"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user