@umituz/react-native-auth 3.4.29 → 3.4.31
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 +426 -0
- package/package.json +1 -3
- package/src/application/README.md +513 -0
- package/src/domain/ConfigAndErrors.md +545 -0
- package/src/domain/README.md +293 -0
- package/src/domain/entities/AuthUser.md +377 -0
- package/src/domain/entities/UserProfile.md +443 -0
- package/src/infrastructure/README.md +576 -0
- package/src/infrastructure/services/README.md +417 -0
- package/src/presentation/README.md +770 -0
- package/src/presentation/components/AuthBackground.tsx +21 -0
- package/src/presentation/components/AuthContainer.tsx +3 -3
- package/src/presentation/components/LoginForm.md +331 -0
- package/src/presentation/components/PasswordIndicators.md +471 -0
- package/src/presentation/components/ProfileComponents.md +432 -0
- package/src/presentation/components/README.md +117 -0
- package/src/presentation/components/SocialLoginButtons.md +303 -0
- package/src/presentation/hooks/README.md +122 -0
- package/src/presentation/hooks/useAccountManagement.md +380 -0
- package/src/presentation/hooks/useAuth.md +255 -0
- package/src/presentation/hooks/useAuthBottomSheet.md +417 -0
- package/src/presentation/hooks/useAuthRequired.md +248 -0
- package/src/presentation/hooks/useProfileUpdate.md +327 -0
- package/src/presentation/hooks/useSocialLogin.md +411 -0
- package/src/presentation/hooks/useUserProfile.md +230 -0
- package/src/presentation/screens/README.md +198 -0
- package/src/presentation/components/AuthGradientBackground.tsx +0 -33
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Domain Layer
|
|
2
|
+
|
|
3
|
+
React Native Auth paketinin domain katmanı. İş kurallarını, domain entity'lerini ve value object'leri içerir.
|
|
4
|
+
|
|
5
|
+
## Yapı
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
domain/
|
|
9
|
+
├── entities/
|
|
10
|
+
│ ├── AuthUser.ts # Authentication kullanıcısı
|
|
11
|
+
│ └── UserProfile.ts # Kullanıcı profili
|
|
12
|
+
├── value-objects/
|
|
13
|
+
│ └── AuthConfig.ts # Auth konfigürasyonu
|
|
14
|
+
├── errors/
|
|
15
|
+
│ └── AuthError.ts # Auth hataları
|
|
16
|
+
└── utils/
|
|
17
|
+
├── anonymousNameGenerator.ts # Anonymous isim oluşturucu
|
|
18
|
+
└── migration.ts # Veri taşıma
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Entities
|
|
22
|
+
|
|
23
|
+
### AuthUser
|
|
24
|
+
|
|
25
|
+
Authentication işlemleri için kullanıcı entity'si.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import type { AuthUser } from '@umituz/react-native-auth';
|
|
29
|
+
|
|
30
|
+
const user: AuthUser = {
|
|
31
|
+
uid: 'user-123',
|
|
32
|
+
email: 'user@example.com',
|
|
33
|
+
displayName: 'John Doe',
|
|
34
|
+
photoURL: 'https://...',
|
|
35
|
+
isAnonymous: false,
|
|
36
|
+
provider: 'google',
|
|
37
|
+
emailVerified: true,
|
|
38
|
+
};
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Özellikler:**
|
|
42
|
+
- Firebase User ile uyumlu
|
|
43
|
+
- Provider bilgisi
|
|
44
|
+
- Anonymous kontrolü
|
|
45
|
+
- Email verification durumu
|
|
46
|
+
|
|
47
|
+
**Kullanım:**
|
|
48
|
+
```typescript
|
|
49
|
+
function getUserDisplayName(user: AuthUser): string {
|
|
50
|
+
return user.displayName || user.email || 'Anonymous';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isSocialLogin(user: AuthUser): boolean {
|
|
54
|
+
return user.provider === 'google' || user.provider === 'apple';
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### UserProfile
|
|
59
|
+
|
|
60
|
+
Kullanıcı profili entity'si.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import type { UserProfile } from '@umituz/react-native-auth';
|
|
64
|
+
|
|
65
|
+
const profile: UserProfile = {
|
|
66
|
+
displayName: 'John Doe',
|
|
67
|
+
photoURL: 'https://...',
|
|
68
|
+
bio: 'Software developer',
|
|
69
|
+
location: 'Istanbul',
|
|
70
|
+
website: 'https://johndoe.com',
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Güncelleme:**
|
|
75
|
+
```typescript
|
|
76
|
+
async function updateProfile(userId: string, updates: UpdateProfileParams) {
|
|
77
|
+
await updateDoc(doc(db, 'users', userId), updates);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Value Objects
|
|
82
|
+
|
|
83
|
+
### AuthConfig
|
|
84
|
+
|
|
85
|
+
Authentication konfigürasyonu.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { AuthConfig, DEFAULT_AUTH_CONFIG } from '@umituz/react-native-auth';
|
|
89
|
+
|
|
90
|
+
const config: AuthConfig = {
|
|
91
|
+
password: {
|
|
92
|
+
minLength: 8,
|
|
93
|
+
requireUppercase: true,
|
|
94
|
+
requireLowercase: true,
|
|
95
|
+
requireNumbers: true,
|
|
96
|
+
requireSpecialChars: true,
|
|
97
|
+
},
|
|
98
|
+
social: {
|
|
99
|
+
google: {
|
|
100
|
+
iosClientId: '...',
|
|
101
|
+
webClientId: '...',
|
|
102
|
+
},
|
|
103
|
+
apple: {
|
|
104
|
+
enabled: true,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Errors
|
|
111
|
+
|
|
112
|
+
### AuthError Hierarchy
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import {
|
|
116
|
+
AuthError,
|
|
117
|
+
AuthInitializationError,
|
|
118
|
+
AuthConfigurationError,
|
|
119
|
+
AuthValidationError,
|
|
120
|
+
AuthNetworkError,
|
|
121
|
+
AuthUserNotFoundError,
|
|
122
|
+
AuthWrongPasswordError,
|
|
123
|
+
AuthEmailAlreadyInUseError,
|
|
124
|
+
AuthWeakPasswordError,
|
|
125
|
+
AuthInvalidEmailError,
|
|
126
|
+
} from '@umituz/react-native-auth';
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Hata Yakalama:**
|
|
130
|
+
```typescript
|
|
131
|
+
try {
|
|
132
|
+
await signIn({ email, password });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof AuthUserNotFoundError) {
|
|
135
|
+
// Kullanıcı bulunamadı
|
|
136
|
+
} else if (error instanceof AuthWrongPasswordError) {
|
|
137
|
+
// Şifre hatalı
|
|
138
|
+
} else if (error instanceof AuthNetworkError) {
|
|
139
|
+
// Network hatası
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Utils
|
|
145
|
+
|
|
146
|
+
### Anonymous Name Generator
|
|
147
|
+
|
|
148
|
+
Anonymous kullanıcılar için rastgele isim oluşturur.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import {
|
|
152
|
+
generateAnonymousName,
|
|
153
|
+
getAnonymousDisplayName
|
|
154
|
+
} from '@umituz/react-native-auth';
|
|
155
|
+
|
|
156
|
+
const name1 = generateAnonymousName('user-123');
|
|
157
|
+
// "User_Witty_Badger_1234"
|
|
158
|
+
|
|
159
|
+
const name2 = generateAnonymousName('user-456', {
|
|
160
|
+
prefix: 'Guest',
|
|
161
|
+
adjectiveCount: 1,
|
|
162
|
+
nounCount: 1,
|
|
163
|
+
});
|
|
164
|
+
// "Guest_Clever_Fox_456"
|
|
165
|
+
|
|
166
|
+
const displayName = getAnonymousDisplayName('user-123');
|
|
167
|
+
// "Witty Badger"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Konfigürasyon:**
|
|
171
|
+
```typescript
|
|
172
|
+
interface AnonymousNameConfig {
|
|
173
|
+
prefix?: string; // Varsayılan: 'User'
|
|
174
|
+
adjectiveCount?: number; // Varsayılan: 2
|
|
175
|
+
nounCount?: number; // Varsayılan: 1
|
|
176
|
+
showNumbers?: boolean; // Varsayılan: true
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Migration
|
|
181
|
+
|
|
182
|
+
Kullanıcı verilerini taşımak için utility.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { migrateUserData, configureMigration } from '@umituz/react-native-auth';
|
|
186
|
+
|
|
187
|
+
// Migration konfigürasyonu
|
|
188
|
+
configureMigration({
|
|
189
|
+
from: 'legacy_users',
|
|
190
|
+
to: 'users',
|
|
191
|
+
transform: (legacyData) => ({
|
|
192
|
+
displayName: legacyData.name,
|
|
193
|
+
email: legacyData.email_address,
|
|
194
|
+
createdAt: legacyData.joined_at,
|
|
195
|
+
}),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Migration çalıştırma
|
|
199
|
+
await migrateUserData(userId);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Tam Örnek:**
|
|
203
|
+
```typescript
|
|
204
|
+
async function migrateLegacyUser(userId: string) {
|
|
205
|
+
configureMigration({
|
|
206
|
+
from: 'old_users_collection',
|
|
207
|
+
to: 'users',
|
|
208
|
+
transform: (old) => ({
|
|
209
|
+
displayName: old.full_name,
|
|
210
|
+
email: old.email_addr,
|
|
211
|
+
photoURL: old.profile_pic,
|
|
212
|
+
createdAt: old.created_at,
|
|
213
|
+
metadata: {
|
|
214
|
+
migratedFrom: 'legacy',
|
|
215
|
+
migratedAt: Date.now(),
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
await migrateUserData(userId);
|
|
222
|
+
console.log('Migration successful');
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('Migration failed:', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Type Guards
|
|
230
|
+
|
|
231
|
+
Domain type'ları için guard'lar:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
function isAuthenticatedUser(user: AuthUser | null): user is AuthUser {
|
|
235
|
+
return user !== null && !user.isAnonymous;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function isEmailVerified(user: AuthUser): boolean {
|
|
239
|
+
return !!user.emailVerified;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function hasProvider(user: AuthUser, provider: string): boolean {
|
|
243
|
+
return user.provider === provider;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Domain Services
|
|
248
|
+
|
|
249
|
+
### User Validation
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
function validateUserForRegistration(user: Partial<AuthUser>): ValidationResult {
|
|
253
|
+
const errors: string[] = [];
|
|
254
|
+
|
|
255
|
+
if (!user.email) {
|
|
256
|
+
errors.push('Email is required');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!user.displayName || user.displayName.length < 2) {
|
|
260
|
+
errors.push('Display name must be at least 2 characters');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
isValid: errors.length === 0,
|
|
265
|
+
errors,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Domain Rules
|
|
271
|
+
|
|
272
|
+
1. **AuthUser Rules:**
|
|
273
|
+
- Her kullanıcının unique bir `uid`'si olmalı
|
|
274
|
+
- Anonymous kullanıcıların `email`'i yoktur
|
|
275
|
+
- Social login kullanıcıları provider bilgisi taşır
|
|
276
|
+
|
|
277
|
+
2. **UserProfile Rules:**
|
|
278
|
+
- `displayName` en az 2 karakter olmalı
|
|
279
|
+
- `email` geçerli formatta olmalı
|
|
280
|
+
- `photoURL` geçerli URL olmalı
|
|
281
|
+
|
|
282
|
+
3. **Password Rules:**
|
|
283
|
+
- Minimum 8 karakter
|
|
284
|
+
- En az 1 büyük harf
|
|
285
|
+
- En az 1 küçük harf
|
|
286
|
+
- En az 1 rakam
|
|
287
|
+
- En az 1 özel karakter
|
|
288
|
+
|
|
289
|
+
## İlgili Modüller
|
|
290
|
+
|
|
291
|
+
- **[Application](../application/README.md)** - Application ports
|
|
292
|
+
- **[Infrastructure](../infrastructure/README.md)** - Infrastructure implementation
|
|
293
|
+
- **[Presentation](../presentation/README.md)** - UI components ve hooks
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# AuthUser Entity
|
|
2
|
+
|
|
3
|
+
Authentication için provider-agnostic kullanıcı entity'si. Firebase User ile uyumlu, ancak herhangi bir auth provider ile kullanılabilir.
|
|
4
|
+
|
|
5
|
+
## Tip Tanımı
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import type { AuthUser, AuthProviderType } from '@umituz/react-native-auth';
|
|
9
|
+
|
|
10
|
+
interface AuthUser {
|
|
11
|
+
uid: string; // Unique kullanıcı ID'si
|
|
12
|
+
email: string | null; // Email adresi (anonymous'da null)
|
|
13
|
+
displayName: string | null; // Görünen ad
|
|
14
|
+
isAnonymous: boolean; // Anonymous kullanıcı mı
|
|
15
|
+
emailVerified: boolean; // Email doğrulanmış mı
|
|
16
|
+
photoURL: string | null; // Profil fotoğrafı URL'si
|
|
17
|
+
provider: AuthProviderType; // Auth provider tipi
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type AuthProviderType =
|
|
21
|
+
| "google.com" // Google ile giriş
|
|
22
|
+
| "apple.com" // Apple ile giriş
|
|
23
|
+
| "password" // Email/password ile giriş
|
|
24
|
+
| "anonymous" // Anonymous kullanıcı
|
|
25
|
+
| "unknown"; // Bilinmeyen provider
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Örnekler
|
|
29
|
+
|
|
30
|
+
### Email/Password Kullanıcısı
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const emailUser: AuthUser = {
|
|
34
|
+
uid: 'user-123',
|
|
35
|
+
email: 'john@example.com',
|
|
36
|
+
displayName: 'John Doe',
|
|
37
|
+
isAnonymous: false,
|
|
38
|
+
emailVerified: true,
|
|
39
|
+
photoURL: null,
|
|
40
|
+
provider: 'password',
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Google Kullanıcısı
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const googleUser: AuthUser = {
|
|
48
|
+
uid: 'google-456',
|
|
49
|
+
email: 'jane@gmail.com',
|
|
50
|
+
displayName: 'Jane Smith',
|
|
51
|
+
isAnonymous: false,
|
|
52
|
+
emailVerified: true,
|
|
53
|
+
photoURL: 'https://lh3.googleusercontent.com/...',
|
|
54
|
+
provider: 'google.com',
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Apple Kullanıcısı
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const appleUser: AuthUser = {
|
|
62
|
+
uid: 'apple-789',
|
|
63
|
+
email: 'user@icloud.com',
|
|
64
|
+
displayName: 'Apple User',
|
|
65
|
+
isAnonymous: false,
|
|
66
|
+
emailVerified: true,
|
|
67
|
+
photoURL: null,
|
|
68
|
+
provider: 'apple.com',
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Anonymous Kullanıcı
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const anonymousUser: AuthUser = {
|
|
76
|
+
uid: 'anon-abc',
|
|
77
|
+
email: null,
|
|
78
|
+
displayName: null,
|
|
79
|
+
isAnonymous: true,
|
|
80
|
+
emailVerified: false,
|
|
81
|
+
photoURL: null,
|
|
82
|
+
provider: 'anonymous',
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Kullanım
|
|
87
|
+
|
|
88
|
+
### Firebase User'dan AuthUser'a Dönüşüm
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { getAuth } from 'firebase/auth';
|
|
92
|
+
import type { AuthUser } from '@umituz/react-native-auth';
|
|
93
|
+
|
|
94
|
+
function firebaseUserToAuthUser(firebaseUser: FirebaseUser): AuthUser {
|
|
95
|
+
return {
|
|
96
|
+
uid: firebaseUser.uid,
|
|
97
|
+
email: firebaseUser.email,
|
|
98
|
+
displayName: firebaseUser.displayName,
|
|
99
|
+
isAnonymous: firebaseUser.isAnonymous,
|
|
100
|
+
emailVerified: firebaseUser.emailVerified,
|
|
101
|
+
photoURL: firebaseUser.photoURL,
|
|
102
|
+
provider: firebaseUser.providerData[0]?.providerId || 'unknown',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Kullanım
|
|
107
|
+
const auth = getAuth();
|
|
108
|
+
const firebaseUser = auth.currentUser;
|
|
109
|
+
if (firebaseUser) {
|
|
110
|
+
const user: AuthUser = firebaseUserToAuthUser(firebaseUser);
|
|
111
|
+
console.log(user.displayName);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### DisplayName Alma
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
function getUserDisplayName(user: AuthUser): string {
|
|
119
|
+
// Önce displayName'i dene
|
|
120
|
+
if (user.displayName) {
|
|
121
|
+
return user.displayName;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Yoksa email'i kullan
|
|
125
|
+
if (user.email) {
|
|
126
|
+
// Email'den isim çıkar
|
|
127
|
+
const name = user.email.split('@')[0];
|
|
128
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Hiçbiri yoksa varsayılan
|
|
132
|
+
return 'Kullanıcı';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Kullanım
|
|
136
|
+
const name = getUserDisplayName(user);
|
|
137
|
+
console.log(name); // "John" veya "Johndoe"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Provider Kontrolü
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
function isSocialLogin(user: AuthUser): boolean {
|
|
144
|
+
return user.provider === 'google.com' || user.provider === 'apple.com';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isEmailPasswordUser(user: AuthUser): boolean {
|
|
148
|
+
return user.provider === 'password';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function canChangePassword(user: AuthUser): boolean {
|
|
152
|
+
// Sadece email/password kullanıcıları şifre değiştirebilir
|
|
153
|
+
return user.provider === 'password';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Kullanım
|
|
157
|
+
if (isSocialLogin(user)) {
|
|
158
|
+
console.log('Social login ile giriş yaptı');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (canChangePassword(user)) {
|
|
162
|
+
// Şifre değiştirme butonunu göster
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Email Doğrulama Kontrolü
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
function requireEmailVerification(user: AuthUser): void {
|
|
170
|
+
if (user.provider === 'password' && !user.emailVerified) {
|
|
171
|
+
throw new Error('Email doğrulanması gerekiyor');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function shouldShowVerifyEmailBanner(user: AuthUser): boolean {
|
|
176
|
+
return (
|
|
177
|
+
!user.isAnonymous &&
|
|
178
|
+
user.provider === 'password' &&
|
|
179
|
+
!user.emailVerified
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Avatar URL Alma
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
function getUserAvatar(user: AuthUser): string | null {
|
|
188
|
+
// Önce photoURL'yi dene
|
|
189
|
+
if (user.photoURL) {
|
|
190
|
+
return user.photoURL;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Anonymous ise null döndür
|
|
194
|
+
if (user.isAnonymous) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Default avatar oluştur (örn: UI Avatars)
|
|
199
|
+
if (user.email) {
|
|
200
|
+
return `https://ui-avatars.com/api/?name=${encodeURIComponent(user.email)}&background=random`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### User İsim Oluşturma
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
function getUserInitials(user: AuthUser): string {
|
|
211
|
+
if (user.displayName) {
|
|
212
|
+
const names = user.displayName.trim().split(' ');
|
|
213
|
+
if (names.length >= 2) {
|
|
214
|
+
return (names[0][0] + names[names.length - 1][0]).toUpperCase();
|
|
215
|
+
}
|
|
216
|
+
return names[0][0].toUpperCase();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (user.email) {
|
|
220
|
+
return user.email[0].toUpperCase();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return '?';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Kullanım
|
|
227
|
+
const initials = getUserInitials(user);
|
|
228
|
+
console.log(initials); // "JD" veya "J" veya "?"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Type Guards
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
function isAuthenticatedUser(user: AuthUser | null): user is AuthUser {
|
|
235
|
+
return user !== null && !user.isAnonymous;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function isAnonymousUser(user: AuthUser | null): user is AuthUser {
|
|
239
|
+
return user !== null && user.isAnonymous;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function hasEmail(user: AuthUser | null): user is AuthUser {
|
|
243
|
+
return user !== null && user.email !== null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Kullanım
|
|
247
|
+
if (isAuthenticatedUser(user)) {
|
|
248
|
+
// TypeScript burada user'ın AuthUser olduğunu bilir
|
|
249
|
+
console.log(user.email);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Firebase ile Entegrasyon
|
|
254
|
+
|
|
255
|
+
### Auth State Değişikliği Dinleme
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { getAuth, onAuthStateChanged } from 'firebase/auth';
|
|
259
|
+
import type { AuthUser } from '@umituz/react-native-auth';
|
|
260
|
+
|
|
261
|
+
function useAuthUser() {
|
|
262
|
+
const [user, setUser] = useState<AuthUser | null>(null);
|
|
263
|
+
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
const auth = getAuth();
|
|
266
|
+
const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
|
|
267
|
+
if (firebaseUser) {
|
|
268
|
+
const authUser: AuthUser = {
|
|
269
|
+
uid: firebaseUser.uid,
|
|
270
|
+
email: firebaseUser.email,
|
|
271
|
+
displayName: firebaseUser.displayName,
|
|
272
|
+
isAnonymous: firebaseUser.isAnonymous,
|
|
273
|
+
emailVerified: firebaseUser.emailVerified,
|
|
274
|
+
photoURL: firebaseUser.photoURL,
|
|
275
|
+
provider: firebaseUser.providerData[0]?.providerId || 'unknown',
|
|
276
|
+
};
|
|
277
|
+
setUser(authUser);
|
|
278
|
+
} else {
|
|
279
|
+
setUser(null);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return unsubscribe;
|
|
284
|
+
}, []);
|
|
285
|
+
|
|
286
|
+
return user;
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### User Meta Verileri
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
interface ExtendedAuthUser extends AuthUser {
|
|
294
|
+
createdAt?: number;
|
|
295
|
+
lastSignInAt?: number;
|
|
296
|
+
metadata?: {
|
|
297
|
+
lastSignInTime?: string;
|
|
298
|
+
creationTime?: string;
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function enrichAuthUser(user: AuthUser, firebaseUser: FirebaseUser): ExtendedAuthUser {
|
|
303
|
+
return {
|
|
304
|
+
...user,
|
|
305
|
+
metadata: {
|
|
306
|
+
lastSignInTime: firebaseUser.metadata.lastSignInTime,
|
|
307
|
+
creationTime: firebaseUser.metadata.creationTime,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Validasyon
|
|
314
|
+
|
|
315
|
+
### User Validasyonu
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
function validateAuthUser(user: AuthUser): { valid: boolean; errors: string[] } {
|
|
319
|
+
const errors: string[] = [];
|
|
320
|
+
|
|
321
|
+
if (!user.uid || user.uid.length === 0) {
|
|
322
|
+
errors.push('User ID is required');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!user.isAnonymous && !user.email) {
|
|
326
|
+
errors.push('Email is required for non-anonymous users');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (user.email && !isValidEmail(user.email)) {
|
|
330
|
+
errors.push('Invalid email format');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
valid: errors.length === 0,
|
|
335
|
+
errors,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isValidEmail(email: string): boolean {
|
|
340
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Testler
|
|
345
|
+
|
|
346
|
+
### Mock User Oluşturma
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
function createMockAuthUser(overrides?: Partial<AuthUser>): AuthUser {
|
|
350
|
+
return {
|
|
351
|
+
uid: 'mock-user-123',
|
|
352
|
+
email: 'mock@example.com',
|
|
353
|
+
displayName: 'Mock User',
|
|
354
|
+
isAnonymous: false,
|
|
355
|
+
emailVerified: true,
|
|
356
|
+
photoURL: null,
|
|
357
|
+
provider: 'password',
|
|
358
|
+
...overrides,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Testlerde kullanım
|
|
363
|
+
const mockUser = createMockAuthUser({
|
|
364
|
+
provider: 'google.com',
|
|
365
|
+
photoURL: 'https://example.com/avatar.jpg',
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## İlgili Entity'ler
|
|
370
|
+
|
|
371
|
+
- **[`UserProfile`](./UserProfile.md)** - Kullanıcı profili entity'si
|
|
372
|
+
- **[`AuthError`](../errors/AuthError.md)** - Auth hataları
|
|
373
|
+
|
|
374
|
+
## İlgili Type'lar
|
|
375
|
+
|
|
376
|
+
- **[`AuthProviderType`](#tip-tanımı)** - Provider tipi
|
|
377
|
+
- **[`UpdateProfileParams`](../UserProfile.md)** - Profil güncelleme parametreleri
|