create-gufran-expo-app 2.0.3 → 2.0.5
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 +1 -2
- package/package.json +3 -3
- package/template/src/navigation/AuthStack.tsx +6 -25
- package/template/src/navigation/MainStack.tsx +0 -148
- package/template/src/navigation/RootNavigator.tsx +4 -26
- package/template/src/navigation/index.ts +0 -1
- package/template/src/navigation/navigationRef.ts +1 -1
- package/template/src/screens/HomeScreen.tsx +3 -215
- package/template/src/screens/auth/LoginScreen.tsx +13 -13
- package/template/src/screens/auth/index.ts +1 -6
- package/template/src/screens/index.ts +0 -35
- package/template/src/services/api.ts +5 -5
- package/template/src/services/authService.ts +3 -299
- package/template/src/services/mainServices.ts +19 -1914
- package/template/src/types/navigation.ts +5 -155
- package/template/src/utils/index.ts +5 -8
- package/template/src/navigation/MiddleStack.tsx +0 -35
- package/template/src/screens/auth/AddMamber.tsx +0 -428
- package/template/src/screens/auth/ForgotPasswordScreen.tsx +0 -176
- package/template/src/screens/auth/OTPVerifyScreen.tsx +0 -359
- package/template/src/screens/auth/RegisterScreen.tsx +0 -430
- package/template/src/screens/auth/SuccessScreen.tsx +0 -201
- package/template/src/screens/chat/ChatScreen.tsx +0 -1819
- package/template/src/screens/chat/ChatThreadsScreen.tsx +0 -360
- package/template/src/screens/chat/ReportMessageScreen.tsx +0 -238
- package/template/src/screens/clubs/Announcements.tsx +0 -426
- package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +0 -568
- package/template/src/screens/clubs/ClubDeteils.tsx +0 -497
- package/template/src/screens/clubs/JoinClub.tsx +0 -841
- package/template/src/screens/events/EventScreen.tsx +0 -460
- package/template/src/screens/raffles/MyReferralMembersScreen.tsx +0 -758
- package/template/src/screens/raffles/RaffleDetailsScreen.tsx +0 -762
- package/template/src/screens/raffles/RafflesScreen.tsx +0 -495
- package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +0 -390
- package/template/src/screens/teams/JoinTeamScreen.tsx +0 -464
- package/template/src/screens/teams/MyTeamDetailsScreen.tsx +0 -979
- package/template/src/screens/teams/MyTeamScreen.tsx +0 -568
- package/template/src/screens/teams/PendingRequestsScreen.tsx +0 -426
- package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +0 -631
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +0 -1049
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +0 -608
- package/template/src/utils/ClubSearchManager.ts +0 -222
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback, useRef } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
StatusBar,
|
|
7
|
-
TouchableOpacity,
|
|
8
|
-
FlatList,
|
|
9
|
-
Image,
|
|
10
|
-
Platform,
|
|
11
|
-
Alert,
|
|
12
|
-
} from 'react-native';
|
|
13
|
-
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
14
|
-
import { Button } from '../../components/common';
|
|
15
|
-
import { theme } from '../../constants';
|
|
16
|
-
import { Strings } from '../../constants/strings';
|
|
17
|
-
import { moderateScale } from '../../utils/scaling';
|
|
18
|
-
import { Fonts } from '../../constants/Fonts';
|
|
19
|
-
import SVG from '../../assets/icons';
|
|
20
|
-
import { AddMemberScreenProps } from '../../types/navigation';
|
|
21
|
-
import { useAuthStore } from '../../stores/authStore';
|
|
22
|
-
import { getApiErrorInfo, MembersRequest, useMembers, useGetImageUrl } from '../../services/authService';
|
|
23
|
-
import { useUpdateMember, UpdateMemberRequest } from '../../services/authService';
|
|
24
|
-
import ToastManager from '../../components/common/ToastManager';
|
|
25
|
-
import { SuccessModal } from './SuccessScreen';
|
|
26
|
-
import { AddMemberModal } from '../../components/common/index';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
interface Member {
|
|
30
|
-
id: string;
|
|
31
|
-
name: string;
|
|
32
|
-
profileImage: string;
|
|
33
|
-
isOwner: boolean;
|
|
34
|
-
memberImageUrl?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const AddMemberScreen: React.FC<AddMemberScreenProps> = ({ navigation }) => {
|
|
38
|
-
const updateMemberMutation = useUpdateMember();
|
|
39
|
-
const getImageUrlMutation = useGetImageUrl();
|
|
40
|
-
|
|
41
|
-
const [showModal, setShowModal] = useState(false);
|
|
42
|
-
const [addMemberSuccessClose, setAddMemberSuccessClose] = useState(false);
|
|
43
|
-
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
|
44
|
-
const [addMemberImageUrls, setAddMemberImageUrls] = useState<Record<number, string>>({});
|
|
45
|
-
const { setUser } = useAuthStore();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const membersParams: MembersRequest = {
|
|
50
|
-
PageNumber: 0,
|
|
51
|
-
PageSize: 20,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const handleUpdateMember = (memberId: number, memberData: UpdateMemberRequest) => {
|
|
55
|
-
updateMemberMutation.mutate({
|
|
56
|
-
memberId,
|
|
57
|
-
memberData: memberData
|
|
58
|
-
}, {
|
|
59
|
-
onSuccess: (response) => {
|
|
60
|
-
if(response?.status === 202){
|
|
61
|
-
Alert.alert("Add Member", response.data.message);
|
|
62
|
-
}else{
|
|
63
|
-
setAddMemberSuccessClose(true);
|
|
64
|
-
setTimeout(() => {
|
|
65
|
-
setShowModal(false);
|
|
66
|
-
ToastManager.success( response.data.message);
|
|
67
|
-
refetch()
|
|
68
|
-
setAddMemberSuccessClose(false);
|
|
69
|
-
}, 1000);
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
onError: (error) => {
|
|
73
|
-
const errorInfo = getApiErrorInfo(error);
|
|
74
|
-
ToastManager.error(errorInfo?.message);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const { data, isLoading, isError, refetch } = useMembers(membersParams);
|
|
80
|
-
const rawMembers = (data?.data?.data || []).filter((member: Member) => !member.isOwner);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Add memberImageUrl from state
|
|
84
|
-
const members = rawMembers.map((member: any) => ({
|
|
85
|
-
...member,
|
|
86
|
-
memberImageUrl: addMemberImageUrls[member.id] || undefined
|
|
87
|
-
}));
|
|
88
|
-
|
|
89
|
-
const ownerName = data?.data?.data?.find(member => member.isOwner === true)?.name;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const handleSkip = () => {
|
|
93
|
-
setShowSuccessModal(true);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const handleAddMember = () => {
|
|
97
|
-
setShowModal(!showModal);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const handleSaveMember = (memberData: any) => {
|
|
101
|
-
const newMember: UpdateMemberRequest = {
|
|
102
|
-
birthDate: memberData.dateOfBirth,
|
|
103
|
-
relation: memberData.relationshipType,
|
|
104
|
-
name: memberData.name,
|
|
105
|
-
profileImage: memberData.profileImage?.fileName || '',
|
|
106
|
-
};
|
|
107
|
-
handleUpdateMember(0, newMember)
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const handleNext = () => {
|
|
111
|
-
// Update user to mark that member addition is complete
|
|
112
|
-
const currentUser = useAuthStore.getState().user;
|
|
113
|
-
if (currentUser) {
|
|
114
|
-
setUser({
|
|
115
|
-
...currentUser,
|
|
116
|
-
isAddMember: false
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const getAddMemberImageUrls = async (memberItem: any) => {
|
|
122
|
-
if (memberItem?.profileImage == null) return;
|
|
123
|
-
|
|
124
|
-
// Skip if already fetched
|
|
125
|
-
if (addMemberImageUrls[memberItem.id] !== undefined) return;
|
|
126
|
-
|
|
127
|
-
const [folder, blobName] = memberItem?.profileImage.split('/');
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
getImageUrlMutation.mutate({
|
|
131
|
-
containerName: folder,
|
|
132
|
-
blobName: blobName
|
|
133
|
-
}, {
|
|
134
|
-
onSuccess: (response) => {
|
|
135
|
-
const imageUrl = response?.data?.data?.url;
|
|
136
|
-
console.log('AddMember Image URL response', response.data);
|
|
137
|
-
|
|
138
|
-
// Prefetch image into React Native cache to avoid blink when scrolling back
|
|
139
|
-
if (imageUrl) {
|
|
140
|
-
Image.prefetch(imageUrl).catch((prefetchError) => {
|
|
141
|
-
console.warn('AddMember Image prefetch failed:', prefetchError);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Simple state update instead of recreating entire query cache
|
|
146
|
-
setAddMemberImageUrls(prev => ({
|
|
147
|
-
...prev,
|
|
148
|
-
[memberItem.id]: imageUrl
|
|
149
|
-
}));
|
|
150
|
-
},
|
|
151
|
-
onError: (error) => {
|
|
152
|
-
const errorInfo = getApiErrorInfo(error);
|
|
153
|
-
ToastManager.error(errorInfo?.message);
|
|
154
|
-
console.error('Failed to get AddMember image URL:', error);
|
|
155
|
-
|
|
156
|
-
// Simple state update for failed image fetch
|
|
157
|
-
setAddMemberImageUrls(prev => ({
|
|
158
|
-
...prev,
|
|
159
|
-
[memberItem.id]: ""
|
|
160
|
-
}));
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.error('Error in getAddMemberImageUrls:', err);
|
|
165
|
-
setAddMemberImageUrls(prev => ({
|
|
166
|
-
...prev,
|
|
167
|
-
[memberItem.id]: ""
|
|
168
|
-
}));
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const onAddMemberViewableItemsChanged = useCallback(
|
|
173
|
-
({
|
|
174
|
-
viewableItems,
|
|
175
|
-
}: {
|
|
176
|
-
viewableItems: Array<{ item: any }>;
|
|
177
|
-
}) => {
|
|
178
|
-
const items = viewableItems.map(({ item }) => item);
|
|
179
|
-
|
|
180
|
-
// On-demand image fetching for viewable member items
|
|
181
|
-
items.forEach((item) => {
|
|
182
|
-
if (item && !item.memberImageUrl) {
|
|
183
|
-
console.log('AddMember onViewableItemsChanged>>>>>', JSON.stringify(item));
|
|
184
|
-
getAddMemberImageUrls(item);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
},
|
|
188
|
-
[getAddMemberImageUrls]
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const addMemberViewabilityConfig = useRef({
|
|
192
|
-
viewAreaCoveragePercentThreshold: 50, // Increased threshold for better on-demand loading
|
|
193
|
-
minimumViewTime: 100, // Minimum time item must be visible before triggering
|
|
194
|
-
waitForInteraction: false, // Don't wait for user interaction
|
|
195
|
-
}).current;
|
|
196
|
-
|
|
197
|
-
const renderMemberItem = ({ item }: { item: Member }) => {
|
|
198
|
-
return (
|
|
199
|
-
<View style={styles.memberItem}>
|
|
200
|
-
{item?.profileImage ? (
|
|
201
|
-
<Image source={{ uri: item?.profileImage }} style={styles.profileImage} />
|
|
202
|
-
) : (
|
|
203
|
-
<View style={[styles.profileImage, { backgroundColor: theme.colors.surface, justifyContent: 'center', alignItems: 'center' }]}>
|
|
204
|
-
<SVG.emptyUser width={moderateScale(20)} height={moderateScale(20)} />
|
|
205
|
-
</View>
|
|
206
|
-
)}
|
|
207
|
-
<Text style={styles.memberName}>{item.name}</Text>
|
|
208
|
-
</View>
|
|
209
|
-
);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const keyExtractor = (item: Member, index: number) => {
|
|
213
|
-
// Create a more robust key that combines multiple attributes to ensure uniqueness
|
|
214
|
-
const id = item?.id?.toString() || '';
|
|
215
|
-
const name = item?.name || '';
|
|
216
|
-
const email = item?.profileImage || '';
|
|
217
|
-
|
|
218
|
-
// Create a unique composite key using multiple attributes
|
|
219
|
-
const keyParts = [id, name, email].filter(part => part && part.toString().trim() !== '');
|
|
220
|
-
|
|
221
|
-
if (keyParts.length > 0) {
|
|
222
|
-
return `member-${keyParts.join('-')}-${index}`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Ultimate fallback with index to ensure uniqueness
|
|
226
|
-
return `member-unknown-${index}`;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
return (
|
|
230
|
-
<View style={styles.container}>
|
|
231
|
-
<StatusBar
|
|
232
|
-
barStyle="light-content"
|
|
233
|
-
backgroundColor={theme.colors.blue}
|
|
234
|
-
translucent={Platform.OS === 'android' ? true : false}
|
|
235
|
-
/>
|
|
236
|
-
|
|
237
|
-
{/* Status Bar Background for iOS */}
|
|
238
|
-
{Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
|
|
239
|
-
|
|
240
|
-
{/* Header Section */}
|
|
241
|
-
<SafeAreaView style={styles.header} edges={['top']}>
|
|
242
|
-
<TouchableOpacity onPress={handleSkip} style={styles.skipButton}>
|
|
243
|
-
<Text style={styles.skipButtonText}>{Strings.AUTH.SKIP}</Text>
|
|
244
|
-
</TouchableOpacity>
|
|
245
|
-
<Text style={styles.headerTitle}>{Strings.AUTH.ADD_MEMBER_TITLE}</Text>
|
|
246
|
-
</SafeAreaView>
|
|
247
|
-
|
|
248
|
-
{/* Add Member Button */}
|
|
249
|
-
<View style={styles.addMemberContainer}>
|
|
250
|
-
<TouchableOpacity style={styles.addMemberButton} onPress={handleAddMember}>
|
|
251
|
-
<SVG.icAdd width={moderateScale(20)} height={moderateScale(20)} />
|
|
252
|
-
<Text style={styles.addMemberButtonText}>{Strings.AUTH.ADD_MEMBER_SUBTITLE}</Text>
|
|
253
|
-
</TouchableOpacity>
|
|
254
|
-
</View>
|
|
255
|
-
|
|
256
|
-
{/* Content Section */}
|
|
257
|
-
<View style={styles.content}>
|
|
258
|
-
<Text style={styles.sectionTitle}>{Strings.AUTH.ADD_MEMBER_SECTION_TITLE}</Text>
|
|
259
|
-
|
|
260
|
-
<FlatList
|
|
261
|
-
data={members}
|
|
262
|
-
renderItem={renderMemberItem}
|
|
263
|
-
keyExtractor={keyExtractor}
|
|
264
|
-
style={styles.membersList}
|
|
265
|
-
showsVerticalScrollIndicator={false}
|
|
266
|
-
contentContainerStyle={styles.flatListContent}
|
|
267
|
-
removeClippedSubviews={true}
|
|
268
|
-
maxToRenderPerBatch={10}
|
|
269
|
-
windowSize={10}
|
|
270
|
-
initialNumToRender={10}
|
|
271
|
-
// onViewableItemsChanged={onAddMemberViewableItemsChanged}
|
|
272
|
-
// viewabilityConfig={addMemberViewabilityConfig}
|
|
273
|
-
getItemLayout={(data, index) => ({
|
|
274
|
-
length: moderateScale(70), // Approximate item height
|
|
275
|
-
offset: moderateScale(70) * index,
|
|
276
|
-
index,
|
|
277
|
-
})}
|
|
278
|
-
refreshing={isLoading}
|
|
279
|
-
onRefresh={refetch}
|
|
280
|
-
ListEmptyComponent={() => (
|
|
281
|
-
<Text style={styles.emptyListText}>No members found</Text>
|
|
282
|
-
)}
|
|
283
|
-
|
|
284
|
-
/>
|
|
285
|
-
</View>
|
|
286
|
-
|
|
287
|
-
<View style={styles.bottomContainer}>
|
|
288
|
-
<Button
|
|
289
|
-
disabled={members?.length <= 0 ? true : false}
|
|
290
|
-
title={Strings.AUTH.NEXT}
|
|
291
|
-
onPress={handleSkip}
|
|
292
|
-
variant="primary"
|
|
293
|
-
size="medium"
|
|
294
|
-
style={styles.nextButton}
|
|
295
|
-
/>
|
|
296
|
-
</View>
|
|
297
|
-
<AddMemberModal
|
|
298
|
-
visible={showModal}
|
|
299
|
-
onClose={() => setShowModal(false)}
|
|
300
|
-
onSave={handleSaveMember}
|
|
301
|
-
onSuccessClose={addMemberSuccessClose}
|
|
302
|
-
/>
|
|
303
|
-
<SuccessModal
|
|
304
|
-
visible={showSuccessModal}
|
|
305
|
-
userName={ownerName}
|
|
306
|
-
onClose={() => setShowSuccessModal(false)}
|
|
307
|
-
onGoToHome={() => handleNext()}
|
|
308
|
-
onSkipToAddMember={() => navigation.navigate('AddMember' as never)}
|
|
309
|
-
/>
|
|
310
|
-
</View>
|
|
311
|
-
);
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const styles = StyleSheet.create({
|
|
315
|
-
container: {
|
|
316
|
-
flex: 1,
|
|
317
|
-
backgroundColor: theme.colors.blue,
|
|
318
|
-
},
|
|
319
|
-
statusBarBackground: {
|
|
320
|
-
position: 'absolute',
|
|
321
|
-
top: 0,
|
|
322
|
-
left: 0,
|
|
323
|
-
right: 0,
|
|
324
|
-
height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
|
|
325
|
-
backgroundColor: theme.colors.blue,
|
|
326
|
-
zIndex: 1000,
|
|
327
|
-
},
|
|
328
|
-
header: {
|
|
329
|
-
backgroundColor: theme.colors.blue,
|
|
330
|
-
paddingHorizontal: theme.spacing.lg,
|
|
331
|
-
paddingVertical: theme.spacing.md,
|
|
332
|
-
justifyContent: 'space-between',
|
|
333
|
-
alignItems: 'center',
|
|
334
|
-
},
|
|
335
|
-
headerTitle: {
|
|
336
|
-
alignSelf: 'flex-start',
|
|
337
|
-
fontFamily: Fonts.outfitSemiBold,
|
|
338
|
-
fontSize: moderateScale(24),
|
|
339
|
-
color: theme.colors.white,
|
|
340
|
-
marginTop: moderateScale(20),
|
|
341
|
-
},
|
|
342
|
-
skipButton: {
|
|
343
|
-
alignSelf: 'flex-end',
|
|
344
|
-
paddingHorizontal: theme.spacing.sm,
|
|
345
|
-
paddingVertical: theme.spacing.xs,
|
|
346
|
-
},
|
|
347
|
-
skipButtonText: {
|
|
348
|
-
fontFamily: Fonts.outfitSemiBold,
|
|
349
|
-
fontSize: theme.typography.fontSize.md,
|
|
350
|
-
color: theme.colors.white,
|
|
351
|
-
},
|
|
352
|
-
addMemberContainer: {
|
|
353
|
-
backgroundColor: "theme.colors.blue",
|
|
354
|
-
paddingHorizontal: theme.spacing.lg,
|
|
355
|
-
paddingBottom: theme.spacing.md,
|
|
356
|
-
},
|
|
357
|
-
addMemberButton: {
|
|
358
|
-
flexDirection: 'row',
|
|
359
|
-
alignItems: 'center',
|
|
360
|
-
justifyContent: 'center',
|
|
361
|
-
backgroundColor: 'transparent',
|
|
362
|
-
borderWidth: 1.5,
|
|
363
|
-
borderColor: theme.colors.appleGreen,
|
|
364
|
-
borderRadius: theme.borderRadius.xxl,
|
|
365
|
-
paddingVertical: theme.spacing.sm,
|
|
366
|
-
paddingHorizontal: theme.spacing.lg,
|
|
367
|
-
gap: theme.spacing.sm,
|
|
368
|
-
},
|
|
369
|
-
addMemberButtonText: {
|
|
370
|
-
fontFamily: Fonts.outfitMedium,
|
|
371
|
-
fontSize: theme.typography.fontSize.md,
|
|
372
|
-
color: theme.colors.appleGreen,
|
|
373
|
-
},
|
|
374
|
-
content: {
|
|
375
|
-
flex: 1,
|
|
376
|
-
backgroundColor: theme.colors.background,
|
|
377
|
-
paddingHorizontal: theme.spacing.lg,
|
|
378
|
-
paddingTop: theme.spacing.md,
|
|
379
|
-
borderTopLeftRadius: moderateScale(30),
|
|
380
|
-
borderTopRightRadius: moderateScale(30),
|
|
381
|
-
},
|
|
382
|
-
sectionTitle: {
|
|
383
|
-
fontFamily: Fonts.outfitMedium,
|
|
384
|
-
fontSize: theme.typography.fontSize.xs,
|
|
385
|
-
color: theme.colors.blue,
|
|
386
|
-
marginBottom: theme.spacing.md,
|
|
387
|
-
letterSpacing: 0.5,
|
|
388
|
-
},
|
|
389
|
-
membersList: {
|
|
390
|
-
flex: 1,
|
|
391
|
-
},
|
|
392
|
-
flatListContent: {
|
|
393
|
-
paddingBottom: theme.spacing.sm,
|
|
394
|
-
},
|
|
395
|
-
memberItem: {
|
|
396
|
-
flexDirection: 'row',
|
|
397
|
-
alignItems: 'center',
|
|
398
|
-
paddingVertical: theme.spacing.sm,
|
|
399
|
-
gap: theme.spacing.md,
|
|
400
|
-
},
|
|
401
|
-
profileImage: {
|
|
402
|
-
width: moderateScale(50),
|
|
403
|
-
height: moderateScale(50),
|
|
404
|
-
borderRadius: moderateScale(25),
|
|
405
|
-
backgroundColor: theme.colors.surface,
|
|
406
|
-
},
|
|
407
|
-
memberName: {
|
|
408
|
-
fontFamily: Fonts.outfitBold,
|
|
409
|
-
fontSize: theme.typography.fontSize.md,
|
|
410
|
-
color: theme.colors.text,
|
|
411
|
-
},
|
|
412
|
-
bottomContainer: {
|
|
413
|
-
height: moderateScale(120),
|
|
414
|
-
paddingHorizontal: theme.spacing.lg,
|
|
415
|
-
paddingVertical: theme.spacing.md,
|
|
416
|
-
backgroundColor: theme.colors.white,
|
|
417
|
-
},
|
|
418
|
-
nextButton: {
|
|
419
|
-
width: '100%',
|
|
420
|
-
marginTop: moderateScale(-10),
|
|
421
|
-
},
|
|
422
|
-
emptyListText: {
|
|
423
|
-
fontFamily: Fonts.outfitMedium,
|
|
424
|
-
fontSize: theme.typography.fontSize.md,
|
|
425
|
-
color: theme.colors.text,
|
|
426
|
-
textAlign: 'center',
|
|
427
|
-
},
|
|
428
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
TouchableOpacity,
|
|
7
|
-
Keyboard,
|
|
8
|
-
Platform,
|
|
9
|
-
} from 'react-native';
|
|
10
|
-
import { Button, hideLoader, showLoader, TextInput } from '../../components/common';
|
|
11
|
-
import { theme } from '../../constants';
|
|
12
|
-
import { Strings } from '../../constants/strings';
|
|
13
|
-
import { Validation } from '../../utils';
|
|
14
|
-
import { ImageBackground } from 'react-native';
|
|
15
|
-
import Images from '../../assets/images';
|
|
16
|
-
import SVG from '../../assets/icons';
|
|
17
|
-
import { moderateScale } from '../../utils/scaling';
|
|
18
|
-
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
|
19
|
-
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
20
|
-
import { StatusBar } from 'react-native';
|
|
21
|
-
import { Fonts } from '../../constants/Fonts';
|
|
22
|
-
import ToastManager from '../../components/common/ToastManager';
|
|
23
|
-
import { useForgotPassword } from '../../services/authService';
|
|
24
|
-
|
|
25
|
-
interface ForgotPasswordScreenProps {
|
|
26
|
-
navigation: any;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const ForgotPasswordScreen: React.FC<ForgotPasswordScreenProps> = ({ navigation }) => {
|
|
30
|
-
const [email, setEmail] = useState('');
|
|
31
|
-
const forgotPasswordMutation = useForgotPassword();
|
|
32
|
-
|
|
33
|
-
const handleSubmit = async () => {
|
|
34
|
-
Keyboard.dismiss();
|
|
35
|
-
if (!Validation.validateForgotPasswordFormAndShowErrors(email)) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
showLoader();
|
|
40
|
-
forgotPasswordMutation.mutate({ email: email }
|
|
41
|
-
, {
|
|
42
|
-
onSuccess: (response) => {
|
|
43
|
-
navigation.reset({
|
|
44
|
-
index: 0,
|
|
45
|
-
routes: [{ name: 'Login' }],
|
|
46
|
-
});
|
|
47
|
-
ToastManager.success(response.data.message || 'Forgot password successful!');
|
|
48
|
-
hideLoader();
|
|
49
|
-
},
|
|
50
|
-
onError: (error: any) => {
|
|
51
|
-
console.error('Forgot password error:', error);
|
|
52
|
-
if (error.response?.data?.message) {
|
|
53
|
-
ToastManager.error( error.response.data.message);
|
|
54
|
-
} else if (error.message) {
|
|
55
|
-
ToastManager.error( error.message);
|
|
56
|
-
} else {
|
|
57
|
-
ToastManager.error('Something went wrong. Please try again.');
|
|
58
|
-
}
|
|
59
|
-
hideLoader();
|
|
60
|
-
},
|
|
61
|
-
onSettled: () => {
|
|
62
|
-
hideLoader();
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const onEmailChange = (text: string) => {
|
|
70
|
-
const trimmedText = text.trim();
|
|
71
|
-
setEmail(trimmedText);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<ImageBackground
|
|
77
|
-
source={Images.backgroundImage}
|
|
78
|
-
style={styles.backgroundImage}
|
|
79
|
-
resizeMode="contain"
|
|
80
|
-
>
|
|
81
|
-
<SafeAreaView edges={['top']} style={{ flex: 1 }}>
|
|
82
|
-
<StatusBar
|
|
83
|
-
barStyle="dark-content"
|
|
84
|
-
backgroundColor={theme.colors.background}
|
|
85
|
-
translucent={Platform.OS === 'android' ? true : false}
|
|
86
|
-
/>
|
|
87
|
-
<KeyboardAwareScrollView
|
|
88
|
-
enableOnAndroid={true}
|
|
89
|
-
contentContainerStyle={styles.container}
|
|
90
|
-
keyboardShouldPersistTaps="handled"
|
|
91
|
-
>
|
|
92
|
-
<TouchableOpacity style={{ marginLeft: theme.spacing.md }} onPress={() => navigation.goBack()}>
|
|
93
|
-
<SVG.arrowLeft height={moderateScale(30)} width={moderateScale(30)} style={styles.logoIcon} />
|
|
94
|
-
</TouchableOpacity>
|
|
95
|
-
<View style={styles.logoSection}>
|
|
96
|
-
<SVG.login_logo height={moderateScale(150)} width={moderateScale(150)} style={styles.logoIcon} />
|
|
97
|
-
</View>
|
|
98
|
-
|
|
99
|
-
{/* Login Form */}
|
|
100
|
-
<View style={styles.loginSection}>
|
|
101
|
-
<Text style={styles.loginTitle}>{Strings.AUTH.FORGOT_PASSWORD_TITLE}</Text>
|
|
102
|
-
<Text style={styles.loginSubTitle}>{Strings.AUTH.FORGOT_PASSWORD_SUBTITLE}</Text>
|
|
103
|
-
|
|
104
|
-
<View style={styles.form}>
|
|
105
|
-
<TextInput
|
|
106
|
-
label={Strings.COMMON.EMAIL}
|
|
107
|
-
value={email}
|
|
108
|
-
onChangeText={onEmailChange}
|
|
109
|
-
placeholder={Strings.COMMON.EMAIL_PLACEHOLDER}
|
|
110
|
-
keyboardType="email-address"
|
|
111
|
-
autoCapitalize="none"
|
|
112
|
-
autoCorrect={false}
|
|
113
|
-
variant="outlined"
|
|
114
|
-
leftIcon={SVG.Email}
|
|
115
|
-
maxLength={50}
|
|
116
|
-
/>
|
|
117
|
-
<Button
|
|
118
|
-
title={Strings.COMMON.SUBMIT}
|
|
119
|
-
onPress={handleSubmit}
|
|
120
|
-
variant="primary"
|
|
121
|
-
size="medium"
|
|
122
|
-
style={styles.button}
|
|
123
|
-
/>
|
|
124
|
-
</View>
|
|
125
|
-
</View>
|
|
126
|
-
</KeyboardAwareScrollView>
|
|
127
|
-
</SafeAreaView>
|
|
128
|
-
</ImageBackground>
|
|
129
|
-
);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const styles = StyleSheet.create({
|
|
133
|
-
backgroundImage: {
|
|
134
|
-
flex: 1,
|
|
135
|
-
width: '100%',
|
|
136
|
-
height: '100%',
|
|
137
|
-
},
|
|
138
|
-
container: {
|
|
139
|
-
flexGrow: 1,
|
|
140
|
-
},
|
|
141
|
-
// Logo Section
|
|
142
|
-
logoSection: {
|
|
143
|
-
justifyContent: 'center',
|
|
144
|
-
alignItems: 'center',
|
|
145
|
-
},
|
|
146
|
-
logoIcon: {
|
|
147
|
-
marginBottom: theme.spacing.xs,
|
|
148
|
-
},
|
|
149
|
-
// Login Section
|
|
150
|
-
loginSection: {
|
|
151
|
-
flex: 1,
|
|
152
|
-
backgroundColor: theme.colors.background,
|
|
153
|
-
paddingHorizontal: theme.spacing.lg,
|
|
154
|
-
borderTopLeftRadius: moderateScale(30),
|
|
155
|
-
borderTopRightRadius: moderateScale(30),
|
|
156
|
-
paddingTop: moderateScale(20),
|
|
157
|
-
},
|
|
158
|
-
loginTitle: {
|
|
159
|
-
fontFamily: Fonts.outfitSemiBold,
|
|
160
|
-
fontSize: moderateScale(24),
|
|
161
|
-
color: theme.colors.black,
|
|
162
|
-
marginBottom: theme.spacing.sm,
|
|
163
|
-
},
|
|
164
|
-
loginSubTitle: {
|
|
165
|
-
fontFamily: Fonts.outfitRegular,
|
|
166
|
-
fontSize: moderateScale(13),
|
|
167
|
-
color: theme.colors.black,
|
|
168
|
-
marginBottom: theme.spacing.md,
|
|
169
|
-
},
|
|
170
|
-
form: {
|
|
171
|
-
gap: theme.spacing.sm,
|
|
172
|
-
},
|
|
173
|
-
button: {
|
|
174
|
-
marginTop: theme.spacing.md,
|
|
175
|
-
}
|
|
176
|
-
});
|