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.
Files changed (42) hide show
  1. package/README.md +1 -2
  2. package/package.json +3 -3
  3. package/template/src/navigation/AuthStack.tsx +6 -25
  4. package/template/src/navigation/MainStack.tsx +0 -148
  5. package/template/src/navigation/RootNavigator.tsx +4 -26
  6. package/template/src/navigation/index.ts +0 -1
  7. package/template/src/navigation/navigationRef.ts +1 -1
  8. package/template/src/screens/HomeScreen.tsx +3 -215
  9. package/template/src/screens/auth/LoginScreen.tsx +13 -13
  10. package/template/src/screens/auth/index.ts +1 -6
  11. package/template/src/screens/index.ts +0 -35
  12. package/template/src/services/api.ts +5 -5
  13. package/template/src/services/authService.ts +3 -299
  14. package/template/src/services/mainServices.ts +19 -1914
  15. package/template/src/types/navigation.ts +5 -155
  16. package/template/src/utils/index.ts +5 -8
  17. package/template/src/navigation/MiddleStack.tsx +0 -35
  18. package/template/src/screens/auth/AddMamber.tsx +0 -428
  19. package/template/src/screens/auth/ForgotPasswordScreen.tsx +0 -176
  20. package/template/src/screens/auth/OTPVerifyScreen.tsx +0 -359
  21. package/template/src/screens/auth/RegisterScreen.tsx +0 -430
  22. package/template/src/screens/auth/SuccessScreen.tsx +0 -201
  23. package/template/src/screens/chat/ChatScreen.tsx +0 -1819
  24. package/template/src/screens/chat/ChatThreadsScreen.tsx +0 -360
  25. package/template/src/screens/chat/ReportMessageScreen.tsx +0 -238
  26. package/template/src/screens/clubs/Announcements.tsx +0 -426
  27. package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +0 -568
  28. package/template/src/screens/clubs/ClubDeteils.tsx +0 -497
  29. package/template/src/screens/clubs/JoinClub.tsx +0 -841
  30. package/template/src/screens/events/EventScreen.tsx +0 -460
  31. package/template/src/screens/raffles/MyReferralMembersScreen.tsx +0 -758
  32. package/template/src/screens/raffles/RaffleDetailsScreen.tsx +0 -762
  33. package/template/src/screens/raffles/RafflesScreen.tsx +0 -495
  34. package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +0 -390
  35. package/template/src/screens/teams/JoinTeamScreen.tsx +0 -464
  36. package/template/src/screens/teams/MyTeamDetailsScreen.tsx +0 -979
  37. package/template/src/screens/teams/MyTeamScreen.tsx +0 -568
  38. package/template/src/screens/teams/PendingRequestsScreen.tsx +0 -426
  39. package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +0 -631
  40. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +0 -1049
  41. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +0 -608
  42. package/template/src/utils/ClubSearchManager.ts +0 -222
