create-gufran-expo-app 2.0.4 → 2.0.6

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.
Files changed (43) hide show
  1. package/README.md +60 -321
  2. package/bin/cli.js +0 -1
  3. package/package.json +4 -3
  4. package/template/src/navigation/AuthStack.tsx +6 -25
  5. package/template/src/navigation/MainStack.tsx +0 -148
  6. package/template/src/navigation/RootNavigator.tsx +4 -26
  7. package/template/src/navigation/index.ts +0 -1
  8. package/template/src/navigation/navigationRef.ts +1 -1
  9. package/template/src/screens/HomeScreen.tsx +3 -215
  10. package/template/src/screens/auth/LoginScreen.tsx +13 -13
  11. package/template/src/screens/auth/index.ts +1 -6
  12. package/template/src/screens/index.ts +0 -35
  13. package/template/src/services/api.ts +5 -5
  14. package/template/src/services/authService.ts +3 -299
  15. package/template/src/services/mainServices.ts +19 -1914
  16. package/template/src/types/navigation.ts +5 -155
  17. package/template/src/utils/index.ts +5 -8
  18. package/template/src/navigation/MiddleStack.tsx +0 -35
  19. package/template/src/screens/auth/AddMamber.tsx +0 -428
  20. package/template/src/screens/auth/ForgotPasswordScreen.tsx +0 -176
  21. package/template/src/screens/auth/OTPVerifyScreen.tsx +0 -359
  22. package/template/src/screens/auth/RegisterScreen.tsx +0 -430
  23. package/template/src/screens/auth/SuccessScreen.tsx +0 -201
  24. package/template/src/screens/chat/ChatScreen.tsx +0 -1819
  25. package/template/src/screens/chat/ChatThreadsScreen.tsx +0 -360
  26. package/template/src/screens/chat/ReportMessageScreen.tsx +0 -238
  27. package/template/src/screens/clubs/Announcements.tsx +0 -426
  28. package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +0 -568
  29. package/template/src/screens/clubs/ClubDeteils.tsx +0 -497
  30. package/template/src/screens/clubs/JoinClub.tsx +0 -841
  31. package/template/src/screens/events/EventScreen.tsx +0 -460
  32. package/template/src/screens/raffles/MyReferralMembersScreen.tsx +0 -758
  33. package/template/src/screens/raffles/RaffleDetailsScreen.tsx +0 -762
  34. package/template/src/screens/raffles/RafflesScreen.tsx +0 -495
  35. package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +0 -390
  36. package/template/src/screens/teams/JoinTeamScreen.tsx +0 -464
  37. package/template/src/screens/teams/MyTeamDetailsScreen.tsx +0 -979
  38. package/template/src/screens/teams/MyTeamScreen.tsx +0 -568
  39. package/template/src/screens/teams/PendingRequestsScreen.tsx +0 -426
  40. package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +0 -631
  41. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +0 -1049
  42. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +0 -608
  43. package/template/src/utils/ClubSearchManager.ts +0 -222
