fix(panels): eliminate getSession() from transcription store and cabinets panel
Same GoTrueClient lock contention fix as CCFilesPanel: - transcriptionStore: add _accessToken/_userId state + setAuthInfo() action; replace all 6 getSession() calls (startSession, flushCanvasEvents, loadSessions, loadKeywordWatches, addKeywordWatch, deleteKeywordWatch, checkSegmentForKeywords) with stored values — zero getSession() calls remain in the store - CCTranscriptionPanel: destructure accessToken from useAuth; sync both values into store via setAuthInfo() on every auth change; gate loadSessions on authUser.id - CCCabinetsPanel: same pattern as CCFilesPanel — useAuth for token, useCallback on apiFetch/loadCabinets, gate effect on authUser.id Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ThemeProvider, createTheme, useMediaQuery, Box, Grid, Card, CardContent, CardActions, Typography, Button, TextField, Dialog, DialogTitle, DialogContent, DialogActions, IconButton, styled } from '@mui/material';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import { useTLDraw } from '../../../../../contexts/TLDrawContext';
|
||||
import { supabase } from '../../../../../supabaseClient';
|
||||
import { useAuth } from '../../../../../contexts/AuthContext';
|
||||
|
||||
type Cabinet = { id: string; name: string };
|
||||
|
||||
const Toolbar = styled('div')(() => ({ display: 'flex', gap: '8px', marginBottom: '8px' }));
|
||||
|
||||
export const CCCabinetsPanel: React.FC = () => {
|
||||
const { tldrawPreferences, authToken } = useTLDraw() as { tldrawPreferences?: { colorScheme?: 'light' | 'dark' | 'system' }, authToken?: string };
|
||||
const { tldrawPreferences } = useTLDraw() as { tldrawPreferences?: { colorScheme?: 'light' | 'dark' | 'system' } };
|
||||
const { user: authUser, accessToken } = useAuth();
|
||||
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||
const [cabinets, setCabinets] = useState<Cabinet[]>([]);
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
@@ -28,27 +29,29 @@ export const CCCabinetsPanel: React.FC = () => {
|
||||
const API_BASE: string = import.meta.env.VITE_API_BASE || (location.port.startsWith('517') ? 'http://127.0.0.1:8080' : '/api');
|
||||
|
||||
type RequestInitLite = { method?: string; body?: string | FormData | Blob | null; headers?: Record<string, string> } | undefined;
|
||||
const apiFetch = async (url: string, init?: RequestInitLite) => {
|
||||
const apiFetch = useCallback(async (url: string, init?: RequestInitLite) => {
|
||||
const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}`;
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
const bearer = session?.access_token || authToken || '';
|
||||
const res = await fetch(fullUrl, {
|
||||
...init,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${bearer}`,
|
||||
'Authorization': `Bearer ${accessToken || ''}`,
|
||||
...(init?.headers || {})
|
||||
}
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json();
|
||||
};
|
||||
}, [accessToken, API_BASE]);
|
||||
|
||||
const loadCabinets = async () => {
|
||||
const loadCabinets = useCallback(async () => {
|
||||
const data = await apiFetch('/database/cabinets');
|
||||
setCabinets([...(data.owned || []), ...(data.shared || [])]);
|
||||
};
|
||||
}, [apiFetch]);
|
||||
|
||||
useEffect(() => { loadCabinets(); /* eslint-disable-line react-hooks/exhaustive-deps */ }, []);
|
||||
useEffect(() => {
|
||||
if (authUser?.id) {
|
||||
loadCabinets();
|
||||
}
|
||||
}, [loadCabinets, authUser?.id]);
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!newName.trim()) return;
|
||||
|
||||
@@ -67,6 +67,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
flushCanvasEvents,
|
||||
loadSessions,
|
||||
setTimetableContext,
|
||||
setAuthInfo,
|
||||
llmConfig,
|
||||
summaryText,
|
||||
isGeneratingSummary,
|
||||
@@ -93,7 +94,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
|
||||
// Modal state
|
||||
const [showSettingsModal, setShowSettingsModal] = useState(false);
|
||||
const { user: authUser } = useAuth();
|
||||
const { user: authUser, accessToken } = useAuth();
|
||||
const [showSummaryModal, setShowSummaryModal] = useState(false);
|
||||
const [summaryType, setSummaryType] = useState('full_lesson');
|
||||
|
||||
@@ -101,6 +102,11 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
const [newKeyword, setNewKeyword] = useState('');
|
||||
const [isAddingKeyword, setIsAddingKeyword] = useState(false);
|
||||
|
||||
// Sync access token into Zustand store so all store actions can use it without getSession()
|
||||
useEffect(() => {
|
||||
setAuthInfo(accessToken, authUser?.id ?? null);
|
||||
}, [accessToken, authUser?.id, setAuthInfo]);
|
||||
|
||||
// Load sessions when auth is confirmed (avoids GoTrueClient lock on mount)
|
||||
useEffect(() => {
|
||||
if (authUser?.id) {
|
||||
|
||||
Reference in New Issue
Block a user