@@ -1,464 +0,0 @@
1
- import React, { useState, useEffect, useCallback, useRef } from 'react';
2
- import { View, Text, StyleSheet, FlatList, StatusBar, Platform, TouchableOpacity, RefreshControl, ActivityIndicator, Keyboard } from 'react-native';
3
- import { Strings, theme } from '../../constants';
4
- import { moderateScale } from '../../utils/scaling';
5
- import { Fonts } from '../../constants/Fonts';
6
- import SVG from '../../assets/icons';
7
- import { SafeAreaView } from 'react-native-safe-area-context';
8
- import { useInfiniteTeamSearch, useJoinTeam } from '../../services/mainServices';
9
- import { useQueryClient } from '@tanstack/react-query';
10
- import { TeamCard, TextInput } from '../../components/common';
11
- import ToastManager from '../../components/common/ToastManager';
12
- import { getApiErrorInfo } from '../../services';
13
- import { JoinTeamProps } from '../../types/navigation';
14
- import { useFocusEffect } from '@react-navigation/native';
15
- const PAGE_SIZE = 10;
16
-
17
- export const JoinTeam: React.FC<JoinTeamProps> = ({ navigation, route }) => {
18
- const { selectedClub, selectedMember: initialSelectedMember } = route?.params ?? {}
19
- const clubId = selectedClub?.id
20
- // Pagination state
21
- const [isRefreshing, setIsRefreshing] = useState(false);
22
- const [searchQuery, setSearchQuery] = useState('');
23
- const [searchTimeout, setSearchTimeout] = useState<NodeJS.Timeout | null>(null);
24
- const [joinClubImageUrls, setJoinClubImageUrls] = useState<Record<number, string>>({});
25
- const queryClient = useQueryClient();
26
- const joinTeamMutation = useJoinTeam();
27
- // const getImageUrlMutation = useGetImageUrl();
28
-
29
-
30
-
31
-
32
- const keyExtractor = (item: any, index: number) => {
33
- // Create a more robust key that combines multiple attributes to ensure uniqueness
34
- const id = item?.id?.toString() || '';
35
- const name = item?.name || item?.clubName || '';
36
- const clubCode = item?.clubCode || item?.uniqueCode || '';
37
- const timestamp = item?.createdAt || item?.timestamp || '';
38
-
39
- // Create a unique composite key using multiple attributes
40
- const keyParts = [id, name, clubCode, timestamp].filter(part => part && part.toString().trim() !== '');
41
-
42
- if (keyParts.length > 0) {
43
- return `club-${keyParts.join('-')}-${index}`;
44
- }
45
-
46
- // Ultimate fallback with index to ensure uniqueness
47
- return `club-unknown-${index}`;
48
- };
49
-
50
-
51
- const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, refetch } = useInfiniteTeamSearch(searchQuery, PAGE_SIZE, clubId, initialSelectedMember?.id);
52
-
53
- // Flatten all pages into a single array and add clubImageUrl from state
54
- const allClubs = data?.pages?.flatMap(page =>
55
- (page?.data?.data || []).map((club: any) => ({
56
- ...club,
57
- clubImageUrl: joinClubImageUrls[club.id] || undefined
58
- }))
59
- ) || [];
60
-
61
-
62
- useFocusEffect(useCallback(() => {
63
- refetch();
64
- }, [refetch]));
65
-
66
- // Pull to refresh
67
- const onRefresh = useCallback(async () => {
68
- try {
69
- setIsRefreshing(true);
70
- // Clear all cached data for this query to start fresh from page 0
71
- queryClient.removeQueries({
72
- queryKey: ['infiniteTeamSearch', searchQuery, PAGE_SIZE, clubId]
73
- });
74
- // This will trigger a fresh fetch starting from page 0
75
- await refetch();
76
- } catch (error) {
77
- console.error('Refresh failed:', error);
78
- } finally {
79
- setIsRefreshing(false);
80
- }
81
- }, [refetch, queryClient, searchQuery]);
82
-
83
- // Handle search input with debouncing
84
- const handleSearch = useCallback((text: string) => {
85
- setSearchQuery(text);
86
- if (text.length <= 3) {
87
- return;
88
- }
89
-
90
- // Clear existing timeout
91
- if (searchTimeout) {
92
- clearTimeout(searchTimeout);
93
- }
94
-
95
- // Set new timeout for debounced search
96
- const timeout = setTimeout(() => {
97
- // Clear cache and refetch with new search term
98
- queryClient.removeQueries({
99
- queryKey: ['infiniteTeamSearch', text, PAGE_SIZE, clubId]
100
- });
101
- }, 500); // 500ms debounce
102
-
103
- setSearchTimeout(timeout);
104
- }, [searchTimeout, queryClient]);
105
-
106
- // Cleanup timeout on unmount
107
- useEffect(() => {
108
- return () => {
109
- if (searchTimeout) {
110
- clearTimeout(searchTimeout);
111
- }
112
- };
113
- }, [searchTimeout]);
114
-
115
- // Load more data
116
- const onEndReached = useCallback(() => {
117
- if (isFetchingNextPage || !hasNextPage) return;
118
- fetchNextPage();
119
- }, [isFetchingNextPage, hasNextPage, fetchNextPage, allClubs.length]);
120
-
121
- // Render loading footer
122
- const renderFooter = () => {
123
- if (!isFetchingNextPage) return null;
124
- return (
125
- <View style={styles.footerLoader}>
126
- <ActivityIndicator size="small" color={theme.colors.primary} />
127
- <Text style={styles.footerText}>Loading more teams...</Text>
128
- </View>
129
- );
130
- };
131
-
132
- const renderEmptyComponent = () => {
133
- if (isLoading) return null;
134
- return (
135
- <View style={styles.emptyContainer}>
136
- <Text style={styles.emptyListText}>No teams found</Text>
137
- <Text style={styles.emptySubText}>Try refreshing or check back later</Text>
138
- </View>
139
- );
140
- };
141
-
142
- const handleScroll = useCallback(() => {
143
- Keyboard.dismiss();
144
- }, []);
145
-
146
-
147
-
148
- const renderClubItem = ({ item }: any) => {
149
- return (
150
- <TeamCard
151
- team={item}
152
- onJoin={true}
153
- onJoinPress={() => {
154
- joinTeam(item);
155
- }}
156
- />
157
- );
158
- };
159
-
160
- const joinTeam = (item: any) => {
161
-
162
- const body = {
163
- clubId: item?.clubId,
164
- memberId: initialSelectedMember?.id,
165
- teamId: item?.teamId
166
- }
167
- joinTeamMutation.mutate(body, {
168
- onSuccess: (response) => {
169
- ToastManager.success(response.data.message || 'Join team successful');
170
- navigation.goBack();
171
- },
172
- onError: (error) => {
173
- const errorInfo = getApiErrorInfo(error);
174
- ToastManager.error(errorInfo?.message);
175
-
176
- console.error('Join club failed:', error);
177
- },
178
- });
179
- }
180
-
181
-
182
-
183
- return (
184
- <View style={styles.container}>
185
- <StatusBar
186
- barStyle="light-content"
187
- backgroundColor={theme.colors.blue}
188
- translucent={Platform.OS === 'android' ? true : false}
189
- />
190
- {Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
191
- <SafeAreaView style={styles.header} >
192
- <View style={{ paddingHorizontal: theme.spacing.lg }}>
193
- <TouchableOpacity onPress={() => navigation.goBack()}>
194
- <SVG.arrowLeft_white width={moderateScale(25)} height={moderateScale(25)} />
195
- </TouchableOpacity>
196
- <Text style={styles.headerTitle}>{Strings.Teams.SEARCH_AND_JOIN_TEAM}</Text>
197
- </View>
198
- <View style={styles.addMemberContainer}>
199
- </View>
200
- <View style={styles.content}>
201
- <View style={styles.searchContainer}>
202
- <TextInput
203
- leftIconStyle={{ marginLeft: theme.spacing.sm }}
204
- leftIconSizeWidth={moderateScale(17)}
205
- leftIconSizeHeight={moderateScale(17)}
206
- placeholder={Strings.Teams.SEARCH_BY_TEAM_NAME}
207
- style={styles.searchInput}
208
- leftIcon={SVG.search}
209
- variant="outlined"
210
- maxLength={30}
211
- value={searchQuery}
212
- onChangeText={handleSearch}
213
- />
214
- </View>
215
- <FlatList
216
- data={allClubs}
217
- renderItem={renderClubItem}
218
- keyExtractor={keyExtractor}
219
- style={styles.clubsList}
220
- showsVerticalScrollIndicator={false}
221
- contentContainerStyle={styles.flatListContent}
222
- removeClippedSubviews={false}
223
- initialNumToRender={10}
224
- onEndReached={onEndReached}
225
- onEndReachedThreshold={0.9}
226
- ListFooterComponent={renderFooter}
227
- ListEmptyComponent={renderEmptyComponent}
228
- // onViewableItemsChanged={onJoinClubViewableItemsChanged}
229
- // viewabilityConfig={joinClubViewabilityConfig}
230
- onScroll={handleScroll}
231
- scrollEventThrottle={16}
232
- refreshControl={
233
- <RefreshControl
234
- refreshing={isRefreshing}
235
- onRefresh={onRefresh}
236
- colors={[theme.colors.primary]}
237
- tintColor={theme.colors.primary}
238
- />
239
- }
240
- />
241
- </View>
242
- </SafeAreaView>
243
-
244
- </View>
245
- );
246
- };
247
-
248
- const styles = StyleSheet.create({
249
- container: {
250
- flex: 1,
251
- backgroundColor: theme.colors.blue,
252
- },
253
- statusBarBackground: {
254
- position: 'absolute',
255
- top: 0,
256
- left: 0,
257
- right: 0,
258
- height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
259
- backgroundColor: theme.colors.blue,
260
- zIndex: 1000,
261
- },
262
- header: {
263
- flex: 1,
264
- backgroundColor: theme.colors.blue,
265
- paddingVertical: theme.spacing.xs,
266
- paddingTop: Platform.OS === 'android' ? theme.spacing.lg : theme.spacing.xs,
267
- },
268
- headerTitle: {
269
- marginTop: moderateScale(20),
270
- fontFamily: Fonts.outfitMedium,
271
- fontSize: moderateScale(22),
272
- color: theme.colors.white,
273
- },
274
- skipButton: {
275
- alignSelf: 'flex-end',
276
- paddingHorizontal: theme.spacing.sm,
277
- paddingVertical: theme.spacing.xs,
278
- },
279
- skipButtonText: {
280
- fontFamily: Fonts.outfitSemiBold,
281
- fontSize: theme.typography.fontSize.md,
282
- color: theme.colors.white,
283
- },
284
- addMemberContainer: {
285
- backgroundColor: theme.colors.blue,
286
- paddingHorizontal: theme.spacing.lg,
287
- paddingBottom: theme.spacing.md,
288
- },
289
- addMemberButton: {
290
- flexDirection: 'row',
291
- alignItems: 'center',
292
- justifyContent: 'center',
293
- backgroundColor: 'transparent',
294
- borderWidth: 1.5,
295
- borderColor: theme.colors.appleGreen,
296
- borderRadius: theme.borderRadius.xxl,
297
- paddingVertical: theme.spacing.sm,
298
- paddingHorizontal: theme.spacing.lg,
299
- gap: theme.spacing.sm,
300
- },
301
- addMemberButtonText: {
302
- fontFamily: Fonts.outfitMedium,
303
- fontSize: theme.typography.fontSize.md,
304
- color: theme.colors.appleGreen,
305
- },
306
- content: {
307
- flex: 1,
308
- backgroundColor: theme.colors.background,
309
- paddingHorizontal: theme.spacing.lg,
310
- paddingTop: theme.spacing.md,
311
- borderTopLeftRadius: moderateScale(30),
312
- borderTopRightRadius: moderateScale(30),
313
- },
314
- sectionTitle: {
315
- fontFamily: Fonts.outfitMedium,
316
- fontSize: theme.typography.fontSize.xs,
317
- color: theme.colors.blue,
318
- marginBottom: theme.spacing.md,
319
- letterSpacing: 0.5,
320
- },
321
- clubsList: {
322
- flex: 1,
323
- },
324
- flatListContent: {
325
- paddingBottom: theme.spacing.xl,
326
- },
327
- emptyContainer: {
328
- flex: 1,
329
- justifyContent: 'center',
330
- alignItems: 'center',
331
- paddingVertical: theme.spacing.xl,
332
- },
333
- bottomContainer: {
334
- height: moderateScale(120),
335
- paddingHorizontal: theme.spacing.lg,
336
- paddingVertical: theme.spacing.md,
337
- backgroundColor: theme.colors.white,
338
- },
339
- nextButton: {
340
- width: '100%',
341
- marginBottom: theme.spacing.md,
342
- },
343
- emptyListText: {
344
- fontFamily: Fonts.outfitMedium,
345
- fontSize: theme.typography.fontSize.md,
346
- color: theme.colors.text,
347
- textAlign: 'center',
348
- marginBottom: theme.spacing.xs,
349
- },
350
- emptySubText: {
351
- fontFamily: Fonts.outfitRegular,
352
- fontSize: theme.typography.fontSize.sm,
353
- color: theme.colors.textSecondary,
354
- textAlign: 'center',
355
- },
356
- footerLoader: {
357
- flexDirection: 'row',
358
- justifyContent: 'center',
359
- alignItems: 'center',
360
- paddingVertical: theme.spacing.md,
361
- gap: theme.spacing.sm,
362
- },
363
- footerText: {
364
- fontFamily: Fonts.outfitMedium,
365
- fontSize: theme.typography.fontSize.sm,
366
- color: theme.colors.textSecondary,
367
- },
368
- errorText: {
369
- fontFamily: Fonts.outfitMedium,
370
- fontSize: theme.typography.fontSize.md,
371
- color: theme.colors.error,
372
- textAlign: 'center',
373
- marginBottom: theme.spacing.md,
374
- },
375
- retryButton: {
376
- backgroundColor: theme.colors.primary,
377
- paddingHorizontal: theme.spacing.lg,
378
- paddingVertical: theme.spacing.sm,
379
- borderRadius: theme.borderRadius.md,
380
- },
381
- retryButtonText: {
382
- fontFamily: Fonts.outfitMedium,
383
- fontSize: theme.typography.fontSize.md,
384
- color: theme.colors.white,
385
- textAlign: 'center',
386
- },
387
- modalOverlay: {
388
- flex: 1,
389
- justifyContent: 'flex-end',
390
- },
391
- modalBackdrop: {
392
- position: 'absolute',
393
- top: 0,
394
- left: 0,
395
- right: 0,
396
- bottom: 0,
397
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
398
- },
399
- modalContainer: {
400
- backgroundColor: theme.colors.white,
401
- borderTopLeftRadius: moderateScale(20),
402
- borderTopRightRadius: moderateScale(20),
403
- paddingHorizontal: theme.spacing.lg,
404
- paddingTop: theme.spacing.sm,
405
- paddingBottom: theme.spacing.xl,
406
- height: '45%',
407
- maxHeight: '45%',
408
- },
409
- dragHandle: {
410
- width: moderateScale(40),
411
- height: moderateScale(4),
412
- backgroundColor: theme.colors.border,
413
- borderRadius: moderateScale(2),
414
- alignSelf: 'center',
415
- marginBottom: theme.spacing.sm,
416
- },
417
- membersList: {
418
- flex: 1,
419
- },
420
- memberListItem: {
421
- height: moderateScale(70),
422
- alignItems: 'center',
423
- paddingVertical: theme.spacing.md,
424
- flexDirection: 'row',
425
- },
426
- headerContainer: {
427
- paddingVertical: theme.spacing.xs,
428
- },
429
- mamberHeaderTitle: {
430
- fontFamily: Fonts.outfitSemiBold,
431
- fontSize: moderateScale(14),
432
- color: theme.colors.text,
433
- },
434
- profileImage: {
435
- width: moderateScale(50),
436
- height: moderateScale(50),
437
- borderRadius: moderateScale(25),
438
- marginRight: moderateScale(10),
439
- },
440
- memberName: {
441
- fontFamily: Fonts.outfitSemiBold,
442
- fontSize: moderateScale(14),
443
- color: theme.colors.text,
444
- },
445
- memberNameContainer: {
446
- flex: 1,
447
- justifyContent: 'center',
448
- },
449
- memberNameSubtitle: {
450
- fontFamily: Fonts.outfitMedium,
451
- fontSize: moderateScale(12),
452
- color: theme.colors.border,
453
- },
454
- searchInput: {
455
- },
456
- searchContainer: {
457
- marginBottom: theme.spacing.md,
458
- },
459
- placeholderStyle: {
460
- fontFamily: Fonts.outfitRegular,
461
- fontSize: moderateScale(5),
462
- color: theme.colors.textSecondary,
463
- },
464
- });