@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 +1 -1
- package/src/domains/firestore/services/firestore.service.ts +5 -1
- package/src/domains/storage/services/storage.service.ts +5 -1
- package/src/index.ts +2 -11
- package/src/infrastructure/firebase/auth.adapter.ts +5 -1
- package/src/infrastructure/firebase/client.ts +98 -44
- package/src/infrastructure/firebase/firestore.adapter.ts +5 -1
- package/src/infrastructure/firebase/storage.adapter.ts +5 -1
- package/src/presentation/hooks/useAuth.ts +0 -392
- package/src/presentation/hooks/useFirebaseAuth.ts +0 -122
- package/src/presentation/hooks/useFirestore.ts +0 -125
- package/src/presentation/hooks/useStorage.ts +0 -141
- package/src/presentation/index.ts +0 -10
- package/src/presentation/providers/FirebaseProvider.tsx +0 -90
package/package.json
CHANGED
|
@@ -21,7 +21,11 @@ import type { User } from '../entities'
|
|
|
21
21
|
|
|
22
22
|
class FirestoreService implements IFirestoreService {
|
|
23
23
|
private get db() {
|
|
24
|
-
|
|
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
|
-
|
|
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
|
|
3
|
+
* Firebase client initialization and core services
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* import {
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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 (
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|