@propel-nsl/propel-react-native-sdk 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@propel-nsl/propel-react-native-sdk",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Propel Mobile SDK - React Native Core",
5
5
  "files": [
6
6
  "src/",
@@ -80,6 +80,9 @@ const PropelSDKScreen: React.FC<PropelSDKScreenProps> = (props) => {
80
80
  },
81
81
  // Flag so SDKApp knows it's running inside a RN host (no native bridge)
82
82
  __rnHostMode: true,
83
+ // Pass navigation and onClose for proper back navigation
84
+ hostNavigation: props.navigation,
85
+ onClose: props.onClose,
83
86
  };
84
87
 
85
88
  return (
package/src/SDKApp.tsx CHANGED
@@ -508,8 +508,11 @@ const SDKApp: React.FC<any> = (props) => {
508
508
  const isDeepLinkEntryValue = deepLinkToRedemptionHistory || deepLinkToRedeem;
509
509
  const deepLinkTargetValue = deepLinkToRedemptionHistory ? 'RedemptionHistory' : (deepLinkToRedeem ? 'Redeem' : undefined);
510
510
  const isRNHostMode = !!(initialParams as any)?.__rnHostMode;
511
+ const hostNavigation = (initialParams as any)?.hostNavigation;
512
+ const onClose = (initialParams as any)?.onClose;
511
513
 
512
514
  console.log('🔍 SDKApp render: deepLinkToRedemptionHistory=', deepLinkToRedemptionHistory, 'deepLinkToRedeem=', deepLinkToRedeem, 'deepLinkTargetValue=', deepLinkTargetValue);
515
+ console.log('🔍 SDKApp render: isRNHostMode=', isRNHostMode, 'hasHostNavigation=', !!hostNavigation, 'hasOnClose=', !!onClose);
513
516
 
514
517
  return (
515
518
  <GestureHandlerRootView style={{ flex: 1 }}>
@@ -518,6 +521,8 @@ const SDKApp: React.FC<any> = (props) => {
518
521
  isSDK={true}
519
522
  initialDeepLinkEntry={isDeepLinkEntryValue}
520
523
  initialDeepLinkTarget={deepLinkTargetValue}
524
+ hostNavigation={hostNavigation}
525
+ onClose={onClose}
521
526
  >
522
527
  <SafeAreaProvider>
523
528
  <NavigationContainer
@@ -17,17 +17,28 @@ const HIDDEN_TABS = [
17
17
 
18
18
  const SDKBackHandler: React.FC<{ children: React.ReactNode }> = ({ children }) => {
19
19
  const navigation = useNavigation();
20
- const { isDeepLinkEntry } = useSDKContext();
20
+ const { isDeepLinkEntry, hostNavigation, onClose } = useSDKContext();
21
21
  const dispatch = useAppDispatch();
22
22
 
23
23
  // Helper to exit SDK and clear auth state
24
24
  const exitSDK = React.useCallback(() => {
25
25
  console.log('🚀 Exiting SDK - clearing auth state');
26
26
  dispatch(clearAllAuthState());
27
- if (NativeModules.PropelSDKBridge?.closeSDK) {
27
+
28
+ // Check if we're in RN host mode (hostNavigation or onClose available)
29
+ if (hostNavigation) {
30
+ console.log('🚀 RN Host Mode: Using hostNavigation.goBack()');
31
+ hostNavigation.goBack();
32
+ } else if (onClose) {
33
+ console.log('🚀 RN Host Mode: Using onClose callback');
34
+ onClose();
35
+ } else if (NativeModules.PropelSDKBridge?.closeSDK) {
36
+ console.log('🚀 Native Mode: Using PropelSDKBridge.closeSDK()');
28
37
  NativeModules.PropelSDKBridge.closeSDK();
38
+ } else {
39
+ console.log('⚠️ No exit method available - SDK may not close properly');
29
40
  }
30
- }, [dispatch]);
41
+ }, [dispatch, hostNavigation, onClose]);
31
42
 
32
43
  // Shared back handler logic
33
44
  const handleBackPress = React.useCallback(() => {
@@ -19,6 +19,10 @@ interface SDKContextType {
19
19
  deepLinkTarget?: string;
20
20
  /** Set the deep link entry state */
21
21
  setDeepLinkEntry: (isDeepLink: boolean, target?: string) => void;
22
+ /** Host app navigation (for RN host mode) */
23
+ hostNavigation?: any;
24
+ /** Callback to close SDK (for RN host mode) */
25
+ onClose?: () => void;
22
26
  }
23
27
 
24
28
  const SDKContext = createContext<SDKContextType>({
@@ -26,6 +30,8 @@ const SDKContext = createContext<SDKContextType>({
26
30
  isDeepLinkEntry: false,
27
31
  deepLinkTarget: undefined,
28
32
  setDeepLinkEntry: () => {},
33
+ hostNavigation: undefined,
34
+ onClose: undefined,
29
35
  });
30
36
 
31
37
  export const useSDKContext = () => useContext(SDKContext);
@@ -36,6 +42,8 @@ interface SDKProviderProps {
36
42
  sdkNavigation?: any;
37
43
  initialDeepLinkEntry?: boolean;
38
44
  initialDeepLinkTarget?: string;
45
+ hostNavigation?: any;
46
+ onClose?: () => void;
39
47
  }
40
48
 
41
49
  export const SDKProvider: React.FC<SDKProviderProps> = ({
@@ -44,6 +52,8 @@ export const SDKProvider: React.FC<SDKProviderProps> = ({
44
52
  sdkNavigation,
45
53
  initialDeepLinkEntry = false,
46
54
  initialDeepLinkTarget,
55
+ hostNavigation,
56
+ onClose,
47
57
  }) => {
48
58
  const [isDeepLinkEntry, setIsDeepLinkEntry] = useState(initialDeepLinkEntry);
49
59
  const [deepLinkTarget, setDeepLinkTarget] = useState<string | undefined>(initialDeepLinkTarget);
@@ -60,6 +70,8 @@ export const SDKProvider: React.FC<SDKProviderProps> = ({
60
70
  isDeepLinkEntry,
61
71
  deepLinkTarget,
62
72
  setDeepLinkEntry,
73
+ hostNavigation,
74
+ onClose,
63
75
  };
64
76
 
65
77
  return (
@@ -9,6 +9,7 @@ import {
9
9
  ImageBackground,
10
10
  Dimensions,
11
11
  NativeModules,
12
+ BackHandler,
12
13
  } from "react-native";
13
14
  import { StackNavigationProp } from "@react-navigation/stack";
14
15
  import { AppStackParamList } from "../../../src-app/types/navigation";
@@ -36,6 +37,7 @@ import { useFocusEffect } from "@react-navigation/native";
36
37
  import { pointsFromCentsFixed } from "../../../src-app/constants/Formatter";
37
38
  import CustomLoader from "../../../src-app/components/CustomLoader";
38
39
  import { ROUTES } from "../../../src-app/constants/Routes";
40
+ import { useSDKContext } from "../../contexts/SDKContext";
39
41
 
40
42
  type DashboardNavigationProp = StackNavigationProp<
41
43
  AppStackParamList,
@@ -53,6 +55,7 @@ const Dashboard: React.FC<Props> = ({ navigation }) => {
53
55
  const [resetOtpTrigger, setResetOtpTrigger] = useState(0);
54
56
  const [messagepopUpVisible, setMessagepopUpVisible] = useState(false);
55
57
  const [screen, setScreen] = useState("");
58
+ const { hostNavigation, onClose } = useSDKContext();
56
59
 
57
60
  // Debug screen state changes
58
61
  useEffect(() => {
@@ -196,6 +199,44 @@ const Dashboard: React.FC<Props> = ({ navigation }) => {
196
199
  dispatch(viewMyCartRequest({ id: cartId }));
197
200
  }, [cartId, dispatch]);
198
201
 
202
+ const handleBackToHost = () => {
203
+ console.log('🔙 Dashboard: handleBackToHost called');
204
+ // Clear all auth state before exiting SDK to prevent stale data on next launch
205
+ dispatch(clearAllAuthState());
206
+
207
+ // Check if we're in RN host mode (hostNavigation or onClose available)
208
+ if (hostNavigation) {
209
+ console.log('🚀 Dashboard: RN Host Mode - Using hostNavigation.goBack()');
210
+ hostNavigation.goBack();
211
+ } else if (onClose) {
212
+ console.log('🚀 Dashboard: RN Host Mode - Using onClose callback');
213
+ onClose();
214
+ } else if (NativeModules.PropelSDKBridge?.closeSDK) {
215
+ console.log('🚀 Dashboard: Native Mode - Using NativeModules.PropelSDKBridge.closeSDK()');
216
+ NativeModules.PropelSDKBridge.closeSDK();
217
+ } else if (NativeModules.PropelSDKBridge?.dismiss) {
218
+ console.log('🚀 Dashboard: Native Mode - Using NativeModules.PropelSDKBridge.dismiss()');
219
+ NativeModules.PropelSDKBridge.dismiss();
220
+ } else {
221
+ console.log('⚠️ Dashboard: No closeSDK method available');
222
+ }
223
+ };
224
+
225
+ // Add hardware back handler as fallback for Dashboard
226
+ useFocusEffect(
227
+ useCallback(() => {
228
+ const onBackPress = () => {
229
+ console.log('🔙 Dashboard: Hardware back pressed - calling handleBackToHost');
230
+ handleBackToHost();
231
+ return true; // Prevent default back behavior
232
+ };
233
+
234
+ const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
235
+
236
+ return () => subscription.remove();
237
+ }, [handleBackToHost])
238
+ );
239
+
199
240
  const navigateToDashboardScreen = (screenName: string, params?: any) => {
200
241
  // Navigate within DashboardStack for screens that are part of the stack
201
242
  // For screens like MyCart, we need to navigate within the stack
@@ -280,16 +321,6 @@ const Dashboard: React.FC<Props> = ({ navigation }) => {
280
321
  navigateToDashboardScreen("MyCart", { from: "Dashboard" });
281
322
  };
282
323
 
283
- const handleBackToHost = () => {
284
- // Clear all auth state before exiting SDK to prevent stale data on next launch
285
- dispatch(clearAllAuthState());
286
- if (NativeModules.PropelSDKBridge?.closeSDK) {
287
- NativeModules.PropelSDKBridge.closeSDK();
288
- } else if (NativeModules.PropelSDKBridge?.dismiss) {
289
- NativeModules.PropelSDKBridge.dismiss();
290
- }
291
- };
292
-
293
324
  // Categories data with actual images
294
325
  const categoriesData = [
295
326
  { id: "1", name: "Apparel", image: Images.categoryApparel },
@@ -19,7 +19,7 @@ import {
19
19
  resetAuthState,
20
20
  uploadProfileImageRequest,
21
21
  } from "../../../src-app/redux/authSlice";
22
- import { CustomImage, Logout } from "../../../src-app/components";
22
+ import { CustomImage } from "../../../src-app/components";
23
23
  import Images from "../../../src-app/constants/Images";
24
24
  import { useAppSelector } from "../../../src-app/hooks/useAppSelector";
25
25
  import { RootState } from "../../../src-app/redux/store";
@@ -79,7 +79,6 @@ const Profile: React.FC<ProfileProps> = ({ navigation }) => {
79
79
  uploadProfileImageLoading,
80
80
  uploadProfileImageError
81
81
  } = useAppSelector((state: RootState) => state.auth);
82
- const [logoutModalVisible, setLogoutModalVisible] = useState(false);
83
82
  const myProfileLoading = useAppSelector((state: RootState) => state.auth.myProfileLoading);
84
83
  const { myOrdersValidateOtpSuccessMessage } = useAppSelector(
85
84
  (state: RootState) => state.auth
@@ -119,9 +118,6 @@ const Profile: React.FC<ProfileProps> = ({ navigation }) => {
119
118
  }, [otpModalVisible, myOrdersValidateOtpSuccessMessage, screen, navigation, dispatch]);
120
119
 
121
120
 
122
- const handleLogout = () => {
123
- setLogoutModalVisible(true);
124
- };
125
121
 
126
122
  const handleMenuPress = (screen: string) => {
127
123
  console.log('🚀 Profile handleMenuPress called with screen:', screen);
@@ -314,11 +310,6 @@ const Profile: React.FC<ProfileProps> = ({ navigation }) => {
314
310
  />
315
311
  </View>
316
312
 
317
- {/* Logout Button */}
318
- <TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
319
- <CustomImage source={Images.logout} imgStyle={styles.logoutIcon} />
320
- <Text style={styles.logoutButtonText}>Logout</Text>
321
- </TouchableOpacity>
322
313
  </ScrollView>
323
314
  </TouchableWithoutFeedback>
324
315
  <OTPModal
@@ -344,16 +335,6 @@ const Profile: React.FC<ProfileProps> = ({ navigation }) => {
344
335
  otpLength={4}
345
336
  screen={screen}
346
337
  />
347
- <Logout
348
- visible={logoutModalVisible}
349
- onClose={() => setLogoutModalVisible(false)}
350
- onPress={() => {
351
- setLogoutModalVisible(false);
352
- navigation.reset({ index: 0, routes: [{ name: 'Login' }], });
353
- dispatch(resetAuthState("verifyOtpSuccessMessage"));
354
- dispatch(resetAuthState("otpSuccessMessage"));
355
- }}
356
- />
357
338
  <CustomLoader loading={!!myProfileLoading} text="Loading profile..." fullScreen />
358
339
  </SafeAreaView>
359
340
  );
@@ -47,7 +47,7 @@ const RedemptionHistory: React.FC<Props> = ({ navigation }) => {
47
47
  const from = route.params?.from;
48
48
  const exitToHost = (route.params as any)?.exitToHost;
49
49
  const insets = useSafeAreaInsets();
50
- const { isDeepLinkEntry } = useSDKContext();
50
+ const { isDeepLinkEntry, hostNavigation, onClose } = useSDKContext();
51
51
  const dispatch = useAppDispatch();
52
52
 
53
53
  // OTP modal state for deep link entry or direct access without prior OTP verification
@@ -72,9 +72,19 @@ const RedemptionHistory: React.FC<Props> = ({ navigation }) => {
72
72
  if (exitToHost || (isDeepLinkEntry && (!from || from === 'DeepLink' || from === 'HostApp'))) {
73
73
  console.log('🚀 RedemptionHistory: Deep link entry - exiting SDK to host app');
74
74
  dispatch(clearAllAuthState());
75
- if (NativeModules.PropelSDKBridge?.closeSDK) {
75
+
76
+ // Check if we're in RN host mode (hostNavigation or onClose available)
77
+ if (hostNavigation) {
78
+ console.log('🚀 RedemptionHistory: RN Host Mode - Using hostNavigation.goBack()');
79
+ hostNavigation.goBack();
80
+ } else if (onClose) {
81
+ console.log('🚀 RedemptionHistory: RN Host Mode - Using onClose callback');
82
+ onClose();
83
+ } else if (NativeModules.PropelSDKBridge?.closeSDK) {
84
+ console.log('🚀 RedemptionHistory: Native Mode - Using closeSDK');
76
85
  NativeModules.PropelSDKBridge.closeSDK();
77
86
  } else if (NativeModules.PropelSDKBridge?.dismiss) {
87
+ console.log('🚀 RedemptionHistory: Native Mode - Using dismiss');
78
88
  NativeModules.PropelSDKBridge.dismiss();
79
89
  }
80
90
  return true;
@@ -1,4 +1,4 @@
1
- import { useSelector } from 'react-redux';
1
+ import { TypedUseSelectorHook, useSelector } from 'react-redux';
2
2
  import type { RootState } from '../redux/store';
3
3
 
4
- export const useAppSelector = useSelector.withTypes<RootState>();
4
+ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;