Fix file sharing load speed and move error; misc updates

- Remove recursive directory size calculations (single Seafile API call per list)
- Remove 'Used in this location' usage display
- Fix move using v2 per-type endpoints instead of broken batch endpoint
- Send entry type from frontend for correct move routing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-05-13 14:20:38 -04:00
parent c9e7816e28
commit eee0885811
117 changed files with 17592 additions and 4057 deletions
+94 -39
View File
@@ -1,60 +1,121 @@
import { createContext, useContext, useState, useEffect } from 'react';
import { createContext, useContext, useState, useEffect, useEffectEvent } from 'react';
import { supabase } from '../lib/supabase';
import PageLoader from '../components/PageLoader';
const AuthContext = createContext(null);
const PROFILE_CACHE_KEY = 'fourge_profile';
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(() => {
// Seed from cache instantly — no loading flash for returning users
try {
const cached = localStorage.getItem(PROFILE_CACHE_KEY);
return cached ? JSON.parse(cached) : null;
} catch { return null; }
});
const [currentUser, setCurrentUser] = useState(null);
const [loading, setLoading] = useState(true);
const fetchAndCacheProfile = async (authUser) => {
const fetchAndCacheProfile = async (authUser, attempt = 0) => {
try {
const { data } = await supabase
.from('profiles')
.select('*, company:companies(id, name, phone, address)')
.eq('id', authUser.id)
.single();
const { data, error } = await Promise.race([
supabase
.from('profiles')
.select('*, company:companies(id, name, phone, address)')
.eq('id', authUser.id)
.single(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Profile fetch timeout')), 8000)),
]);
if (data) {
const profile = { ...data, email: authUser.email };
const { data: memberships } = await supabase
.from('company_members')
.select('company:companies(id, name, phone, address)')
.eq('profile_id', authUser.id);
const companies = (memberships || [])
.map(membership => membership.company)
.filter(Boolean);
if (data.role === 'client' && data.company && !companies.some(company => company.id === data.company.id)) {
companies.unshift(data.company);
}
const profile = { ...data, email: authUser.email, companies };
setCurrentUser(profile);
localStorage.setItem(PROFILE_CACHE_KEY, JSON.stringify(profile));
return profile;
}
// Profile row not found yet (trigger race on first login) — retry once
if (error && attempt === 0) {
await new Promise(r => setTimeout(r, 800));
return fetchAndCacheProfile(authUser, 1);
}
console.error('Profile fetch failed:', error);
} catch (err) {
console.error('Profile fetch failed:', err);
}
return null;
};
useEffect(() => {
// Fallback — stop blocking after 2s max
const timeout = setTimeout(() => setLoading(false), 2000);
let resolved = false;
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, session) => {
if (session?.user) {
// Fetch fresh profile in background (cache already seeded above)
fetchAndCacheProfile(session.user);
} else {
setCurrentUser(null);
const syncSessionUser = useEffectEvent(async (session) => {
if (session?.user) {
try {
const cached = localStorage.getItem(PROFILE_CACHE_KEY);
if (cached && JSON.parse(cached)?.id !== session.user.id) {
localStorage.removeItem(PROFILE_CACHE_KEY);
}
} catch {
localStorage.removeItem(PROFILE_CACHE_KEY);
}
if (!resolved) {
resolved = true;
clearTimeout(timeout);
setLoading(false);
await fetchAndCacheProfile(session.user);
return;
}
setCurrentUser(null);
localStorage.removeItem(PROFILE_CACHE_KEY);
});
useEffect(() => {
let active = true;
const bootstrap = async () => {
let hasCachedProfile = false;
// Show cached profile immediately so returning users aren't stuck on the loader
try {
const cached = localStorage.getItem(PROFILE_CACHE_KEY);
if (cached) {
const profile = JSON.parse(cached);
if (profile?.id && active) {
hasCachedProfile = true;
setCurrentUser(profile);
setLoading(false);
}
}
} catch { /* corrupt cache, ignore */ }
try {
// Timeout getSession — Edge can stall on this indefinitely
const sessionPromise = supabase.auth.getSession();
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Session timeout')), 6000)
);
const { data: { session } } = await Promise.race([sessionPromise, timeout]);
if (!active) return;
await syncSessionUser(session);
if (active) setLoading(false);
} catch (err) {
console.warn('Auth bootstrap failed:', err.message);
// If we have no cached user either, clear so they see the login page
if (active && !hasCachedProfile) {
setCurrentUser(null);
setLoading(false);
}
}
};
bootstrap();
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, session) => {
if (!active) return;
await syncSessionUser(session);
if (active) setLoading(false);
});
return () => {
clearTimeout(timeout);
active = false;
subscription.unsubscribe();
};
}, []);
@@ -83,14 +144,7 @@ export function AuthProvider({ children }) {
localStorage.removeItem(PROFILE_CACHE_KEY);
};
if (loading) return (
<div style={{
minHeight: '100vh', background: '#111111',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<img src="/fourge-logo.png" alt="Fourge Branding" style={{ width: 160, opacity: 0.6 }} />
</div>
);
if (loading) return <PageLoader />;
return (
<AuthContext.Provider value={{ currentUser, login, signup, logout }}>
@@ -99,4 +153,5 @@ export function AuthProvider({ children }) {
);
}
// eslint-disable-next-line react-refresh/only-export-components
export const useAuth = () => useContext(AuthContext);