diff --git a/frontend/src/gallery/components/AlbumList.jsx b/frontend/src/gallery/components/AlbumList.jsx index c8a9872..fc3dbeb 100644 --- a/frontend/src/gallery/components/AlbumList.jsx +++ b/frontend/src/gallery/components/AlbumList.jsx @@ -49,7 +49,7 @@ export default function AlbumList({ currentAlbumName }) { return () => { ignore = true; } }, [currentAlbumName, open]) return ( -
+

Albums

setOpen(true)} /> diff --git a/frontend/src/gallery/components/MediaList.jsx b/frontend/src/gallery/components/MediaList.jsx new file mode 100644 index 0000000..702136d --- /dev/null +++ b/frontend/src/gallery/components/MediaList.jsx @@ -0,0 +1,50 @@ +import { Upload } from 'lucide-react' +import MediaUploadModal from './MediaUploadModal' +import { useState, useEffect } from 'react' +import { getServerUrl } from '../../hooks/getConstants' +import { useAccount } from '../../contexts/useAccount' +import { useNotifier } from '../../contexts/useNotifier' + +export default function MediaList({ albumId, albumName }) { + const { getAccessToken } = useAccount() + const [open, setOpen] = useState(false) + const [media, setMedia] = useState([]) + const { showError } = useNotifier() + + useEffect(() => { + let ignore = false; + const getMedia = async () => { + if (!albumId) return + + // TODO: Implement media fetching from API + // const response = await fetch(...) + } + + getMedia() + return () => { ignore = true; } + }, [albumId]) + + return ( +
+
+

Media

+ setOpen(true)} /> +
+ + {/* Media Grid Placeholder */} +
+ {media.length === 0 && ( +

No media in this album

+ )} + {/* Render media items here */} +
+ + +
+ ) +} diff --git a/frontend/src/gallery/components/MediaUploadModal.jsx b/frontend/src/gallery/components/MediaUploadModal.jsx new file mode 100644 index 0000000..53b9bd3 --- /dev/null +++ b/frontend/src/gallery/components/MediaUploadModal.jsx @@ -0,0 +1,156 @@ +import Modal from '../../components/Modal' +import { useAccount } from '../../contexts/useAccount' +import { useState, useRef } from 'react' +import { X, Upload, FileImage, FileVideo, Trash2 } from 'lucide-react' + +export default function MediaUploadModal({ open, onOpenChange, trigger, albumName, albumId }) { + const { getAccessToken } = useAccount() + const [files, setFiles] = useState([]) + const fileInputRef = useRef(null) + + const handleFileSelect = (e) => { + if (e.target.files) { + const newFiles = Array.from(e.target.files).map(file => ({ + file, + progress: 0, + status: 'pending', + id: Math.random().toString(36).substring(7) + })) + setFiles(prev => [...prev, ...newFiles]) + } + // Reset input so the same file can be selected again if needed + if (fileInputRef.current) { + fileInputRef.current.value = '' + } + } + + const removeFile = (id) => { + setFiles(prev => prev.filter(f => f.id !== id)) + } + + const handleUpload = async () => { + // Placeholder for server-side logic hook + const performUpload = async (fileWrapper) => { + // TODO: Implement actual server upload here + // Example: + // const formData = new FormData() + // formData.append('file', fileWrapper.file) + // formData.append('albumId', albumId) + // await fetch('/api/upload', { method: 'POST', body: formData, ... }) + + // Simulation: + return new Promise((resolve) => { + let progress = 0 + const interval = setInterval(() => { + progress += 5 + setFiles(prev => prev.map(f => { + if (f.id === fileWrapper.id) { + if (progress >= 100) { + clearInterval(interval) + resolve() + return { ...f, progress: 100, status: 'completed' } + } + return { ...f, progress, status: 'uploading' } + } + return f + })) + }, 100) + }) + } + + // Start uploads + files.forEach(fileWrapper => { + if (fileWrapper.status === 'pending') { + performUpload(fileWrapper) + } + }) + } + + const formatFileSize = (bytes) => { + if (bytes === 0) return '0 Bytes' + const k = 1024 + const sizes = ['Bytes', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + } + + return ( + +
+ {/* File Selection Area */} +
fileInputRef.current?.click()} + > + +

Click to select files

+

Images and Videos

+ +
+ + {/* File List */} + {files.length > 0 && ( +
+ {files.map((fileWrapper) => ( +
+
+
+ {fileWrapper.file.type.startsWith('video') ? ( + + ) : ( + + )} +
+ + {fileWrapper.file.name} + + + {formatFileSize(fileWrapper.file.size)} + +
+
+ +
+ + {/* Progress Bar */} +
+
+
+
+ ))} +
+ )} + + {/* Upload Button */} + +
+ + ) +} diff --git a/frontend/src/gallery/index.jsx b/frontend/src/gallery/index.jsx index 4ed5a9a..947023e 100644 --- a/frontend/src/gallery/index.jsx +++ b/frontend/src/gallery/index.jsx @@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'; import AlbumList from './components/AlbumList'; import { getServerUrl } from '../hooks/getConstants'; import { useNotifier } from '../contexts/useNotifier'; +import MediaList from './components/MediaList'; export default function Gallery() { const currentPath = useLocation().pathname; const pathList = currentPath.split('/').slice(1); @@ -53,6 +54,7 @@ export default function Gallery() {
+
) } \ No newline at end of file