create-gufran-expo-app 2.0.0 → 2.0.2

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 (266) hide show
  1. package/lib/createApp.js +7 -2
  2. package/package.json +1 -1
  3. package/template/App.tsx +118 -0
  4. package/template/ReactotronConfig.js +5 -0
  5. package/template/android/app/build.gradle +184 -0
  6. package/template/android/app/debug.keystore +0 -0
  7. package/template/android/app/google-services.json +29 -0
  8. package/template/android/app/proguard-rules.pro +14 -0
  9. package/template/android/app/src/debug/AndroidManifest.xml +7 -0
  10. package/template/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
  11. package/template/android/app/src/main/AndroidManifest.xml +28 -0
  12. package/template/android/app/src/main/assets/fonts/Outfit-Bold.ttf +0 -0
  13. package/template/android/app/src/main/assets/fonts/Outfit-Light.ttf +0 -0
  14. package/template/android/app/src/main/assets/fonts/Outfit-Medium.ttf +0 -0
  15. package/template/android/app/src/main/assets/fonts/Outfit-Regular.ttf +0 -0
  16. package/template/android/app/src/main/assets/fonts/Outfit-SemiBold.ttf +0 -0
  17. package/template/android/app/src/main/java/com/club/yakka/MainActivity.kt +61 -0
  18. package/template/android/app/src/main/java/com/club/yakka/MainApplication.kt +60 -0
  19. package/template/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
  20. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  21. package/template/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
  22. package/template/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
  23. package/template/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
  24. package/template/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
  25. package/template/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
  26. package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  27. package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  28. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  29. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
  30. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  31. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  32. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
  33. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  34. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  35. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
  36. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  37. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  38. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
  39. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  40. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  41. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
  42. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  43. package/template/android/app/src/main/res/values/colors.xml +6 -0
  44. package/template/android/app/src/main/res/values/strings.xml +5 -0
  45. package/template/android/app/src/main/res/values/styles.xml +14 -0
  46. package/template/android/app/src/main/res/values-night/colors.xml +1 -0
  47. package/template/android/build.gradle +33 -0
  48. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  49. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  50. package/template/android/gradle.properties +65 -0
  51. package/template/android/gradlew +251 -0
  52. package/template/android/gradlew.bat +94 -0
  53. package/template/android/settings.gradle +39 -0
  54. package/template/app.json +69 -0
  55. package/template/assets/adaptive-icon.png +0 -0
  56. package/template/assets/adaptive-icon1.png +0 -0
  57. package/template/assets/app_icon.png +0 -0
  58. package/template/assets/favicon.png +0 -0
  59. package/template/assets/icon.png +0 -0
  60. package/template/assets/splash-icon.png +0 -0
  61. package/template/babel-plugin-disable-font-scaling.js +41 -0
  62. package/template/babel.config.js +28 -0
  63. package/template/firebase.json +5 -0
  64. package/template/index.ts +24 -0
  65. package/template/ios/.xcode.env +11 -0
  66. package/template/ios/ClubYakka/AppDelegate.swift +74 -0
  67. package/template/ios/ClubYakka/ClubYakka-Bridging-Header.h +3 -0
  68. package/template/ios/ClubYakka/ClubYakka.entitlements +8 -0
  69. package/template/ios/ClubYakka/GoogleService-Info.plist +30 -0
  70. package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  71. package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
  72. package/template/ios/ClubYakka/Images.xcassets/Contents.json +6 -0
  73. package/template/ios/ClubYakka/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
  74. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
  75. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
  76. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
  77. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
  78. package/template/ios/ClubYakka/Info.plist +101 -0
  79. package/template/ios/ClubYakka/PrivacyInfo.xcprivacy +50 -0
  80. package/template/ios/ClubYakka/SplashScreen.storyboard +48 -0
  81. package/template/ios/ClubYakka/Supporting/Expo.plist +12 -0
  82. package/template/ios/ClubYakka.xcodeproj/project.pbxproj +669 -0
  83. package/template/ios/ClubYakka.xcodeproj/xcshareddata/xcschemes/ClubYakka.xcscheme +88 -0
  84. package/template/ios/ClubYakka.xcworkspace/contents.xcworkspacedata +10 -0
  85. package/template/ios/ClubYakka.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +5 -0
  86. package/template/ios/Podfile +84 -0
  87. package/template/ios/Podfile.lock +2698 -0
  88. package/template/ios/Podfile.properties.json +8 -0
  89. package/template/metro.config.js +17 -0
  90. package/template/package.json +70 -0
  91. package/template/patches/react-native-background-upload+6.6.0.patch +704 -0
  92. package/template/react-native.config.js +7 -0
  93. package/template/src/assets/fonts/Outfit-Bold.ttf +0 -0
  94. package/template/src/assets/fonts/Outfit-Light.ttf +0 -0
  95. package/template/src/assets/fonts/Outfit-Medium.ttf +0 -0
  96. package/template/src/assets/fonts/Outfit-Regular.ttf +0 -0
  97. package/template/src/assets/fonts/Outfit-SemiBold.ttf +0 -0
  98. package/template/src/assets/icons/BG.svg +33 -0
  99. package/template/src/assets/icons/Ic_Users.svg +6 -0
  100. package/template/src/assets/icons/arrow-left-white.svg +4 -0
  101. package/template/src/assets/icons/arrow-left.svg +4 -0
  102. package/template/src/assets/icons/bell.svg +4 -0
  103. package/template/src/assets/icons/bottomSheetIcone.svg +3 -0
  104. package/template/src/assets/icons/call.svg +4 -0
  105. package/template/src/assets/icons/camera.svg +0 -0
  106. package/template/src/assets/icons/chatAppleGreenBG.svg +22 -0
  107. package/template/src/assets/icons/check.svg +4 -0
  108. package/template/src/assets/icons/check_Radio.svg +4 -0
  109. package/template/src/assets/icons/chevron-right.svg +3 -0
  110. package/template/src/assets/icons/clubDefauldImage.png +0 -0
  111. package/template/src/assets/icons/curvBackgroundView.svg +3 -0
  112. package/template/src/assets/icons/defaultClub.png +0 -0
  113. package/template/src/assets/icons/email.svg +3 -0
  114. package/template/src/assets/icons/emptyUser.svg +5 -0
  115. package/template/src/assets/icons/eye-Hide.svg +8 -0
  116. package/template/src/assets/icons/eye.svg +4 -0
  117. package/template/src/assets/icons/gallery.svg +4 -0
  118. package/template/src/assets/icons/home.svg +4 -0
  119. package/template/src/assets/icons/ic_Calendar.svg +14 -0
  120. package/template/src/assets/icons/ic_Calendar_blue.svg +12 -0
  121. package/template/src/assets/icons/ic_Calendar_white.svg +12 -0
  122. package/template/src/assets/icons/ic_Chat.svg +14 -0
  123. package/template/src/assets/icons/ic_ChatAppleGreen.svg +21 -0
  124. package/template/src/assets/icons/ic_ChatAppleWhite.svg +21 -0
  125. package/template/src/assets/icons/ic_Download.svg +6 -0
  126. package/template/src/assets/icons/ic_Events.svg +6 -0
  127. package/template/src/assets/icons/ic_HeadCoachIcon.svg +3 -0
  128. package/template/src/assets/icons/ic_Membership.svg +10 -0
  129. package/template/src/assets/icons/ic_Notification.svg +6 -0
  130. package/template/src/assets/icons/ic_Raffles.svg +14 -0
  131. package/template/src/assets/icons/ic_Referral_Members.svg +12 -0
  132. package/template/src/assets/icons/ic_Shop.svg +13 -0
  133. package/template/src/assets/icons/ic_Teams.svg +8 -0
  134. package/template/src/assets/icons/ic_Volunteer.svg +9 -0
  135. package/template/src/assets/icons/ic_add.svg +3 -0
  136. package/template/src/assets/icons/ic_addCircle.svg +5 -0
  137. package/template/src/assets/icons/ic_chatSend.svg +4 -0
  138. package/template/src/assets/icons/ic_chat_blue_bg.svg +22 -0
  139. package/template/src/assets/icons/ic_clock_blue.svg +4 -0
  140. package/template/src/assets/icons/ic_delete.svg +8 -0
  141. package/template/src/assets/icons/ic_more.svg +5 -0
  142. package/template/src/assets/icons/ic_mute.svg +10 -0
  143. package/template/src/assets/icons/ic_pdf.svg +3 -0
  144. package/template/src/assets/icons/ic_pending_AppleGreen.svg +11 -0
  145. package/template/src/assets/icons/ic_right_appleGreen.svg +11 -0
  146. package/template/src/assets/icons/ic_unread_chat_blue_bg.svg +23 -0
  147. package/template/src/assets/icons/ic_volunteer_Member.svg +8 -0
  148. package/template/src/assets/icons/index.ts +144 -0
  149. package/template/src/assets/icons/location-blue.svg +4 -0
  150. package/template/src/assets/icons/location-white.svg +4 -0
  151. package/template/src/assets/icons/location.svg +4 -0
  152. package/template/src/assets/icons/lock.svg +5 -0
  153. package/template/src/assets/icons/log-out.svg +5 -0
  154. package/template/src/assets/icons/login_logo.svg +9 -0
  155. package/template/src/assets/icons/mail.svg +4 -0
  156. package/template/src/assets/icons/or_saprater.svg +5 -0
  157. package/template/src/assets/icons/password.svg +4 -0
  158. package/template/src/assets/icons/profile-appleGreen.svg +6 -0
  159. package/template/src/assets/icons/search.svg +3 -0
  160. package/template/src/assets/icons/settings.svg +4 -0
  161. package/template/src/assets/icons/success.svg +4 -0
  162. package/template/src/assets/icons/unCheck_Radio.svg +3 -0
  163. package/template/src/assets/icons/uncheck.svg +3 -0
  164. package/template/src/assets/icons/upload_Image.svg +6 -0
  165. package/template/src/assets/icons/upload_Image_Member.svg +6 -0
  166. package/template/src/assets/icons/user.svg +4 -0
  167. package/template/src/assets/icons/wifi.svg +1 -0
  168. package/template/src/assets/images/Splash.png +0 -0
  169. package/template/src/assets/images/SplashLogo.png +0 -0
  170. package/template/src/assets/images/background.png +0 -0
  171. package/template/src/assets/images/clubDefauldImage.png +0 -0
  172. package/template/src/assets/images/index.tsx +9 -0
  173. package/template/src/assets/index.ts +1 -0
  174. package/template/src/components/common/AddMemberModal.tsx +543 -0
  175. package/template/src/components/common/AppLoader.tsx +91 -0
  176. package/template/src/components/common/Button.tsx +173 -0
  177. package/template/src/components/common/ClubCard.tsx +248 -0
  178. package/template/src/components/common/Icon.tsx +93 -0
  179. package/template/src/components/common/IconAlt.tsx +65 -0
  180. package/template/src/components/common/IconButton.tsx +92 -0
  181. package/template/src/components/common/ImagePicker.tsx +451 -0
  182. package/template/src/components/common/LoadingSpinner.tsx +30 -0
  183. package/template/src/components/common/OTPInput.tsx +128 -0
  184. package/template/src/components/common/ReminderCalendar.tsx +129 -0
  185. package/template/src/components/common/ReminderCardItem.tsx +91 -0
  186. package/template/src/components/common/SafeAreaWrapper.tsx +27 -0
  187. package/template/src/components/common/SetReminderModal.tsx +308 -0
  188. package/template/src/components/common/SlowInternet.js +57 -0
  189. package/template/src/components/common/TeamCard.tsx +297 -0
  190. package/template/src/components/common/TextInput.tsx +227 -0
  191. package/template/src/components/common/ToastConfig.tsx +103 -0
  192. package/template/src/components/common/ToastManager.ts +86 -0
  193. package/template/src/components/common/UploadProgressModal.tsx +284 -0
  194. package/template/src/components/common/WrapperContainer.tsx +39 -0
  195. package/template/src/components/common/index.ts +19 -0
  196. package/template/src/constants/Constants.tsx +7 -0
  197. package/template/src/constants/Fonts.tsx +30 -0
  198. package/template/src/constants/index.ts +45 -0
  199. package/template/src/constants/strings.ts +211 -0
  200. package/template/src/constants/theme.ts +72 -0
  201. package/template/src/contexts/AuthContext.tsx +268 -0
  202. package/template/src/contexts/index.ts +2 -0
  203. package/template/src/hooks/index.ts +3 -0
  204. package/template/src/hooks/useImageUpload.ts +199 -0
  205. package/template/src/index.ts +8 -0
  206. package/template/src/navigation/AuthStack.tsx +67 -0
  207. package/template/src/navigation/MainStack.tsx +183 -0
  208. package/template/src/navigation/MiddleStack.tsx +35 -0
  209. package/template/src/navigation/RootNavigator.tsx +101 -0
  210. package/template/src/navigation/index.ts +5 -0
  211. package/template/src/navigation/navigationRef.ts +5 -0
  212. package/template/src/providers/QueryProvider.tsx +30 -0
  213. package/template/src/screens/DetailsScreen.tsx +271 -0
  214. package/template/src/screens/HomeScreen.tsx +736 -0
  215. package/template/src/screens/ProfileScreen.tsx +202 -0
  216. package/template/src/screens/SettingsScreen.tsx +253 -0
  217. package/template/src/screens/SportHubScreen.tsx +280 -0
  218. package/template/src/screens/auth/AddMamber.tsx +428 -0
  219. package/template/src/screens/auth/ForgotPasswordScreen.tsx +176 -0
  220. package/template/src/screens/auth/LoginScreen.tsx +286 -0
  221. package/template/src/screens/auth/OTPVerifyScreen.tsx +359 -0
  222. package/template/src/screens/auth/RegisterScreen.tsx +430 -0
  223. package/template/src/screens/auth/SuccessScreen.tsx +201 -0
  224. package/template/src/screens/auth/WalcomeScreen.tsx +274 -0
  225. package/template/src/screens/auth/index.ts +8 -0
  226. package/template/src/screens/chat/ChatScreen.tsx +1819 -0
  227. package/template/src/screens/chat/ChatThreadsScreen.tsx +360 -0
  228. package/template/src/screens/chat/ReportMessageScreen.tsx +238 -0
  229. package/template/src/screens/clubs/Announcements.tsx +426 -0
  230. package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +568 -0
  231. package/template/src/screens/clubs/ClubDeteils.tsx +497 -0
  232. package/template/src/screens/clubs/JoinClub.tsx +841 -0
  233. package/template/src/screens/events/EventScreen.tsx +460 -0
  234. package/template/src/screens/index.ts +42 -0
  235. package/template/src/screens/raffles/MyReferralMembersScreen.tsx +758 -0
  236. package/template/src/screens/raffles/RaffleDetailsScreen.tsx +762 -0
  237. package/template/src/screens/raffles/RafflesScreen.tsx +495 -0
  238. package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +390 -0
  239. package/template/src/screens/teams/JoinTeamScreen.tsx +464 -0
  240. package/template/src/screens/teams/MyTeamDetailsScreen.tsx +979 -0
  241. package/template/src/screens/teams/MyTeamScreen.tsx +568 -0
  242. package/template/src/screens/teams/PendingRequestsScreen.tsx +426 -0
  243. package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +631 -0
  244. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +1049 -0
  245. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +608 -0
  246. package/template/src/services/api.ts +167 -0
  247. package/template/src/services/authService.ts +422 -0
  248. package/template/src/services/index.ts +5 -0
  249. package/template/src/services/mainServices.ts +1963 -0
  250. package/template/src/stores/authStore.ts +159 -0
  251. package/template/src/stores/index.ts +2 -0
  252. package/template/src/types/index.ts +85 -0
  253. package/template/src/types/navigation.ts +206 -0
  254. package/template/src/utils/AzureUploaderService.ts +371 -0
  255. package/template/src/utils/ClubSearchManager.ts +222 -0
  256. package/template/src/utils/NotificationManager.ts +503 -0
  257. package/template/src/utils/UploadDebugUtil.ts +107 -0
  258. package/template/src/utils/imagePicker.ts +321 -0
  259. package/template/src/utils/index.ts +111 -0
  260. package/template/src/utils/permissions.ts +277 -0
  261. package/template/src/utils/scaling.ts +14 -0
  262. package/template/src/utils/storage.ts +247 -0
  263. package/template/src/utils/usePermissions.ts +275 -0
  264. package/template/src/utils/validation.ts +340 -0
  265. package/template/tsconfig.json +50 -0
  266. package/template/types/svg.d.ts +6 -0
