mirror of
https://github.com/wisplite/raster.git
synced 2026-05-01 06:32:44 -05:00
fix album list styling and add media list with uploadfilemodal
This commit is contained in:
@@ -49,7 +49,7 @@ export default function AlbumList({ currentAlbumName }) {
|
||||
return () => { ignore = true; }
|
||||
}, [currentAlbumName, open])
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start h-full w-full bg-[#141414]">
|
||||
<div className="flex flex-col items-center justify-start h-min w-full bg-[#141414]">
|
||||
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
|
||||
<h1 className="text-xl font-bold text-white red-hat-mono">Albums</h1>
|
||||
<PlusIcon className="w-6 h-6 cursor-pointer" color="white" onClick={() => setOpen(true)} />
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col items-center justify-start w-full bg-[#141414]">
|
||||
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
|
||||
<h1 className="text-xl font-bold text-white red-hat-mono">Media</h1>
|
||||
<Upload className="w-6 h-6 cursor-pointer" color="white" onClick={() => setOpen(true)} />
|
||||
</div>
|
||||
|
||||
{/* Media Grid Placeholder */}
|
||||
<div className="flex flex-row items-center justify-start gap-2 w-full px-6 flex-wrap">
|
||||
{media.length === 0 && (
|
||||
<p className="text-gray-500 red-hat-text">No media in this album</p>
|
||||
)}
|
||||
{/* Render media items here */}
|
||||
</div>
|
||||
|
||||
<MediaUploadModal
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
albumId={albumId}
|
||||
albumName={albumName}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<Modal open={open} onOpenChange={onOpenChange} trigger={trigger} title={`Upload to ${albumName || 'Album'}`}>
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* File Selection Area */}
|
||||
<div
|
||||
className="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-[#2B2B2B] rounded-lg cursor-pointer hover:border-[#3B3B3B] transition-colors bg-[#1A1A1A]"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
<Upload className="w-8 h-8 text-gray-400 mb-2" />
|
||||
<p className="text-gray-400 text-sm font-medium red-hat-text">Click to select files</p>
|
||||
<p className="text-gray-600 text-xs mt-1 red-hat-text">Images and Videos</p>
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
multiple
|
||||
accept="image/*,video/*"
|
||||
onChange={handleFileSelect}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* File List */}
|
||||
{files.length > 0 && (
|
||||
<div className="flex flex-col gap-2 max-h-[40vh] overflow-y-auto pr-1">
|
||||
{files.map((fileWrapper) => (
|
||||
<div key={fileWrapper.id} className="flex flex-col gap-2 bg-[#1A1A1A] p-3 rounded-md border border-[#2B2B2B]">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3 overflow-hidden">
|
||||
{fileWrapper.file.type.startsWith('video') ? (
|
||||
<FileVideo className="w-5 h-5 text-blue-400 flex-shrink-0" />
|
||||
) : (
|
||||
<FileImage className="w-5 h-5 text-green-400 flex-shrink-0" />
|
||||
)}
|
||||
<div className="flex flex-col overflow-hidden">
|
||||
<span className="text-white text-sm truncate red-hat-text" title={fileWrapper.file.name}>
|
||||
{fileWrapper.file.name}
|
||||
</span>
|
||||
<span className="text-gray-500 text-xs red-hat-mono">
|
||||
{formatFileSize(fileWrapper.file.size)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => removeFile(fileWrapper.id)}
|
||||
className="text-gray-500 hover:text-red-400 transition-colors p-1"
|
||||
disabled={fileWrapper.status === 'uploading'}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="w-full h-1.5 bg-[#2B2B2B] rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full transition-all duration-300 ${fileWrapper.status === 'completed' ? 'bg-green-500' :
|
||||
fileWrapper.status === 'error' ? 'bg-red-500' : 'bg-blue-500'
|
||||
}`}
|
||||
style={{ width: `${fileWrapper.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Upload Button */}
|
||||
<button
|
||||
className={`w-full py-2.5 bg-[#2B2B2B] text-white rounded-md font-medium transition-colors red-hat-mono ${files.length === 0 || files.every(f => f.status === 'completed')
|
||||
? 'opacity-50 cursor-not-allowed'
|
||||
: 'hover:bg-[#3B3B3B] cursor-pointer'
|
||||
}`}
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || files.every(f => f.status === 'completed')}
|
||||
>
|
||||
{files.some(f => f.status === 'uploading') ? 'Uploading...' : 'Upload Files'}
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -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() {
|
||||
<div className="flex flex-col items-center justify-start h-full w-full bg-[#141414]">
|
||||
<NavBar path={pathList} />
|
||||
<AlbumList currentAlbumName={currentAlbumID} />
|
||||
<MediaList albumId={currentAlbumID} albumName={currentAlbumName} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user