Files
calendar/companion/contexts/QueryContext.tsx
T
Dhairyashil ShindeandGitHub 9203cb262c feat(companion): component improvements, migrate to path aliases, expo-haptics, expo-image (#26226)
* react compiler

* remove compilation mode 'all', it will use infer by default

* feat(companion): add theme tokens, path aliases, and component improvements

- Extend tailwind.config.js with Cal.com brand color tokens (cal.text, cal.bg, cal.border, cal.accent, cal.brand)
- Create theme/colors.ts for JS usage where Tailwind classes can't be used
- Configure path aliases in tsconfig.json (@/components, @/hooks, @/utils, etc.)
- Add expo-image and expo-haptics dependencies
- Enhance AppPressable with Reanimated animations (opacity + scale) and haptic feedback
- Extract shared logic from BookingListItem into useBookingListItemData hook and BookingListItemParts
- Extract shared logic from EventTypeListItem into useEventTypeListItemData hook and EventTypeListItemParts
- Extract shared logic from AvailabilityListItem into AvailabilityListItemParts
- Update all list item components to use new theme tokens instead of hardcoded hex values

* fix react compiler non memo issue

* feat(companion): migrate to path aliases and expo-image

- Remove theme/colors.ts and theme/index.ts (defer dark mode to future PR)
- Replace colors import with hardcoded hex values in components
- Migrate all files to use path aliases (@/components/*, @/hooks/*, etc.)
- Migrate SvgImage to use expo-image for better performance
- Update tsconfig.json to remove @/theme/* alias

* fix(companion): add path aliases to extension tsconfig

Add baseUrl and paths configuration to tsconfig.extension.json to support
path aliases in shared types used by the extension build.
2025-12-28 08:08:33 -03:00

201 lines
5.5 KiB
TypeScript

/**
* React Query Context Provider
*
* This module sets up React Query with:
* - Optimized default configurations
* - Offline persistence support
* - Environment-based cache duration settings
* - Cross-platform compatibility (mobile + extension)
*/
import { onlineManager, QueryClient } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import React, { type ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import { Platform } from "react-native";
import { CACHE_CONFIG } from "@/config/cache.config";
import { clearQueryCache, createQueryPersister } from "@/utils/queryPersister";
/**
* Create and configure the QueryClient instance
*/
const createQueryClient = (): QueryClient => {
return new QueryClient({
defaultOptions: {
queries: {
// How long data is considered fresh
staleTime: CACHE_CONFIG.defaultStaleTime,
// How long to keep unused data in cache
gcTime: CACHE_CONFIG.gcTime,
// Refetch behavior
refetchOnWindowFocus: CACHE_CONFIG.refetch.onWindowFocus,
refetchOnReconnect: CACHE_CONFIG.refetch.onReconnect,
refetchOnMount: CACHE_CONFIG.refetch.onMount,
// Retry configuration
retry: CACHE_CONFIG.retry.count,
retryDelay: CACHE_CONFIG.retry.delay,
// Network mode - always try to fetch, use cache as fallback
networkMode: "offlineFirst",
},
mutations: {
// Retry failed mutations
retry: 1,
retryDelay: 1000,
// Network mode for mutations
networkMode: "offlineFirst",
},
},
});
};
/**
* Props for the QueryProvider component
*/
interface QueryProviderProps {
children: ReactNode;
}
/**
* Context for exposing query utilities
*/
interface QueryContextValue {
/** Invalidate all queries and refetch */
invalidateAllQueries: () => Promise<void>;
/** Clear the persisted cache */
clearCache: () => Promise<void>;
/** Check if the app is online */
isOnline: boolean;
}
const QueryContext = React.createContext<QueryContextValue | undefined>(undefined);
/**
* QueryProvider component that wraps the app with React Query functionality
*
* Features:
* - Automatic cache persistence to device storage
* - Online/offline detection
* - Configurable cache durations via environment variables
*/
export function QueryProvider({ children }: QueryProviderProps) {
// Create QueryClient instance (stable reference)
const [queryClient] = useState(() => createQueryClient());
// Create persister instance (stable reference)
const [persister] = useState(() => createQueryPersister());
// Track online status
const [isOnline, setIsOnline] = useState(true);
// Setup online/offline detection
useEffect(() => {
// For web/extension
if (Platform.OS === "web") {
const handleOnline = () => {
setIsOnline(true);
onlineManager.setOnline(true);
};
const handleOffline = () => {
setIsOnline(false);
onlineManager.setOnline(false);
};
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
// Set initial state
setIsOnline(navigator.onLine);
onlineManager.setOnline(navigator.onLine);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}
// For React Native, we could use NetInfo but it requires additional setup
// For now, assume online on mobile (React Query handles network errors gracefully)
return undefined;
}, []);
// Listen for reload messages from extension
useEffect(() => {
if (Platform.OS === "web") {
const handleMessage = (event: MessageEvent) => {
if (event.data?.type === "cal-companion-reload-cache") {
queryClient.invalidateQueries();
}
};
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}
return undefined;
}, [queryClient]);
/**
* Invalidate all queries and trigger refetch
*/
const invalidateAllQueries = useCallback(async () => {
await queryClient.invalidateQueries();
}, [queryClient]);
/**
* Clear the persisted cache
*/
const clearCache = useCallback(async () => {
queryClient.clear();
await clearQueryCache();
}, [queryClient]);
const contextValue: QueryContextValue = useMemo(
() => ({
invalidateAllQueries,
clearCache,
isOnline,
}),
[invalidateAllQueries, clearCache, isOnline]
);
return (
<QueryContext.Provider value={contextValue}>
<PersistQueryClientProvider
client={queryClient}
persistOptions={{
persister,
maxAge: CACHE_CONFIG.persistence.maxAge,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
// Only persist successful queries
return query.state.status === "success";
},
},
}}
>
{children}
</PersistQueryClientProvider>
</QueryContext.Provider>
);
}
/**
* Hook to access query utilities
*/
export function useQueryContext(): QueryContextValue {
const context = React.useContext(QueryContext);
if (context === undefined) {
throw new Error("useQueryContext must be used within a QueryProvider");
}
return context;
}
/**
* Export for direct QueryClient access when needed
* (e.g., for prefetching outside of components)
*/
export { QueryClient };