@@ -0,0 +1,841 @@
1
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { View, Text, StyleSheet, FlatList, StatusBar, Platform, TouchableOpacity, Image, RefreshControl, ActivityIndicator, Modal, Animated, Dimensions, PanResponder, Keyboard } from 'react-native';
3
+ import { JoinClubScreenProps } from '../../types/navigation';
4
+ import { 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 { useInfiniteClubSearch, useJoinClub } from '../../services/mainServices';
10
+ import { useQueryClient } from '@tanstack/react-query';
11
+ import { Button, ClubCard, TextInput } from '../../components/common';
12
+ import ToastManager from '../../components/common/ToastManager';
13
+ import { getApiErrorInfo, useMembers, useGetImageUrl } from '../../services';
14
+ import { useFocusEffect } from '@react-navigation/native';
15
+ const PAGE_SIZE = 10;
16
+
17
+ type MemberListItemProps = {
18
+ item: any;
19
+ isSelected: boolean;
20
+ onSelect: (member: any) => void;
21
+ };
22
+
23
+ const MemberListItem: React.FC<MemberListItemProps> = ({ item, isSelected, onSelect }) => {
24
+ const [memberImageError, setMemberImageError] = useState(false);
25
+ return (
26
+ <TouchableOpacity style={styles.memberListItem} onPress={() => onSelect(item)}>
27
+ {item?.profileImage && !memberImageError ? (
28
+ <Image
29
+ source={{ uri: item?.profileImage }}
30
+ onError={() => {
31
+ setMemberImageError(true);
32
+ }}
33
+ style={styles.profileImage}
34
+ />
35
+ ) : (
36
+ <SVG.emptyUser style={{ marginRight: moderateScale(10) }} width={moderateScale(50)} height={moderateScale(50)} />
37
+ )}
38
+ {/* {memberImageError && (
39
+ <SVG.emptyUser style={{ marginRight: moderateScale(10) }} width={moderateScale(50)} height={moderateScale(50)} />
40
+ )} */}
41
+ <View style={styles.memberNameContainer}>
42
+ <Text style={styles.memberName}>{item?.name}</Text>
43
+ {item?.isOwner && (
44
+ <Text style={styles.memberNameSubtitle}>Yourself</Text>
45
+ )}
46
+ </View>
47
+ <View>
48
+ {isSelected ? (
49
+ <SVG.checkRadio width={moderateScale(20)} height={moderateScale(20)} />
50
+ ) : (
51
+ <SVG.uncheckRadio width={moderateScale(20)} height={moderateScale(20)} />
52
+ )}
53
+ </View>
54
+ </TouchableOpacity>
55
+ );
56
+ };
57
+
58
+ export const JoinClubScreen: React.FC<JoinClubScreenProps> = ({ navigation }) => {
59
+
60
+ // Pagination state
61
+ const [isRefreshing, setIsRefreshing] = useState(false);
62
+ const [membersModal, setMembersModal] = useState<boolean>(false);
63
+ const [selectedMember, setSelectedMember] = useState<any>(null);
64
+ // Bottom sheet animation values
65
+ const translateY = useRef(new Animated.Value(0)).current;
66
+ const screenHeight = Dimensions.get('window').height;
67
+ const bottomSheetHeight = screenHeight * 0.45; // 45% of screen height
68
+ const [searchQuery, setSearchQuery] = useState('');
69
+ const [searchTimeout, setSearchTimeout] = useState<NodeJS.Timeout | null>(null);
70
+ const [joinClubDetails, setJoinClubDetails] = useState<any>(null);
71
+ const [joinClubImageUrls, setJoinClubImageUrls] = useState<Record<number, string>>({});
72
+ const [joinClubMemberImageUrls, setJoinClubMemberImageUrls] = useState<Record<number, string>>({});
73
+ const queryClient = useQueryClient();
74
+ const joinClubMutation = useJoinClub();
75
+ const getImageUrlMutation = useGetImageUrl();
76
+
77
+
78
+
79
+
80
+ const keyExtractor = (item: any, index: number) => {
81
+ // Create a more robust key that combines multiple attributes to ensure uniqueness
82
+ const id = item?.id?.toString() || '';
83
+ const name = item?.name || item?.clubName || '';
84
+ const clubCode = item?.clubCode || item?.uniqueCode || '';
85
+ const timestamp = item?.createdAt || item?.timestamp || '';
86
+
87
+ // Create a unique composite key using multiple attributes
88
+ const keyParts = [id, name, clubCode, timestamp].filter(part => part && part.toString().trim() !== '');
89
+
90
+ if (keyParts.length > 0) {
91
+ return `club-${keyParts.join('-')}-${index}`;
92
+ }
93
+
94
+ // Ultimate fallback with index to ensure uniqueness
95
+ return `club-unknown-${index}`;
96
+ };
97
+
98
+
99
+ const { data, fetchNextPage,
100
+ hasNextPage,
101
+ isFetchingNextPage,
102
+ isLoading,
103
+ isError,
104
+ refetch
105
+ } = useInfiniteClubSearch(searchQuery, PAGE_SIZE);
106
+
107
+ // Flatten all pages into a single array and add clubImageUrl from state
108
+ const allClubs = data?.pages?.flatMap(page =>
109
+ (page?.data?.data || []).map((club: any) => ({
110
+ ...club,
111
+ clubImageUrl: joinClubImageUrls[club.id] || undefined
112
+ }))
113
+ ) || [];
114
+
115
+
116
+ const { data: membersData, isLoading: isLoadingMembers, isError: isErrorMembers, refetch: refetchMembers } = useMembers({ PageNumber: 0, PageSize: 10 });
117
+ const rawMembers = Array.isArray(membersData?.data?.data) ? membersData.data.data : [];
118
+ const sortedMembers = rawMembers.sort((a: any, b: any) => {
119
+ if (a?.isOwner) return -1;
120
+ if (b?.isOwner) return 1;
121
+ return 0;
122
+ });
123
+
124
+ const members = sortedMembers.map((member: any) => ({
125
+ ...member,
126
+ profileImageUrl: joinClubMemberImageUrls[member.id] || undefined
127
+ }));
128
+
129
+
130
+ useFocusEffect(useCallback(() => {
131
+ refetch();
132
+ }, [refetch]));
133
+
134
+
135
+ // Pull to refresh
136
+ const onRefresh = useCallback(async () => {
137
+ try {
138
+ setIsRefreshing(true);
139
+ // Clear all cached data for this query to start fresh from page 0
140
+ queryClient.removeQueries({
141
+ queryKey: ['infiniteClubSearch', searchQuery, PAGE_SIZE]
142
+ });
143
+ // This will trigger a fresh fetch starting from page 0
144
+ await refetch();
145
+ } catch (error) {
146
+ console.error('Refresh failed:', error);
147
+ } finally {
148
+ setIsRefreshing(false);
149
+ }
150
+ }, [refetch, queryClient, searchQuery]);
151
+
152
+ // Handle search input with debouncing
153
+ const handleSearch = useCallback((text: string) => {
154
+ setSearchQuery(text);
155
+ if (text.length < 3) {
156
+ return;
157
+ }
158
+
159
+ // Clear existing timeout
160
+ if (searchTimeout) {
161
+ clearTimeout(searchTimeout);
162
+ }
163
+
164
+ // Set new timeout for debounced search
165
+ const timeout = setTimeout(() => {
166
+ // Clear cache and refetch with new search term
167
+ queryClient.removeQueries({
168
+ queryKey: ['infiniteClubSearch', text || 'club', PAGE_SIZE]
169
+ });
170
+ }, 500); // 500ms debounce
171
+
172
+ setSearchTimeout(timeout);
173
+ }, [searchTimeout, queryClient]);
174
+
175
+ // Cleanup timeout on unmount
176
+ useEffect(() => {
177
+ return () => {
178
+ if (searchTimeout) {
179
+ clearTimeout(searchTimeout);
180
+ }
181
+ };
182
+ }, [searchTimeout]);
183
+
184
+ // Load more data
185
+ const onEndReached = useCallback(() => {
186
+ if (isFetchingNextPage || !hasNextPage) return;
187
+ fetchNextPage();
188
+ }, [isFetchingNextPage, hasNextPage, fetchNextPage, allClubs.length]);
189
+
190
+ // Render loading footer
191
+ const renderFooter = () => {
192
+ if (!isFetchingNextPage) return null;
193
+ return (
194
+ <View style={styles.footerLoader}>
195
+ <ActivityIndicator size="small" color={theme.colors.primary} />
196
+ <Text style={styles.footerText}>Loading more clubs...</Text>
197
+ </View>
198
+ );
199
+ };
200
+
201
+
202
+
203
+ const renderEmptyComponent = () => {
204
+ if (isLoading) return null;
205
+ return (
206
+ <View style={styles.emptyContainer}>
207
+ <Text style={styles.emptyListText}>No clubs found</Text>
208
+ <Text style={styles.emptySubText}>Try refreshing or check back later</Text>
209
+ </View>
210
+ );
211
+ };
212
+
213
+ const handleScroll = useCallback(() => {
214
+ Keyboard.dismiss();
215
+ }, []);
216
+
217
+ const handleMemberSelection = (member: any) => {
218
+ setSelectedMember(member);
219
+ };
220
+
221
+ // Bottom sheet animation functions
222
+ const showBottomSheet = () => {
223
+ setSelectedMember(null);
224
+ setMembersModal(true);
225
+ Animated.spring(translateY, {
226
+ toValue: 0,
227
+ useNativeDriver: true,
228
+ tension: 100,
229
+ friction: 8,
230
+ }).start();
231
+ };
232
+
233
+ const hideBottomSheet = () => {
234
+ Animated.timing(translateY, {
235
+ toValue: bottomSheetHeight,
236
+ duration: 300,
237
+ useNativeDriver: true,
238
+ }).start(() => {
239
+ setMembersModal(false);
240
+ translateY.setValue(bottomSheetHeight);
241
+ });
242
+ };
243
+
244
+ // PanResponder for swipe gestures
245
+ const panResponder = useRef(
246
+ PanResponder.create({
247
+ onMoveShouldSetPanResponder: (_, gestureState) => {
248
+ return Math.abs(gestureState.dy) > 5;
249
+ },
250
+ onPanResponderGrant: () => {
251
+ translateY.setOffset((translateY as any)._value);
252
+ translateY.setValue(0);
253
+ },
254
+ onPanResponderMove: (_, gestureState) => {
255
+ // Only allow downward movement
256
+ if (gestureState.dy > 0) {
257
+ translateY.setValue(gestureState.dy);
258
+ }
259
+ },
260
+ onPanResponderRelease: (_, gestureState) => {
261
+ translateY.flattenOffset();
262
+
263
+ // If swiped down more than 100px or with high velocity, close the modal
264
+ if (gestureState.dy > 100 || gestureState.vy > 0.5) {
265
+ hideBottomSheet();
266
+ } else {
267
+ // Snap back to original position
268
+ Animated.spring(translateY, {
269
+ toValue: 0,
270
+ useNativeDriver: true,
271
+ tension: 100,
272
+ friction: 8,
273
+ }).start();
274
+ }
275
+ },
276
+ })
277
+ ).current;
278
+
279
+ const renderMemberListItem = ({ item }: { item: any }) => (
280
+ <MemberListItem
281
+ item={item}
282
+ isSelected={selectedMember?.id === item?.id}
283
+ onSelect={handleMemberSelection}
284
+ />
285
+ );
286
+
287
+
288
+ const getJoinClubImageUrls = async (clubItem: any) => {
289
+ if (clubItem?.clubImage == null) return;
290
+
291
+ // Skip if already fetched
292
+ if (joinClubImageUrls[clubItem.id] !== undefined) return;
293
+
294
+ const [folder, blobName] = clubItem?.clubImage.split('/');
295
+
296
+ try {
297
+ getImageUrlMutation.mutate({
298
+ containerName: folder,
299
+ blobName: blobName
300
+ }, {
301
+ onSuccess: (response) => {
302
+ const imageUrl = response?.data?.data?.url;
303
+ console.log('JoinClub Image URL response', response.data);
304
+
305
+ // Prefetch image into React Native cache to avoid blink when scrolling back
306
+ if (imageUrl) {
307
+ Image.prefetch(imageUrl).catch((prefetchError) => {
308
+ console.warn('JoinClub Image prefetch failed:', prefetchError);
309
+ });
310
+ }
311
+
312
+ // Simple state update instead of recreating entire query cache
313
+ setJoinClubImageUrls(prev => ({
314
+ ...prev,
315
+ [clubItem.id]: imageUrl
316
+ }));
317
+ },
318
+ onError: (error) => {
319
+ const errorInfo = getApiErrorInfo(error);
320
+ ToastManager.error(errorInfo.message);
321
+ setJoinClubImageUrls(prev => ({
322
+ ...prev,
323
+ [clubItem.id]: ""
324
+ }));
325
+ }
326
+ });
327
+ } catch (err) {
328
+ setJoinClubImageUrls(prev => ({
329
+ ...prev,
330
+ [clubItem.id]: ""
331
+ }));
332
+ }
333
+ };
334
+
335
+
336
+ const getJoinClubImageMembersUrls = async (memberItem: any) => {
337
+ console.log('JoinClub getJoinClubImageMembersUrls>>>>>14798', JSON.stringify(memberItem));
338
+ if (memberItem?.profileImage == null) return;
339
+
340
+ // Skip if already fetched
341
+ if (joinClubMemberImageUrls[memberItem.id] !== undefined) return;
342
+ console.log('JoinClub getJoinClubImageMembersUrls>>>>>147', JSON.stringify(memberItem));
343
+
344
+ const [folder, blobName] = memberItem?.profileImage.split('/');
345
+
346
+ try {
347
+ getImageUrlMutation.mutate({
348
+ containerName: folder,
349
+ blobName: blobName
350
+ }, {
351
+ onSuccess: (response) => {
352
+ const imageUrl = response?.data?.data?.url;
353
+ console.log('JoinClub Image Members URL response', response.data);
354
+
355
+ // Prefetch image into React Native cache to avoid blink when scrolling back
356
+ if (imageUrl) {
357
+ Image.prefetch(imageUrl).catch((prefetchError) => {
358
+ console.warn('JoinClub Image prefetch failed:', prefetchError);
359
+ });
360
+ }
361
+
362
+ // Simple state update instead of recreating entire query cache
363
+ setJoinClubMemberImageUrls(prev => ({
364
+ ...prev,
365
+ [memberItem.id]: imageUrl
366
+ }));
367
+ },
368
+ onError: (error) => {
369
+ const errorInfo = getApiErrorInfo(error);
370
+ ToastManager.error(errorInfo.message);
371
+ setJoinClubMemberImageUrls(prev => ({
372
+ ...prev,
373
+ [memberItem.id]: ""
374
+ }));
375
+ }
376
+ });
377
+ } catch (err) {
378
+ setJoinClubMemberImageUrls(prev => ({
379
+ ...prev,
380
+ [memberItem.id]: ""
381
+ }));
382
+ }
383
+ };
384
+
385
+ const onJoinClubViewableItemsChanged = useCallback(
386
+ ({
387
+ viewableItems,
388
+ }: {
389
+ viewableItems: Array<{ item: any }>;
390
+ }) => {
391
+ const items = viewableItems.map(({ item }) => item);
392
+ items.forEach((item) => {
393
+ if (item && !item.clubImageUrl) {
394
+ console.log('JoinClub onViewableItemsChanged>>>>>', JSON.stringify(item));
395
+ getJoinClubImageUrls(item);
396
+ }
397
+ });
398
+ },
399
+ [getJoinClubImageUrls]
400
+ );
401
+ const onJoinClubViewableItemsChangedMembers = useCallback(
402
+ ({
403
+ viewableItems,
404
+ }: {
405
+ viewableItems: Array<{ item: any }>;
406
+ }) => {
407
+ const items = viewableItems.map(({ item }) => item);
408
+ items.forEach((item) => {
409
+ if (item && !item.profileImageUrl) {
410
+ getJoinClubImageMembersUrls(item);
411
+ }
412
+ });
413
+ },
414
+ [getJoinClubImageMembersUrls]
415
+ );
416
+
417
+ const joinClubViewabilityConfig = useRef({
418
+ viewAreaCoveragePercentThreshold: 50, // Increased threshold for better on-demand loading
419
+ minimumViewTime: 100, // Minimum time item must be visible before triggering
420
+ waitForInteraction: false, // Don't wait for user interaction
421
+ }).current;
422
+
423
+ const joinClubViewabilityConfigMembers = useRef({
424
+ viewAreaCoveragePercentThreshold: 50, // Increased threshold for better on-demand loading
425
+ minimumViewTime: 100, // Minimum time item must be visible before triggering
426
+ waitForInteraction: false, // Don't wait for user interaction
427
+ }).current;
428
+
429
+ const renderClubItem = ({ item }: any) => {
430
+ return (
431
+ <ClubCard
432
+ club={item}
433
+ onJoin={true}
434
+ clubCodeShow={true}
435
+ onJoinPress={() => {
436
+ setJoinClubDetails(item);
437
+
438
+ // Check if there's only one member with isOwner: true
439
+ if (rawMembers.length === 1 && rawMembers[0].isOwner === true) {
440
+ // Auto-select the single owner member and join directly
441
+ setSelectedMember(rawMembers[0]);
442
+
443
+ // Join club directly without showing modal
444
+ joinClubMutation.mutate({
445
+ clubId: item.id,
446
+ memberId: rawMembers[0].id,
447
+ }, {
448
+ onSuccess: (response) => {
449
+ ToastManager.success(response.data.message || 'Join club successful');
450
+ navigation.goBack();
451
+ },
452
+ onError: (error) => {
453
+ const errorInfo = getApiErrorInfo(error);
454
+ ToastManager.error(errorInfo?.message);
455
+ console.error('Join club failed:', error);
456
+ },
457
+ });
458
+ } else {
459
+ // Show modal for member selection as usual
460
+ setMembersModal(true);
461
+ showBottomSheet();
462
+ }
463
+ }}
464
+ />
465
+ );
466
+ };
467
+
468
+ const joinClub = () => {
469
+ if (joinClubDetails && selectedMember) {
470
+
471
+ joinClubMutation.mutate({
472
+ clubId: joinClubDetails?.id,
473
+ memberId: selectedMember?.id,
474
+ }, {
475
+ onSuccess: (response) => {
476
+ ToastManager.success(response.data.message || 'Join club successful');
477
+ navigation.goBack();
478
+ },
479
+ onError: (error) => {
480
+ const errorInfo = getApiErrorInfo(error);
481
+ ToastManager.error(errorInfo?.message);
482
+
483
+ console.error('Join club failed:', error);
484
+ },
485
+ });
486
+ }
487
+ };
488
+
489
+ return (
490
+ <View style={styles.container}>
491
+ <StatusBar
492
+ barStyle="light-content"
493
+ backgroundColor={theme.colors.blue}
494
+ translucent={Platform.OS === 'android' ? true : false}
495
+ />
496
+ {Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
497
+ <SafeAreaView style={styles.header} >
498
+ <View style={{ paddingHorizontal: theme.spacing.lg }}>
499
+ <TouchableOpacity onPress={() => navigation.goBack()}>
500
+ <SVG.arrowLeft_white width={moderateScale(25)} height={moderateScale(25)} />
501
+ </TouchableOpacity>
502
+ <Text style={styles.headerTitle}>Search and join club</Text>
503
+ </View>
504
+ <View style={styles.addMemberContainer}>
505
+ </View>
506
+ <View style={styles.content}>
507
+ <View style={styles.searchContainer}>
508
+ <TextInput
509
+ leftIconStyle={{ marginLeft: theme.spacing.sm }}
510
+ leftIconSizeWidth={moderateScale(17)}
511
+ leftIconSizeHeight={moderateScale(17)}
512
+ placeholder="Unique code and club name"
513
+ style={styles.searchInput}
514
+ leftIcon={SVG.search}
515
+ variant="outlined"
516
+ maxLength={50}
517
+ value={searchQuery}
518
+ onChangeText={handleSearch}
519
+ />
520
+ </View>
521
+ <FlatList
522
+ data={allClubs}
523
+ renderItem={renderClubItem}
524
+ keyExtractor={keyExtractor}
525
+ style={styles.clubsList}
526
+ showsVerticalScrollIndicator={false}
527
+ contentContainerStyle={styles.flatListContent}
528
+ removeClippedSubviews={false}
529
+ initialNumToRender={10}
530
+ onEndReached={onEndReached}
531
+ onEndReachedThreshold={0.9}
532
+ ListFooterComponent={renderFooter}
533
+ ListEmptyComponent={renderEmptyComponent}
534
+ // onViewableItemsChanged={onJoinClubViewableItemsChanged}
535
+ // viewabilityConfig={joinClubViewabilityConfig}
536
+ onScroll={handleScroll}
537
+ scrollEventThrottle={16}
538
+ refreshControl={
539
+ <RefreshControl
540
+ refreshing={isRefreshing}
541
+ onRefresh={onRefresh}
542
+ colors={[theme.colors.primary]}
543
+ tintColor={theme.colors.primary}
544
+ />
545
+ }
546
+ />
547
+ </View>
548
+ </SafeAreaView>
549
+
550
+
551
+ <Modal
552
+ visible={membersModal}
553
+ animationType="fade"
554
+ transparent={true}
555
+ onRequestClose={hideBottomSheet}
556
+ >
557
+ <View style={styles.modalOverlay}>
558
+ <TouchableOpacity
559
+ style={styles.modalBackdrop}
560
+ activeOpacity={1}
561
+ onPress={hideBottomSheet}
562
+ />
563
+ <Animated.View
564
+ style={[
565
+ styles.modalContainer,
566
+ {
567
+ transform: [{ translateY }],
568
+ },
569
+ ]}
570
+ >
571
+ <View style={styles.dragHandle} {...panResponder.panHandlers} />
572
+ <FlatList
573
+ data={members}
574
+ renderItem={renderMemberListItem}
575
+ style={styles.membersList}
576
+ showsVerticalScrollIndicator={false}
577
+ ListHeaderComponent={() => {
578
+ return (
579
+ <View>
580
+ <View style={styles.headerContainer}>
581
+ <Text style={styles.mamberHeaderTitle}>Continue as</Text>
582
+ </View>
583
+ </View>
584
+ )
585
+ }}
586
+ keyExtractor={(item, index) => {
587
+ // Create a more robust key for members
588
+ const id = item?.id?.toString() || '';
589
+ const name = item?.name || '';
590
+ const email = item?.email || '';
591
+ const memberCode = item?.membershipCode || '';
592
+ const createdAt = item?.createdAt || '';
593
+
594
+ // Create a unique composite key using multiple attributes
595
+ const keyParts = [id, name, email, memberCode, createdAt].filter(part => part && part.toString().trim() !== '');
596
+
597
+ if (keyParts.length > 0) {
598
+ return `member-${keyParts.join('-')}`;
599
+ }
600
+
601
+ // Ultimate fallback with index to ensure uniqueness
602
+ return `member-unknown-${index}`;
603
+ }}
604
+
605
+ />
606
+
607
+ <Button
608
+ title="Continue"
609
+ onPress={() => {
610
+ joinClub();
611
+ hideBottomSheet();
612
+ }}
613
+ variant="primary"
614
+ size="medium"
615
+ style={styles.nextButton}
616
+ disabled={selectedMember == null ? true : false}
617
+ />
618
+ </Animated.View>
619
+ </View>
620
+ </Modal>
621
+ </View>
622
+ );
623
+ };
624
+
625
+ const styles = StyleSheet.create({
626
+ container: {
627
+ flex: 1,
628
+ backgroundColor: theme.colors.blue,
629
+ },
630
+ statusBarBackground: {
631
+ position: 'absolute',
632
+ top: 0,
633
+ left: 0,
634
+ right: 0,
635
+ height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
636
+ backgroundColor: theme.colors.blue,
637
+ zIndex: 1000,
638
+ },
639
+ header: {
640
+ flex: 1,
641
+ backgroundColor: theme.colors.blue,
642
+ paddingVertical: theme.spacing.xs,
643
+ paddingTop: Platform.OS === 'android' ? theme.spacing.lg : theme.spacing.xs,
644
+ },
645
+ headerTitle: {
646
+ marginTop: moderateScale(20),
647
+ fontFamily: Fonts.outfitMedium,
648
+ fontSize: moderateScale(22),
649
+ color: theme.colors.white,
650
+ },
651
+ skipButton: {
652
+ alignSelf: 'flex-end',
653
+ paddingHorizontal: theme.spacing.sm,
654
+ paddingVertical: theme.spacing.xs,
655
+ },
656
+ skipButtonText: {
657
+ fontFamily: Fonts.outfitSemiBold,
658
+ fontSize: theme.typography.fontSize.md,
659
+ color: theme.colors.white,
660
+ },
661
+ addMemberContainer: {
662
+ backgroundColor: theme.colors.blue,
663
+ paddingHorizontal: theme.spacing.lg,
664
+ paddingBottom: theme.spacing.md,
665
+ },
666
+ addMemberButton: {
667
+ flexDirection: 'row',
668
+ alignItems: 'center',
669
+ justifyContent: 'center',
670
+ backgroundColor: 'transparent',
671
+ borderWidth: 1.5,
672
+ borderColor: theme.colors.appleGreen,
673
+ borderRadius: theme.borderRadius.xxl,
674
+ paddingVertical: theme.spacing.sm,
675
+ paddingHorizontal: theme.spacing.lg,
676
+ gap: theme.spacing.sm,
677
+ },
678
+ addMemberButtonText: {
679
+ fontFamily: Fonts.outfitMedium,
680
+ fontSize: theme.typography.fontSize.md,
681
+ color: theme.colors.appleGreen,
682
+ },
683
+ content: {
684
+ flex: 1,
685
+ backgroundColor: theme.colors.background,
686
+ paddingHorizontal: theme.spacing.lg,
687
+ paddingTop: theme.spacing.md,
688
+ borderTopLeftRadius: moderateScale(30),
689
+ borderTopRightRadius: moderateScale(30),
690
+ },
691
+ sectionTitle: {
692
+ fontFamily: Fonts.outfitMedium,
693
+ fontSize: theme.typography.fontSize.xs,
694
+ color: theme.colors.blue,
695
+ marginBottom: theme.spacing.md,
696
+ letterSpacing: 0.5,
697
+ },
698
+ clubsList: {
699
+ flex: 1,
700
+ },
701
+ flatListContent: {
702
+ paddingBottom: theme.spacing.xl,
703
+ },
704
+ emptyContainer: {
705
+ flex: 1,
706
+ justifyContent: 'center',
707
+ alignItems: 'center',
708
+ paddingVertical: theme.spacing.xl,
709
+ },
710
+ bottomContainer: {
711
+ height: moderateScale(120),
712
+ paddingHorizontal: theme.spacing.lg,
713
+ paddingVertical: theme.spacing.md,
714
+ backgroundColor: theme.colors.white,
715
+ },
716
+ nextButton: {
717
+ width: '100%',
718
+ marginBottom: theme.spacing.md,
719
+ },
720
+ emptyListText: {
721
+ fontFamily: Fonts.outfitMedium,
722
+ fontSize: theme.typography.fontSize.md,
723
+ color: theme.colors.text,
724
+ textAlign: 'center',
725
+ marginBottom: theme.spacing.xs,
726
+ },
727
+ emptySubText: {
728
+ fontFamily: Fonts.outfitRegular,
729
+ fontSize: theme.typography.fontSize.sm,
730
+ color: theme.colors.textSecondary,
731
+ textAlign: 'center',
732
+ },
733
+ footerLoader: {
734
+ flexDirection: 'row',
735
+ justifyContent: 'center',
736
+ alignItems: 'center',
737
+ paddingVertical: theme.spacing.md,
738
+ gap: theme.spacing.sm,
739
+ },
740
+ footerText: {
741
+ fontFamily: Fonts.outfitMedium,
742
+ fontSize: theme.typography.fontSize.sm,
743
+ color: theme.colors.textSecondary,
744
+ },
745
+ errorText: {
746
+ fontFamily: Fonts.outfitMedium,
747
+ fontSize: theme.typography.fontSize.md,
748
+ color: theme.colors.error,
749
+ textAlign: 'center',
750
+ marginBottom: theme.spacing.md,
751
+ },
752
+ retryButton: {
753
+ backgroundColor: theme.colors.primary,
754
+ paddingHorizontal: theme.spacing.lg,
755
+ paddingVertical: theme.spacing.sm,
756
+ borderRadius: theme.borderRadius.md,
757
+ },
758
+ retryButtonText: {
759
+ fontFamily: Fonts.outfitMedium,
760
+ fontSize: theme.typography.fontSize.md,
761
+ color: theme.colors.white,
762
+ textAlign: 'center',
763
+ },
764
+ modalOverlay: {
765
+ flex: 1,
766
+ justifyContent: 'flex-end',
767
+ },
768
+ modalBackdrop: {
769
+ position: 'absolute',
770
+ top: 0,
771
+ left: 0,
772
+ right: 0,
773
+ bottom: 0,
774
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
775
+ },
776
+ modalContainer: {
777
+ backgroundColor: theme.colors.white,
778
+ borderTopLeftRadius: moderateScale(20),
779
+ borderTopRightRadius: moderateScale(20),
780
+ paddingHorizontal: theme.spacing.lg,
781
+ paddingTop: theme.spacing.sm,
782
+ paddingBottom: theme.spacing.xl,
783
+ height: '45%',
784
+ maxHeight: '45%',
785
+ },
786
+ dragHandle: {
787
+ width: moderateScale(40),
788
+ height: moderateScale(4),
789
+ backgroundColor: theme.colors.border,
790
+ borderRadius: moderateScale(2),
791
+ alignSelf: 'center',
792
+ marginBottom: theme.spacing.sm,
793
+ },
794
+ membersList: {
795
+ flex: 1,
796
+ },
797
+ memberListItem: {
798
+ height: moderateScale(70),
799
+ alignItems: 'center',
800
+ paddingVertical: theme.spacing.md,
801
+ flexDirection: 'row',
802
+ },
803
+ headerContainer: {
804
+ paddingVertical: theme.spacing.xs,
805
+ },
806
+ mamberHeaderTitle: {
807
+ fontFamily: Fonts.outfitSemiBold,
808
+ fontSize: moderateScale(14),
809
+ color: theme.colors.text,
810
+ },
811
+ profileImage: {
812
+ width: moderateScale(50),
813
+ height: moderateScale(50),
814
+ borderRadius: moderateScale(25),
815
+ marginRight: moderateScale(10),
816
+ },
817
+ memberName: {
818
+ fontFamily: Fonts.outfitSemiBold,
819
+ fontSize: moderateScale(14),
820
+ color: theme.colors.text,
821
+ },
822
+ memberNameContainer: {
823
+ flex: 1,
824
+ justifyContent: 'center',
825
+ },
826
+ memberNameSubtitle: {
827
+ fontFamily: Fonts.outfitMedium,
828
+ fontSize: moderateScale(12),
829
+ color: theme.colors.border,
830
+ },
831
+ searchInput: {
832
+ },
833
+ searchContainer: {
834
+ marginBottom: theme.spacing.md,
835
+ },
836
+ placeholderStyle: {
837
+ fontFamily: Fonts.outfitRegular,
838
+ fontSize: moderateScale(5),
839
+ color: theme.colors.textSecondary,
840
+ },
841
+ });