expo-starter/lib/firestore.ts

70 lines
1.8 KiB
TypeScript

import {
collection,
doc,
type DocumentData,
type FirestoreDataConverter,
type QueryDocumentSnapshot,
type SnapshotOptions,
} from 'firebase/firestore';
import { db } from '@/lib/firebase';
import type { UserDocument, UserFirestoreDocument } from '@/types/firestore';
type TimestampFieldValue = {
toDate: () => Date;
};
function hasToDate(value: unknown): value is TimestampFieldValue {
return (
typeof value === 'object' &&
value !== null &&
'toDate' in value &&
typeof value.toDate === 'function'
);
}
export function mapTimestampFields<
Source extends Record<string, unknown>,
Result extends Record<string, unknown>,
>(data: Source, fields: Array<keyof Source>) {
const nextData = { ...data } as Record<string, unknown>;
for (const field of fields) {
const value = nextData[field as string];
if (hasToDate(value)) {
nextData[field as string] = value.toDate();
}
}
return nextData as Result;
}
const converter = <AppModel extends DocumentData, DbModel extends DocumentData>(
fromFirestoreMapper: (data: DbModel) => AppModel
): FirestoreDataConverter<AppModel> => ({
toFirestore: (value) => value as DocumentData,
fromFirestore: (
snapshot: QueryDocumentSnapshot,
options: SnapshotOptions
) => fromFirestoreMapper(snapshot.data(options) as DbModel),
});
const userConverter = converter<UserDocument, UserFirestoreDocument>((data) =>
mapTimestampFields<UserFirestoreDocument, UserDocument>(data, [
'createdAt',
'updatedAt',
])
);
export const collectionNames = {
users: 'users',
} as const;
export const usersCollectionRef = collection(db, collectionNames.users);
export const usersCollection = usersCollectionRef.withConverter(userConverter);
export const userDoc = (uid: string) => doc(usersCollection, uid);
export const userDocRef = (uid: string) => doc(usersCollectionRef, uid);