added firestore config
This commit is contained in:
@@ -13,7 +13,9 @@ import {
|
|||||||
signOut as firebaseSignOut,
|
signOut as firebaseSignOut,
|
||||||
type User,
|
type User,
|
||||||
} from 'firebase/auth'
|
} from 'firebase/auth'
|
||||||
import { auth, googleProvider } from '../lib/firebase'
|
import { auth, googleProvider, db } from '../lib/firebase'
|
||||||
|
import { doc, setDoc } from 'firebase/firestore'
|
||||||
|
import { COLLECTIONS } from '../lib/firestoreConfig'
|
||||||
|
|
||||||
type AuthContextValue = {
|
type AuthContextValue = {
|
||||||
user: User | null
|
user: User | null
|
||||||
@@ -28,9 +30,28 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
const [user, setUser] = useState<User | null>(null)
|
const [user, setUser] = useState<User | null>(null)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
// Save user info to Firestore when they authenticate
|
||||||
|
async function saveUserToFirestore(authUser: User) {
|
||||||
|
try {
|
||||||
|
const userRef = doc(db, COLLECTIONS.USERS, authUser.uid)
|
||||||
|
await setDoc(userRef, {
|
||||||
|
id: authUser.uid,
|
||||||
|
email: authUser.email || '',
|
||||||
|
displayName: authUser.displayName || '',
|
||||||
|
photoURL: authUser.photoURL || '',
|
||||||
|
lastLoginAt: Date.now(),
|
||||||
|
}, { merge: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving user to Firestore:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = onAuthStateChanged(auth, (u) => {
|
const unsubscribe = onAuthStateChanged(auth, async (u) => {
|
||||||
setUser(u)
|
setUser(u)
|
||||||
|
if (u) {
|
||||||
|
await saveUserToFirestore(u)
|
||||||
|
}
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
return () => unsubscribe()
|
return () => unsubscribe()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { initializeApp } from 'firebase/app'
|
import { initializeApp } from 'firebase/app'
|
||||||
import { getAuth, GoogleAuthProvider } from 'firebase/auth'
|
import { getAuth, GoogleAuthProvider } from 'firebase/auth'
|
||||||
|
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'
|
||||||
|
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
|
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
|
||||||
@@ -12,5 +13,15 @@ const firebaseConfig = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = initializeApp(firebaseConfig)
|
const app = initializeApp(firebaseConfig)
|
||||||
|
|
||||||
|
// Auth initialization
|
||||||
export const auth = getAuth(app)
|
export const auth = getAuth(app)
|
||||||
export const googleProvider = new GoogleAuthProvider()
|
export const googleProvider = new GoogleAuthProvider()
|
||||||
|
|
||||||
|
// Firestore initialization
|
||||||
|
export const db = getFirestore(app)
|
||||||
|
|
||||||
|
// Enable Firestore emulator in development (uncomment when testing locally)
|
||||||
|
// if (import.meta.env.DEV) {
|
||||||
|
// connectFirestoreEmulator(db, 'localhost', 8080)
|
||||||
|
// }
|
||||||
|
|||||||
65
src/lib/firestoreConfig.ts
Normal file
65
src/lib/firestoreConfig.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Firestore Collections Configuration
|
||||||
|
* Define all collection names and common data structure interfaces here
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Collection names
|
||||||
|
export const COLLECTIONS = {
|
||||||
|
USERS: 'users',
|
||||||
|
ENTRIES: 'entries',
|
||||||
|
SETTINGS: 'settings',
|
||||||
|
TAGS: 'tags',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
// User document interface
|
||||||
|
export interface FirestoreUser {
|
||||||
|
id: string
|
||||||
|
email: string
|
||||||
|
displayName?: string
|
||||||
|
photoURL?: string
|
||||||
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
|
theme?: 'light' | 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry (Journal Entry) document interface
|
||||||
|
export interface JournalEntry {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
mood?: 'happy' | 'sad' | 'neutral' | 'anxious' | 'grateful'
|
||||||
|
tags?: string[]
|
||||||
|
isPublic?: boolean
|
||||||
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings document interface
|
||||||
|
export interface UserSettings {
|
||||||
|
userId: string
|
||||||
|
notifications?: boolean
|
||||||
|
emailNotifications?: boolean
|
||||||
|
theme?: 'light' | 'dark' | 'system'
|
||||||
|
language?: string
|
||||||
|
updatedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags document interface
|
||||||
|
export interface Tag {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
name: string
|
||||||
|
color?: string
|
||||||
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firestore emulator configuration
|
||||||
|
export function isEmulatorEnabled(): boolean {
|
||||||
|
return import.meta.env.VITE_FIREBASE_EMULATOR_ENABLED === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEmulatorHost(): string {
|
||||||
|
return import.meta.env.VITE_FIRESTORE_EMULATOR_HOST || 'localhost:8080'
|
||||||
|
}
|
||||||
130
src/lib/firestoreService.ts
Normal file
130
src/lib/firestoreService.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import {
|
||||||
|
collection,
|
||||||
|
doc,
|
||||||
|
getDoc,
|
||||||
|
getDocs,
|
||||||
|
setDoc,
|
||||||
|
updateDoc,
|
||||||
|
deleteDoc,
|
||||||
|
query,
|
||||||
|
where,
|
||||||
|
orderBy,
|
||||||
|
WriteBatch,
|
||||||
|
writeBatch,
|
||||||
|
} from 'firebase/firestore'
|
||||||
|
import { db } from './firebase'
|
||||||
|
|
||||||
|
type FirestoreData = Record<string, any>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to add or update a document
|
||||||
|
*/
|
||||||
|
export async function setDocument<T extends FirestoreData>(
|
||||||
|
collectionName: string,
|
||||||
|
docId: string,
|
||||||
|
data: T,
|
||||||
|
merge = true
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await setDoc(doc(db, collectionName, docId), data, { merge })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error setting document in ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to get a single document
|
||||||
|
*/
|
||||||
|
export async function getDocument<T extends FirestoreData>(
|
||||||
|
collectionName: string,
|
||||||
|
docId: string
|
||||||
|
): Promise<T | null> {
|
||||||
|
try {
|
||||||
|
const docRef = doc(db, collectionName, docId)
|
||||||
|
const docSnapshot = await getDoc(docRef)
|
||||||
|
return (docSnapshot.exists() ? docSnapshot.data() : null) as T | null
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error getting document from ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to get all documents from a collection
|
||||||
|
*/
|
||||||
|
export async function getDocuments<T extends FirestoreData>(
|
||||||
|
collectionName: string
|
||||||
|
): Promise<T[]> {
|
||||||
|
try {
|
||||||
|
const querySnapshot = await getDocs(collection(db, collectionName))
|
||||||
|
return querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() } as T))
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error getting documents from ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to query documents with conditions
|
||||||
|
*/
|
||||||
|
export async function queryDocuments<T extends FirestoreData>(
|
||||||
|
collectionName: string,
|
||||||
|
constraints: any[]
|
||||||
|
): Promise<T[]> {
|
||||||
|
try {
|
||||||
|
const q = query(collection(db, collectionName), ...constraints)
|
||||||
|
const querySnapshot = await getDocs(q)
|
||||||
|
return querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() } as T))
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error querying ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to update a document
|
||||||
|
*/
|
||||||
|
export async function updateDocument<T extends Partial<FirestoreData>>(
|
||||||
|
collectionName: string,
|
||||||
|
docId: string,
|
||||||
|
data: T
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await updateDoc(doc(db, collectionName, docId), data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error updating document in ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to delete a document
|
||||||
|
*/
|
||||||
|
export async function deleteDocument(
|
||||||
|
collectionName: string,
|
||||||
|
docId: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await deleteDoc(doc(db, collectionName, docId))
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error deleting document from ${collectionName}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch write operations
|
||||||
|
*/
|
||||||
|
export function createWriteBatch(): WriteBatch {
|
||||||
|
return writeBatch(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function commitBatch(batch: WriteBatch): Promise<void> {
|
||||||
|
try {
|
||||||
|
await batch.commit()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error committing batch write:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user