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,503 @@
1
+ import messaging, {
2
+ getMessaging,
3
+ getInitialNotification,
4
+ onMessage,
5
+ onNotificationOpenedApp,
6
+ requestPermission,
7
+ getToken
8
+ } from "@react-native-firebase/messaging";
9
+ import { getApp } from "@react-native-firebase/app";
10
+ import { MMKV } from "react-native-mmkv";
11
+
12
+ import { Platform, PermissionsAndroid, AppState } from "react-native";
13
+ import notifee, {
14
+ AndroidImportance,
15
+ AndroidVisibility,
16
+ EventDetail,
17
+ EventType,
18
+ InitialNotification,
19
+ } from "@notifee/react-native";
20
+ import { navigationRef } from "../navigation/navigationRef";
21
+
22
+ const firebaseApp = getApp();
23
+ const messagingInstance = getMessaging(firebaseApp);
24
+ const storage = new MMKV();
25
+
26
+ class NotificationManager {
27
+ private notifeeForegroundHandler: (() => void) | null = null;
28
+ private onMessageListener: (() => void) | null = null;
29
+ private pendingNavigationData: any = null; // Store navigation data when app is not ready
30
+
31
+ private backgroundAndroidEventHandler:
32
+ | ((type: EventType, detail: EventDetail) => void)
33
+ | null = null;
34
+
35
+ public async initialize(): Promise<void> {
36
+ try {
37
+ const hasPermission = await this.requestPermission();
38
+
39
+ if (hasPermission) {
40
+ await this.setupFCMListeners();
41
+ await this.handleInitialNotification();
42
+ console.log("NotificationManager initialized successfully");
43
+ } else {
44
+ console.log("FCM permissions not granted");
45
+ }
46
+ } catch (error) {
47
+ console.error("Error initializing NotificationManager:", error);
48
+ }
49
+ }
50
+
51
+ // Handle navigation from notification data
52
+ private async handleNotificationNavigation(
53
+ notificationData: any
54
+ ): Promise<void> {
55
+ console.log(
56
+ "Navigation from notification:",
57
+ JSON.stringify(notificationData, null, 2)
58
+ );
59
+
60
+ // Normalize payload for chat navigation
61
+ const data = notificationData?.data ?? notificationData ?? {};
62
+
63
+ console.log("data===>>>>", data);
64
+
65
+ const threadId = data?.threadId;
66
+ const threadName = data?.title || data?.ThreadName || data?.title;
67
+ const memberId = 125;
68
+ const teamId = data?.teamId || data?.TeamId || data?.team_id || "";
69
+ const teamName = data?.teamName || data?.TeamName || "";
70
+ const teamProfileImage = data?.teamProfileImage || data?.TeamProfileImage || data?.teamImage || "";
71
+ const totalMembers = data?.totalMembers || data?.TotalMembers || "5";
72
+
73
+ if (!threadId || !memberId) {
74
+ console.log("Missing required chat fields (threadId/memberId) in notification data");
75
+ return;
76
+ }
77
+
78
+ const navigationParams: any = {
79
+ itemDetails: {
80
+ threadId: threadId,
81
+ threadName: threadName || "",
82
+ },
83
+ memberId: String(memberId),
84
+ teamId: teamId ? String(teamId) : undefined,
85
+ team: {
86
+ teamName: teamName || undefined,
87
+ teamProfileImage: teamProfileImage || undefined,
88
+ totalMembers: totalMembers ? Number(totalMembers) : undefined,
89
+ },
90
+ };
91
+
92
+ // Check if navigation is ready
93
+ if (navigationRef.isReady()) {
94
+ // (navigationRef as any).navigate('ChatScreen', navigationParams);
95
+ } else {
96
+ // this.pendingNavigationData = { route: 'ChatScreen', params: navigationParams };
97
+
98
+ // Wait for navigation to be ready and then navigate
99
+ const checkNavigation = () => {
100
+ if (navigationRef.isReady() && this.pendingNavigationData) {
101
+ const pending: any = this.pendingNavigationData;
102
+ (navigationRef as any).navigate(pending.route, pending.params);
103
+ this.pendingNavigationData = null;
104
+ } else {
105
+ // Keep checking every 100ms for up to 5 seconds
106
+ setTimeout(checkNavigation, 100);
107
+ }
108
+ };
109
+
110
+ setTimeout(checkNavigation, 100);
111
+ }
112
+ }
113
+
114
+ // Handle app launch from notification (killed state)
115
+ private async handleInitialNotification(): Promise<void> {
116
+ try {
117
+ // Handle FCM initial notification (when app is opened from notification while killed)
118
+ const initialMessage = await getInitialNotification(messagingInstance);
119
+ if (initialMessage) {
120
+ console.log("App opened from FCM notification:", initialMessage);
121
+ await this.handleNotificationNavigation(initialMessage);
122
+ }
123
+
124
+ // Handle Notifee initial notification
125
+ if (Platform.OS === "android") {
126
+ const initialNotification = await notifee.getInitialNotification();
127
+ if (initialNotification) {
128
+ console.log(
129
+ "App opened from Notifee notification:",
130
+ initialNotification
131
+ );
132
+ const notificationData =
133
+ initialNotification.notification.data?.originalMessage ||
134
+ initialNotification.notification.data;
135
+ await this.handleNotificationNavigation(notificationData);
136
+ }
137
+ }
138
+ } catch (error) {
139
+ console.error("Error handling initial notification:", error);
140
+ }
141
+ }
142
+
143
+ public async requestPermission(): Promise<boolean> {
144
+ if (Platform.OS === "ios") {
145
+ return this.requestIOSPermission();
146
+ } else if (Platform.OS === "android") {
147
+ return this.requestAndroidPermission();
148
+ }
149
+ return false;
150
+ }
151
+
152
+ private async requestIOSPermission(): Promise<boolean> {
153
+ try {
154
+ const authStatus = await requestPermission(messagingInstance);
155
+ const enabled =
156
+ authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
157
+ authStatus === messaging.AuthorizationStatus.PROVISIONAL;
158
+
159
+ if (enabled) {
160
+ console.log("iOS FCM authorization status:", authStatus);
161
+ } else {
162
+ console.log("iOS FCM permission denied");
163
+ }
164
+
165
+ return enabled;
166
+ } catch (error) {
167
+ console.error("Error requesting iOS FCM permission:", error);
168
+ return false;
169
+ }
170
+ }
171
+
172
+ private async requestAndroidPermission(): Promise<boolean> {
173
+ try {
174
+ if (typeof Platform.Version === "number" && Platform.Version >= 33) {
175
+ const granted = await PermissionsAndroid.request(
176
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
177
+ );
178
+ console.log("Android POST_NOTIFICATIONS permission status:", granted);
179
+
180
+ if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
181
+ console.log("Android POST_NOTIFICATIONS permission denied");
182
+ return false;
183
+ }
184
+ }
185
+ return true;
186
+ } catch (error) {
187
+ console.error("Error requesting Android FCM permission:", error);
188
+ return false;
189
+ }
190
+ }
191
+
192
+ public async getFCMToken(): Promise<string | null> {
193
+ try {
194
+ let fcmToken = storage.getString("fcmToken");
195
+ if (!fcmToken) {
196
+ fcmToken = await getToken(messagingInstance);
197
+ console.log("FCM token:", fcmToken);
198
+ if (fcmToken) {
199
+ storage.set("fcmToken", fcmToken);
200
+ }
201
+ }
202
+ return fcmToken;
203
+ } catch (error) {
204
+ console.error("Error getting FCM token:", error);
205
+ return null;
206
+ }
207
+ }
208
+
209
+ private async setupFCMListeners(): Promise<void> {
210
+ // Handle foreground messages (when app is active)
211
+ this.onMessageListener = onMessage(messagingInstance, async (remoteMessage) => {
212
+ console.log("Foreground FCM message received");
213
+
214
+ // ONLY display custom notification for data-only messages
215
+ if (remoteMessage.data) {
216
+ try {
217
+ await this.displayNotification(remoteMessage);
218
+ } catch (error) {
219
+ console.error("Error displaying foreground notification:", error);
220
+ }
221
+ }
222
+ });
223
+
224
+ // Handle notification opened from background/killed state
225
+ onNotificationOpenedApp(messagingInstance, async (remoteMessage) => {
226
+ console.log("App opened from background via notification");
227
+ await this.handleNotificationNavigation(remoteMessage);
228
+ });
229
+ }
230
+
231
+ public setForegroundEventListener(
232
+ handler: (type: EventType, detail: EventDetail) => void
233
+ ): void {
234
+ this.notifeeForegroundHandler = notifee.onForegroundEvent(
235
+ async ({ type, detail }: any) => {
236
+ console.log(
237
+ "Notifee Foreground Event:",
238
+ `Type: ${type}`,
239
+ JSON.stringify(detail, null, 2)
240
+ );
241
+
242
+ // Handle notification dismissal
243
+ if (type === EventType.DISMISSED) {
244
+ console.log("Notification dismissed:", detail.notification?.id);
245
+ return;
246
+ }
247
+
248
+ // Handle notification press (FOREGROUND state)
249
+ if (type === EventType.PRESS && detail.notification?.data) {
250
+ console.log("Notification pressed in foreground");
251
+ const notificationData =
252
+ detail?.notification?.data?.originalMessage ||
253
+ detail?.notification?.data;
254
+ await this.handleNotificationNavigation(notificationData);
255
+ }
256
+
257
+ if (handler) handler(type, detail);
258
+ }
259
+ );
260
+ }
261
+
262
+ public setInitialNotificationAndroidEvent(
263
+ handler: (notification: InitialNotification) => void
264
+ ): void {
265
+ if (Platform.OS === "android") {
266
+ notifee
267
+ .getInitialNotification()
268
+ .then((initialNotification) => {
269
+ console.log(
270
+ "Initial notification",
271
+ JSON.stringify(initialNotification)
272
+ );
273
+
274
+ if (initialNotification) {
275
+ handler(initialNotification);
276
+ }
277
+ })
278
+ .catch((error: Error) => {
279
+ console.log("error in getInitial notification", error);
280
+ });
281
+ }
282
+ }
283
+
284
+ public setBackgroundEventListener(
285
+ handler: (type: EventType, detail: EventDetail) => void
286
+ ): void {
287
+ if (Platform.OS === "android") {
288
+ this.backgroundAndroidEventHandler = (
289
+ type: EventType,
290
+ detail: EventDetail
291
+ ) => {
292
+ console.log("Android Background Event:", type, detail);
293
+
294
+ // Handle notification press (BACKGROUND state)
295
+ if (type === EventType.PRESS && detail.notification?.data) {
296
+ console.log("Notification pressed in background (Android)");
297
+ const notificationData =
298
+ detail?.notification?.data?.originalMessage ||
299
+ detail?.notification?.data;
300
+ this.handleNotificationNavigation(notificationData);
301
+ }
302
+
303
+ if (handler) handler(type, detail);
304
+ };
305
+ }
306
+
307
+ if (Platform.OS === "ios") {
308
+ notifee.onBackgroundEvent(async ({ type, detail }: any) => {
309
+ console.log("iOS Background event:", type, detail);
310
+
311
+ // Handle notification dismissal in background
312
+ if (type === EventType.DISMISSED) {
313
+ console.log(
314
+ "Background notification dismissed:",
315
+ detail?.notification?.id
316
+ );
317
+ return;
318
+ }
319
+
320
+ // Handle notification press (BACKGROUND state - iOS)
321
+ if (type === EventType.PRESS && detail.notification?.data) {
322
+ console.log("Notification pressed in background (iOS)");
323
+ const notificationData =
324
+ detail?.notification?.data?.originalMessage ||
325
+ detail?.notification?.data;
326
+ await this.handleNotificationNavigation(notificationData);
327
+ }
328
+
329
+ if (handler) handler(type, detail);
330
+ });
331
+ }
332
+ }
333
+ //invoked from root index file to delegate method call
334
+ public invokeAndroidBackgroundEvent(
335
+ type: EventType,
336
+ detail: EventDetail
337
+ ): void {
338
+ if (this.backgroundAndroidEventHandler) {
339
+ this.backgroundAndroidEventHandler(type, detail);
340
+ }
341
+ }
342
+
343
+ displayNotification = async (notifData: any) => {
344
+ try {
345
+ console.log("Processing notification display");
346
+
347
+ // Extract title and body from EITHER notification payload OR data payload
348
+ let title, body;
349
+
350
+ if (notifData?.notification) {
351
+ // Message has notification payload (FCM format)
352
+ title = notifData.notification.title;
353
+ body = notifData.notification.body;
354
+ } else if (notifData?.data) {
355
+ // Data-only message (preferred format)
356
+ title = notifData.data.title;
357
+ body = notifData.data.body;
358
+ } else {
359
+ console.log("No valid content found in message - skipping");
360
+ return;
361
+ }
362
+
363
+ // Fallback to defaults if needed
364
+ title = title || "HomeSafeAlert";
365
+ body = body || "New incident notification";
366
+
367
+ // Create notification channel with more explicit configuration
368
+ const channelId = await notifee.createChannel({
369
+ id: "homesafealert_incidents",
370
+ name: "HomeSafeAlert Incidents",
371
+ importance: AndroidImportance.HIGH,
372
+ sound: "default",
373
+ vibration: true,
374
+ description: "Notifications for HomeSafeAlert incidents",
375
+ });
376
+
377
+ const notificationId = `hsa_${Date.now()}`;
378
+
379
+ const notificationData = {
380
+ ...notifData?.data,
381
+ originalMessage: notifData,
382
+ timestamp: Date.now(),
383
+ };
384
+
385
+ // Try with basic configuration first
386
+ await notifee.displayNotification({
387
+ id: notificationId,
388
+ title: title,
389
+ body: body,
390
+ data: notificationData,
391
+ android: {
392
+ channelId,
393
+ importance: AndroidImportance.HIGH,
394
+ autoCancel: true,
395
+ ongoing: false,
396
+ smallIcon: "ic_launcher_background",
397
+
398
+ // colorized: true,
399
+ visibility: AndroidVisibility.PUBLIC,
400
+ showTimestamp: true,
401
+ pressAction: {
402
+ id: "default",
403
+ },
404
+ style: {
405
+ type: 1, // BigTextStyle
406
+ text: body,
407
+ },
408
+ },
409
+ });
410
+ } catch (error) {
411
+ console.error("Error displaying notification:", error);
412
+
413
+ // Emergency fallback notification with minimal configuration
414
+ try {
415
+ console.log("Attempting emergency fallback notification...");
416
+
417
+ // Create a default channel first
418
+ await notifee.createChannel({
419
+ id: "default",
420
+ name: "Default",
421
+ importance: AndroidImportance.DEFAULT,
422
+ });
423
+
424
+ await notifee.displayNotification({
425
+ title: "HomeSafeAlert",
426
+ body: "New notification (fallback)",
427
+ android: {
428
+ channelId: "default",
429
+ importance: AndroidImportance.DEFAULT,
430
+ smallIcon: "ic_notification",
431
+ color: "#FF6B35",
432
+ visibility: AndroidVisibility.PUBLIC,
433
+ },
434
+ });
435
+ console.log("Fallback notification displayed");
436
+ } catch (fallbackError) {
437
+ console.error("Fallback notification failed:", fallbackError);
438
+ }
439
+ }
440
+ };
441
+
442
+ // Test method to display a sample notification for debugging
443
+ public async displayTestNotification(): Promise<void> {
444
+ console.log("Displaying test notification");
445
+
446
+ const testData = {
447
+ notification: {
448
+ title: "Test Notification",
449
+ body: "This is a test notification from HomeSafeAlert",
450
+ },
451
+ data: {
452
+ NotificationId: "12345",
453
+ incidentId: "12345",
454
+ },
455
+ };
456
+
457
+ await this.displayNotification(testData);
458
+ }
459
+
460
+ // Check and execute any pending navigation from notifications
461
+ // Call this method when navigation becomes ready
462
+ public executePendingNavigation(): void {
463
+ if (this.pendingNavigationData && navigationRef.isReady()) {
464
+ console.log("Executing pending notification navigation");
465
+ const pending: any = this.pendingNavigationData;
466
+ if (pending?.route) {
467
+ (navigationRef as any).navigate(pending.route, pending.params);
468
+ } else {
469
+ // (navigationRef as any).navigate('ChatScreen', pending);
470
+ }
471
+ this.pendingNavigationData = null;
472
+ }
473
+ }
474
+
475
+ public async clearAllNotifications(): Promise<void> {
476
+ try {
477
+ await notifee.cancelAllNotifications();
478
+ console.log("All notifications cleared");
479
+ } catch (error) {
480
+ console.error("Error clearing notifications:", error);
481
+ }
482
+ }
483
+
484
+ public async clearNotification(notificationId: string): Promise<void> {
485
+ try {
486
+ await notifee.cancelNotification(notificationId);
487
+ console.log("Notification cleared:", notificationId);
488
+ } catch (error) {
489
+ console.error("Error clearing notification:", error);
490
+ }
491
+ }
492
+
493
+ public removeListeners(): void {
494
+ if (this.onMessageListener) {
495
+ this.onMessageListener();
496
+ }
497
+ if (this.notifeeForegroundHandler) {
498
+ this.notifeeForegroundHandler();
499
+ }
500
+ }
501
+ }
502
+
503
+ export default new NotificationManager();
@@ -0,0 +1,107 @@
1
+ import { Platform } from "react-native";
2
+ import Upload from "react-native-background-upload";
3
+ import { File } from "expo-file-system";
4
+ import { getInfoAsync } from "expo-file-system/legacy";
5
+
6
+ export class UploadDebugUtil {
7
+ /**
8
+ * Debug function to check upload service status and configuration
9
+ */
10
+ static debugUploadService() {
11
+ if (Platform.OS === "android") {
12
+ console.log("=== Android Upload Service Debug ===");
13
+ console.log("Platform:", Platform.OS);
14
+ console.log("Platform Version:", Platform.Version);
15
+
16
+ // Check if Upload service is available
17
+ try {
18
+ const uploadMethods = Object.keys(Upload);
19
+ console.log("Available Upload methods:", uploadMethods);
20
+ } catch (error) {
21
+ console.error("Upload service not available:", error);
22
+ }
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Validate file URI format for Android
28
+ * Android upload service only supports absolute paths (/) and content:// URIs
29
+ */
30
+ static validateFileUri(fileUri: string): boolean {
31
+ if (Platform.OS === "android") {
32
+ const validPatterns = [
33
+ /^\//, // Absolute path starting with /
34
+ /^content:\/\//, // Content URI
35
+ ];
36
+
37
+ const isValid = validPatterns.some((pattern) => pattern.test(fileUri));
38
+ console.log(`File URI validation - URI: ${fileUri}, Valid: ${isValid}`);
39
+
40
+ if (fileUri.startsWith("file://")) {
41
+ console.warn(
42
+ "Android upload service does not support file:// scheme. Use absolute path instead."
43
+ );
44
+ return false;
45
+ }
46
+
47
+ return isValid;
48
+ }
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * Log current upload service status
54
+ */
55
+ static logUploadServiceStatus() {
56
+ try {
57
+ console.log("Upload service methods available:", {
58
+ startUpload: typeof Upload.startUpload,
59
+ cancelUpload: typeof Upload.cancelUpload,
60
+ addListener: typeof Upload.addListener,
61
+ });
62
+ } catch (error) {
63
+ console.error("Error checking upload service status:", error);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Check if file exists at given path (useful for debugging Android file access)
69
+ */
70
+ static async checkFileExists(fileUri: string): Promise<boolean> {
71
+ try {
72
+ let pathToCheck = fileUri;
73
+
74
+ // Convert file:// URI to expo-file-system compatible path
75
+ if (fileUri.startsWith("file://")) {
76
+ pathToCheck = fileUri;
77
+ } else if (fileUri.startsWith("/")) {
78
+ pathToCheck = `file://${fileUri}`;
79
+ }
80
+
81
+ // Try the new File API first
82
+ // Try the new FileSystem API first (Expo FileSystem)
83
+ try {
84
+ // Dynamically import to avoid issues if not available
85
+ const { getInfoAsync } = await import('expo-file-system');
86
+ const fileInfo = await getInfoAsync(pathToCheck);
87
+
88
+ console.log(
89
+ `File check (new API) - Path: ${pathToCheck}, Exists: ${fileInfo.exists}, Size: ${fileInfo.size || "N/A"}`
90
+ );
91
+ return fileInfo.exists;
92
+ } catch (newApiError) {
93
+ console.warn(`New File API failed, trying legacy API: ${newApiError}`);
94
+
95
+ // Fallback to legacy API
96
+ const fileInfo = await getInfoAsync(pathToCheck);
97
+ console.log(
98
+ `File check (legacy API) - Path: ${pathToCheck}, Exists: ${fileInfo.exists}, Size: ${fileInfo.size || "N/A"}`
99
+ );
100
+ return fileInfo.exists;
101
+ }
102
+ } catch (error) {
103
+ console.error(`Error checking file existence for ${fileUri}:`, error);
104
+ return false;
105
+ }
106
+ }
107
+ }