@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.
@@ -0,0 +1,327 @@
1
+ # useProfileUpdate & useProfileEdit
2
+
3
+ Hooks for profile update operations and profile editing form management.
4
+
5
+ ---
6
+
7
+ ## useProfileUpdate
8
+
9
+ Hook for profile update operations. Implementation should be provided by the app using Firebase SDK or backend API.
10
+
11
+ ### Usage
12
+
13
+ ```typescript
14
+ import { useProfileUpdate } from '@umituz/react-native-auth';
15
+
16
+ function ProfileSettings() {
17
+ const { updateProfile, isUpdating, error } = useProfileUpdate();
18
+
19
+ const handleUpdate = async (data: UpdateProfileParams) => {
20
+ try {
21
+ await updateProfile(data);
22
+ } catch (err) {
23
+ console.error(err);
24
+ }
25
+ };
26
+
27
+ return <ProfileForm onSave={handleUpdate} />;
28
+ }
29
+ ```
30
+
31
+ ### API
32
+
33
+ | Prop | Type | Description |
34
+ |------|------|-------------|
35
+ | `updateProfile` | `(params: UpdateProfileParams) => Promise<void>` | Profile update function |
36
+ | `isUpdating` | `boolean` | Update in progress |
37
+ | `error` | `string \| null` | Error message |
38
+
39
+ ### Create Your Own Implementation
40
+
41
+ ```typescript
42
+ function useProfileUpdate() {
43
+ const { user } = useAuth();
44
+ const [isUpdating, setIsUpdating] = useState(false);
45
+ const [error, setError] = useState<string | null>(null);
46
+
47
+ const updateProfile = async (params: UpdateProfileParams) => {
48
+ if (!user) {
49
+ throw new Error("No user logged in");
50
+ }
51
+
52
+ if (user.isAnonymous) {
53
+ throw new Error("Anonymous users cannot update profile");
54
+ }
55
+
56
+ setIsUpdating(true);
57
+ setError(null);
58
+
59
+ try {
60
+ // Update profile in Firebase Auth
61
+ await updateProfile(user, {
62
+ displayName: params.displayName,
63
+ photoURL: params.photoURL,
64
+ });
65
+
66
+ // Update user document in Firestore
67
+ await updateDoc(doc(db, 'users', user.uid), {
68
+ displayName: params.displayName,
69
+ photoURL: params.photoURL,
70
+ updatedAt: serverTimestamp(),
71
+ });
72
+ } catch (err) {
73
+ const message = err instanceof Error ? err.message : 'Update failed';
74
+ setError(message);
75
+ throw err;
76
+ } finally {
77
+ setIsUpdating(false);
78
+ }
79
+ };
80
+
81
+ return { updateProfile, isUpdating, error };
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## useProfileEdit
88
+
89
+ Hook for simple profile editing with form state management.
90
+
91
+ ### Usage
92
+
93
+ ```typescript
94
+ import { useProfileEdit } from '@umituz/react-native-auth';
95
+
96
+ function EditProfileScreen({ navigation }) {
97
+ const {
98
+ formState,
99
+ setDisplayName,
100
+ setEmail,
101
+ setPhotoURL,
102
+ resetForm,
103
+ validateForm,
104
+ } = useProfileEdit({
105
+ displayName: user?.displayName || '',
106
+ email: user?.email || '',
107
+ photoURL: user?.photoURL || null,
108
+ });
109
+
110
+ const handleSave = () => {
111
+ const { isValid, errors } = validateForm();
112
+
113
+ if (!isValid) {
114
+ Alert.alert('Error', errors.join('\n'));
115
+ return;
116
+ }
117
+
118
+ updateProfile({
119
+ displayName: formState.displayName,
120
+ photoURL: formState.photoURL,
121
+ });
122
+
123
+ navigation.goBack();
124
+ };
125
+
126
+ return (
127
+ <ScrollView>
128
+ <TextInput
129
+ value={formState.displayName}
130
+ onChangeText={setDisplayName}
131
+ placeholder="Full Name"
132
+ />
133
+
134
+ <TextInput
135
+ value={formState.email}
136
+ onChangeText={setEmail}
137
+ placeholder="Email"
138
+ editable={false}
139
+ />
140
+
141
+ <AvatarUploader
142
+ photoURL={formState.photoURL}
143
+ onImageSelected={setPhotoURL}
144
+ />
145
+
146
+ <View style={styles.buttons}>
147
+ <Button onPress={navigation.goBack}>Cancel</Button>
148
+ <Button
149
+ onPress={handleSave}
150
+ disabled={!formState.isModified}
151
+ >
152
+ Save
153
+ </Button>
154
+ </View>
155
+ </ScrollView>
156
+ );
157
+ }
158
+ ```
159
+
160
+ ### API
161
+
162
+ #### Return Value
163
+
164
+ | Prop | Type | Description |
165
+ |------|------|-------------|
166
+ | `formState` | `ProfileEditFormState` | Form state |
167
+ | `setDisplayName` | `(value: string) => void` | Set display name |
168
+ | `setEmail` | `(value: string) => void` | Set email |
169
+ | `setPhotoURL` | `(value: string \| null) => void` | Set photo URL |
170
+ | `resetForm` | `(initial: Partial<ProfileEditFormState>) => void` | Reset form |
171
+ | `validateForm` | `() => { isValid: boolean; errors: string[] }` | Validate form |
172
+
173
+ #### ProfileEditFormState
174
+
175
+ | Prop | Type | Description |
176
+ |------|------|-------------|
177
+ | `displayName` | `string` | Display name |
178
+ | `email` | `string` | Email |
179
+ | `photoURL` | `string \| null` | Photo URL |
180
+ | `isModified` | `boolean` | Form has been modified |
181
+
182
+ ### Validation
183
+
184
+ `validateForm()` checks:
185
+
186
+ - **Display name**: Cannot be empty
187
+ - **Email**: Valid email format (if provided)
188
+
189
+ ```typescript
190
+ const { isValid, errors } = validateForm();
191
+
192
+ if (!isValid) {
193
+ errors.forEach(error => console.log(error));
194
+ // ["Display name is required", "Invalid email format"]
195
+ }
196
+ ```
197
+
198
+ ## Examples
199
+
200
+ ### Profile Photo Upload
201
+
202
+ ```typescript
203
+ function ProfilePhotoSection() {
204
+ const { formState, setPhotoURL } = useProfileEdit(initialState);
205
+
206
+ const handlePickImage = async () => {
207
+ const result = await launchImageLibrary({
208
+ mediaType: 'photo',
209
+ quality: 0.8,
210
+ });
211
+
212
+ if (result.assets?.[0]) {
213
+ // Upload to storage and get URL
214
+ const url = await uploadToStorage(result.assets[0].uri);
215
+ setPhotoURL(url);
216
+ }
217
+ };
218
+
219
+ return (
220
+ <TouchableOpacity onPress={handlePickImage}>
221
+ {formState.photoURL ? (
222
+ <Image source={{ uri: formState.photoURL }} />
223
+ ) : (
224
+ <View style={styles.placeholder}>
225
+ <Text>Select Photo</Text>
226
+ </View>
227
+ )}
228
+ </TouchableOpacity>
229
+ );
230
+ }
231
+ ```
232
+
233
+ ### Unsaved Changes Warning
234
+
235
+ ```typescript
236
+ function EditProfileScreen({ navigation }) {
237
+ const {
238
+ formState,
239
+ resetForm,
240
+ validateForm
241
+ } = useProfileEdit(initialState);
242
+
243
+ useEffect(() => {
244
+ const unsubscribe = navigation.addListener('beforeRemove', (e) => {
245
+ if (!formState.isModified) {
246
+ return;
247
+ }
248
+
249
+ e.preventDefault();
250
+
251
+ Alert.alert(
252
+ 'Unsaved Changes',
253
+ 'You have unsaved changes. What would you like to do?',
254
+ [
255
+ { text: 'Don\'t Save', style: 'cancel' },
256
+ {
257
+ text: 'Save',
258
+ onPress: () => {
259
+ saveChanges();
260
+ navigation.dispatch(e.data.action);
261
+ }
262
+ },
263
+ {
264
+ text: 'Discard',
265
+ style: 'destructive',
266
+ onPress: () => {
267
+ resetForm(initialState);
268
+ navigation.dispatch(e.data.action);
269
+ }
270
+ },
271
+ ]
272
+ );
273
+ });
274
+
275
+ return unsubscribe;
276
+ }, [navigation, formState.isModified]);
277
+
278
+ // ...
279
+ }
280
+ ```
281
+
282
+ ### Custom Validation
283
+
284
+ ```typescript
285
+ function ExtendedProfileEdit() {
286
+ const {
287
+ formState,
288
+ setDisplayName,
289
+ setEmail,
290
+ setPhotoURL,
291
+ validateForm
292
+ } = useProfileEdit(initialState);
293
+
294
+ const handleSave = () => {
295
+ // Base validation
296
+ const { isValid, errors } = validateForm();
297
+
298
+ // Custom validation
299
+ const customErrors = [];
300
+
301
+ if (formState.displayName.length < 3) {
302
+ customErrors.push('Display name must be at least 3 characters');
303
+ }
304
+
305
+ if (formState.photoURL && !isValidImageUrl(formState.photoURL)) {
306
+ customErrors.push('Invalid image URL');
307
+ }
308
+
309
+ const allErrors = [...errors, ...customErrors];
310
+
311
+ if (allErrors.length > 0) {
312
+ Alert.alert('Error', allErrors.join('\n'));
313
+ return;
314
+ }
315
+
316
+ saveProfile();
317
+ };
318
+
319
+ // ...
320
+ }
321
+ ```
322
+
323
+ ## Related Hooks
324
+
325
+ - [`useUserProfile`](./useUserProfile.md) - Display profile data
326
+ - [`useAuth`](./useAuth.md) - Main auth state management
327
+ - [`useAccountManagement`](./useAccountManagement.md) - Account operations