@@ -1,568 +0,0 @@
1
- import React, { useState, useEffect, useCallback, useRef } from 'react';
2
- import { View, Text, StyleSheet, FlatList, StatusBar, Platform, TouchableOpacity, Image, RefreshControl, ActivityIndicator, Keyboard } from 'react-native';
3
- import { MyTeamScreen as MyTeamScreenProps } from '../../types/navigation';
4
- import { Strings, theme } from '../../constants';
5
- import { moderateScale } from '../../utils/scaling';
6
- import { Fonts } from '../../constants/Fonts';
7
- import SVG from '../../assets/icons';
8
- import { SafeAreaView } from 'react-native-safe-area-context';
9
- import { useInfiniteMyTeams } from '../../services/mainServices';
10
- import { useQueryClient } from '@tanstack/react-query';
11
- import { TeamCard } from '../../components/common';
12
- import ToastManager from '../../components/common/ToastManager';
13
- import { getApiErrorInfo, useGetImageUrl } from '../../services';
14
- import { useFocusEffect } from '@react-navigation/native';
15
- const PAGE_SIZE = 10;
16
-
17
- export const MyTeamScreen: React.FC<MyTeamScreenProps> = ({ navigation, route }) => {
18
- const { selectedClub, selectedMember: initialSelectedMember } = route?.params ?? {}
19
- const [isRefreshing, setIsRefreshing] = useState(false);
20
- const [searchQuery, setSearchQuery] = useState('');
21
- const [searchTimeout, setSearchTimeout] = useState<NodeJS.Timeout | null>(null);
22
- const [joinClubImageUrls, setJoinClubImageUrls] = useState<Record<number, string>>({});
23
- const queryClient = useQueryClient();
24
- const getImageUrlMutation = useGetImageUrl();
25
-
26
-
27
-
28
- const createNavigationData = () => {
29
- const navigationData: any = {
30
- selectedClub: selectedClub,
31
- selectedMember: initialSelectedMember
32
- };
33
- console.log('Navigation data being passed:', navigationData);
34
- return navigationData;
35
- };
36
-
37
-
38
-
39
- const keyExtractor = (item: any, index: number) => {
40
- // Create a more robust key that combines multiple attributes to ensure uniqueness
41
- const id = item?.id?.toString() || '';
42
- const name = item?.name || item?.clubName || '';
43
- const clubCode = item?.clubCode || item?.uniqueCode || '';
44
- const timestamp = item?.createdAt || item?.timestamp || '';
45
-
46
- // Create a unique composite key using multiple attributes
47
- const keyParts = [id, name, clubCode, timestamp].filter(part => part && part.toString().trim() !== '');
48
-
49
- if (keyParts.length > 0) {
50
- return `club-${keyParts.join('-')}-${index}`;
51
- }
52
-
53
- // Ultimate fallback with index to ensure uniqueness
54
- return `club-unknown-${index}`;
55
- };
56
-
57
-
58
- const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, refetch, error } = useInfiniteMyTeams(selectedClub?.selectedMember?.id, PAGE_SIZE);
59
- console.log("MyTeamScreen data", JSON.stringify(data));
60
-
61
- // const pandingRequests = data?.pages
62
- const pendingRequestsCount = data?.pages?.reduce((sum: number, p: any) => sum + (p?.data?.data?.pendingRequest ?? 0), 0) ?? 0;
63
-
64
-
65
- // Flatten all pages into a single array and add clubImageUrl from state
66
- const allClubs = data?.pages?.flatMap(page =>
67
- (page?.data?.data?.members || []).map((club: any) => ({
68
- ...club,
69
- clubImageUrl: joinClubImageUrls[club.id] || undefined
70
- }))
71
- ) || [];
72
-
73
-
74
- useFocusEffect(useCallback(() => {
75
- refetch();
76
- }, [refetch]));
77
-
78
-
79
-
80
- // Pull to refresh
81
- const onRefresh = useCallback(async () => {
82
- try {
83
- setIsRefreshing(true);
84
- // Clear all cached data for this query to start fresh from page 0
85
- queryClient.removeQueries({
86
- queryKey: ['infiniteClubSearch', searchQuery, PAGE_SIZE]
87
- });
88
- // This will trigger a fresh fetch starting from page 0
89
- await refetch();
90
- } catch (error) {
91
- console.error('Refresh failed:', error);
92
- } finally {
93
- setIsRefreshing(false);
94
- }
95
- }, [refetch, queryClient, searchQuery]);
96
-
97
- // Cleanup timeout on unmount
98
- useEffect(() => {
99
- return () => {
100
- if (searchTimeout) {
101
- clearTimeout(searchTimeout);
102
- }
103
- };
104
- }, [searchTimeout]);
105
-
106
- // Load more data
107
- const onEndReached = useCallback(() => {
108
- if (isFetchingNextPage || !hasNextPage) return;
109
- fetchNextPage();
110
- }, [isFetchingNextPage, hasNextPage, fetchNextPage, allClubs.length]);
111
-
112
- // Render loading footer
113
- const renderFooter = () => {
114
- if (!isFetchingNextPage) return null;
115
- return (
116
- <View style={styles.footerLoader}>
117
- <ActivityIndicator size="small" color={theme.colors.primary} />
118
- <Text style={styles.footerText}>Loading more teams...</Text>
119
- </View>
120
- );
121
- };
122
-
123
-
124
-
125
- const renderEmptyComponent = () => {
126
- if (isLoading) return null;
127
- return (
128
- <View style={styles.emptyContainer}>
129
- <Text style={styles.emptyListText}>No Teams found</Text>
130
- <Text style={styles.emptySubText}>Try refreshing or check back later</Text>
131
- </View>
132
- );
133
- };
134
-
135
- const handleScroll = useCallback(() => {
136
- Keyboard.dismiss();
137
- }, []);
138
-
139
-
140
-
141
- const getJoinClubImageUrls = async (clubItem: any) => {
142
- if (clubItem?.clubImage == null) return;
143
-
144
- // Skip if already fetched
145
- if (joinClubImageUrls[clubItem.id] !== undefined) return;
146
-
147
- const [folder, blobName] = clubItem?.clubImage.split('/');
148
-
149
- try {
150
- getImageUrlMutation.mutate({
151
- containerName: folder,
152
- blobName: blobName
153
- }, {
154
- onSuccess: (response) => {
155
- const imageUrl = response?.data?.data?.url;
156
- console.log('JoinClub Image URL response', response.data);
157
-
158
- // Prefetch image into React Native cache to avoid blink when scrolling back
159
- if (imageUrl) {
160
- Image.prefetch(imageUrl).catch((prefetchError) => {
161
- console.warn('JoinClub Image prefetch failed:', prefetchError);
162
- });
163
- }
164
-
165
- // Simple state update instead of recreating entire query cache
166
- setJoinClubImageUrls(prev => ({
167
- ...prev,
168
- [clubItem.id]: imageUrl
169
- }));
170
- },
171
- onError: (error) => {
172
- const errorInfo = getApiErrorInfo(error);
173
- ToastManager.error(errorInfo.message);
174
- setJoinClubImageUrls(prev => ({
175
- ...prev,
176
- [clubItem.id]: ""
177
- }));
178
- }
179
- });
180
- } catch (err) {
181
- setJoinClubImageUrls(prev => ({
182
- ...prev,
183
- [clubItem.id]: ""
184
- }));
185
- }
186
- };
187
-
188
- const onJoinClubViewableItemsChanged = useCallback(
189
- ({
190
- viewableItems,
191
- }: {
192
- viewableItems: Array<{ item: any }>;
193
- }) => {
194
- const items = viewableItems.map(({ item }) => item);
195
- items.forEach((item) => {
196
- if (item && !item.clubImageUrl) {
197
- console.log('JoinClub onViewableItemsChanged>>>>>', JSON.stringify(item));
198
- getJoinClubImageUrls(item);
199
- }
200
- });
201
- },
202
- [getJoinClubImageUrls]
203
- );
204
-
205
- const joinClubViewabilityConfig = useRef({
206
- viewAreaCoveragePercentThreshold: 50, // Increased threshold for better on-demand loading
207
- minimumViewTime: 100, // Minimum time item must be visible before triggering
208
- waitForInteraction: false, // Don't wait for user interaction
209
- }).current;
210
-
211
-
212
- const renderClubItem = ({ item }: any) => {
213
- return (
214
- <TeamCard
215
- team={item}
216
- onChat={true}
217
- onChatPress={() => {
218
-
219
- navigation.navigate('ChatThreads', {
220
- team: item,
221
- selectedMember: initialSelectedMember,
222
- selectedClub: selectedClub
223
- });
224
- }}
225
-
226
- onPress={() => {
227
- navigation.navigate('MyTeamDetails', {
228
- club: item,
229
- selectedMember: initialSelectedMember,
230
- selectedClub: selectedClub
231
- });
232
- }}
233
- />
234
- );
235
- };
236
-
237
- return (
238
- <View style={styles.container}>
239
- <StatusBar
240
- barStyle="light-content"
241
- backgroundColor={theme.colors.blue}
242
- translucent={Platform.OS === 'android' ? true : false}
243
- />
244
- {Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
245
- <SafeAreaView style={styles.header} >
246
- <View style={{ flexDirection: 'row', paddingHorizontal: theme.spacing.lg }}>
247
- <TouchableOpacity onPress={() => navigation.goBack()}>
248
- <SVG.arrowLeft_white width={moderateScale(25)} height={moderateScale(25)} />
249
- </TouchableOpacity>
250
- <View style={{ flexDirection: 'row' }}>
251
- <View style={styles.userConSty}>
252
- {!!selectedClub?.clubImage ? (<Image
253
- // onLoadEnd={() => setIsLoading(false)}
254
- // onLoad={() => setIsLoading(true)}
255
- // onLoadStart={() => setIsLoading(true)}
256
- source={{ uri: selectedClub?.clubImage }} style={styles.userDetailsSty}
257
- resizeMode='cover'
258
- />
259
- ) : (
260
- <View style={styles.placeholderLogoHeader}>
261
- <SVG.UsersIcon width={moderateScale(20)} height={moderateScale(20)} />
262
- </View>
263
- )}
264
- </View>
265
- <Text style={styles.userNameSty}>{selectedClub?.clubName || 'Unknown Club'}</Text>
266
- </View>
267
- </View>
268
- <Text style={styles.headerTitle}>{Strings.Teams.TEAMS}</Text>
269
-
270
-
271
- <View style={styles.addMemberContainer}>
272
- <TouchableOpacity style={styles.addMemberButton} onPress={() => navigation.navigate('JoinTeam', createNavigationData())}>
273
- <SVG.icAdd width={moderateScale(20)} height={moderateScale(20)} />
274
- <Text style={styles.addMemberButtonText}>{Strings.Teams.JOIN_TEAM_SUBTITLE}</Text>
275
- </TouchableOpacity>
276
-
277
- {pendingRequestsCount > 0 && (
278
- <TouchableOpacity
279
- style={{ alignItems: 'center', marginTop: moderateScale(10) }}
280
- onPress={() => navigation.navigate('PendingRequests',createNavigationData())}
281
- >
282
- <Text style={[styles.addMemberButtonText, { textDecorationLine: 'underline' }]}>{`${pendingRequestsCount} ${Strings.Teams.PENDING_REQUEST}`}</Text>
283
- </TouchableOpacity>
284
- )}
285
- </View>
286
-
287
- <View style={styles.content}>
288
- <Text style={styles.sectionTitle}>{Strings.Teams.MY_TEAMS_SECTION_TITLE}</Text>
289
- <FlatList
290
- data={allClubs}
291
- renderItem={renderClubItem}
292
- keyExtractor={keyExtractor}
293
- style={styles.clubsList}
294
- showsVerticalScrollIndicator={false}
295
- contentContainerStyle={styles.flatListContent}
296
- removeClippedSubviews={false}
297
- initialNumToRender={10}
298
- onEndReached={onEndReached}
299
- onEndReachedThreshold={0.9}
300
- ListFooterComponent={renderFooter}
301
- ListEmptyComponent={renderEmptyComponent}
302
- onViewableItemsChanged={onJoinClubViewableItemsChanged}
303
- viewabilityConfig={joinClubViewabilityConfig}
304
- onScroll={handleScroll}
305
- scrollEventThrottle={16}
306
- refreshControl={
307
- <RefreshControl
308
- refreshing={isRefreshing}
309
- onRefresh={onRefresh}
310
- colors={[theme.colors.primary]}
311
- tintColor={theme.colors.primary}
312
- />
313
- }
314
- />
315
- </View>
316
- </SafeAreaView>
317
- </View>
318
- );
319
- };
320
-
321
- const styles = StyleSheet.create({
322
- container: {
323
- flex: 1,
324
- backgroundColor: theme.colors.blue,
325
- },
326
- statusBarBackground: {
327
- position: 'absolute',
328
- top: 0,
329
- left: 0,
330
- right: 0,
331
- height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
332
- backgroundColor: theme.colors.blue,
333
- zIndex: 1000,
334
- },
335
- header: {
336
- flex: 1,
337
- backgroundColor: theme.colors.blue,
338
- paddingTop: theme.spacing.xs,
339
- },
340
- headerTitle: {
341
- marginTop: moderateScale(10),
342
- fontFamily: Fonts.outfitMedium,
343
- fontSize: moderateScale(22),
344
- color: theme.colors.white,
345
- marginLeft: theme.spacing.lg
346
- },
347
- skipButton: {
348
- alignSelf: 'flex-end',
349
- paddingHorizontal: theme.spacing.sm,
350
- paddingVertical: theme.spacing.xs,
351
- },
352
- skipButtonText: {
353
- fontFamily: Fonts.outfitSemiBold,
354
- fontSize: theme.typography.fontSize.md,
355
- color: theme.colors.white,
356
- },
357
- addMemberContainer: {
358
- marginTop: moderateScale(10),
359
- backgroundColor: theme.colors.blue,
360
- paddingHorizontal: theme.spacing.lg,
361
- paddingBottom: theme.spacing.md,
362
- },
363
- addMemberButton: {
364
- flexDirection: 'row',
365
- alignItems: 'center',
366
- justifyContent: 'center',
367
- backgroundColor: 'transparent',
368
- borderWidth: 1.5,
369
- borderColor: theme.colors.appleGreen,
370
- borderRadius: theme.borderRadius.xxl,
371
- paddingVertical: theme.spacing.sm,
372
- paddingHorizontal: theme.spacing.lg,
373
- gap: theme.spacing.sm,
374
- },
375
- addMemberButtonText: {
376
- fontFamily: Fonts.outfitMedium,
377
- fontSize: theme.typography.fontSize.md,
378
- color: theme.colors.appleGreen,
379
- },
380
- content: {
381
- flex: 1,
382
- backgroundColor: theme.colors.background,
383
- paddingHorizontal: theme.spacing.lg,
384
- paddingTop: theme.spacing.md,
385
- borderTopLeftRadius: moderateScale(30),
386
- borderTopRightRadius: moderateScale(30),
387
- },
388
- sectionTitle: {
389
- fontFamily: Fonts.outfitMedium,
390
- fontSize: theme.typography.fontSize.xs,
391
- color: theme.colors.blue,
392
- marginBottom: theme.spacing.md,
393
- letterSpacing: 0.5,
394
- },
395
- clubsList: {
396
- flex: 1,
397
- },
398
- flatListContent: {
399
- paddingBottom: theme.spacing.xl,
400
- },
401
- emptyContainer: {
402
- flex: 1,
403
- justifyContent: 'center',
404
- alignItems: 'center',
405
- paddingVertical: theme.spacing.xl,
406
- },
407
- bottomContainer: {
408
- height: moderateScale(120),
409
- paddingHorizontal: theme.spacing.lg,
410
- paddingVertical: theme.spacing.md,
411
- backgroundColor: theme.colors.white,
412
- },
413
- nextButton: {
414
- width: '100%',
415
- marginBottom: theme.spacing.md,
416
- },
417
- emptyListText: {
418
- fontFamily: Fonts.outfitMedium,
419
- fontSize: theme.typography.fontSize.md,
420
- color: theme.colors.text,
421
- textAlign: 'center',
422
- marginBottom: theme.spacing.xs,
423
- },
424
- emptySubText: {
425
- fontFamily: Fonts.outfitRegular,
426
- fontSize: theme.typography.fontSize.sm,
427
- color: theme.colors.textSecondary,
428
- textAlign: 'center',
429
- },
430
- footerLoader: {
431
- flexDirection: 'row',
432
- justifyContent: 'center',
433
- alignItems: 'center',
434
- paddingVertical: theme.spacing.md,
435
- gap: theme.spacing.sm,
436
- },
437
- footerText: {
438
- fontFamily: Fonts.outfitMedium,
439
- fontSize: theme.typography.fontSize.sm,
440
- color: theme.colors.textSecondary,
441
- },
442
- errorText: {
443
- fontFamily: Fonts.outfitMedium,
444
- fontSize: theme.typography.fontSize.md,
445
- color: theme.colors.error,
446
- textAlign: 'center',
447
- marginBottom: theme.spacing.md,
448
- },
449
- retryButton: {
450
- backgroundColor: theme.colors.primary,
451
- paddingHorizontal: theme.spacing.lg,
452
- paddingVertical: theme.spacing.sm,
453
- borderRadius: theme.borderRadius.md,
454
- },
455
- retryButtonText: {
456
- fontFamily: Fonts.outfitMedium,
457
- fontSize: theme.typography.fontSize.md,
458
- color: theme.colors.white,
459
- textAlign: 'center',
460
- },
461
- modalOverlay: {
462
- flex: 1,
463
- justifyContent: 'flex-end',
464
- },
465
- modalBackdrop: {
466
- position: 'absolute',
467
- top: 0,
468
- left: 0,
469
- right: 0,
470
- bottom: 0,
471
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
472
- },
473
- modalContainer: {
474
- backgroundColor: theme.colors.white,
475
- borderTopLeftRadius: moderateScale(20),
476
- borderTopRightRadius: moderateScale(20),
477
- paddingHorizontal: theme.spacing.lg,
478
- paddingTop: theme.spacing.sm,
479
- paddingBottom: theme.spacing.xl,
480
- height: '45%',
481
- maxHeight: '45%',
482
- },
483
- dragHandle: {
484
- width: moderateScale(40),
485
- height: moderateScale(4),
486
- backgroundColor: theme.colors.border,
487
- borderRadius: moderateScale(2),
488
- alignSelf: 'center',
489
- marginBottom: theme.spacing.sm,
490
- },
491
- membersList: {
492
- flex: 1,
493
- },
494
- memberListItem: {
495
- height: moderateScale(70),
496
- alignItems: 'center',
497
- paddingVertical: theme.spacing.md,
498
- flexDirection: 'row',
499
- },
500
- headerContainer: {
501
- paddingVertical: theme.spacing.xs,
502
- },
503
- mamberHeaderTitle: {
504
- fontFamily: Fonts.outfitSemiBold,
505
- fontSize: moderateScale(14),
506
- color: theme.colors.text,
507
- },
508
- profileImage: {
509
- width: moderateScale(50),
510
- height: moderateScale(50),
511
- borderRadius: moderateScale(25),
512
- marginRight: moderateScale(10),
513
- },
514
- memberName: {
515
- fontFamily: Fonts.outfitSemiBold,
516
- fontSize: moderateScale(14),
517
- color: theme.colors.text,
518
- },
519
- memberNameContainer: {
520
- flex: 1,
521
- justifyContent: 'center',
522
- },
523
- memberNameSubtitle: {
524
- fontFamily: Fonts.outfitMedium,
525
- fontSize: moderateScale(12),
526
- color: theme.colors.border,
527
- },
528
- searchInput: {
529
- },
530
- searchContainer: {
531
- marginBottom: theme.spacing.md,
532
- },
533
- placeholderStyle: {
534
- fontFamily: Fonts.outfitRegular,
535
- fontSize: moderateScale(5),
536
- color: theme.colors.textSecondary,
537
- },
538
- userConSty: {
539
- marginHorizontal: moderateScale(10),
540
- width: moderateScale(30),
541
- height: moderateScale(30),
542
- borderRadius: moderateScale(15),
543
- borderWidth: 1.5,
544
- borderColor: theme.colors.imageBorder,
545
- backgroundColor: theme.colors.background,
546
- alignItems: 'center',
547
- justifyContent: 'center'
548
- },
549
- userNameSty: {
550
- marginTop: moderateScale(5),
551
- color: theme.colors.white,
552
- fontFamily: Fonts.outfitMedium,
553
- fontSize: moderateScale(15)
554
- },
555
- userDetailsSty: {
556
- width: moderateScale(36),
557
- height: moderateScale(36),
558
- borderRadius: moderateScale(18),
559
-
560
- },
561
- placeholderLogoHeader: {
562
- width: moderateScale(20),
563
- height: moderateScale(20),
564
- borderRadius: moderateScale(10),
565
- alignItems: 'center',
566
- justifyContent: 'center'
567
- },
568
- });