chore: ran frontend and backend linting

This commit is contained in:
Anish Sarkar 2026-01-15 15:55:27 +05:30
parent ab63b23f0a
commit b9dc785a1c
9 changed files with 52 additions and 50 deletions

View file

@ -19,7 +19,7 @@ from alembic import context, op
# Get Electric SQL user credentials from env.py configuration
_config = context.config
ELECTRIC_DB_USER =_config.get_main_option("electric_db_user", "electric")
ELECTRIC_DB_USER = _config.get_main_option("electric_db_user", "electric")
ELECTRIC_DB_PASSWORD = _config.get_main_option(
"electric_db_password", "electric_password"
)
@ -52,10 +52,18 @@ def upgrade() -> None:
)
# Create indexes (using IF NOT EXISTS for idempotency)
op.execute("CREATE INDEX IF NOT EXISTS ix_notifications_user_id ON notifications (user_id);")
op.execute("CREATE INDEX IF NOT EXISTS ix_notifications_read ON notifications (read);")
op.execute("CREATE INDEX IF NOT EXISTS ix_notifications_created_at ON notifications (created_at);")
op.execute("CREATE INDEX IF NOT EXISTS ix_notifications_user_read ON notifications (user_id, read);")
op.execute(
"CREATE INDEX IF NOT EXISTS ix_notifications_user_id ON notifications (user_id);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_notifications_read ON notifications (read);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_notifications_created_at ON notifications (created_at);"
)
op.execute(
"CREATE INDEX IF NOT EXISTS ix_notifications_user_read ON notifications (user_id, read);"
)
# =====================================================
# Electric SQL Setup - User and Publication

View file

