@umituz/web-firebase 3.2.5 → 3.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-firebase",
3
- "version": "3.2.5",
3
+ "version": "3.3.0",
4
4
  "description": "Comprehensive Firebase integration with domain-based architecture for web applications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -21,7 +21,11 @@ import type { User } from '../entities'
21
21
 
22
22
  class FirestoreService implements IFirestoreService {
23
23
  private get db() {
24
- return getFirebaseDB()
24
+ const db = getFirebaseDB()
25
+ if (!db) {
26
+ throw new Error('Firestore not initialized. Call initializeFirebase() first.')
27
+ }
28
+ return db
25
29
  }
26
30
 
27
31
  private readonly USERS_COLLECTION = 'users'
@@ -21,7 +21,11 @@ import type {
21
21
 
22
22
  class StorageService implements IStorageService {
23
23
  private get storage() {
24
- return getFirebaseStorage()
24
+ const storage = getFirebaseStorage()
25
+ if (!storage) {
26
+ throw new Error('Firebase Storage not initialized. Call initializeFirebase() first.')
27
+ }
28
+ return storage
25
29
  }
26
30
 
27
31
  // Upload Methods
package/src/index.ts CHANGED
@@ -1,19 +1,10 @@
1
1
  /**
2
2
  * @umituz/web-firebase
3
- * Firebase integration for web applications with domain-based architecture
3
+ * Firebase client initialization and core services
4
4
  *
5
5
  * Usage:
6
- * import { AuthService } from '@umituz/web-firebase/auth'
7
- * import { FirestoreService } from '@umituz/web-firebase/firestore'
8
- * import { StorageService } from '@umituz/web-firebase/storage'
6
+ * import { initializeFirebase, getFirebaseDB } from '@umituz/web-firebase'
9
7
  */
10
8
 
11
9
  // Firebase client initialization
12
10
  export * from './infrastructure/firebase/client'
13
-
14
- // Presentation Layer (React components & hooks)
15
- export * from './presentation/index'
16
-
17
- // Re-export common types for convenience
18
- export type { FirebaseInstances } from './infrastructure/firebase/client'
19
- export type { FirebaseContextValue, FirebaseProviderProps } from './presentation/providers/FirebaseProvider'
@@ -36,7 +36,11 @@ import { getAuthConfig } from '../../domain/config/auth.config'
36
36
  */
37
37
  export class AuthAdapter implements IAuthRepository {
38
38
  private get auth() {
39
- return getFirebaseAuth()
39
+ const auth = getFirebaseAuth()
40
+ if (!auth) {
41
+ throw new Error('Firebase Auth not initialized. Call initializeFirebase() first.')
42
+ }
43
+ return auth
40
44
  }
41
45
 
42
46
  private config = getAuthConfig()
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Firebase Client
3
3
  * @description Firebase initialization and singleton instances
4
- * Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/services/client.ts
4
+ * Migrated from: /Users/umuz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/services/client.ts
5
5
  */
6
6
 
7
7
  import { initializeApp, getApps, FirebaseApp } from 'firebase/app'
@@ -11,50 +11,75 @@ import { getStorage, FirebaseStorage } from 'firebase/storage'
11
11
  import { getAnalytics, Analytics } from 'firebase/analytics'
12
12
  import { getFunctions, Functions } from 'firebase/functions'
13
13
 
14
- const firebaseConfig = {
15
- apiKey: (import.meta as any).env.VITE_FIREBASE_API_KEY,
16
- authDomain: (import.meta as any).env.VITE_FIREBASE_AUTH_DOMAIN,
17
- projectId: (import.meta as any).env.VITE_FIREBASE_PROJECT_ID,
18
- storageBucket: (import.meta as any).env.VITE_FIREBASE_STORAGE_BUCKET,
19
- messagingSenderId: (import.meta as any).env.VITE_FIREBASE_MESSAGING_SENDER_ID,
20
- appId: (import.meta as any).env.VITE_FIREBASE_APP_ID,
21
- measurementId: (import.meta as any).env.VITE_FIREBASE_MEASUREMENT_ID,
14
+ /**
15
+ * Firebase Configuration interface
16
+ * Pass this to initializeFirebase()
17
+ */
18
+ export interface FirebaseConfig {
19
+ apiKey: string
20
+ authDomain: string
21
+ projectId: string
22
+ storageBucket: string
23
+ messagingSenderId: string
24
+ appId: string
25
+ measurementId?: string
22
26
  }
23
27
 
24
28
  // Singleton instances
25
- let app: FirebaseApp
26
- let auth: Auth
27
- let db: Firestore
28
- let storage: FirebaseStorage
29
- let functions: Functions
29
+ let app: FirebaseApp | null = null
30
+ let auth: Auth | null = null
31
+ let db: Firestore | null = null
32
+ let storage: FirebaseStorage | null = null
33
+ let functions: Functions | null = null
30
34
  let analytics: Analytics | null = null
31
35
 
32
36
  /**
33
- * Initialize Firebase App
37
+ * Initialize Firebase with provided configuration
38
+ * Must be called before using any Firebase services
39
+ *
40
+ * @param config - Firebase configuration object
41
+ * @returns FirebaseApp instance
34
42
  */
35
- export function initializeFirebase(): FirebaseApp {
36
- if (!getApps().length) {
37
- app = initializeApp(firebaseConfig)
38
- } else {
43
+ export function initializeFirebase(config: FirebaseConfig): FirebaseApp {
44
+ if (app) {
45
+ console.warn('Firebase already initialized')
46
+ return app
47
+ }
48
+
49
+ if (getApps().length > 0) {
39
50
  app = getApps()[0]
51
+ return app
40
52
  }
53
+
54
+ // Validate config
55
+ if (!config.apiKey || !config.projectId) {
56
+ throw new Error('Invalid Firebase config: apiKey and projectId are required')
57
+ }
58
+
59
+ app = initializeApp(config)
41
60
  return app
42
61
  }
43
62
 
44
63
  /**
45
64
  * Get Firebase App instance
65
+ * @returns FirebaseApp instance or null if not initialized
46
66
  */
47
- export function getFirebaseApp(): FirebaseApp {
48
- return app || initializeFirebase()
67
+ export function getFirebaseApp(): FirebaseApp | null {
68
+ return app
49
69
  }
50
70
 
51
71
  /**
52
72
  * Get Firebase Auth instance
73
+ * @returns Auth instance or null if not initialized
53
74
  */
54
- export function getFirebaseAuth(): Auth {
75
+ export function getFirebaseAuth(): Auth | null {
76
+ if (!app) {
77
+ console.warn('Firebase not initialized. Call initializeFirebase(config) first.')
78
+ return null
79
+ }
55
80
  if (!auth) {
56
81
  const firebaseApp = getFirebaseApp()
57
- if (typeof window !== 'undefined') {
82
+ if (firebaseApp && typeof window !== 'undefined') {
58
83
  try {
59
84
  auth = getAuth(firebaseApp)
60
85
  } catch (e) {
@@ -63,7 +88,7 @@ export function getFirebaseAuth(): Auth {
63
88
  persistence: browserLocalPersistence,
64
89
  })
65
90
  }
66
- } else {
91
+ } else if (firebaseApp) {
67
92
  auth = getAuth(firebaseApp)
68
93
  }
69
94
  }
@@ -72,70 +97,99 @@ export function getFirebaseAuth(): Auth {
72
97
 
73
98
  /**
74
99
  * Get Firestore instance
100
+ * @returns Firestore instance or null if not initialized
75
101
  */
76
- export function getFirebaseDB(): Firestore {
102
+ export function getFirebaseDB(): Firestore | null {
103
+ if (!app) {
104
+ console.warn('Firebase not initialized. Call initializeFirebase(config) first.')
105
+ return null
106
+ }
77
107
  if (!db) {
78
- db = getFirestore(getFirebaseApp())
108
+ db = getFirestore(getFirebaseApp()!)
79
109
  }
80
110
  return db
81
111
  }
82
112
 
83
113
  /**
84
114
  * Get Firebase Storage instance
115
+ * @returns FirebaseStorage instance or null if not initialized
85
116
  */
86
- export function getFirebaseStorage(): FirebaseStorage {
117
+ export function getFirebaseStorage(): FirebaseStorage | null {
118
+ if (!app) {
119
+ console.warn('Firebase not initialized. Call initializeFirebase(config) first.')
120
+ return null
121
+ }
87
122
  if (!storage) {
88
- storage = getStorage(getFirebaseApp())
123
+ storage = getStorage(getFirebaseApp()!)
89
124
  }
90
125
  return storage
91
126
  }
92
127
 
93
128
  /**
94
129
  * Get Firebase Functions instance
130
+ * @returns Functions instance or null if not initialized
95
131
  */
96
- export function getFirebaseFunctions(): Functions {
132
+ export function getFirebaseFunctions(): Functions | null {
133
+ if (!app) {
134
+ console.warn('Firebase not initialized. Call initializeFirebase(config) first.')
135
+ return null
136
+ }
97
137
  if (!functions) {
98
- functions = getFunctions(getFirebaseApp())
138
+ functions = getFunctions(getFirebaseApp()!)
99
139
  }
100
140
  return functions
101
141
  }
102
142
 
103
143
  /**
104
144
  * Get Firebase Analytics instance
145
+ * @returns Analytics instance or null (not available in SSR or not initialized)
105
146
  */
106
147
  export function getFirebaseAnalytics(): Analytics | null {
148
+ if (!app) {
149
+ console.warn('Firebase not initialized. Call initializeFirebase(config) first.')
150
+ return null
151
+ }
107
152
  if (!analytics && typeof window !== 'undefined') {
108
- analytics = getAnalytics(getFirebaseApp())
153
+ analytics = getAnalytics(getFirebaseApp()!)
109
154
  }
110
155
  return analytics
111
156
  }
112
157
 
113
158
  /**
114
159
  * Firebase Instances
115
- * All Firebase service instances
160
+ * All Firebase service instances (may be null if not initialized)
116
161
  */
117
162
  export interface FirebaseInstances {
118
163
  app: FirebaseApp
119
- auth: Auth
120
- db: Firestore
121
- storage: FirebaseStorage
122
- functions: Functions
164
+ auth: Auth | null
165
+ db: Firestore | null
166
+ storage: FirebaseStorage | null
167
+ functions: Functions | null
123
168
  analytics: Analytics | null
124
169
  }
125
170
 
126
171
  /**
127
172
  * Get all Firebase instances
173
+ * @returns FirebaseInstances or null if not initialized
128
174
  */
129
- export function getFirebaseInstances(): FirebaseInstances {
175
+ export function getFirebaseInstances(): FirebaseInstances | null {
176
+ const app = getFirebaseApp()
177
+ if (!app) {
178
+ return null
179
+ }
130
180
  return {
131
- app: getFirebaseApp(),
132
- auth: getFirebaseAuth(),
133
- db: getFirebaseDB(),
134
- storage: getFirebaseStorage(),
135
- functions: getFirebaseFunctions(),
181
+ app,
182
+ auth: getFirebaseAuth()!,
183
+ db: getFirebaseDB()!,
184
+ storage: getFirebaseStorage()!,
185
+ functions: getFirebaseFunctions()!,
136
186
  analytics: getFirebaseAnalytics(),
137
187
  }
138
188
  }
139
189
 
140
- // Export singleton instances for convenience
141
- export { app, auth, db, storage, functions, analytics }
190
+ /**
191
+ * Check if Firebase is initialized
192
+ */
193
+ export function isFirebaseInitialized(): boolean {
194
+ return app !== null
195
+ }
@@ -23,7 +23,11 @@ import { createRepositoryError, RepositoryErrorCode } from '../../domain/errors/
23
23
 
24
24
  export class FirestoreAdapter implements IUserRepository {
25
25
  private get db() {
26
- return getFirebaseDB()
26
+ const db = getFirebaseDB()
27
+ if (!db) {
28
+ throw new Error('Firestore not initialized. Call initializeFirebase() first.')
29
+ }
30
+ return db
27
31
  }
28
32
 
29
33
  private readonly USERS_COLLECTION = 'users'
@@ -26,7 +26,11 @@ import { createRepositoryError, RepositoryErrorCode } from '../../domain/errors/
26
26
 
27
27
  export class StorageAdapter implements IFileRepository {
28
28
  private get storage() {
29
- return getFirebaseStorage()
29
+ const storage = getFirebaseStorage()
30
+ if (!storage) {
31
+ throw new Error('Firebase Storage not initialized. Call initializeFirebase() first.')
32
+ }
33
+ return storage
30
34
  }
31
35
 
32
36
  // Upload Methods
@@ -1,392 +0,0 @@
1
- /**
2
- * Auth Hook
3
- * @description React hook for authentication with Google & Apple OAuth
4
- */
5
-
6
- import { useCallback, useEffect, useState } from 'react'
7
- import type { User as FirebaseUser } from 'firebase/auth'
8
- import type { User as FirestoreUser } from '../../domains/firestore/entities'
9
- import { authService } from '../../domains/auth/services'
10
- import { firestoreService } from '../../domains/firestore/services'
11
- import { useFirebaseContext } from '../providers/FirebaseProvider'
12
- import { getAuthConfig } from '../../domain/config/auth.config'
13
-
14
- export interface UseAuthResult {
15
- // State
16
- firebaseUser: FirebaseUser | null
17
- user: FirestoreUser | null
18
- loading: boolean
19
- error: Error | null
20
- isAuthenticated: boolean
21
- isEmailVerified: boolean
22
-
23
- // Email/Password Auth
24
- signIn: (email: string, password: string) => Promise<FirebaseUser>
25
- signUp: (email: string, password: string, displayName: string) => Promise<FirebaseUser>
26
-
27
- // OAuth Providers
28
- signInWithGoogle: (useRedirect?: boolean) => Promise<FirebaseUser>
29
- signInWithApple: (useRedirect?: boolean) => Promise<FirebaseUser>
30
-
31
- // Provider Management
32
- linkGoogle: () => Promise<void>
33
- linkApple: () => Promise<void>
34
- unlinkProvider: (providerId: string) => Promise<void>
35
-
36
- // Account Management
37
- signOut: () => Promise<void>
38
- sendPasswordReset: (email: string) => Promise<void>
39
- resendEmailVerification: () => Promise<void>
40
- updateProfile: (updates: { displayName?: string; photoURL?: string }) => Promise<void>
41
- updateEmail: (newEmail: string, password: string) => Promise<void>
42
- updatePassword: (currentPassword: string, newPassword: string) => Promise<void>
43
- deleteAccount: (password: string) => Promise<void>
44
-
45
- // Token Management
46
- getIdToken: (forceRefresh?: boolean) => Promise<string>
47
- refreshToken: () => Promise<void>
48
-
49
- // User Data
50
- refreshUser: () => Promise<void>
51
-
52
- // Config
53
- googleEnabled: boolean
54
- appleEnabled: boolean
55
- emailPasswordEnabled: boolean
56
- }
57
-
58
- export function useAuth(): UseAuthResult {
59
- const { instances, loading, error } = useFirebaseContext()
60
- const [user, setUser] = useState<FirestoreUser | null>(null)
61
- const config = getAuthConfig()
62
-
63
- // Fetch user data from Firestore
64
- useEffect(() => {
65
- const fetchUser = async () => {
66
- const currentUser = instances?.auth.currentUser
67
- if (currentUser) {
68
- try {
69
- const userData = await firestoreService.getUser(currentUser.uid)
70
- setUser(userData)
71
- } catch (err) {
72
- console.error('Error fetching user data:', err)
73
- setUser(null)
74
- }
75
- } else {
76
- setUser(null)
77
- }
78
- }
79
- fetchUser()
80
- }, [instances?.auth.currentUser])
81
-
82
- // Email/Password Sign In
83
- const signIn = useCallback(async (email: string, password: string) => {
84
- const result = await authService.signIn(email, password)
85
- return result.user
86
- }, [])
87
-
88
- // Email/Password Sign Up
89
- const signUp = useCallback(async (email: string, password: string, displayName: string) => {
90
- const userCredential = await authService.signUp(email, password, displayName)
91
-
92
- // Create user profile in Firestore if auto-create is enabled
93
- if (config.shouldCreateUserDocument()) {
94
- const now = Date.now()
95
- const defaultSettings = config.getConfig().defaultUserSettings
96
-
97
- await firestoreService.createUser(userCredential.user.uid, {
98
- profile: {
99
- id: userCredential.user.uid,
100
- email: email,
101
- displayName: displayName,
102
- createdAt: now,
103
- updatedAt: now,
104
- lastLoginAt: now,
105
- emailVerified: false,
106
- },
107
- settings: {
108
- theme: defaultSettings?.theme ?? 'system',
109
- language: defaultSettings?.language ?? 'en',
110
- timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
111
- currency: defaultSettings?.currency ?? 'USD',
112
- notifications: {
113
- email: defaultSettings?.notifications?.email ?? true,
114
- push: defaultSettings?.notifications?.push ?? true,
115
- marketing: defaultSettings?.notifications?.marketing ?? false,
116
- security: defaultSettings?.notifications?.security ?? true,
117
- weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
118
- },
119
- privacy: {
120
- profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
121
- showEmail: defaultSettings?.privacy?.showEmail ?? false,
122
- showPhone: defaultSettings?.privacy?.showPhone ?? false,
123
- dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
124
- },
125
- },
126
- subscription: {
127
- plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
128
- status: 'active',
129
- cancelAtPeriodEnd: false,
130
- createdAt: now,
131
- updatedAt: now,
132
- },
133
- })
134
- }
135
-
136
- return userCredential.user
137
- }, [config])
138
-
139
- // Google Sign In
140
- const signInWithGoogleCallback = useCallback(async (useRedirect = false): Promise<FirebaseUser> => {
141
- const result = await authService.signInWithGoogle(useRedirect)
142
-
143
- // Create user profile in Firestore if it doesn't exist
144
- if (config.shouldCreateUserDocument()) {
145
- const existingUser = await firestoreService.getUser(result.user.uid)
146
- if (!existingUser) {
147
- const now = Date.now()
148
- const defaultSettings = config.getConfig().defaultUserSettings
149
-
150
- await firestoreService.createUser(result.user.uid, {
151
- profile: {
152
- id: result.user.uid,
153
- email: result.user.email ?? '',
154
- displayName: result.user.displayName ?? '',
155
- photoURL: result.user.photoURL || undefined,
156
- createdAt: now,
157
- updatedAt: now,
158
- lastLoginAt: now,
159
- emailVerified: result.user.emailVerified,
160
- },
161
- settings: {
162
- theme: defaultSettings?.theme ?? 'system',
163
- language: defaultSettings?.language ?? 'en',
164
- timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
165
- currency: defaultSettings?.currency ?? 'USD',
166
- notifications: {
167
- email: defaultSettings?.notifications?.email ?? true,
168
- push: defaultSettings?.notifications?.push ?? true,
169
- marketing: defaultSettings?.notifications?.marketing ?? false,
170
- security: defaultSettings?.notifications?.security ?? true,
171
- weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
172
- },
173
- privacy: {
174
- profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
175
- showEmail: defaultSettings?.privacy?.showEmail ?? false,
176
- showPhone: defaultSettings?.privacy?.showPhone ?? false,
177
- dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
178
- },
179
- },
180
- subscription: {
181
- plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
182
- status: 'active',
183
- cancelAtPeriodEnd: false,
184
- createdAt: now,
185
- updatedAt: now,
186
- },
187
- })
188
- }
189
- }
190
-
191
- return result.user
192
- }, [config])
193
-
194
- // Apple Sign In
195
- const signInWithAppleCallback = useCallback(async (useRedirect = false): Promise<FirebaseUser> => {
196
- const result = await authService.signInWithApple(useRedirect)
197
-
198
- // Create user profile in Firestore if it doesn't exist
199
- if (config.shouldCreateUserDocument()) {
200
- const existingUser = await firestoreService.getUser(result.user.uid)
201
- if (!existingUser) {
202
- const now = Date.now()
203
- const defaultSettings = config.getConfig().defaultUserSettings
204
-
205
- await firestoreService.createUser(result.user.uid, {
206
- profile: {
207
- id: result.user.uid,
208
- email: result.user.email ?? '',
209
- displayName: result.user.displayName ?? '',
210
- photoURL: result.user.photoURL || undefined,
211
- createdAt: now,
212
- updatedAt: now,
213
- lastLoginAt: now,
214
- emailVerified: result.user.emailVerified,
215
- },
216
- settings: {
217
- theme: defaultSettings?.theme ?? 'system',
218
- language: defaultSettings?.language ?? 'en',
219
- timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
220
- currency: defaultSettings?.currency ?? 'USD',
221
- notifications: {
222
- email: defaultSettings?.notifications?.email ?? true,
223
- push: defaultSettings?.notifications?.push ?? true,
224
- marketing: defaultSettings?.notifications?.marketing ?? false,
225
- security: defaultSettings?.notifications?.security ?? true,
226
- weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
227
- },
228
- privacy: {
229
- profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
230
- showEmail: defaultSettings?.privacy?.showEmail ?? false,
231
- showPhone: defaultSettings?.privacy?.showPhone ?? false,
232
- dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
233
- },
234
- },
235
- subscription: {
236
- plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
237
- status: 'active',
238
- cancelAtPeriodEnd: false,
239
- createdAt: now,
240
- updatedAt: now,
241
- },
242
- })
243
- }
244
- }
245
-
246
- return result.user
247
- }, [config])
248
-
249
- // Provider Linking
250
- const linkGoogleCallback = useCallback(async () => {
251
- await authService.linkGoogle()
252
- // Refresh user data
253
- const currentUser = instances?.auth.currentUser
254
- if (currentUser) {
255
- const userData = await firestoreService.getUser(currentUser.uid)
256
- setUser(userData)
257
- }
258
- }, [instances])
259
-
260
- const linkAppleCallback = useCallback(async () => {
261
- await authService.linkApple()
262
- // Refresh user data
263
- const currentUser = instances?.auth.currentUser
264
- if (currentUser) {
265
- const userData = await firestoreService.getUser(currentUser.uid)
266
- setUser(userData)
267
- }
268
- }, [instances])
269
-
270
- const unlinkProviderCallback = useCallback(async (providerId: string) => {
271
- await authService.unlinkProvider(providerId)
272
- // Refresh user data
273
- const currentUser = instances?.auth.currentUser
274
- if (currentUser) {
275
- const userData = await firestoreService.getUser(currentUser.uid)
276
- setUser(userData)
277
- }
278
- }, [instances])
279
-
280
- // Sign Out
281
- const signOut = useCallback(async () => {
282
- await authService.signOut()
283
- setUser(null)
284
- }, [])
285
-
286
- // Password Reset
287
- const sendPasswordReset = useCallback(async (email: string) => {
288
- await authService.sendPasswordReset(email)
289
- }, [])
290
-
291
- // Email Verification
292
- const resendEmailVerification = useCallback(async () => {
293
- await authService.resendEmailVerification()
294
- }, [])
295
-
296
- // Update Profile
297
- const updateProfile = useCallback(async (updates: { displayName?: string; photoURL?: string }) => {
298
- await authService.updateProfile(updates)
299
-
300
- // Refresh user data from Firestore
301
- const currentUser = instances?.auth.currentUser
302
- if (currentUser) {
303
- const userData = await firestoreService.getUser(currentUser.uid)
304
- setUser(userData)
305
- }
306
- }, [instances])
307
-
308
- // Update Email
309
- const updateEmailCallback = useCallback(async (newEmail: string, password: string) => {
310
- await authService.updateEmail(newEmail, password)
311
-
312
- // Refresh user data
313
- const currentUser = instances?.auth.currentUser
314
- if (currentUser) {
315
- const userData = await firestoreService.getUser(currentUser.uid)
316
- setUser(userData)
317
- }
318
- }, [instances])
319
-
320
- // Update Password
321
- const updatePasswordCallback = useCallback(async (currentPassword: string, newPassword: string) => {
322
- await authService.updatePassword(currentPassword, newPassword)
323
- }, [])
324
-
325
- // Delete Account
326
- const deleteAccountCallback = useCallback(async (password: string) => {
327
- await authService.deleteAccount(password)
328
- setUser(null)
329
- }, [])
330
-
331
- // Token Management
332
- const getIdTokenCallback = useCallback(async (forceRefresh = false) => {
333
- return await authService.getIdToken(forceRefresh)
334
- }, [])
335
-
336
- const refreshTokenCallback = useCallback(async () => {
337
- await authService.refreshToken()
338
- }, [])
339
-
340
- // Refresh User Data
341
- const refreshUser = useCallback(async () => {
342
- const currentUser = instances?.auth.currentUser
343
- if (currentUser) {
344
- const userData = await firestoreService.getUser(currentUser.uid)
345
- setUser(userData)
346
- }
347
- }, [instances])
348
-
349
- return {
350
- // State
351
- firebaseUser: instances?.auth.currentUser || null,
352
- user,
353
- loading,
354
- error,
355
- isAuthenticated: !!instances?.auth.currentUser,
356
- isEmailVerified: instances?.auth.currentUser?.emailVerified || false,
357
-
358
- // Email/Password Auth
359
- signIn,
360
- signUp,
361
-
362
- // OAuth Providers
363
- signInWithGoogle: signInWithGoogleCallback,
364
- signInWithApple: signInWithAppleCallback,
365
-
366
- // Provider Management
367
- linkGoogle: linkGoogleCallback,
368
- linkApple: linkAppleCallback,
369
- unlinkProvider: unlinkProviderCallback,
370
-
371
- // Account Management
372
- signOut,
373
- sendPasswordReset,
374
- resendEmailVerification,
375
- updateProfile,
376
- updateEmail: updateEmailCallback,
377
- updatePassword: updatePasswordCallback,
378
- deleteAccount: deleteAccountCallback,
379
-
380
- // Token Management
381
- getIdToken: getIdTokenCallback,
382
- refreshToken: refreshTokenCallback,
383
-
384
- // User Data
385
- refreshUser,
386
-
387
- // Config
388
- googleEnabled: config.isGoogleEnabled(),
389
- appleEnabled: config.isAppleEnabled(),
390
- emailPasswordEnabled: config.isEmailPasswordEnabled(),
391
- }
392
- }
@@ -1,122 +0,0 @@
1
- import { useState, useEffect, useCallback } from 'react';
2
- import {
3
- type Auth,
4
- type UserCredential,
5
- type User,
6
- onAuthStateChanged,
7
- signInWithEmailAndPassword,
8
- createUserWithEmailAndPassword,
9
- signOut as firebaseSignOut,
10
- updateProfile,
11
- updatePassword,
12
- sendPasswordResetEmail,
13
- } from 'firebase/auth';
14
- import type { FirebaseUser } from '../../domain/entities/firebase.entity';
15
-
16
- function mapUser(u: User): FirebaseUser {
17
- return {
18
- uid: u.uid,
19
- email: u.email,
20
- displayName: u.displayName,
21
- photoURL: u.photoURL,
22
- emailVerified: u.emailVerified,
23
- };
24
- }
25
-
26
- /**
27
- * useFirebaseAuth Hook
28
- * @description Hook to manage Firebase authentication state and operations
29
- */
30
-
31
- export interface UseFirebaseAuthOptions {
32
- onUserChange?: (user: FirebaseUser | null) => void;
33
- }
34
-
35
- export interface UseFirebaseAuthReturn {
36
- user: FirebaseUser | null;
37
- loading: boolean;
38
- isAuthenticated: boolean;
39
- signIn(email: string, password: string): Promise<UserCredential>;
40
- signUp(email: string, password: string, name?: string): Promise<UserCredential>;
41
- signOut(): Promise<void>;
42
- updateUserProfile(name: string, photoURL?: string): Promise<void>;
43
- updateUserPassword(newPassword: string): Promise<void>;
44
- resetPassword(email: string): Promise<void>;
45
- getIdToken(): Promise<string>;
46
- }
47
-
48
- export function useFirebaseAuth(
49
- auth: Auth,
50
- options?: UseFirebaseAuthOptions,
51
- ): UseFirebaseAuthReturn {
52
- const [user, setUser] = useState<FirebaseUser | null>(null);
53
- const [loading, setLoading] = useState(true);
54
-
55
- useEffect(() => {
56
- const unsub = onAuthStateChanged(auth, (firebaseUser) => {
57
- const mapped = firebaseUser ? mapUser(firebaseUser) : null;
58
- setUser(mapped);
59
- setLoading(false);
60
- options?.onUserChange?.(mapped);
61
- });
62
- return unsub;
63
- }, [auth, options]);
64
-
65
- const signIn = useCallback(
66
- (email: string, password: string) => signInWithEmailAndPassword(auth, email, password),
67
- [auth],
68
- );
69
-
70
- const signUp = useCallback(
71
- async (email: string, password: string, name?: string) => {
72
- const cred = await createUserWithEmailAndPassword(auth, email, password);
73
- if (name && cred.user) await updateProfile(cred.user, { displayName: name });
74
- return cred;
75
- },
76
- [auth],
77
- );
78
-
79
- const signOut = useCallback(() => firebaseSignOut(auth), [auth]);
80
-
81
- const updateUserProfile = useCallback(
82
- async (name: string, photoURL?: string) => {
83
- if (!auth.currentUser) throw new Error('No authenticated user');
84
- await updateProfile(auth.currentUser, {
85
- displayName: name,
86
- ...(photoURL !== undefined && { photoURL }),
87
- });
88
- },
89
- [auth],
90
- );
91
-
92
- const updateUserPassword = useCallback(
93
- async (newPassword: string) => {
94
- if (!auth.currentUser) throw new Error('No authenticated user');
95
- await updatePassword(auth.currentUser, newPassword);
96
- },
97
- [auth],
98
- );
99
-
100
- const resetPassword = useCallback(
101
- (email: string) => sendPasswordResetEmail(auth, email),
102
- [auth],
103
- );
104
-
105
- const getIdToken = useCallback(async () => {
106
- if (!auth.currentUser) throw new Error('No authenticated user');
107
- return auth.currentUser.getIdToken();
108
- }, [auth]);
109
-
110
- return {
111
- user,
112
- loading,
113
- isAuthenticated: !!user,
114
- signIn,
115
- signUp,
116
- signOut,
117
- updateUserProfile,
118
- updateUserPassword,
119
- resetPassword,
120
- getIdToken,
121
- };
122
- }
@@ -1,125 +0,0 @@
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
- }
@@ -1,141 +0,0 @@
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
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Presentation Layer
3
- * Subpath: @umituz/web-firebase/presentation
4
- */
5
-
6
- export * from './hooks/useAuth';
7
- export * from './hooks/useFirebaseAuth';
8
- export * from './hooks/useFirestore';
9
- export * from './hooks/useStorage';
10
- export * from './providers/FirebaseProvider';
@@ -1,90 +0,0 @@
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 type { AuthUser } from '../../domains/auth/entities'
11
- import { authService } from '../../domains/auth/services'
12
-
13
- export interface FirebaseContextValue {
14
- instances: FirebaseInstances
15
- isInitialized: boolean
16
- user: User | null
17
- authUser: AuthUser | null
18
- loading: boolean
19
- error: Error | null
20
- }
21
-
22
- const FirebaseContext = createContext<FirebaseContextValue | null>(null)
23
-
24
- export interface FirebaseProviderProps {
25
- children: ReactNode
26
- config?: {
27
- apiKey: string
28
- authDomain: string
29
- projectId: string
30
- storageBucket: string
31
- messagingSenderId: string
32
- appId: string
33
- }
34
- }
35
-
36
- export function FirebaseProvider({ children, config }: FirebaseProviderProps) {
37
- const [instances, setInstances] = useState<FirebaseInstances | null>(null)
38
- const [user, setUser] = useState<User | null>(null)
39
- const [authUser, setAuthUser] = useState<AuthUser | null>(null)
40
- const [loading, setLoading] = useState(true)
41
- const [error, setError] = useState<Error | null>(null)
42
-
43
- useEffect(() => {
44
- try {
45
- // Initialize Firebase (singleton - won't re-initialize)
46
- const firebaseInstances = getFirebaseInstances()
47
- setInstances(firebaseInstances)
48
-
49
- // Set up auth state listener
50
- const unsubscribe = authService.onAuthStateChanged(
51
- (newAuthUser) => {
52
- setAuthUser(newAuthUser)
53
- setUser(newAuthUser ? (firebaseInstances.auth.currentUser || null) : null)
54
- setLoading(false)
55
- setError(null)
56
- },
57
- (err) => {
58
- setError(err)
59
- setLoading(false)
60
- }
61
- )
62
-
63
- return () => {
64
- if (unsubscribe) unsubscribe()
65
- }
66
- } catch (err) {
67
- setError(err as Error)
68
- setLoading(false)
69
- }
70
- }, [config])
71
-
72
- const value: FirebaseContextValue = {
73
- instances: instances!,
74
- isInitialized: !!instances,
75
- user,
76
- authUser,
77
- loading,
78
- error,
79
- }
80
-
81
- return <FirebaseContext.Provider value={value}>{children}</FirebaseContext.Provider>
82
- }
83
-
84
- export function useFirebaseContext(): FirebaseContextValue {
85
- const context = useContext(FirebaseContext)
86
- if (!context) {
87
- throw new Error('useFirebaseContext must be used within FirebaseProvider')
88
- }
89
- return context
90
- }