react-native-timacare 3.3.44 → 3.3.46

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.
@@ -2,30 +2,79 @@
2
2
  /**
3
3
  * Navigation bridge for the Timacare SDK.
4
4
  *
5
- * The host app already owns its <NavigationContainer ref={...}>, so instead of
6
- * creating a second ref the host registers its existing one here once (typically
7
- * right after the container mounts). The SDK then uses it to drive navigation
8
- * imperatively from outside React — e.g. deep-linking into a loan detail via
9
- * `goDetail()`.
5
+ * SDK screens (DetailLoan, DetailLoanTima, ...) live ONLY inside the SDK's
6
+ * PrimaryNavigator. There are two situations the SDK has to drive navigation in:
10
7
  *
11
- * Accepts either form of ref:
12
- * - the object returned by `createNavigationContainerRef()` /
13
- * `useNavigationContainerRef()` (methods live directly on it), or
14
- * - a plain `useRef()` passed to <NavigationContainer ref={ref}> (methods live
15
- * on `ref.current`).
8
+ * 1. The SDK navigator is currently mounted (the user is somewhere inside the
9
+ * SDK). We then navigate using the navigator's OWN navigation object — see
10
+ * `sdkNavigation`, captured from inside PrimaryNavigator. React Navigation's
11
+ * `navigate()` only bubbles UP to parents, never DOWN into descendants, so
12
+ * the host's root container ref cannot reach a nested SDK screen — but the
13
+ * SDK navigator's own object can.
14
+ *
15
+ * 2. The SDK navigator is NOT mounted (e.g. the host shows its own home with a
16
+ * loan list and only mounts <RNTimacare/> under a dedicated route). Here we
17
+ * need the host's NavigationContainer ref AND the name of the host route that
18
+ * mounts the SDK, so we can mount it and deep-link in one nested navigate.
19
+ *
20
+ * The host registers both via `setNavigationRef(ref, hostRoute)`.
16
21
  */
22
+
23
+ // Host's NavigationContainer ref (or a plain useRef passed to the container).
17
24
  let externalNavigationRef = null;
18
25
 
