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:
+94
-39
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user