fix: resolve folder drag-and-drop, add remember me, fix pinned folder navigation
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m18s

This commit is contained in:
Elijah 2026-05-23 13:08:13 -07:00
parent 7278008d7e
commit b668418951
5 changed files with 89 additions and 14 deletions

View file

@ -433,7 +433,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
if (section === 'files') {
navigateTo('.');
loadFiles();
loadFiles('.');
} else if (section === 'trash') {
loadTrash();
}
@ -748,11 +748,12 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
}
}
async function uploadFile(file: File) {
async function uploadFile(file: File, targetDir?: string) {
const id = `${Date.now()}-${file.name}`;
setUploads(prev => [...prev, { id, name: file.name, progress: 0, status: 'uploading' }]);
try {
await api.upload(file, currentPath, {
const uploadPath = targetDir ? (currentPath === '.' ? targetDir : `${currentPath}/${targetDir}`) : currentPath;
await api.upload(file, uploadPath, {
onProgress: (percent) => {
setUploads(prev => prev.map(u => u.id === id ? { ...u, progress: percent } : u));
},
@ -770,8 +771,56 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
e.preventDefault();
setDragOver(false);
if (draggedFile) return;
const droppedFiles = Array.from(e.dataTransfer.files);
await Promise.all(droppedFiles.map(file => uploadFile(file)));
if (e.dataTransfer.items) {
const filesToUpload: { file: File; path: string }[] = [];
const promises: Promise<void>[] = [];
const processEntry = async (entry: any, path: string = '') => {
if (entry.isFile) {
const file = await new Promise<File>((resolve) => entry.file(resolve));
filesToUpload.push({ file, path });
} else if (entry.isDirectory) {
const dirReader = entry.createReader();
let entries: any[] = [];
const readAllEntries = async () => {
const batch = await new Promise<any[]>((resolve) => dirReader.readEntries(resolve));
if (batch.length > 0) {
entries = entries.concat(batch);
await readAllEntries();
}
};
await readAllEntries();
for (const child of entries) {
await processEntry(child, `${path}${entry.name}/`);
}
}
};
for (let i = 0; i < e.dataTransfer.items.length; i++) {
const item = e.dataTransfer.items[i];
if (item.kind === 'file') {
const entry = item.webkitGetAsEntry();
if (entry) {
promises.push(processEntry(entry));
}
}
}
await Promise.all(promises);
if (filesToUpload.length > 0) {
await Promise.all(filesToUpload.map(({file, path}) =>
uploadFile(file, path.replace(/\/$/, ''))
));
}
} else {
const droppedFiles = Array.from(e.dataTransfer.files);
await Promise.all(droppedFiles.map(file => uploadFile(file)));
}
loadFiles(undefined, true);
}

View file

@ -17,6 +17,7 @@ export default function LoginPage({ onSuccess }: LoginPageProps) {
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const [needs2FA, setNeeds2FA] = useState(false);
const [rememberMe, setRememberMe] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
@ -24,7 +25,7 @@ export default function LoginPage({ onSuccess }: LoginPageProps) {
setLoading(true);
try {
const { ok, status, data } = await api.login(username, password, needs2FA ? totpCode : undefined);
const { ok, status, data } = await api.login(username, password, needs2FA ? totpCode : undefined, rememberMe);
if (ok) {
onSuccess();
@ -189,6 +190,25 @@ export default function LoginPage({ onSuccess }: LoginPageProps) {
</motion.div>
)}
{!needs2FA && (
<label className="flex items-center gap-2 mt-2 cursor-pointer select-none">
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="w-4 h-4 rounded"
style={{
backgroundColor: 'var(--color-bg-tertiary)',
borderColor: 'var(--color-border)',
accentColor: 'var(--color-accent)'
}}
/>
<span className="text-sm" style={{ color: 'var(--color-text-secondary)' }}>
Remember me for 3 weeks
</span>
</label>
)}
{error && (
<motion.p
initial={{ opacity: 0 }}

View file

@ -121,10 +121,10 @@ class ApiClient {
}
// --- Auth ---
async login(username: string, password: string, totpCode?: string) {
async login(username: string, password: string, totpCode?: string, rememberMe?: boolean) {
const res = await this.request('/api/auth/login', {
method: 'POST',
body: { username, password, totp_code: totpCode },
body: { username, password, totp_code: totpCode, remember_me: rememberMe },
});
const data = await res.json();
if (res.ok && data.access_token) {