Syncing rework

This commit is contained in:
Milan Paunovic
2024-11-26 21:13:54 +01:00
parent 5cfdc84055
commit f2af60111b
14 changed files with 960 additions and 595 deletions

View File

@ -1,10 +1,29 @@
import {ProfileType} from "@/contexts/AuthContext";
export type ProfileType = 'parent' | 'child';
export interface User {
uid: string;
email: string | null;
export interface CalendarAccount {
accessToken: string;
refreshToken?: string;
resourceId?: string;
email?: string;
expiresAt?: Date;
}
export interface GoogleAccount extends CalendarAccount {
scope?: string;
}
export interface MicrosoftAccount extends CalendarAccount {
subscriptionId?: string;
}
export interface AppleAccount extends CalendarAccount {
identityToken?: string;
}
export type CalendarAccounts = {
[email: string]: GoogleAccount | MicrosoftAccount | AppleAccount;
};
export interface UserProfile {
userType: ProfileType;
firstName: string;
@ -21,23 +40,7 @@ export interface UserProfile {
eventColor?: string | null;
timeZone?: string | null;
firstDayOfWeek?: string | null;
googleAccounts?: Object;
microsoftAccounts?: Object;
appleAccounts?: Object;
}
export interface ParentProfile extends UserProfile {
userType: ProfileType.PARENT;
childrenIds: string[];
}
export interface ChildProfile extends UserProfile {
userType: ProfileType.CHILD;
birthday: Date;
parentId: string;
}
export interface CaregiverProfile extends UserProfile {
userType: ProfileType.CAREGIVER;
contact: string;
}
googleAccounts?: { [email: string]: GoogleAccount };
microsoftAccounts?: { [email: string]: MicrosoftAccount };
appleAccounts?: { [email: string]: AppleAccount };
}

View File

@ -1,51 +1,82 @@
import {useQuery} from "react-query";
import {useQuery, useQueryClient} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {useAuthContext} from "@/contexts/AuthContext";
import {useAtomValue} from "jotai";
import {isFamilyViewAtom} from "@/components/pages/calendar/atoms";
import {colorMap} from "@/constants/colorMap";
import {uuidv4} from "@firebase/util";
import {useEffect} from "react";
export const useGetEvents = () => {
const {user, profileData} = useAuthContext();
const isFamilyView = useAtomValue(isFamilyViewAtom);
const queryClient = useQueryClient();
useEffect(() => {
if (!profileData?.familyId) return;
console.log(`[SYNC] Setting up sync listener for family: ${profileData.familyId}`);
const unsubscribe = firestore()
.collection('Households')
.where("familyId", "==", profileData.familyId)
.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
if (change.type === 'modified') {
const data = change.doc.data();
if (data?.lastSyncTimestamp) {
console.log(`[SYNC] Change detected at ${data.lastSyncTimestamp.toDate()}`);
console.log(`[SYNC] Household ${change.doc.id} triggered refresh`);
queryClient.invalidateQueries(["events", user?.uid, isFamilyView]);
}
}
});
}, (error) => {
console.error('[SYNC] Listener error:', error);
});
return () => {
console.log('[SYNC] Cleaning up sync listener');
unsubscribe();
};
}, [profileData?.familyId, user?.uid, isFamilyView, queryClient]);
return useQuery({
queryKey: ["events", user?.uid, isFamilyView],
queryFn: async () => {
console.log(`Fetching events - Family View: ${isFamilyView}, User: ${user?.uid}`);
const db = firestore();
const userId = user?.uid;
const familyId = profileData?.familyId;
let allEvents = [];
if (isFamilyView) {
// Get public family events
const publicFamilyEvents = await db.collection("Events")
.where("familyId", "==", familyId)
.where("private", "==", false)
.get();
// Get private events where user is creator
const privateCreatorEvents = await db.collection("Events")
.where("familyId", "==", familyId)
.where("private", "==", true)
.where("creatorId", "==", userId)
.get();
// Get private events where user is attendee
const privateAttendeeEvents = await db.collection("Events")
.where("private", "==", true)
.where("attendees", "array-contains", userId)
.get();
console.log(`Found ${publicFamilyEvents.size} public events, ${privateCreatorEvents.size} private creator events, ${privateAttendeeEvents.size} private attendee events`);
allEvents = [
...publicFamilyEvents.docs.map(doc => doc.data()),
...privateCreatorEvents.docs.map(doc => doc.data()),
...privateAttendeeEvents.docs.map(doc => doc.data())
...publicFamilyEvents.docs.map(doc => ({...doc.data(), id: doc.id})),
...privateCreatorEvents.docs.map(doc => ({...doc.data(), id: doc.id})),
...privateAttendeeEvents.docs.map(doc => ({...doc.data(), id: doc.id}))
];
} else {
// Personal view: Only show events where user is creator or attendee
const [creatorEvents, attendeeEvents] = await Promise.all([
db.collection("Events")
.where("creatorId", "==", userId)
@ -55,24 +86,28 @@ export const useGetEvents = () => {
.get()
]);
console.log(`Found ${creatorEvents.size} creator events, ${attendeeEvents.size} attendee events`);
allEvents = [
...creatorEvents.docs.map(doc => doc.data()),
...attendeeEvents.docs.map(doc => doc.data())
...creatorEvents.docs.map(doc => ({...doc.data(), id: doc.id})),
...attendeeEvents.docs.map(doc => ({...doc.data(), id: doc.id}))
];
}
// Ensure uniqueness
const uniqueEventsMap = new Map();
allEvents.forEach(event => {
if (event.id) {
uniqueEventsMap.set(event.id, event);
} else {
uniqueEventsMap.set(uuidv4(), event);
const newId = uuidv4();
console.log(`Generated new ID for event without ID: ${newId}`);
uniqueEventsMap.set(newId, {...event, id: newId});
}
});
// Map events with creator colors
return await Promise.all(
console.log(`Processing ${uniqueEventsMap.size} unique events`);
const processedEvents = await Promise.all(
Array.from(uniqueEventsMap.values()).map(async (event) => {
const profileSnapshot = await db
.collection("Profiles")
@ -96,9 +131,15 @@ export const useGetEvents = () => {
};
})
);
console.log(`Events processing completed, returning ${processedEvents.length} events`);
return processedEvents;
},
staleTime: Infinity,
cacheTime: Infinity,
staleTime: 5 * 60 * 1000,
cacheTime: 30 * 60 * 1000,
keepPreviousData: true,
onError: (error) => {
console.error('Error fetching events:', error);
}
});
};
};