@umituz/web-firebase 1.0.5 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +555 -0
- package/dist/application/index.d.mts +273 -0
- package/dist/application/index.d.ts +273 -0
- package/dist/application/index.js +490 -0
- package/dist/application/index.mjs +19 -0
- package/dist/chunk-34DL2QWQ.mjs +87 -0
- package/dist/chunk-4FP2ELQ5.mjs +96 -0
- package/dist/chunk-7TX3OU3O.mjs +721 -0
- package/dist/chunk-I6WGBPFB.mjs +439 -0
- package/dist/chunk-RZ4QR6TB.mjs +96 -0
- package/dist/chunk-U2XI4MGO.mjs +397 -0
- package/dist/domain/index.d.mts +325 -0
- package/dist/domain/index.d.ts +325 -0
- package/dist/domain/index.js +662 -0
- package/dist/domain/index.mjs +36 -0
- package/dist/file.repository.interface-v5vHgVsZ.d.mts +241 -0
- package/dist/file.repository.interface-v5vHgVsZ.d.ts +241 -0
- package/dist/firebase.entity-xvfEPjXZ.d.mts +15 -0
- package/dist/firebase.entity-xvfEPjXZ.d.ts +15 -0
- package/dist/index.d.mts +14 -96
- package/dist/index.d.ts +14 -96
- package/dist/index.js +1717 -78
- package/dist/index.mjs +88 -175
- package/dist/infrastructure/index.d.mts +170 -0
- package/dist/infrastructure/index.d.ts +170 -0
- package/dist/infrastructure/index.js +856 -0
- package/dist/infrastructure/index.mjs +46 -0
- package/dist/presentation/index.d.mts +25 -0
- package/dist/presentation/index.d.ts +25 -0
- package/dist/presentation/index.js +105 -0
- package/dist/presentation/index.mjs +6 -0
- package/dist/user.repository.interface-DS74TsJ5.d.mts +298 -0
- package/dist/user.repository.interface-DS74TsJ5.d.ts +298 -0
- package/package.json +37 -11
- package/src/application/dto/auth.dto.ts +69 -0
- package/src/application/dto/index.ts +7 -0
- package/src/application/dto/user.dto.ts +64 -0
- package/src/application/index.ts +7 -0
- package/src/application/use-cases/auth/reset-password.use-case.ts +66 -0
- package/src/application/use-cases/auth/sign-in-with-google.use-case.ts +86 -0
- package/src/application/use-cases/auth/sign-in.use-case.ts +77 -0
- package/src/application/use-cases/auth/sign-out.use-case.ts +22 -0
- package/src/application/use-cases/auth/sign-up.use-case.ts +99 -0
- package/src/application/use-cases/index.ts +12 -0
- package/src/application/use-cases/user/delete-account.use-case.ts +77 -0
- package/src/application/use-cases/user/update-profile.use-case.ts +98 -0
- package/src/domain/entities/file.entity.ts +151 -0
- package/src/domain/entities/timestamp.entity.ts +116 -0
- package/src/domain/entities/user.entity.ts +193 -0
- package/src/domain/errors/auth.errors.ts +115 -0
- package/src/domain/errors/repository.errors.ts +121 -0
- package/src/domain/index.ts +25 -2
- package/src/domain/interfaces/auth.repository.interface.ts +83 -0
- package/src/domain/interfaces/file.repository.interface.ts +143 -0
- package/src/domain/interfaces/user.repository.interface.ts +75 -0
- package/src/domain/value-objects/email.vo.ts +105 -0
- package/src/domain/value-objects/file-path.vo.ts +184 -0
- package/src/domain/value-objects/user-id.vo.ts +87 -0
- package/src/index.ts +19 -4
- package/src/infrastructure/firebase/auth.adapter.ts +220 -0
- package/src/infrastructure/firebase/client.ts +141 -0
- package/src/infrastructure/firebase/firestore.adapter.ts +190 -0
- package/src/infrastructure/firebase/storage.adapter.ts +323 -0
- package/src/infrastructure/index.ts +10 -5
- package/src/infrastructure/utils/storage.util.ts +3 -3
- package/src/presentation/hooks/useAuth.ts +108 -0
- package/src/presentation/hooks/useFirestore.ts +125 -0
- package/src/presentation/hooks/useStorage.ts +141 -0
- package/src/presentation/providers/FirebaseProvider.tsx +95 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Hook
|
|
3
|
+
* @description React hook for authentication operations
|
|
4
|
+
* Uses FirebaseProvider context - no repository injection needed
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useCallback } from 'react'
|
|
8
|
+
import type { User as FirebaseUser } from 'firebase/auth'
|
|
9
|
+
import type { User } from '../../domain/entities/user.entity'
|
|
10
|
+
import { AuthAdapter } from '../../infrastructure/firebase/auth.adapter'
|
|
11
|
+
import { FirestoreAdapter } from '../../infrastructure/firebase/firestore.adapter'
|
|
12
|
+
import { useFirebaseContext } from '../providers/FirebaseProvider'
|
|
13
|
+
|
|
14
|
+
export interface UseAuthResult {
|
|
15
|
+
firebaseUser: FirebaseUser | null
|
|
16
|
+
user: User | null
|
|
17
|
+
loading: boolean
|
|
18
|
+
error: Error | null
|
|
19
|
+
isAuthenticated: boolean
|
|
20
|
+
isEmailVerified: boolean
|
|
21
|
+
signIn: (email: string, password: string) => Promise<FirebaseUser>
|
|
22
|
+
signUp: (email: string, password: string, displayName: string) => Promise<FirebaseUser>
|
|
23
|
+
signInWithGoogle: () => Promise<FirebaseUser>
|
|
24
|
+
signOut: () => Promise<void>
|
|
25
|
+
sendPasswordReset: (email: string) => Promise<void>
|
|
26
|
+
resendEmailVerification: () => Promise<void>
|
|
27
|
+
updateProfile: (updates: { displayName?: string; photoURL?: string }) => Promise<void>
|
|
28
|
+
refreshUser: () => Promise<void>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useAuth(): UseAuthResult {
|
|
32
|
+
const { instances, user, loading, error } = useFirebaseContext()
|
|
33
|
+
|
|
34
|
+
const authAdapter = new AuthAdapter()
|
|
35
|
+
const firestoreAdapter = new FirestoreAdapter()
|
|
36
|
+
|
|
37
|
+
const signIn = useCallback(async (email: string, password: string) => {
|
|
38
|
+
return await authAdapter.signIn(email, password)
|
|
39
|
+
}, [])
|
|
40
|
+
|
|
41
|
+
const signUp = useCallback(async (email: string, password: string, displayName: string) => {
|
|
42
|
+
const userCredential = await authAdapter.signUp(email, password, displayName)
|
|
43
|
+
|
|
44
|
+
// Create user profile in Firestore
|
|
45
|
+
await firestoreAdapter.createUser(userCredential.user.uid, {
|
|
46
|
+
profile: {
|
|
47
|
+
email: email,
|
|
48
|
+
displayName: displayName,
|
|
49
|
+
createdAt: Date.now(),
|
|
50
|
+
},
|
|
51
|
+
settings: {
|
|
52
|
+
theme: 'light',
|
|
53
|
+
language: 'tr',
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return userCredential.user
|
|
58
|
+
}, [])
|
|
59
|
+
|
|
60
|
+
const signInWithGoogle = useCallback(async () => {
|
|
61
|
+
return await authAdapter.signInWithGoogle()
|
|
62
|
+
}, [])
|
|
63
|
+
|
|
64
|
+
const signOut = useCallback(async () => {
|
|
65
|
+
await authAdapter.signOut()
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
const sendPasswordReset = useCallback(async (email: string) => {
|
|
69
|
+
await authAdapter.sendPasswordReset(email)
|
|
70
|
+
}, [])
|
|
71
|
+
|
|
72
|
+
const resendEmailVerification = useCallback(async () => {
|
|
73
|
+
await authAdapter.resendEmailVerification()
|
|
74
|
+
}, [])
|
|
75
|
+
|
|
76
|
+
const updateProfile = useCallback(async (updates: { displayName?: string; photoURL?: string }) => {
|
|
77
|
+
await authAdapter.updateProfile(updates)
|
|
78
|
+
|
|
79
|
+
// Refresh user data from Firestore if available
|
|
80
|
+
if (instances?.auth.currentUser) {
|
|
81
|
+
await firestoreAdapter.getUser(instances.auth.currentUser.uid)
|
|
82
|
+
}
|
|
83
|
+
}, [instances])
|
|
84
|
+
|
|
85
|
+
const refreshUser = useCallback(async () => {
|
|
86
|
+
if (instances?.auth.currentUser) {
|
|
87
|
+
await firestoreAdapter.getUser(instances.auth.currentUser.uid)
|
|
88
|
+
}
|
|
89
|
+
}, [instances])
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
firebaseUser: instances?.auth.currentUser || null,
|
|
93
|
+
user,
|
|
94
|
+
loading,
|
|
95
|
+
error,
|
|
96
|
+
isAuthenticated: !!instances?.auth.currentUser,
|
|
97
|
+
isEmailVerified: instances?.auth.currentUser?.emailVerified || false,
|
|
98
|
+
signIn,
|
|
99
|
+
signUp,
|
|
100
|
+
signInWithGoogle,
|
|
101
|
+
signOut,
|
|
102
|
+
sendPasswordReset,
|
|
103
|
+
resendEmailVerification,
|
|
104
|
+
updateProfile,
|
|
105
|
+
refreshUser,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firestore Hook
|
|
3
|
+
* @description React hook for Firestore real-time data
|
|
4
|
+
* Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/hooks/useFirestore.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useEffect, useState } from 'react'
|
|
8
|
+
import { doc, onSnapshot, query, collection, type QuerySnapshot, type DocumentData } from 'firebase/firestore'
|
|
9
|
+
import type { Firestore } from 'firebase/firestore'
|
|
10
|
+
|
|
11
|
+
export function useUser(db: Firestore, userId: string) {
|
|
12
|
+
const [user, setUser] = useState<any>(null)
|
|
13
|
+
const [loading, setLoading] = useState(true)
|
|
14
|
+
const [error, setError] = useState<Error | null>(null)
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!userId) {
|
|
18
|
+
setUser(null)
|
|
19
|
+
setLoading(false)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const unsubscribe = onSnapshot(doc(db, 'users', userId), (docSnap) => {
|
|
24
|
+
if (docSnap.exists()) {
|
|
25
|
+
setUser(docSnap.data())
|
|
26
|
+
} else {
|
|
27
|
+
setUser(null)
|
|
28
|
+
}
|
|
29
|
+
setLoading(false)
|
|
30
|
+
}, (err) => {
|
|
31
|
+
setError(err as Error)
|
|
32
|
+
setLoading(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return () => unsubscribe()
|
|
36
|
+
}, [db, userId])
|
|
37
|
+
|
|
38
|
+
return { user, loading, error }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useUserRealtime<T>(db: Firestore, collectionName: string, docId: string) {
|
|
42
|
+
const [data, setData] = useState<T | null>(null)
|
|
43
|
+
const [loading, setLoading] = useState(true)
|
|
44
|
+
const [error, setError] = useState<Error | null>(null)
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!docId) {
|
|
48
|
+
setData(null)
|
|
49
|
+
setLoading(false)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const unsubscribe = onSnapshot(doc(db, collectionName, docId), (docSnap) => {
|
|
54
|
+
if (docSnap.exists()) {
|
|
55
|
+
setData(docSnap.data() as T)
|
|
56
|
+
} else {
|
|
57
|
+
setData(null)
|
|
58
|
+
}
|
|
59
|
+
setLoading(false)
|
|
60
|
+
}, (err) => {
|
|
61
|
+
setError(err as Error)
|
|
62
|
+
setLoading(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return () => unsubscribe()
|
|
66
|
+
}, [db, collectionName, docId])
|
|
67
|
+
|
|
68
|
+
return { data, loading, error }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function useCollectionRealtime<T>(
|
|
72
|
+
db: Firestore,
|
|
73
|
+
collectionName: string,
|
|
74
|
+
constraints?: any[]
|
|
75
|
+
) {
|
|
76
|
+
const [data, setData] = useState<T[]>([])
|
|
77
|
+
const [loading, setLoading] = useState(true)
|
|
78
|
+
const [error, setError] = useState<Error | null>(null)
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const q = constraints
|
|
82
|
+
? query(collection(db, collectionName), ...constraints)
|
|
83
|
+
: collection(db, collectionName)
|
|
84
|
+
|
|
85
|
+
const unsubscribe = onSnapshot(q, (querySnap: QuerySnapshot<DocumentData>) => {
|
|
86
|
+
const items = querySnap.docs.map((doc) => ({ id: doc.id, ...doc.data() } as T))
|
|
87
|
+
setData(items)
|
|
88
|
+
setLoading(false)
|
|
89
|
+
}, (err) => {
|
|
90
|
+
setError(err as Error)
|
|
91
|
+
setLoading(false)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return () => unsubscribe()
|
|
95
|
+
}, [db, collectionName, JSON.stringify(constraints)])
|
|
96
|
+
|
|
97
|
+
return { data, loading, error }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function useDocumentValue<T>(
|
|
101
|
+
db: Firestore,
|
|
102
|
+
collectionName: string,
|
|
103
|
+
docId: string
|
|
104
|
+
) {
|
|
105
|
+
const [data, setData] = useState<T | null>(null)
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!docId) {
|
|
109
|
+
setData(null)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const unsubscribe = onSnapshot(doc(db, collectionName, docId), (docSnap) => {
|
|
114
|
+
if (docSnap.exists()) {
|
|
115
|
+
setData(docSnap.data() as T)
|
|
116
|
+
} else {
|
|
117
|
+
setData(null)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return () => unsubscribe()
|
|
122
|
+
}, [db, collectionName, docId])
|
|
123
|
+
|
|
124
|
+
return data
|
|
125
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Hook
|
|
3
|
+
* @description React hook for file storage operations
|
|
4
|
+
* Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/hooks/useStorage.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useCallback } from 'react'
|
|
8
|
+
import type { IFileRepository } from '../../domain/interfaces/file.repository.interface'
|
|
9
|
+
import type { UploadProgress, UploadResult } from '../../domain/entities/file.entity'
|
|
10
|
+
|
|
11
|
+
export interface UseStorageOptions {
|
|
12
|
+
fileRepository: IFileRepository
|
|
13
|
+
userId: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useStorage({ fileRepository, userId }: UseStorageOptions) {
|
|
17
|
+
const [uploading, setUploading] = useState(false)
|
|
18
|
+
const [progress, setProgress] = useState(0)
|
|
19
|
+
const [error, setError] = useState<Error | null>(null)
|
|
20
|
+
|
|
21
|
+
const uploadFile = useCallback(
|
|
22
|
+
async (path: string, file: File): Promise<UploadResult> => {
|
|
23
|
+
setUploading(true)
|
|
24
|
+
setProgress(0)
|
|
25
|
+
setError(null)
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const result = await fileRepository.uploadFile(userId, path, file, {
|
|
29
|
+
onProgress: (progress: UploadProgress) => {
|
|
30
|
+
setProgress(progress.progress)
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
setProgress(100)
|
|
35
|
+
return result
|
|
36
|
+
} catch (err) {
|
|
37
|
+
setError(err as Error)
|
|
38
|
+
throw err
|
|
39
|
+
} finally {
|
|
40
|
+
setUploading(false)
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[fileRepository, userId]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const uploadImage = useCallback(
|
|
47
|
+
async (file: File): Promise<UploadResult> => {
|
|
48
|
+
return uploadFile('images', file)
|
|
49
|
+
},
|
|
50
|
+
[uploadFile]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const uploadVideo = useCallback(
|
|
54
|
+
async (file: File): Promise<UploadResult> => {
|
|
55
|
+
return uploadFile('videos', file)
|
|
56
|
+
},
|
|
57
|
+
[uploadFile]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const uploadDocument = useCallback(
|
|
61
|
+
async (file: File): Promise<UploadResult> => {
|
|
62
|
+
return uploadFile('documents', file)
|
|
63
|
+
},
|
|
64
|
+
[uploadFile]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const uploadProfilePicture = useCallback(
|
|
68
|
+
async (file: File): Promise<UploadResult> => {
|
|
69
|
+
setUploading(true)
|
|
70
|
+
setError(null)
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const result = await fileRepository.uploadProfilePicture(userId, file)
|
|
74
|
+
return result
|
|
75
|
+
} catch (err) {
|
|
76
|
+
setError(err as Error)
|
|
77
|
+
throw err
|
|
78
|
+
} finally {
|
|
79
|
+
setUploading(false)
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[fileRepository, userId]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const deleteFile = useCallback(
|
|
86
|
+
async (path: string): Promise<void> => {
|
|
87
|
+
setUploading(true)
|
|
88
|
+
setError(null)
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await fileRepository.deleteFile(path)
|
|
92
|
+
} catch (err) {
|
|
93
|
+
setError(err as Error)
|
|
94
|
+
throw err
|
|
95
|
+
} finally {
|
|
96
|
+
setUploading(false)
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[fileRepository]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const listUserImages = useCallback(
|
|
103
|
+
async (): Promise<string[]> => {
|
|
104
|
+
setError(null)
|
|
105
|
+
try {
|
|
106
|
+
return await fileRepository.listUserImages(userId)
|
|
107
|
+
} catch (err) {
|
|
108
|
+
setError(err as Error)
|
|
109
|
+
throw err
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[fileRepository, userId]
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
const listUserVideos = useCallback(
|
|
116
|
+
async (): Promise<string[]> => {
|
|
117
|
+
setError(null)
|
|
118
|
+
try {
|
|
119
|
+
return await fileRepository.listUserVideos(userId)
|
|
120
|
+
} catch (err) {
|
|
121
|
+
setError(err as Error)
|
|
122
|
+
throw err
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
[fileRepository, userId]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
uploadFile,
|
|
130
|
+
uploadImage,
|
|
131
|
+
uploadVideo,
|
|
132
|
+
uploadDocument,
|
|
133
|
+
uploadProfilePicture,
|
|
134
|
+
deleteFile,
|
|
135
|
+
listUserImages,
|
|
136
|
+
listUserVideos,
|
|
137
|
+
uploading,
|
|
138
|
+
progress,
|
|
139
|
+
error,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FirebaseProvider
|
|
3
|
+
* @description React Context Provider for Firebase with auth state tracking
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
|
|
7
|
+
import type { User } from 'firebase/auth'
|
|
8
|
+
import type { FirebaseInstances } from '../../infrastructure/firebase/client'
|
|
9
|
+
import { getFirebaseInstances } from '../../infrastructure/firebase/client'
|
|
10
|
+
import { AuthAdapter } from '../../infrastructure/firebase/auth.adapter'
|
|
11
|
+
|
|
12
|
+
export interface FirebaseContextValue {
|
|
13
|
+
instances: FirebaseInstances
|
|
14
|
+
isInitialized: boolean
|
|
15
|
+
user: User | null
|
|
16
|
+
loading: boolean
|
|
17
|
+
error: Error | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const FirebaseContext = createContext<FirebaseContextValue | null>(null)
|
|
21
|
+
|
|
22
|
+
export interface FirebaseProviderProps {
|
|
23
|
+
children: ReactNode
|
|
24
|
+
config?: {
|
|
25
|
+
apiKey: string
|
|
26
|
+
authDomain: string
|
|
27
|
+
projectId: string
|
|
28
|
+
storageBucket: string
|
|
29
|
+
messagingSenderId: string
|
|
30
|
+
appId: string
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function FirebaseProvider({ children, config }: FirebaseProviderProps) {
|
|
35
|
+
const [instances, setInstances] = useState<FirebaseInstances | null>(null)
|
|
36
|
+
const [user, setUser] = useState<User | null>(null)
|
|
37
|
+
const [loading, setLoading] = useState(true)
|
|
38
|
+
const [error, setError] = useState<Error | null>(null)
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
try {
|
|
42
|
+
// Initialize Firebase (singleton - won't re-initialize)
|
|
43
|
+
const firebaseInstances = getFirebaseInstances()
|
|
44
|
+
setInstances(firebaseInstances)
|
|
45
|
+
|
|
46
|
+
// Set up auth state listener
|
|
47
|
+
const authAdapter = new AuthAdapter()
|
|
48
|
+
const unsubscribe = authAdapter.onAuthStateChanged(
|
|
49
|
+
(user) => {
|
|
50
|
+
setUser(user)
|
|
51
|
+
setLoading(false)
|
|
52
|
+
setError(null)
|
|
53
|
+
},
|
|
54
|
+
(err) => {
|
|
55
|
+
setError(err)
|
|
56
|
+
setLoading(false)
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
if (unsubscribe) unsubscribe()
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
setError(err as Error)
|
|
65
|
+
setLoading(false)
|
|
66
|
+
}
|
|
67
|
+
}, [config])
|
|
68
|
+
|
|
69
|
+
const value: FirebaseContextValue = {
|
|
70
|
+
instances: instances!,
|
|
71
|
+
isInitialized: !!instances,
|
|
72
|
+
user,
|
|
73
|
+
loading,
|
|
74
|
+
error,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!instances || loading) {
|
|
78
|
+
return (
|
|
79
|
+
<FirebaseContext.Provider value={{ ...value, isInitialized: false }}>
|
|
80
|
+
{children}
|
|
81
|
+
</FirebaseContext.Provider>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return <FirebaseContext.Provider value={value}>{children}</FirebaseContext.Provider>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function useFirebaseContext(): FirebaseContextValue {
|
|
89
|
+
const context = useContext(FirebaseContext)
|
|
90
|
+
if (!context) {
|
|
91
|
+
throw new Error('useFirebaseContext must be used within FirebaseProvider')
|
|
92
|
+
}
|
|
93
|
+
return context
|
|
94
|
+
}
|
|
95
|
+
|