Files
calendar/companion/hooks/useUserProfile.ts
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

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 };