create-gufran-expo-app 2.0.0 → 2.0.1
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/lib/createApp.js +7 -2
- package/package.json +1 -1
- package/template/App.tsx +118 -0
- package/template/ReactotronConfig.js +5 -0
- package/template/android/app/build.gradle +184 -0
- package/template/android/app/debug.keystore +0 -0
- package/template/android/app/google-services.json +29 -0
- package/template/android/app/proguard-rules.pro +14 -0
- package/template/android/app/src/debug/AndroidManifest.xml +7 -0
- package/template/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
- package/template/android/app/src/main/AndroidManifest.xml +28 -0
- package/template/android/app/src/main/assets/fonts/Outfit-Bold.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/Outfit-Light.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/Outfit-Medium.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/Outfit-Regular.ttf +0 -0
- package/template/android/app/src/main/assets/fonts/Outfit-SemiBold.ttf +0 -0
- package/template/android/app/src/main/java/com/club/yakka/MainActivity.kt +61 -0
- package/template/android/app/src/main/java/com/club/yakka/MainApplication.kt +60 -0
- package/template/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
- package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/template/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
- package/template/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
- package/template/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
- package/template/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
- package/template/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
- package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
- package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
- package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
- package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- package/template/android/app/src/main/res/values/colors.xml +6 -0
- package/template/android/app/src/main/res/values/strings.xml +5 -0
- package/template/android/app/src/main/res/values/styles.xml +14 -0
- package/template/android/app/src/main/res/values-night/colors.xml +1 -0
- package/template/android/build.gradle +33 -0
- package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/template/android/gradle.properties +65 -0
- package/template/android/gradlew +251 -0
- package/template/android/gradlew.bat +94 -0
- package/template/android/settings.gradle +39 -0
- package/template/app.json +69 -0
- package/template/assets/adaptive-icon.png +0 -0
- package/template/assets/adaptive-icon1.png +0 -0
- package/template/assets/app_icon.png +0 -0
- package/template/assets/favicon.png +0 -0
- package/template/assets/icon.png +0 -0
- package/template/assets/splash-icon.png +0 -0
- package/template/babel-plugin-disable-font-scaling.js +41 -0
- package/template/babel.config.js +28 -0
- package/template/firebase.json +5 -0
- package/template/index.ts +24 -0
- package/template/ios/.xcode.env +11 -0
- package/template/ios/ClubYakka/AppDelegate.swift +74 -0
- package/template/ios/ClubYakka/ClubYakka-Bridging-Header.h +3 -0
- package/template/ios/ClubYakka/ClubYakka.entitlements +8 -0
- package/template/ios/ClubYakka/GoogleService-Info.plist +30 -0
- package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
- package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
- package/template/ios/ClubYakka/Images.xcassets/Contents.json +6 -0
- package/template/ios/ClubYakka/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
- package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
- package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
- package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
- package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
- package/template/ios/ClubYakka/Info.plist +101 -0
- package/template/ios/ClubYakka/PrivacyInfo.xcprivacy +50 -0
- package/template/ios/ClubYakka/SplashScreen.storyboard +48 -0
- package/template/ios/ClubYakka/Supporting/Expo.plist +12 -0
- package/template/ios/ClubYakka.xcodeproj/project.pbxproj +669 -0
- package/template/ios/ClubYakka.xcodeproj/xcshareddata/xcschemes/ClubYakka.xcscheme +88 -0
- package/template/ios/ClubYakka.xcworkspace/contents.xcworkspacedata +10 -0
- package/template/ios/ClubYakka.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +5 -0
- package/template/ios/Podfile +84 -0
- package/template/ios/Podfile.lock +2698 -0
- package/template/ios/Podfile.properties.json +8 -0
- package/template/metro.config.js +17 -0
- package/template/package.json +70 -0
- package/template/patches/react-native-background-upload+6.6.0.patch +704 -0
- package/template/react-native.config.js +7 -0
- package/template/src/assets/fonts/Outfit-Bold.ttf +0 -0
- package/template/src/assets/fonts/Outfit-Light.ttf +0 -0
- package/template/src/assets/fonts/Outfit-Medium.ttf +0 -0
- package/template/src/assets/fonts/Outfit-Regular.ttf +0 -0
- package/template/src/assets/fonts/Outfit-SemiBold.ttf +0 -0
- package/template/src/assets/icons/BG.svg +33 -0
- package/template/src/assets/icons/Ic_Users.svg +6 -0
- package/template/src/assets/icons/arrow-left-white.svg +4 -0
- package/template/src/assets/icons/arrow-left.svg +4 -0
- package/template/src/assets/icons/bell.svg +4 -0
- package/template/src/assets/icons/bottomSheetIcone.svg +3 -0
- package/template/src/assets/icons/call.svg +4 -0
- package/template/src/assets/icons/camera.svg +0 -0
- package/template/src/assets/icons/chatAppleGreenBG.svg +22 -0
- package/template/src/assets/icons/check.svg +4 -0
- package/template/src/assets/icons/check_Radio.svg +4 -0
- package/template/src/assets/icons/chevron-right.svg +3 -0
- package/template/src/assets/icons/clubDefauldImage.png +0 -0
- package/template/src/assets/icons/curvBackgroundView.svg +3 -0
- package/template/src/assets/icons/defaultClub.png +0 -0
- package/template/src/assets/icons/email.svg +3 -0
- package/template/src/assets/icons/emptyUser.svg +5 -0
- package/template/src/assets/icons/eye-Hide.svg +8 -0
- package/template/src/assets/icons/eye.svg +4 -0
- package/template/src/assets/icons/gallery.svg +4 -0
- package/template/src/assets/icons/home.svg +4 -0
- package/template/src/assets/icons/ic_Calendar.svg +14 -0
- package/template/src/assets/icons/ic_Calendar_blue.svg +12 -0
- package/template/src/assets/icons/ic_Calendar_white.svg +12 -0
- package/template/src/assets/icons/ic_Chat.svg +14 -0
- package/template/src/assets/icons/ic_ChatAppleGreen.svg +21 -0
- package/template/src/assets/icons/ic_ChatAppleWhite.svg +21 -0
- package/template/src/assets/icons/ic_Download.svg +6 -0
- package/template/src/assets/icons/ic_Events.svg +6 -0
- package/template/src/assets/icons/ic_HeadCoachIcon.svg +3 -0
- package/template/src/assets/icons/ic_Membership.svg +10 -0
- package/template/src/assets/icons/ic_Notification.svg +6 -0
- package/template/src/assets/icons/ic_Raffles.svg +14 -0
- package/template/src/assets/icons/ic_Referral_Members.svg +12 -0
- package/template/src/assets/icons/ic_Shop.svg +13 -0
- package/template/src/assets/icons/ic_Teams.svg +8 -0
- package/template/src/assets/icons/ic_Volunteer.svg +9 -0
- package/template/src/assets/icons/ic_add.svg +3 -0
- package/template/src/assets/icons/ic_addCircle.svg +5 -0
- package/template/src/assets/icons/ic_chatSend.svg +4 -0
- package/template/src/assets/icons/ic_chat_blue_bg.svg +22 -0
- package/template/src/assets/icons/ic_clock_blue.svg +4 -0
- package/template/src/assets/icons/ic_delete.svg +8 -0
- package/template/src/assets/icons/ic_more.svg +5 -0
- package/template/src/assets/icons/ic_mute.svg +10 -0
- package/template/src/assets/icons/ic_pdf.svg +3 -0
- package/template/src/assets/icons/ic_pending_AppleGreen.svg +11 -0
- package/template/src/assets/icons/ic_right_appleGreen.svg +11 -0
- package/template/src/assets/icons/ic_unread_chat_blue_bg.svg +23 -0
- package/template/src/assets/icons/ic_volunteer_Member.svg +8 -0
- package/template/src/assets/icons/index.ts +144 -0
- package/template/src/assets/icons/location-blue.svg +4 -0
- package/template/src/assets/icons/location-white.svg +4 -0
- package/template/src/assets/icons/location.svg +4 -0
- package/template/src/assets/icons/lock.svg +5 -0
- package/template/src/assets/icons/log-out.svg +5 -0
- package/template/src/assets/icons/login_logo.svg +9 -0
- package/template/src/assets/icons/mail.svg +4 -0
- package/template/src/assets/icons/or_saprater.svg +5 -0
- package/template/src/assets/icons/password.svg +4 -0
- package/template/src/assets/icons/profile-appleGreen.svg +6 -0
- package/template/src/assets/icons/search.svg +3 -0
- package/template/src/assets/icons/settings.svg +4 -0
- package/template/src/assets/icons/success.svg +4 -0
- package/template/src/assets/icons/unCheck_Radio.svg +3 -0
- package/template/src/assets/icons/uncheck.svg +3 -0
- package/template/src/assets/icons/upload_Image.svg +6 -0
- package/template/src/assets/icons/upload_Image_Member.svg +6 -0
- package/template/src/assets/icons/user.svg +4 -0
- package/template/src/assets/icons/wifi.svg +1 -0
- package/template/src/assets/images/Splash.png +0 -0
- package/template/src/assets/images/SplashLogo.png +0 -0
- package/template/src/assets/images/background.png +0 -0
- package/template/src/assets/images/clubDefauldImage.png +0 -0
- package/template/src/assets/images/index.tsx +9 -0
- package/template/src/assets/index.ts +1 -0
- package/template/src/components/common/AddMemberModal.tsx +543 -0
- package/template/src/components/common/AppLoader.tsx +91 -0
- package/template/src/components/common/Button.tsx +173 -0
- package/template/src/components/common/ClubCard.tsx +248 -0
- package/template/src/components/common/Icon.tsx +93 -0
- package/template/src/components/common/IconAlt.tsx +65 -0
- package/template/src/components/common/IconButton.tsx +92 -0
- package/template/src/components/common/ImagePicker.tsx +451 -0
- package/template/src/components/common/LoadingSpinner.tsx +30 -0
- package/template/src/components/common/OTPInput.tsx +128 -0
- package/template/src/components/common/ReminderCalendar.tsx +129 -0
- package/template/src/components/common/ReminderCardItem.tsx +91 -0
- package/template/src/components/common/SafeAreaWrapper.tsx +27 -0
- package/template/src/components/common/SetReminderModal.tsx +308 -0
- package/template/src/components/common/SlowInternet.js +57 -0
- package/template/src/components/common/TeamCard.tsx +297 -0
- package/template/src/components/common/TextInput.tsx +227 -0
- package/template/src/components/common/ToastConfig.tsx +103 -0
- package/template/src/components/common/ToastManager.ts +86 -0
- package/template/src/components/common/UploadProgressModal.tsx +284 -0
- package/template/src/components/common/WrapperContainer.tsx +39 -0
- package/template/src/components/common/index.ts +19 -0
- package/template/src/constants/Constants.tsx +7 -0
- package/template/src/constants/Fonts.tsx +30 -0
- package/template/src/constants/index.ts +45 -0
- package/template/src/constants/strings.ts +211 -0
- package/template/src/constants/theme.ts +72 -0
- package/template/src/contexts/AuthContext.tsx +268 -0
- package/template/src/contexts/index.ts +2 -0
- package/template/src/hooks/index.ts +3 -0
- package/template/src/hooks/useImageUpload.ts +199 -0
- package/template/src/index.ts +8 -0
- package/template/src/navigation/AuthStack.tsx +67 -0
- package/template/src/navigation/MainStack.tsx +183 -0
- package/template/src/navigation/MiddleStack.tsx +35 -0
- package/template/src/navigation/RootNavigator.tsx +101 -0
- package/template/src/navigation/index.ts +5 -0
- package/template/src/navigation/navigationRef.ts +5 -0
- package/template/src/providers/QueryProvider.tsx +30 -0
- package/template/src/screens/DetailsScreen.tsx +271 -0
- package/template/src/screens/HomeScreen.tsx +736 -0
- package/template/src/screens/ProfileScreen.tsx +202 -0
- package/template/src/screens/SettingsScreen.tsx +253 -0
- package/template/src/screens/SportHubScreen.tsx +280 -0
- package/template/src/screens/auth/AddMamber.tsx +428 -0
- package/template/src/screens/auth/ForgotPasswordScreen.tsx +176 -0
- package/template/src/screens/auth/LoginScreen.tsx +286 -0
- package/template/src/screens/auth/OTPVerifyScreen.tsx +359 -0
- package/template/src/screens/auth/RegisterScreen.tsx +430 -0
- package/template/src/screens/auth/SuccessScreen.tsx +201 -0
- package/template/src/screens/auth/WalcomeScreen.tsx +274 -0
- package/template/src/screens/auth/index.ts +8 -0
- package/template/src/screens/chat/ChatScreen.tsx +1819 -0
- package/template/src/screens/chat/ChatThreadsScreen.tsx +360 -0
- package/template/src/screens/chat/ReportMessageScreen.tsx +238 -0
- package/template/src/screens/clubs/Announcements.tsx +426 -0
- package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +568 -0
- package/template/src/screens/clubs/ClubDeteils.tsx +497 -0
- package/template/src/screens/clubs/JoinClub.tsx +841 -0
- package/template/src/screens/events/EventScreen.tsx +460 -0
- package/template/src/screens/index.ts +42 -0
- package/template/src/screens/raffles/MyReferralMembersScreen.tsx +758 -0
- package/template/src/screens/raffles/RaffleDetailsScreen.tsx +762 -0
- package/template/src/screens/raffles/RafflesScreen.tsx +495 -0
- package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +390 -0
- package/template/src/screens/teams/JoinTeamScreen.tsx +464 -0
- package/template/src/screens/teams/MyTeamDetailsScreen.tsx +979 -0
- package/template/src/screens/teams/MyTeamScreen.tsx +568 -0
- package/template/src/screens/teams/PendingRequestsScreen.tsx +426 -0
- package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +631 -0
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +1049 -0
- package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +608 -0
- package/template/src/services/api.ts +167 -0
- package/template/src/services/authService.ts +422 -0
- package/template/src/services/index.ts +5 -0
- package/template/src/services/mainServices.ts +1963 -0
- package/template/src/stores/authStore.ts +159 -0
- package/template/src/stores/index.ts +2 -0
- package/template/src/types/index.ts +85 -0
- package/template/src/types/navigation.ts +206 -0
- package/template/src/utils/AzureUploaderService.ts +371 -0
- package/template/src/utils/ClubSearchManager.ts +222 -0
- package/template/src/utils/NotificationManager.ts +503 -0
- package/template/src/utils/UploadDebugUtil.ts +107 -0
- package/template/src/utils/imagePicker.ts +321 -0
- package/template/src/utils/index.ts +111 -0
- package/template/src/utils/permissions.ts +277 -0
- package/template/src/utils/scaling.ts +14 -0
- package/template/src/utils/storage.ts +247 -0
- package/template/src/utils/usePermissions.ts +275 -0
- package/template/src/utils/validation.ts +340 -0
- package/template/tsconfig.json +50 -0
- package/template/types/svg.d.ts +6 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import Upload, { UploadOptions } from "react-native-background-upload";
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import { UploadDebugUtil } from "./UploadDebugUtil";
|
|
4
|
+
|
|
5
|
+
// These types help ensure the component using this service provides the correct functions.
|
|
6
|
+
type ProgressCallback = (progress: number) => void;
|
|
7
|
+
type CompletionCallback = (uploadedUrl: string) => void;
|
|
8
|
+
type ErrorCallback = (error: Error) => void;
|
|
9
|
+
type CancelCallback = () => void;
|
|
10
|
+
|
|
11
|
+
export interface UploadResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
url?: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UploadOptions {
|
|
18
|
+
maxRetries?: number;
|
|
19
|
+
retryDelay?: number;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AzureUploadService {
|
|
24
|
+
/**
|
|
25
|
+
* Convert file URI to format compatible with Android upload service
|
|
26
|
+
* Android only supports absolute paths (/) and content:// URIs
|
|
27
|
+
*/
|
|
28
|
+
private static normalizeFileUriForAndroid(fileUri: string): string {
|
|
29
|
+
if (Platform.OS !== "android") {
|
|
30
|
+
return fileUri;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`Normalizing file URI for Android: ${fileUri}`);
|
|
34
|
+
|
|
35
|
+
if (fileUri.startsWith("file://")) {
|
|
36
|
+
// Remove file:// prefix to get absolute path
|
|
37
|
+
const absolutePath = fileUri.replace("file://", "");
|
|
38
|
+
console.log(`Converted file:// to absolute path: ${absolutePath}`);
|
|
39
|
+
return absolutePath;
|
|
40
|
+
} else if (fileUri.startsWith("content://")) {
|
|
41
|
+
// Keep content:// URIs as is
|
|
42
|
+
console.log(`Using content URI: ${fileUri}`);
|
|
43
|
+
return fileUri;
|
|
44
|
+
} else if (fileUri.startsWith("/")) {
|
|
45
|
+
// Already an absolute path, keep as is
|
|
46
|
+
console.log(`Using absolute path: ${fileUri}`);
|
|
47
|
+
return fileUri;
|
|
48
|
+
} else {
|
|
49
|
+
// If it's a relative path, make it absolute (fallback)
|
|
50
|
+
const absolutePath = `/${fileUri}`;
|
|
51
|
+
console.log(`Converted to absolute path: ${absolutePath}`);
|
|
52
|
+
return absolutePath;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Alternative file existence check using React Native's built-in methods
|
|
58
|
+
*/
|
|
59
|
+
private static async checkFileExistsAlternative(fileUri: string): Promise<boolean> {
|
|
60
|
+
try {
|
|
61
|
+
// For Android, try to access the file directly using the legacy API
|
|
62
|
+
if (Platform.OS === "android") {
|
|
63
|
+
const normalizedPath = this.normalizeFileUriForAndroid(fileUri);
|
|
64
|
+
|
|
65
|
+
// Try using the legacy expo-file-system API as fallback
|
|
66
|
+
const { getInfoAsync } = require('expo-file-system/legacy');
|
|
67
|
+
const fileInfo = await getInfoAsync(normalizedPath);
|
|
68
|
+
console.log(`Alternative file check - Path: ${normalizedPath}, Exists: ${fileInfo.exists}`);
|
|
69
|
+
return fileInfo.exists;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// For iOS, use the original method
|
|
73
|
+
return await UploadDebugUtil.checkFileExists(fileUri);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(`Alternative file check failed for ${fileUri}:`, error);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Upload file with retry mechanism and comprehensive error handling
|
|
82
|
+
*/
|
|
83
|
+
public static async uploadFileWithRetry({
|
|
84
|
+
fileUri,
|
|
85
|
+
sasUrl,
|
|
86
|
+
blobUrl,
|
|
87
|
+
options = {},
|
|
88
|
+
onProgress,
|
|
89
|
+
onComplete,
|
|
90
|
+
onError,
|
|
91
|
+
onCancelled,
|
|
92
|
+
}: {
|
|
93
|
+
fileUri: string;
|
|
94
|
+
sasUrl: string;
|
|
95
|
+
blobUrl: string;
|
|
96
|
+
options?: UploadOptions;
|
|
97
|
+
onProgress?: ProgressCallback;
|
|
98
|
+
onComplete?: CompletionCallback;
|
|
99
|
+
onError?: ErrorCallback;
|
|
100
|
+
onCancelled?: CancelCallback;
|
|
101
|
+
}): Promise<UploadResult> {
|
|
102
|
+
const { maxRetries = 3, retryDelay = 1000 } = options;
|
|
103
|
+
let lastError: Error | null = null;
|
|
104
|
+
|
|
105
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
106
|
+
try {
|
|
107
|
+
console.log(`Upload attempt ${attempt}/${maxRetries}`);
|
|
108
|
+
|
|
109
|
+
const result = await this.uploadFile({
|
|
110
|
+
fileUri,
|
|
111
|
+
sasUrl,
|
|
112
|
+
blobUrl,
|
|
113
|
+
onProgress,
|
|
114
|
+
onComplete,
|
|
115
|
+
onError,
|
|
116
|
+
onCancelled,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
url: result,
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
lastError = error as Error;
|
|
125
|
+
console.error(`Upload attempt ${attempt} failed:`, error);
|
|
126
|
+
|
|
127
|
+
if (attempt < maxRetries) {
|
|
128
|
+
console.log(`Retrying in ${retryDelay}ms...`);
|
|
129
|
+
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: lastError?.message || 'Upload failed after all retry attempts',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validate file before upload
|
|
142
|
+
*/
|
|
143
|
+
private static validateFile(fileUri: string): { isValid: boolean; error?: string } {
|
|
144
|
+
if (!fileUri) {
|
|
145
|
+
return { isValid: false, error: 'File URI is required' };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check file extension
|
|
149
|
+
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
|
|
150
|
+
const hasValidExtension = allowedExtensions.some(ext =>
|
|
151
|
+
fileUri.toLowerCase().includes(ext)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (!hasValidExtension) {
|
|
155
|
+
return {
|
|
156
|
+
isValid: false,
|
|
157
|
+
error: `Invalid file type. Allowed types: ${allowedExtensions.join(', ')}`
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { isValid: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public static async uploadFile({
|
|
165
|
+
fileUri,
|
|
166
|
+
sasUrl,
|
|
167
|
+
blobUrl,
|
|
168
|
+
onProgress,
|
|
169
|
+
onComplete,
|
|
170
|
+
onError,
|
|
171
|
+
onCancelled,
|
|
172
|
+
}: {
|
|
173
|
+
fileUri: string;
|
|
174
|
+
sasUrl: string;
|
|
175
|
+
blobUrl: string;
|
|
176
|
+
onProgress?: ProgressCallback;
|
|
177
|
+
onComplete?: CompletionCallback;
|
|
178
|
+
onError?: ErrorCallback;
|
|
179
|
+
onCancelled?: CancelCallback;
|
|
180
|
+
}): Promise<string> {
|
|
181
|
+
try {
|
|
182
|
+
console.log(`Starting upload for file: ${fileUri}`);
|
|
183
|
+
console.log(`Platform: ${Platform.OS}`);
|
|
184
|
+
console.log(`SAS URL: ${sasUrl}`);
|
|
185
|
+
|
|
186
|
+
// Validate file before upload
|
|
187
|
+
const validation = this.validateFile(fileUri);
|
|
188
|
+
if (!validation.isValid) {
|
|
189
|
+
const error = new Error(validation.error || 'File validation failed');
|
|
190
|
+
if (onError) {
|
|
191
|
+
onError(error);
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Debug upload service on Android
|
|
197
|
+
if (Platform.OS === "android") {
|
|
198
|
+
UploadDebugUtil.debugUploadService();
|
|
199
|
+
UploadDebugUtil.logUploadServiceStatus();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Normalize file URI for Android
|
|
203
|
+
const normalizedFileUri = this.normalizeFileUriForAndroid(fileUri);
|
|
204
|
+
|
|
205
|
+
// Check if file exists (helpful for debugging)
|
|
206
|
+
let fileExists = false;
|
|
207
|
+
try {
|
|
208
|
+
fileExists = await UploadDebugUtil.checkFileExists(fileUri);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.warn(`Primary file check failed, trying alternative method: ${error}`);
|
|
211
|
+
try {
|
|
212
|
+
fileExists = await this.checkFileExistsAlternative(fileUri);
|
|
213
|
+
} catch (altError) {
|
|
214
|
+
console.warn(`Alternative file check also failed: ${altError}`);
|
|
215
|
+
// For Android, sometimes the file exists but can't be checked due to permissions
|
|
216
|
+
// Continue with upload anyway - let the upload service handle it
|
|
217
|
+
if (Platform.OS === "android") {
|
|
218
|
+
console.log("Skipping file existence check for Android - proceeding with upload");
|
|
219
|
+
fileExists = true;
|
|
220
|
+
} else {
|
|
221
|
+
fileExists = false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!fileExists) {
|
|
227
|
+
const errorMessage = `File does not exist at path: ${fileUri}`;
|
|
228
|
+
console.error(errorMessage);
|
|
229
|
+
if (onError) {
|
|
230
|
+
onError(new Error(errorMessage));
|
|
231
|
+
}
|
|
232
|
+
throw new Error(errorMessage);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Validate the normalized URI
|
|
236
|
+
if (
|
|
237
|
+
Platform.OS === "android" &&
|
|
238
|
+
!UploadDebugUtil.validateFileUri(normalizedFileUri)
|
|
239
|
+
) {
|
|
240
|
+
const errorMessage = `Invalid file URI for Android: ${normalizedFileUri}. Android upload service only supports absolute paths (/) and content:// URIs.`;
|
|
241
|
+
console.error(errorMessage);
|
|
242
|
+
if (onError) {
|
|
243
|
+
onError(new Error(errorMessage));
|
|
244
|
+
}
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Configure the upload options for the background uploader
|
|
249
|
+
const uploadOptions: UploadOptions = {
|
|
250
|
+
url: sasUrl,
|
|
251
|
+
path: normalizedFileUri,
|
|
252
|
+
method: "PUT",
|
|
253
|
+
type: "raw",
|
|
254
|
+
headers: {
|
|
255
|
+
"x-ms-blob-type": "BlockBlob",
|
|
256
|
+
"Content-Type": "application/octet-stream",
|
|
257
|
+
},
|
|
258
|
+
notification: {
|
|
259
|
+
enabled: true, // Android requires this to be true
|
|
260
|
+
autoClear: true, // Automatically clear the notification after completion
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Add Android-specific configurations
|
|
265
|
+
if (Platform.OS === "android") {
|
|
266
|
+
uploadOptions.customUploadId = `upload_${Date.now()}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log(`Upload options:`, JSON.stringify(uploadOptions, null, 2));
|
|
270
|
+
|
|
271
|
+
let uploadId: string;
|
|
272
|
+
try {
|
|
273
|
+
uploadId = await Upload.startUpload(uploadOptions);
|
|
274
|
+
console.log(`Upload started successfully with ID: ${uploadId}`);
|
|
275
|
+
} catch (startUploadError) {
|
|
276
|
+
const errorMessage = `Failed to start upload: ${startUploadError}`;
|
|
277
|
+
console.error(errorMessage);
|
|
278
|
+
if (onError) {
|
|
279
|
+
onError(new Error(errorMessage));
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Set up timeout for Android
|
|
285
|
+
let uploadTimeout: NodeJS.Timeout | null = null;
|
|
286
|
+
if (Platform.OS === "android") {
|
|
287
|
+
uploadTimeout = setTimeout(() => {
|
|
288
|
+
console.log(`Upload timeout for ID: ${uploadId}`);
|
|
289
|
+
Upload.cancelUpload(uploadId);
|
|
290
|
+
if (onError) {
|
|
291
|
+
onError(
|
|
292
|
+
new Error(
|
|
293
|
+
"Upload timeout - Please check your internet connection"
|
|
294
|
+
)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}, 1800000); // 30 minutes timeout for slow networks
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const clearUploadTimeout = () => {
|
|
301
|
+
if (uploadTimeout) {
|
|
302
|
+
clearTimeout(uploadTimeout);
|
|
303
|
+
uploadTimeout = null;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// Return a Promise that resolves when upload completes
|
|
308
|
+
return new Promise<string>((resolve, reject) => {
|
|
309
|
+
// Add listeners to track the upload lifecycle
|
|
310
|
+
Upload.addListener("progress", uploadId, (data: any) => {
|
|
311
|
+
if (onProgress) {
|
|
312
|
+
onProgress(Math.floor(data.progress));
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
Upload.addListener("completed", uploadId, (data: any) => {
|
|
317
|
+
clearUploadTimeout();
|
|
318
|
+
|
|
319
|
+
// Azure returns 201 on successful PUT. The final URL was already given to us.
|
|
320
|
+
if (data.responseCode === 201) {
|
|
321
|
+
console.log(`Upload completed successfully for ID: ${uploadId}`);
|
|
322
|
+
if (onComplete) {
|
|
323
|
+
onComplete(blobUrl);
|
|
324
|
+
}
|
|
325
|
+
resolve(blobUrl);
|
|
326
|
+
} else {
|
|
327
|
+
const errorMessage = `Upload failed with response code: ${data.responseCode}`;
|
|
328
|
+
console.error(errorMessage);
|
|
329
|
+
const error = new Error(
|
|
330
|
+
`Server responded with status code: ${data.responseCode}\nBody: ${data.responseBody}`
|
|
331
|
+
);
|
|
332
|
+
if (onError) {
|
|
333
|
+
onError(error);
|
|
334
|
+
}
|
|
335
|
+
reject(error);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
Upload.addListener("cancelled", uploadId, () => {
|
|
340
|
+
clearUploadTimeout();
|
|
341
|
+
console.log(`Upload cancelled for ID: ${uploadId}`);
|
|
342
|
+
const error = new Error("Upload was cancelled");
|
|
343
|
+
if (onCancelled) {
|
|
344
|
+
onCancelled();
|
|
345
|
+
}
|
|
346
|
+
reject(error);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
Upload.addListener("error", uploadId, (data: any) => {
|
|
350
|
+
clearUploadTimeout();
|
|
351
|
+
console.error(`Upload error for ID: ${uploadId}`, data.error);
|
|
352
|
+
const error = new Error(data.error || "Unknown upload error");
|
|
353
|
+
if (onError) {
|
|
354
|
+
onError(error);
|
|
355
|
+
}
|
|
356
|
+
reject(error);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error("An error occurred during the file upload process:", error);
|
|
361
|
+
if (onError) {
|
|
362
|
+
onError(error as Error);
|
|
363
|
+
}
|
|
364
|
+
throw error; // Re-throw to reject the Promise
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { ClubSearchRequest, ClubSearchResponse } from '../services/authService';
|
|
2
|
+
|
|
3
|
+
export interface ClubData {
|
|
4
|
+
id?: string | number;
|
|
5
|
+
name?: string;
|
|
6
|
+
clubName?: string;
|
|
7
|
+
image?: string;
|
|
8
|
+
profileImage?: string;
|
|
9
|
+
logo?: string;
|
|
10
|
+
members?: number;
|
|
11
|
+
memberCount?: number;
|
|
12
|
+
address?: string;
|
|
13
|
+
location?: string;
|
|
14
|
+
clubCode?: string;
|
|
15
|
+
color?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PaginationState {
|
|
20
|
+
clubs: ClubData[];
|
|
21
|
+
currentPage: number;
|
|
22
|
+
totalPages: number;
|
|
23
|
+
totalCount: number;
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
isLoadingMore: boolean;
|
|
26
|
+
isRefreshing: boolean;
|
|
27
|
+
hasMoreData: boolean;
|
|
28
|
+
error: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ClubSearchManager {
|
|
32
|
+
private state: PaginationState;
|
|
33
|
+
private onStateChange: (state: PaginationState) => void;
|
|
34
|
+
private searchClubs: (params: ClubSearchRequest) => Promise<any>;
|
|
35
|
+
private pageSize: number;
|
|
36
|
+
|
|
37
|
+
constructor(
|
|
38
|
+
onStateChange: (state: PaginationState) => void,
|
|
39
|
+
searchClubs: (params: ClubSearchRequest) => Promise<any>,
|
|
40
|
+
pageSize: number = 5
|
|
41
|
+
) {
|
|
42
|
+
this.onStateChange = onStateChange;
|
|
43
|
+
this.searchClubs = searchClubs;
|
|
44
|
+
this.pageSize = pageSize;
|
|
45
|
+
|
|
46
|
+
this.state = {
|
|
47
|
+
clubs: [],
|
|
48
|
+
currentPage: 0,
|
|
49
|
+
totalPages: 0,
|
|
50
|
+
totalCount: 0,
|
|
51
|
+
isLoading: false,
|
|
52
|
+
isLoadingMore: false,
|
|
53
|
+
isRefreshing: false,
|
|
54
|
+
hasMoreData: true,
|
|
55
|
+
error: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Initial load
|
|
60
|
+
async loadInitialData(): Promise<void> {
|
|
61
|
+
this.setState({
|
|
62
|
+
...this.state,
|
|
63
|
+
isLoading: true,
|
|
64
|
+
error: null,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const response = await this.searchClubs({
|
|
69
|
+
pageNumber: 1,
|
|
70
|
+
pageSize: this.pageSize,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log('response1', response);
|
|
74
|
+
const data = response?.data?.data || {};
|
|
75
|
+
const clubs = data || [];
|
|
76
|
+
|
|
77
|
+
this.setState({
|
|
78
|
+
...this.state,
|
|
79
|
+
clubs,
|
|
80
|
+
currentPage: 1,
|
|
81
|
+
totalPages: data?.totalPages || 0,
|
|
82
|
+
totalCount: data?.totalCount || 0,
|
|
83
|
+
isLoading: false,
|
|
84
|
+
hasMoreData: clubs.length < (data?.totalCount || 0),
|
|
85
|
+
error: null,
|
|
86
|
+
});
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
this.setState({
|
|
89
|
+
...this.state,
|
|
90
|
+
isLoading: false,
|
|
91
|
+
error: error?.message || 'Failed to load clubs',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Load more data (pagination)
|
|
97
|
+
async loadMoreData(): Promise<void> {
|
|
98
|
+
if (this.state.isLoadingMore || !this.state.hasMoreData) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const nextPage = this.state.currentPage + 1;
|
|
103
|
+
|
|
104
|
+
this.setState({
|
|
105
|
+
...this.state,
|
|
106
|
+
isLoadingMore: true,
|
|
107
|
+
error: null,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const response = await this.searchClubs({
|
|
112
|
+
pageNumber: nextPage,
|
|
113
|
+
pageSize: this.pageSize,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log('response2', response);
|
|
117
|
+
|
|
118
|
+
const data = response?.data?.data || {};
|
|
119
|
+
const newClubs = data || [];
|
|
120
|
+
|
|
121
|
+
this.setState({
|
|
122
|
+
...this.state,
|
|
123
|
+
clubs: [...this.state.clubs, ...newClubs],
|
|
124
|
+
currentPage: nextPage,
|
|
125
|
+
totalPages: data?.totalPages || 0,
|
|
126
|
+
totalCount: data?.totalCount || 0,
|
|
127
|
+
isLoadingMore: false,
|
|
128
|
+
hasMoreData: this.state.clubs.length + newClubs.length < (data?.totalCount || 0),
|
|
129
|
+
error: null,
|
|
130
|
+
});
|
|
131
|
+
} catch (error: any) {
|
|
132
|
+
this.setState({
|
|
133
|
+
...this.state,
|
|
134
|
+
isLoadingMore: false,
|
|
135
|
+
error: error?.message || 'Failed to load more clubs',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Pull to refresh
|
|
141
|
+
async refreshData(): Promise<void> {
|
|
142
|
+
this.setState({
|
|
143
|
+
...this.state,
|
|
144
|
+
isRefreshing: true,
|
|
145
|
+
error: null,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const response = await this.searchClubs({
|
|
150
|
+
pageNumber: 1,
|
|
151
|
+
pageSize: this.pageSize,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
console.log('response3', response);
|
|
156
|
+
|
|
157
|
+
const data = response?.data?.data || {};
|
|
158
|
+
const clubs = data || [];
|
|
159
|
+
|
|
160
|
+
this.setState({
|
|
161
|
+
...this.state,
|
|
162
|
+
clubs,
|
|
163
|
+
currentPage: 0,
|
|
164
|
+
totalPages: data?.totalPages || 0,
|
|
165
|
+
totalCount: data?.totalCount || 0,
|
|
166
|
+
isRefreshing: false,
|
|
167
|
+
hasMoreData: clubs.length < (data?.totalCount || 0),
|
|
168
|
+
error: null,
|
|
169
|
+
});
|
|
170
|
+
} catch (error: any) {
|
|
171
|
+
this.setState({
|
|
172
|
+
...this.state,
|
|
173
|
+
isRefreshing: false,
|
|
174
|
+
error: error?.message || 'Failed to refresh clubs',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Reset data
|
|
180
|
+
resetData(): void {
|
|
181
|
+
this.setState({
|
|
182
|
+
clubs: [],
|
|
183
|
+
currentPage: 0,
|
|
184
|
+
totalPages: 0,
|
|
185
|
+
totalCount: 0,
|
|
186
|
+
isLoading: false,
|
|
187
|
+
isLoadingMore: false,
|
|
188
|
+
isRefreshing: false,
|
|
189
|
+
hasMoreData: true,
|
|
190
|
+
error: null,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Get current state
|
|
195
|
+
getState(): PaginationState {
|
|
196
|
+
return { ...this.state };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Set state helper
|
|
200
|
+
private setState(newState: PaginationState): void {
|
|
201
|
+
this.state = newState;
|
|
202
|
+
this.onStateChange(newState);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check if we can load more
|
|
206
|
+
canLoadMore(): boolean {
|
|
207
|
+
return this.state.hasMoreData && !this.state.isLoadingMore && !this.state.isLoading;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get loading footer text
|
|
211
|
+
getLoadingFooterText(): string {
|
|
212
|
+
if (this.state.isLoadingMore) {
|
|
213
|
+
return 'Loading more clubs...';
|
|
214
|
+
}
|
|
215
|
+
if (!this.state.hasMoreData && this.state.clubs.length > 0) {
|
|
216
|
+
return 'No more clubs to load';
|
|
217
|
+
}
|
|
218
|
+
return '';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export default ClubSearchManager;
|