diff --git a/src/App.tsx b/src/App.tsx index 36884a6..eadb406 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,8 +4,6 @@ import { theme } from './services/themeService'; import { AuthProvider } from './contexts/AuthContext'; import { TLDrawProvider } from './contexts/TLDrawContext'; import { UserProvider } from './contexts/UserContext'; -import { NeoUserProvider } from './contexts/NeoUserContext'; -import { NeoInstituteProvider } from './contexts/NeoInstituteContext'; import AppRoutes from './AppRoutes'; import React from 'react'; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index b6e89ac..3fce4bd 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -4,7 +4,6 @@ import { Session, User } from '@supabase/supabase-js'; import { CCUser, CCUserMetadata, authService } from '../services/auth/authService'; import { logger } from '../debugConfig'; import { supabase } from '../supabaseClient'; -import { DatabaseNameService } from '../services/graph/databaseNameService'; import { storageService, StorageKeys } from '../services/auth/localStorageService'; export interface AuthContextType { @@ -52,20 +51,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const baseDisplayName = metadata.display_name || metadata.name || metadata.preferred_username || baseUsername; const userType = (metadata.user_type || 'email_teacher').trim(); - const storedUserDb = DatabaseNameService.getStoredUserDatabase(); - const storedSchoolDb = DatabaseNameService.getStoredSchoolDatabase(); - - const userDbName = storedUserDb || DatabaseNameService.getUserPrivateDB(userType || 'standard', supabaseUser.id); - const schoolDbName = storedSchoolDb || ''; - const resolvedUser: CCUser = { id: supabaseUser.id, email: supabaseUser.email, user_type: userType, username: baseUsername, display_name: baseDisplayName, - user_db_name: userDbName, - school_db_name: schoolDbName, + school_id: null, created_at: supabaseUser.created_at, updated_at: supabaseUser.updated_at }; diff --git a/src/contexts/UserContext.tsx b/src/contexts/UserContext.tsx index ecfec29..1ff8558 100644 --- a/src/contexts/UserContext.tsx +++ b/src/contexts/UserContext.tsx @@ -4,8 +4,6 @@ import { supabase } from '../supabaseClient'; import { logger } from '../debugConfig'; import { CCUser, CCUserMetadata } from '../services/auth/authService'; import { UserPreferences } from '../services/auth/profileService'; -import { DatabaseNameService } from '../services/graph/databaseNameService'; -import { provisionUser } from '../services/provisioningService'; import { storageService, StorageKeys } from '../services/auth/localStorageService'; export interface UserContextType { @@ -112,33 +110,23 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { let profileRow: Record | null = null; - // Fast-path: build profile from auth metadata + localStorage immediately. - // This clears the spinner before any network call so the page renders on refresh - // without waiting for the Supabase profiles query (~200ms). + // Fast-path: build profile from auth metadata immediately (no spinner on refresh). const fastMetadata = userInfo.user_metadata as CCUserMetadata; - const fastStoredUserDb = DatabaseNameService.getStoredUserDatabase(); - const fastStoredSchoolDb = DatabaseNameService.getStoredSchoolDatabase(); - const fastUserDb = fastStoredUserDb || DatabaseNameService.getUserPrivateDB(fastMetadata?.user_type || '', userInfo.id); const fastProfile: CCUser = { id: userInfo.id, email: userInfo.email, user_type: fastMetadata?.user_type || '', username: fastMetadata?.username || userInfo.email?.split('@')[0] || 'user', display_name: String(fastMetadata?.display_name || ''), - user_db_name: String(fastUserDb || ''), - school_db_name: String(fastStoredSchoolDb || ''), + school_id: null, created_at: userInfo.created_at, updated_at: userInfo.updated_at }; - DatabaseNameService.rememberDatabaseNames({ - userDbName: fastProfile.user_db_name, - schoolDbName: fastProfile.school_db_name - }); if (mountedRef.current && !isInitialized) { setProfile(fastProfile); setLoading(false); setIsInitialized(true); - logger.debug('user-context', '⚡ Fast-path: profile initialized from auth metadata, no spinner'); + logger.debug('user-context', '⚡ Fast-path: profile initialized from auth metadata'); } // Background: query Supabase profiles for authoritative data (user_db_name, school_db_name, display_name). @@ -172,80 +160,15 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { hasSchoolDb: !!profileRow?.school_db_name }); + const metadata = userInfo.user_metadata as CCUserMetadata; - logger.debug('user-context', '🔧 Step 7: Processing user metadata...', { - hasMetadata: !!metadata, - userType: metadata?.user_type - }); - let userDbName = profileRow?.user_db_name ?? null; - let schoolDbName = profileRow?.school_db_name ?? null; - const storedUserDb = DatabaseNameService.getStoredUserDatabase(); - const storedSchoolDb = DatabaseNameService.getStoredSchoolDatabase(); - - // Start provisioning in background (non-blocking) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const provisioningPromise = provisionUser(userInfo.id, authSession?.access_token ?? null) - .then(provisioned => { - if (provisioned) { - logger.debug('user-context', '✅ Provisioning completed in background', { - userDbName: provisioned.user_db_name, - workerDbName: provisioned.worker_db_name - }); - // Update localStorage with provisioned values - DatabaseNameService.rememberDatabaseNames({ - userDbName: provisioned.user_db_name, - schoolDbName: provisioned.worker_db_name || '' - }); - } - }) - .catch(provisionError => { - logger.warn('user-context', 'âš ī¸ Background provisioning failed', { - userId: userInfo?.id, - provisionError: provisionError instanceof Error ? provisionError.message : String(provisionError) - }); - }); - - if (!userDbName && storedUserDb) { - userDbName = storedUserDb; - } - - if (!schoolDbName && storedSchoolDb) { - schoolDbName = storedSchoolDb; - } - - logger.debug('user-context', 'â„šī¸ Database name resolution', { - userDbName, - schoolDbName - }); - - if (!userDbName) { - userDbName = DatabaseNameService.getUserPrivateDB(metadata.user_type || '', userInfo.id); - } - - if (!schoolDbName) { - schoolDbName = ''; - } - - DatabaseNameService.rememberDatabaseNames({ - userDbName: String(userDbName || ''), - schoolDbName: String(schoolDbName || '') - }); - - logger.debug('user-context', '🔧 Creating user profile object...', { - userId: userInfo.id, - userDbName, - schoolDbName, - userType: metadata.user_type - }); - const userProfile: CCUser = { id: userInfo.id, email: userInfo.email, user_type: metadata.user_type || '', username: metadata.username || '', display_name: String(metadata.display_name || ''), - user_db_name: String(userDbName || ''), - school_db_name: String(schoolDbName || ''), + school_id: (profileRow?.school_id as string) ?? null, created_at: userInfo.created_at, updated_at: userInfo.updated_at }; @@ -268,9 +191,7 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { logger.debug('user-context', '✅ User profile loaded', { userId: userProfile.id, userType: userProfile.user_type, - username: userProfile.username, - userDbName: userProfile.user_db_name, - schoolDbName: userProfile.school_db_name + username: userProfile.username }); setError(null); } catch (error) { @@ -295,22 +216,14 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => { user_type: metadata?.user_type || 'email_teacher', username: metadata?.username || userInfo.email?.split('@')[0] || 'user', display_name: metadata?.display_name || userInfo.email?.split('@')[0] || 'User', - user_db_name: DatabaseNameService.getUserPrivateDB(metadata?.user_type || 'email_teacher', userInfo.id), - school_db_name: '', + school_id: null, created_at: userInfo.created_at, updated_at: userInfo.updated_at }; - - DatabaseNameService.rememberDatabaseNames({ - userDbName: fallbackProfile.user_db_name, - schoolDbName: fallbackProfile.school_db_name - }); - setProfile(fallbackProfile); logger.debug('user-context', '✅ Fallback profile created', { userId: fallbackProfile.id, - userType: fallbackProfile.user_type, - userDbName: fallbackProfile.user_db_name + userType: fallbackProfile.user_type }); } else { setProfile(null); diff --git a/src/hooks/useDeviceContext.ts b/src/hooks/useDeviceContext.ts new file mode 100644 index 0000000..0863a09 --- /dev/null +++ b/src/hooks/useDeviceContext.ts @@ -0,0 +1,37 @@ +import { useState, useEffect } from 'react'; + +export type DeviceType = 'desktop' | 'tablet' | 'phone' | 'iwb'; + +function detectDeviceType(): DeviceType { + const width = window.innerWidth; + const touchPoints = navigator.maxTouchPoints ?? 0; + const hasTouch = touchPoints > 0 || 'ontouchstart' in window; + + if (width >= 1280 && !hasTouch) return 'desktop'; + if (width >= 768 && hasTouch) return 'tablet'; + if (width < 768) return 'phone'; + return 'desktop'; +} + +const STORAGE_KEY = 'cc_device_type'; + +export function useDeviceContext() { + const [deviceType, setDeviceTypeState] = useState(() => { + const stored = localStorage.getItem(STORAGE_KEY) as DeviceType | null; + if (stored && ['desktop', 'tablet', 'phone', 'iwb'].includes(stored)) return stored; + return detectDeviceType(); + }); + + useEffect(() => { + localStorage.setItem(STORAGE_KEY, deviceType); + }, [deviceType]); + + const setDeviceType = (type: DeviceType) => { + setDeviceTypeState(type); + }; + + const isTouch = deviceType === 'tablet' || deviceType === 'phone'; + const isMobileLayout = deviceType === 'phone'; + + return { deviceType, setDeviceType, isTouch, isMobileLayout }; +} diff --git a/src/pages/tldraw/singlePlayerPage.tsx b/src/pages/tldraw/singlePlayerPage.tsx index 7ccf24c..5ccbb67 100644 --- a/src/pages/tldraw/singlePlayerPage.tsx +++ b/src/pages/tldraw/singlePlayerPage.tsx @@ -122,7 +122,7 @@ export default function SinglePlayerPage() { const nodeStoragePath = getNodeStoragePath(context.node); if (nodeStoragePath) { logger.debug('single-player-page', 'đŸ“Ĩ Loading snapshot from database', { - dbName: user.user_db_name, + dbName: null, node: context.node, node_storage_path: nodeStoragePath, user_type: user.user_type, @@ -131,7 +131,7 @@ export default function SinglePlayerPage() { await NavigationSnapshotService.loadNodeSnapshotFromDatabase( nodeStoragePath, - user.user_db_name, + null, newStore, setLoadingState, undefined, // sharedStore diff --git a/src/services/auth/authService.ts b/src/services/auth/authService.ts index abeb77f..7cc49cc 100644 --- a/src/services/auth/authService.ts +++ b/src/services/auth/authService.ts @@ -3,7 +3,6 @@ import { TLUserPreferences } from '@tldraw/tldraw'; import { supabase } from '../../supabaseClient'; import { storageService, StorageKeys } from './localStorageService'; import { logger } from '../../debugConfig'; -import { DatabaseNameService } from '../graph/databaseNameService'; export interface CCUser { id: string; @@ -11,8 +10,7 @@ export interface CCUser { user_type: string; username: string; display_name: string; - user_db_name: string; - school_db_name: string; + school_id?: string | null; created_at?: string; updated_at?: string; } @@ -44,28 +42,13 @@ export function convertToCCUser(user: User, metadata: CCUserMetadata): CCUser { // Default to student if no user type specified const userType = metadata.user_type || 'student'; - const storedUserDb = DatabaseNameService.getStoredUserDatabase(); - const storedSchoolDb = DatabaseNameService.getStoredSchoolDatabase(); - - const userDbName = storedUserDb || DatabaseNameService.getUserPrivateDB( - userType, - user.id - ); - const schoolDbName = metadata.school_db_name || metadata.worker_db_name || storedSchoolDb || ''; - - DatabaseNameService.rememberDatabaseNames({ - userDbName, - schoolDbName - }); - return { id: user.id, email: user.email, user_type: userType, - username: username, + username, display_name: displayName, - user_db_name: userDbName, - school_db_name: schoolDbName, + school_id: null, created_at: user.created_at, updated_at: user.updated_at, }; diff --git a/src/services/auth/registrationService.ts b/src/services/auth/registrationService.ts index b4c7871..e704087 100644 --- a/src/services/auth/registrationService.ts +++ b/src/services/auth/registrationService.ts @@ -6,7 +6,6 @@ import { RegistrationResponse } from '../../services/auth/authService'; import { storageService, StorageKeys } from './localStorageService'; import { logger } from '../../debugConfig'; import { provisionUser } from '../provisioningService'; -import { DatabaseNameService } from '../graph/databaseNameService'; const REGISTRATION_SERVICE = 'registration-service'; @@ -87,14 +86,6 @@ export class RegistrationService { try { const provisioned = await provisionUser(ccUser.id, provisioningToken); if (provisioned) { - ccUser.user_db_name = provisioned.user_db_name; - if (provisioned.worker_db_name) { - ccUser.school_db_name = provisioned.worker_db_name; - } - DatabaseNameService.rememberDatabaseNames({ - userDbName: ccUser.user_db_name, - schoolDbName: ccUser.school_db_name - }); logger.info(REGISTRATION_SERVICE, '✅ Provisioning successful', { userId: ccUser.id, userDbName: provisioned.user_db_name, @@ -110,11 +101,6 @@ export class RegistrationService { }); } - DatabaseNameService.rememberDatabaseNames({ - userDbName: ccUser.user_db_name, - schoolDbName: ccUser.school_db_name - }); - return { user: ccUser, accessToken: authData.session?.access_token || null, diff --git a/src/services/tldraw/snapshotService.ts b/src/services/tldraw/snapshotService.ts index 1a9238a..b6b3b64 100644 --- a/src/services/tldraw/snapshotService.ts +++ b/src/services/tldraw/snapshotService.ts @@ -281,8 +281,12 @@ export class NavigationSnapshotService { throw new Error('No user found'); } - const dbName = user.user_db_name; - + const dbName = (user as (typeof user & { user_db_name?: string })).user_db_name ?? ''; + if (!dbName) { + logger.debug('snapshot-service', 'âš ī¸ No db name - snapshot save skipped (Phase B will migrate to Supabase Storage)'); + return; + } + logger.debug('snapshot-service', '💾 Saving snapshot', { nodePath, dbName, @@ -315,8 +319,12 @@ export class NavigationSnapshotService { throw new Error('No user found'); } - const dbName = user.user_db_name; - + const dbName = (user as (typeof user & { user_db_name?: string })).user_db_name ?? ''; + if (!dbName) { + logger.debug('snapshot-service', 'âš ī¸ No db name - snapshot load skipped (Phase B will migrate to Supabase Storage)'); + return; + } + logger.debug('snapshot-service', 'đŸ“Ĩ Loading snapshot', { nodePath: node.node_storage_path, dbName,