* 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.
165 lines
4.2 KiB
TypeScript
165 lines
4.2 KiB
TypeScript
/**
|
|
* User Profile Query Hooks
|
|
*
|
|
* This module provides React Query hooks for fetching and updating user profile.
|
|
* It integrates with the existing CalComAPIService and provides:
|
|
* - Automatic caching with configurable stale times
|
|
* - Profile update mutations with cache invalidation
|
|
*/
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import { CACHE_CONFIG, queryKeys } from "@/config/cache.config";
|
|
import { CalComAPIService, type UserProfile } from "@/services/calcom";
|
|
|
|
/**
|
|
* User profile update input type
|
|
*/
|
|
export interface UpdateUserProfileInput {
|
|
email?: string;
|
|
name?: string;
|
|
timeFormat?: number;
|
|
defaultScheduleId?: number;
|
|
weekStart?: string;
|
|
timeZone?: string;
|
|
locale?: string;
|
|
avatarUrl?: string;
|
|
bio?: string;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch the current user profile
|
|
*
|
|
* @returns Query result with user profile data, loading state, error, and refetch function
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { data: profile, isLoading } = useUserProfile();
|
|
*
|
|
* if (profile) {
|
|
* console.log(profile.username, profile.email);
|
|
* }
|
|
* ```
|
|
*/
|
|
export function useUserProfile() {
|
|
return useQuery({
|
|
queryKey: queryKeys.userProfile.current(),
|
|
queryFn: () => CalComAPIService.getUserProfile(),
|
|
staleTime: CACHE_CONFIG.userProfile.staleTime,
|
|
// Keep previous data while fetching new data (smoother UX)
|
|
placeholderData: (previousData) => previousData,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to get the current username
|
|
*
|
|
* @returns Query result with username
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { data: username } = useUsername();
|
|
* ```
|
|
*/
|
|
export function useUsername() {
|
|
const { data: profile, ...rest } = useUserProfile();
|
|
|
|
return {
|
|
...rest,
|
|
data: profile?.username,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook to update the user profile
|
|
*
|
|
* @returns Mutation function and state
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { mutate: updateProfile, isPending } = useUpdateUserProfile();
|
|
*
|
|
* updateProfile({
|
|
* name: 'New Name',
|
|
* timeZone: 'America/New_York'
|
|
* });
|
|
* ```
|
|
*/
|
|
export function useUpdateUserProfile() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: (updates: UpdateUserProfileInput) => CalComAPIService.updateUserProfile(updates),
|
|
onMutate: async (newData) => {
|
|
// Cancel any outgoing refetches
|
|
await queryClient.cancelQueries({ queryKey: queryKeys.userProfile.current() });
|
|
|
|
// Snapshot the previous value
|
|
const previousProfile = queryClient.getQueryData<UserProfile>(
|
|
queryKeys.userProfile.current()
|
|
);
|
|
|
|
// Optimistically update to the new value
|
|
if (previousProfile) {
|
|
queryClient.setQueryData(queryKeys.userProfile.current(), {
|
|
...previousProfile,
|
|
...newData,
|
|
});
|
|
}
|
|
|
|
return { previousProfile };
|
|
},
|
|
onError: (error, _newData, context) => {
|
|
// Rollback on error
|
|
if (context?.previousProfile) {
|
|
queryClient.setQueryData(queryKeys.userProfile.current(), context.previousProfile);
|
|
}
|
|
console.error("Failed to update user profile");
|
|
if (__DEV__) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const stack = error instanceof Error ? error.stack : undefined;
|
|
console.debug("[useUpdateUserProfile] failed", { message, stack });
|
|
}
|
|
},
|
|
onSettled: () => {
|
|
// Always refetch after error or success
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.userProfile.current() });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Hook to prefetch user profile (useful for app initialization)
|
|
*
|
|
* @returns Function to prefetch user profile
|
|
*/
|
|
export function usePrefetchUserProfile() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return () => {
|
|
queryClient.prefetchQuery({
|
|
queryKey: queryKeys.userProfile.current(),
|
|
queryFn: () => CalComAPIService.getUserProfile(),
|
|
staleTime: CACHE_CONFIG.userProfile.staleTime,
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook to invalidate user profile cache
|
|
*
|
|
* @returns Function to invalidate user profile cache
|
|
*/
|
|
export function useInvalidateUserProfile() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return () => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.userProfile.all });
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Type exports for consumers
|
|
*/
|
|
export type { UserProfile };
|