@ -438,9 +438,7 @@ export default function EditorPage() {
{saving ? (
<>
<Loader2 className="h-3.5 w-3.5 md:h-4 md:w-4 animate-spin" />
<span className="text-xs md:text-sm">
{isNewNote ? "Creating" : "Saving"}
</span>
<span className="text-xs md:text-sm">{isNewNote ? "Creating" : "Saving"}</span>
</>
) : (
<>

View file

@ -14,15 +14,15 @@ import { useParams } from "next/navigation";
export function NotificationButton() {
const { data: user } = useAtomValue(currentUserAtom);
const params = useParams();
const userId = user?.id ? String(user.id) : null;
// Get searchSpaceId from URL params - the component is rendered within /dashboard/[search_space_id]/
const searchSpaceId = params?.search_space_id
? Number(params.search_space_id)
: null;
const { notifications, unreadCount, loading, markAsRead, markAllAsRead } =
useNotifications(userId, searchSpaceId);
const searchSpaceId = params?.search_space_id ? Number(params.search_space_id) : null;
const { notifications, unreadCount, loading, markAsRead, markAllAsRead } = useNotifications(
userId,
searchSpaceId
);
return (
<Popover>

View file

@ -29,7 +29,11 @@ interface ElectricProviderProps {
export function ElectricProvider({ children }: ElectricProviderProps) {
const [electricClient, setElectricClient] = useState<ElectricClient | null>(null);
const [error, setError] = useState<Error | null>(null);
const { data: user, isSuccess: isUserLoaded, isError: isUserError } = useAtomValue(currentUserAtom);
const {
data: user,
isSuccess: isUserLoaded,
isError: isUserError,
} = useAtomValue(currentUserAtom);
const previousUserIdRef = useRef<string | null>(null);
const initializingRef = useRef(false);
@ -104,11 +108,7 @@ export function ElectricProvider({ children }: ElectricProviderProps) {
// For non-authenticated pages (like landing page), render immediately with null context
// Also render immediately if user query failed (e.g., token expired)
if (!isUserLoaded || !user?.id || isUserError) {
return (
<ElectricContext.Provider value={null}>
{children}
</ElectricContext.Provider>
);
return <ElectricContext.Provider value={null}>{children}</ElectricContext.Provider>;
}
// Show loading state while initializing for authenticated users
@ -128,9 +128,5 @@ export function ElectricProvider({ children }: ElectricProviderProps) {
}
// Provide the Electric client to children
return (
<ElectricContext.Provider value={electricClient}>
{children}
</ElectricContext.Provider>
);
return <ElectricContext.Provider value={electricClient}>{children}</ElectricContext.Provider>;
}

View file

@ -7,14 +7,14 @@ import type { SearchSourceConnector } from "@/contracts/types/connector.types";
/**
* Hook for managing connectors with Electric SQL real-time sync
*
*
* Uses the Electric client from context (provided by ElectricProvider)
* instead of initializing its own - prevents race conditions and memory leaks
*/
export function useConnectorsElectric(searchSpaceId: number | string | null) {
// Get Electric client from context - ElectricProvider handles initialization
const electricClient = useElectricClient();
const [connectors, setConnectors] = useState<SearchSourceConnector[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
@ -173,7 +173,7 @@ export function useConnectorsElectric(searchSpaceId: number | string | null) {
return () => {
mounted = false;
syncKeyRef.current = null;
if (syncHandleRef.current) {
syncHandleRef.current.unsubscribe();
syncHandleRef.current = null;

View file

@ -13,14 +13,14 @@ interface Document {
/**
* Hook for managing documents with Electric SQL real-time sync
*
*
* Uses the Electric client from context (provided by ElectricProvider)
* instead of initializing its own - prevents race conditions and memory leaks
*/
export function useDocumentsElectric(searchSpaceId: number | string | null) {
// Get Electric client from context - ElectricProvider handles initialization
const electricClient = useElectricClient();
const [documents, setDocuments] = useState<Document[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
@ -169,7 +169,7 @@ export function useDocumentsElectric(searchSpaceId: number | string | null) {
return () => {
mounted = false;
syncKeyRef.current = null;
if (syncHandleRef.current) {
syncHandleRef.current.unsubscribe();
syncHandleRef.current = null;

View file

@ -10,29 +10,29 @@ export type { Notification } from "@/contracts/types/notification.types";
/**
* Hook for managing notifications with Electric SQL real-time sync
*
*
* Uses the Electric client from context (provided by ElectricProvider)
* instead of initializing its own - prevents race conditions and memory leaks
*
*
* Architecture:
* - User-level sync: Syncs ALL notifications for a user (runs once per user)
* - Search-space-level query: Filters notifications by searchSpaceId (updates on search space change)
*
*
* This separation ensures smooth transitions when switching search spaces (no flash).
*
*
* @param userId - The user ID to fetch notifications for
* @param searchSpaceId - The search space ID to filter notifications (null shows global notifications only)
*/
export function useNotifications(userId: string | null, searchSpaceId: number | null) {
// Get Electric client from context - ElectricProvider handles initialization
const electricClient = useElectricClient();
const [notifications, setNotifications] = useState<Notification[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const syncHandleRef = useRef<SyncHandle | null>(null);
const liveQueryRef = useRef<{ unsubscribe: () => void } | null>(null);
// Track user-level sync key to prevent duplicate sync subscriptions
const userSyncKeyRef = useRef<string | null>(null);
@ -100,7 +100,7 @@ export function useNotifications(userId: string | null, searchSpaceId: number |
return () => {
mounted = false;
userSyncKeyRef.current = null;
if (syncHandleRef.current) {
syncHandleRef.current.unsubscribe();
syncHandleRef.current = null;
@ -135,7 +135,7 @@ export function useNotifications(userId: string | null, searchSpaceId: number |
ORDER BY created_at DESC`,
[userId, searchSpaceId]
);
if (mounted) {
setNotifications(result.rows || []);
}

View file

@ -381,7 +381,10 @@ export async function initElectric(userId: string): Promise<ElectricClient> {
},
};
console.log("[Electric] syncShapeToTable config:", JSON.stringify(shapeConfig, null, 2));
console.log(
"[Electric] syncShapeToTable config:",
JSON.stringify(shapeConfig, null, 2)
);
// Type assertion to PGlite with electric extension
const pgWithElectric = db as PGlite & {
@ -452,9 +455,7 @@ export async function initElectric(userId: string): Promise<ElectricClient> {
(typeof msg === "object" && "up-to-date" in msg)
) {
if (!syncResolved) {
console.log(
`[Electric] ✅ Received up-to-date message for ${table}`
);
console.log(`[Electric] ✅ Received up-to-date message for ${table}`);
resolveInitialSync();
}
// Continue listening for real-time updates - don't return!
@ -477,9 +478,7 @@ export async function initElectric(userId: string): Promise<ElectricClient> {
// Also check stream's isUpToDate property immediately
if (stream?.isUpToDate) {
console.log(
`[Electric] ✅ Stream isUpToDate is true immediately for ${table}`
);
console.log(`[Electric] ✅ Stream isUpToDate is true immediately for ${table}`);
resolveInitialSync();
}
}
@ -530,7 +529,9 @@ export async function initElectric(userId: string): Promise<ElectricClient> {
// Cache the sync handle for reuse (memory optimization)
activeSyncHandles.set(cacheKey, syncHandle);
console.log(`[Electric] Cached sync handle for: ${cacheKey} (total cached: ${activeSyncHandles.size})`);
console.log(
`[Electric] Cached sync handle for: ${cacheKey} (total cached: ${activeSyncHandles.size})`
);
return syncHandle;
} catch (error) {

View file

@ -5,7 +5,7 @@ import type { ElectricClient } from "./client";
/**
* Context for sharing the Electric SQL client across the app
*
*
* This ensures:
* 1. Single initialization point (ElectricProvider only)
* 2. No race conditions (hooks wait for context)
@ -34,4 +34,3 @@ export function useElectricClientOrThrow(): ElectricClient {
}
return client;
}