19
- export const setNavigationRef = (ref) => {
26
+ // Name of the host route whose screen renders <RNTimacare/> (PrimaryNavigator).
27
+ // Required to deep-link into the SDK when the SDK navigator isn't mounted yet.
28
+ let hostRoute = null;
29
+
30
+ // Navigation object scoped to the SDK navigator, captured from inside its tree.
31
+ let sdkNavigation = null;
32
+
33
+ /**
34
+ * Register the host's NavigationContainer ref, and optionally the host route
35
+ * name under which <RNTimacare/> is mounted.
36
+ *
37
+ * Accepts either ref form:
38
+ * - the object from `createNavigationContainerRef()` / `useNavigationContainerRef()`
39
+ * (methods live directly on it), or
40
+ * - a plain `useRef()` passed to <NavigationContainer ref={ref}> (methods on `.current`).
41
+ */
42
+ export const setNavigationRef = (ref, route = null) => {
20
43
  externalNavigationRef = ref;
44
+ if (route != null) {
45
+ hostRoute = route;
46
+ }
47
+ };
48
+
49
+ /** Set/override the host route that mounts the SDK navigator. */
50
+ export const setHostRoute = (route) => {
51
+ hostRoute = route;
21
52
  };
22
53
 
54
+ export const getHostRoute = () => hostRoute;
55
+
23
56
  /**
24
- * Resolves the registered ref down to the actual NavigationContainerRef,
25
- * regardless of which ref form the host provided. Returns null when nothing is
26
- * registered yet or the container isn't ready.
57
+ * Registered from inside PrimaryNavigator while it is mounted (and cleared on
58
+ * unmount, so we never hand out a stale/detached handle).
27
59
  */
28
- export const getNavigation = () => {
60
+ export const setSdkNavigation = (nav) => {
61
+ sdkNavigation = nav;
62
+ };
63
+
64
+ /** The SDK navigator's own navigation object, if currently mounted. */
65
+ export const getSdkNavigation = () => {
66
+ if (sdkNavigation && typeof sdkNavigation.navigate === 'function') {
67
+ return sdkNavigation;
68
+ }
69
+ return null;
70
+ };
71
+
72
+ /**
73
+ * Resolves the host ref down to the actual NavigationContainerRef, regardless of
74
+ * which ref form the host provided. Returns null when nothing is registered yet
75
+ * or the container isn't ready.
76
+ */
77
+ export const getHostNavigation = () => {
29
78
  const ref = externalNavigationRef;
30
79
  if (!ref) {
31
80
  return null;
@@ -38,3 +87,12 @@ export const getNavigation = () => {
38
87
 
39
88
  return nav;
40
89
  };
90
+
91
+ /**
92
+ * Backwards-compatible resolver: prefers the SDK navigator's own object, falling
93
+ * back to the host ref (which only reaches SDK screens when the host mounts
94
+ * PrimaryNavigator as its root). Prefer `goDetail()` for deep-linking.
95
+ */
96
+ export const getNavigation = () => {
97
+ return getSdkNavigation() || getHostNavigation();
98
+ };
@@ -8,7 +8,12 @@
8
8
  import React, { useEffect } from 'react';
9
9
 
10
10
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
11
- import { getNavigation } from './navigationRef';
11
+ import {
12
+ getSdkNavigation,
13
+ getHostNavigation,
14
+ getHostRoute,
15
+ setSdkNavigation,
16
+ } from './navigationRef';
12
17
  import { OTP } from '../screens/otp';
13
18
  import { NationalID } from '../screens/nationalID';
14
19
  import { Selfie } from '../screens/selfie';
@@ -157,12 +162,27 @@ export const ScreenNames = {
157
162
  const Stack = createNativeStackNavigator();
158
163
 
159
164
  export function PrimaryNavigator(props) {
165
+ // When this navigator unmounts, drop the captured navigation so getNavigation()
166
+ // doesn't hand goDetail() a stale (detached) handle — it should cleanly fall
167
+ // back to the host ref / return null instead.
168
+ useEffect(() => {
169
+ return () => setSdkNavigation(null);
170
+ }, []);
171
+
160
172
  return (
161
173
  <Stack.Navigator
162
174
  initialRouteName={ScreenNames.Main}
163
175
  screenOptions={{
164
176
  headerShown: false,
165
177
  }}
178
+ // Capture this navigator's own navigation object so goDetail() (and any
179
+ // other imperative SDK navigation) can reach SDK screens even when the host
180
+ // mounts this navigator nested inside its tree. The screen `navigation`
181
+ // prop is scoped to THIS stack, so navigate() resolves SDK screens directly.
182
+ screenListeners={({ navigation }) => {
183
+ setSdkNavigation(navigation);
184
+ return {};
185
+ }}
166
186
  >
167
187
  <Stack.Screen name={ScreenNames.Main} component={Home} />
168
188
  <Stack.Screen name={ScreenNames.NationalID} component={NationalID} />
@@ -323,20 +343,26 @@ export const canExit = (routeName: string) => exitRoutes.includes(routeName);
323
343
  * Imperatively navigate into a loan detail screen from outside React.
324
344
  *
325
345
  * Intended for the host app to deep-link the user straight into a loan detail
326
- * (e.g. from a push notification or a list rendered outside the SDK). Requires
327
- * the host to register its NavigationContainer ref once via `setNavigationRef()`.
346
+ * (e.g. from a push notification or a list rendered outside the SDK).
328
347
  *
329
348
  * Accepts either a loan id or a partial loan object. The detail screen only
330
349
  * needs `id` to fetch its data; `disbursementPartner` (when provided) selects
331
350
  * the matching screen — 'CIMB' -> DetailLoan, otherwise -> DetailLoanTima.
351
+ *
352
+ * Resolution order:
353
+ * 1. SDK navigator already mounted -> navigate within it directly.
354
+ * 2. SDK not mounted, but the host registered its ref + the route that mounts
355
+ * <RNTimacare/> (via `setNavigationRef(ref, hostRoute)`) -> mount the SDK and
356
+ * deep-link in one nested navigate.
357
+ * 3. Fallback: dispatch on the host ref directly (only reaches SDK screens when
358
+ * the host mounts PrimaryNavigator as its root navigator).
332
359
  */
333
360
  export function goDetail(
334
361
  loan: number | { id: number; disbursementPartner?: string }
335
362
  ) {
336
363
  const loanObj = typeof loan === 'number' ? { id: loan } : loan;
337
- const navigation = getNavigation();
338
364
 
339
- if (!loanObj?.id || !navigation) {
365
+ if (!loanObj?.id) {
340
366
  return;
341
367
  }
342
368
 
@@ -345,5 +371,26 @@ export function goDetail(
345
371
  ? ScreenNames.DetailLoan
346
372
  : ScreenNames.DetailLoanTima;
347
373
 
348
- navigation.navigate(screen, { loan: loanObj });
374
+ // 1) SDK navigator is mounted: navigate within the navigator that owns the screen.
375
+ const sdkNavigation = getSdkNavigation();
376
+ if (sdkNavigation) {
377
+ sdkNavigation.navigate(screen, { loan: loanObj });
378
+ return;
379
+ }
380
+
381
+ // 2) SDK not mounted: mount it via the host route and deep-link in one go.
382
+ const hostNavigation = getHostNavigation();
383
+ const hostRoute = getHostRoute();
384
+ if (hostNavigation && hostRoute) {
385
+ hostNavigation.navigate(hostRoute, {
386
+ screen,
387
+ params: { loan: loanObj },
388
+ });
389
+ return;
390
+ }
391
+
392
+ // 3) Last resort: host ref dispatch (works only if the SDK is the host root).
393
+ if (hostNavigation) {
394
+ hostNavigation.navigate(screen, { loan: loanObj });
395
+ }
349
396
  }
@@ -2423,6 +2423,7 @@ export const Home = observer(function Home() {
2423
2423
  {() =>
2424
2424
  !isLoading &&
2425
2425
  !homeStore.isLoading &&
2426
+ (listLoan?.length ?? 0) === 0 &&
2426
2427
  (homeStore?.listLoan?.length ?? 0) === 0 ? (
2427
2428
  <View
2428
2429
  style={{