react-native-timacare 3.3.40 → 3.3.42

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 (40) hide show
  1. package/lib/commonjs/components/AlertLoan.js +1 -1
  2. package/lib/commonjs/components/AlertLoan.js.flow +10 -1
  3. package/lib/commonjs/components/AlertLoan.js.map +1 -1
  4. package/lib/commonjs/components/ModalOptionLoan.js +1 -1
  5. package/lib/commonjs/components/ModalOptionLoan.js.flow +12 -1
  6. package/lib/commonjs/components/ModalOptionLoan.js.map +1 -1
  7. package/lib/commonjs/components/ModalOptionLoanDigital.js +1 -1
  8. package/lib/commonjs/components/ModalOptionLoanDigital.js.flow +12 -1
  9. package/lib/commonjs/components/ModalOptionLoanDigital.js.map +1 -1
  10. package/lib/commonjs/screens/home/index.js +1 -1
  11. package/lib/commonjs/screens/home/index.js.flow +190 -70
  12. package/lib/commonjs/screens/home/index.js.map +1 -1
  13. package/lib/commonjs/screens/home/not_found.png +0 -0
  14. package/lib/commonjs/screens/toan-trinh-so/store.js +1 -1
  15. package/lib/commonjs/screens/toan-trinh-so/store.js.flow +1 -1
  16. package/lib/commonjs/screens/toan-trinh-so/store.js.map +1 -1
  17. package/lib/module/components/AlertLoan.js +1 -1
  18. package/lib/module/components/AlertLoan.js.map +1 -1
  19. package/lib/module/components/ModalOptionLoan.js +1 -1
  20. package/lib/module/components/ModalOptionLoan.js.map +1 -1
  21. package/lib/module/components/ModalOptionLoanDigital.js +1 -1
  22. package/lib/module/components/ModalOptionLoanDigital.js.map +1 -1
  23. package/lib/module/screens/home/index.js +1 -1
  24. package/lib/module/screens/home/index.js.map +1 -1
  25. package/lib/module/screens/home/not_found.png +0 -0
  26. package/lib/module/screens/toan-trinh-so/store.js +1 -1
  27. package/lib/module/screens/toan-trinh-so/store.js.map +1 -1
  28. package/lib/typescript/components/AlertLoan.d.ts.map +1 -1
  29. package/lib/typescript/components/ModalOptionLoan.d.ts +2 -1
  30. package/lib/typescript/components/ModalOptionLoan.d.ts.map +1 -1
  31. package/lib/typescript/components/ModalOptionLoanDigital.d.ts +2 -1
  32. package/lib/typescript/components/ModalOptionLoanDigital.d.ts.map +1 -1
  33. package/lib/typescript/screens/home/index.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/components/AlertLoan.tsx +10 -1
  36. package/src/components/ModalOptionLoan.tsx +12 -1
  37. package/src/components/ModalOptionLoanDigital.tsx +12 -1
  38. package/src/screens/home/index.tsx +190 -70
  39. package/src/screens/home/not_found.png +0 -0
  40. package/src/screens/toan-trinh-so/store.ts +1 -1
@@ -6,6 +6,7 @@ import {
6
6
  Dimensions,
7
7
  Image,
8
8
  ImageBackground,
9
+ InteractionManager,
9
10
  Linking,
10
11
  Platform,
11
12
  RefreshControl,
@@ -116,15 +117,36 @@ const $action: ViewStyle = {
116
117
  };
117
118
 
118
119
  let _unsubscribe;
120
+ // Dữ liệu tham chiếu tĩnh (ngân hàng, nghề nghiệp, tỉnh...) chỉ cần prefetch 1 lần/phiên
121
+ let _didPrefetchRefData = false;
122
+
123
+ // Props an toàn cho react-native-modal:
124
+ // - backdropTransitionOutTiming={0}: tránh lớp backdrop fade-out còn sót lại chặn touch (treo màn hình)
125
+ // - useNativeDriver/useNativeDriverForBackdrop: animation mượt, không rớt animation đóng
126
+ // - hideModalContentWhileAnimating: ẩn nội dung khi đang animate, giảm giật
127
+ const modalSafeProps = {
128
+ useNativeDriver: true,
129
+ useNativeDriverForBackdrop: true,
130
+ hideModalContentWhileAnimating: true,
131
+ backdropTransitionOutTiming: 0,
132
+ };
133
+
134
+ // Tên các modal - dùng 1 state duy nhất để đảm bảo chỉ 1 modal hiển thị tại 1 thời điểm
135
+ const MODAL = {
136
+ CIMB_ERROR: 'CIMB_ERROR', // showModal
137
+ CIMB_APPROVE: 'CIMB_APPROVE', // showModal2
138
+ TTS_APPROVE: 'TTS_APPROVE', // showModal3
139
+ SUGGEST: 'SUGGEST', // showModal4
140
+ ALERT: 'ALERT', // showModal5
141
+ DIGITAL_SUGGEST: 'DIGITAL_SUGGEST', // showModal6
142
+ };
119
143
 
120
144
  export const Home = observer(function Home() {
121
145
  const navigation = useNavigation();
122
- const [showModal, setShowModal] = useState(false);
123
- const [showModal2, setShowModal2] = useState(false);
124
- const [showModal3, setShowModal3] = useState(false);
125
- const [showModal4, setShowModal4] = useState(false);
126
- const [showModal5, setShowModal5] = useState(false);
127
- const [showModal6, setShowModal6] = useState(false);
146
+ // 1 modal tại 1 thời điểm: tránh stack backdrop gây treo màn hình
147
+ const [activeModal, setActiveModal] = useState<string | null>(null);
148
+ // Hành động điều hướng chờ chạy SAU khi modal đóng hẳn (onModalHide)
149
+ const pendingNavRef = React.useRef<(() => void) | null>(null);
128
150
  const [listLoan, setListLoan] = useState<any[]>([]);
129
151
  const [checkEKYC, setCheckEKYC] = useState<any>({});
130
152
  const [isLoading, setIsLoading] = useState(false);
@@ -133,6 +155,21 @@ export const Home = observer(function Home() {
133
155
  const [dataSuggest, setDataSuggest] = useState<any>();
134
156
  const insets = useSafeAreaInsets();
135
157
 
158
+ // Chỉ mở modal khi chưa có modal nào đang mở -> không bao giờ stack 2 modal
159
+ const openModal = (name: string) =>
160
+ setActiveModal((prev) => prev ?? name);
161
+ const closeModal = () => setActiveModal(null);
162
+ // Đóng modal rồi điều hướng sau khi animation đóng hoàn tất (chống treo màn hình)
163
+ const closeModalThen = (fn?: () => void) => {
164
+ pendingNavRef.current = fn ?? null;
165
+ setActiveModal(null);
166
+ };
167
+ const runPendingNav = () => {
168
+ const fn = pendingNavRef.current;
169
+ pendingNavRef.current = null;
170
+ if (fn) fn();
171
+ };
172
+
136
173
  const onRefresh = async () => {
137
174
  homeStore.getListLoan();
138
175
  setIsLoading(true);
@@ -153,8 +190,8 @@ export const Home = observer(function Home() {
153
190
  if (item?.messageErrorCIMB && item.messageErrorCIMB !== '') {
154
191
  setLoanCimb(item);
155
192
  setTimeout(() => {
156
- setShowModal(true);
157
- }, 200);
193
+ openModal(MODAL.CIMB_ERROR);
194
+ }, 300);
158
195
  }
159
196
  if (
160
197
  (item?.typeLoan === 4 || item?.typeLoan === 2) &&
@@ -164,8 +201,8 @@ export const Home = observer(function Home() {
164
201
  if (!checkShow || checkShow !== true) {
165
202
  setLoanCimb(item);
166
203
  setTimeout(() => {
167
- setShowModal2(true);
168
- }, 200);
204
+ openModal(MODAL.CIMB_APPROVE);
205
+ }, 300);
169
206
  }
170
207
  }
171
208
  }
@@ -175,7 +212,7 @@ export const Home = observer(function Home() {
175
212
  ) {
176
213
  setLoan(item);
177
214
  setTimeout(() => {
178
- setShowModal3(true);
215
+ openModal(MODAL.TTS_APPROVE);
179
216
  }, 300);
180
217
  }
181
218
  if (
@@ -192,7 +229,7 @@ export const Home = observer(function Home() {
192
229
  typeLoan: item?.typeLoan,
193
230
  });
194
231
  setTimeout(() => {
195
- setShowModal4(true);
232
+ openModal(MODAL.SUGGEST);
196
233
  }, 300);
197
234
  }
198
235
  }
@@ -214,7 +251,7 @@ export const Home = observer(function Home() {
214
251
  typeLoan: item?.typeLoan,
215
252
  });
216
253
  setTimeout(() => {
217
- setShowModal6(true);
254
+ openModal(MODAL.DIGITAL_SUGGEST);
218
255
  }, 300);
219
256
  }
220
257
  }
@@ -224,7 +261,9 @@ export const Home = observer(function Home() {
224
261
  (item?.typeLoan === 5 || item?.typeLoan === 6) &&
225
262
  (item?.step === 7 || item?.step === 100)
226
263
  ) {
227
- setShowModal5(true);
264
+ setTimeout(() => {
265
+ openModal(MODAL.ALERT);
266
+ }, 300);
228
267
  }
229
268
  })
230
269
  );
@@ -253,18 +292,34 @@ export const Home = observer(function Home() {
253
292
  }, [navigation]);
254
293
 
255
294
  useEffect(() => {
256
- setTimeout(() => {
257
- FullSubmitStore.getTypeMerried();
258
- FullSubmitStore.getListBank();
259
- FullSubmitStore.getIncomeCIMB();
260
- FullSubmitStore.getPositionCIMB();
261
- FullSubmitStore.getJobStatusCIMB();
262
- QuickSubmitStore.getJobsCIMB();
263
- appStore.getLoanTimeAll();
264
- appStore.getRateTypeAll();
265
- appStore.getInsurenceTimeAll();
266
- ttsStore.getProvinces();
267
- }, 2000);
295
+ if (_didPrefetchRefData) return;
296
+ // Chạy sau khi màn hình render/animation xong (thay cho setTimeout 2000ms cố định)
297
+ const task = InteractionManager.runAfterInteractions(async () => {
298
+ await Promise.all([
299
+ FullSubmitStore.getTypeMerried(),
300
+ FullSubmitStore.getListBank(),
301
+ FullSubmitStore.getIncomeCIMB(),
302
+ FullSubmitStore.getPositionCIMB(),
303
+ FullSubmitStore.getJobStatusCIMB(),
304
+ QuickSubmitStore.getJobsCIMB(),
305
+ appStore.getLoanTimeAll(),
306
+ appStore.getRateTypeAll(),
307
+ appStore.getInsurenceTimeAll(),
308
+ ttsStore.getProvinces(),
309
+ ]);
310
+ // Các hàm store nuốt lỗi (không throw) -> coi là thành công khi dữ liệu
311
+ // tham chiếu thực sự về store. Nếu mất mạng (list rỗng) sẽ thử lại ở lần mount sau.
312
+ const loaded =
313
+ FullSubmitStore.listTypeMerried?.length > 0 &&
314
+ FullSubmitStore.listBanks?.length > 0 &&
315
+ QuickSubmitStore.listJobCimb?.length > 0 &&
316
+ appStore.listLoanTime?.length > 0 &&
317
+ appStore.listRateType?.length > 0 &&
318
+ appStore.listInsurenceTimeAll?.length > 0 &&
319
+ ttsStore.listProvince?.length > 0;
320
+ if (loaded) _didPrefetchRefData = true;
321
+ });
322
+ return () => task.cancel();
268
323
  }, []);
269
324
 
270
325
  const signLoan = (loan) => {
@@ -388,10 +443,7 @@ export const Home = observer(function Home() {
388
443
  setIsLoading(false);
389
444
  if (response.kind === 'ok') {
390
445
  if (response.data?.meta?.errorCode === 200) {
391
- setShowModal3(false);
392
- setTimeout(() => {
393
- onRefresh();
394
- }, 300);
446
+ closeModalThen(() => onRefresh());
395
447
  } else {
396
448
  Alert.alert('Thông báo', response.data?.meta?.errorMessage);
397
449
  }
@@ -413,10 +465,7 @@ export const Home = observer(function Home() {
413
465
  setIsLoading(false);
414
466
  if (response.kind === 'ok') {
415
467
  if (response.data?.meta?.errorCode === 200) {
416
- setShowModal3(false);
417
- setTimeout(() => {
418
- onRefresh();
419
- }, 300);
468
+ closeModalThen(() => onRefresh());
420
469
  } else {
421
470
  Alert.alert('Thông báo', response.data?.meta?.errorMessage);
422
471
  }
@@ -491,7 +540,10 @@ export const Home = observer(function Home() {
491
540
  </LinearGradient>
492
541
  <ScrollView
493
542
  refreshControl={
494
- <RefreshControl refreshing={false} onRefresh={onRefresh} />
543
+ <RefreshControl
544
+ refreshing={isLoading || homeStore.isLoading}
545
+ onRefresh={onRefresh}
546
+ />
495
547
  }
496
548
  style={{ flex: 1, marginBottom: 16 }}
497
549
  >
@@ -2333,12 +2385,72 @@ export const Home = observer(function Home() {
2333
2385
  </View>
2334
2386
  )}
2335
2387
  </Observer>
2388
+
2389
+ {/* Empty state: không có cả đơn mới lẫn đơn đang vay */}
2390
+ <Observer>
2391
+ {() =>
2392
+ !isLoading &&
2393
+ !homeStore.isLoading &&
2394
+ (listLoan?.length ?? 0) === 0 &&
2395
+ (homeStore?.listLoan?.length ?? 0) === 0 ? (
2396
+ <View
2397
+ style={{
2398
+ alignItems: 'center',
2399
+ justifyContent: 'center',
2400
+ paddingVertical: 48,
2401
+ }}
2402
+ >
2403
+ <Image
2404
+ source={require('./not_found.png')}
2405
+ style={{
2406
+ width: 180,
2407
+ height: 180,
2408
+ resizeMode: 'contain',
2409
+ }}
2410
+ />
2411
+ <MText
2412
+ style={{
2413
+ marginTop: 16,
2414
+ fontSize: 14,
2415
+ color: '#828282',
2416
+ textAlign: 'center',
2417
+ }}
2418
+ >
2419
+ Oops! Bạn chưa có khoản vay nào
2420
+ </MText>
2421
+ <TouchableOpacity
2422
+ onPress={() => onRefresh()}
2423
+ style={{
2424
+ marginTop: 16,
2425
+ borderWidth: 1,
2426
+ borderColor: '#EF592E',
2427
+ borderRadius: 30,
2428
+ paddingHorizontal: 32,
2429
+ height: 40,
2430
+ alignItems: 'center',
2431
+ justifyContent: 'center',
2432
+ }}
2433
+ >
2434
+ <MText style={{ color: '#EF592E', fontSize: 14 }}>
2435
+ Tải lại trang
2436
+ </MText>
2437
+ </TouchableOpacity>
2438
+ </View>
2439
+ ) : (
2440
+ <View />
2441
+ )
2442
+ }
2443
+ </Observer>
2336
2444
  </View>
2337
- <Modal isVisible={showModal}>
2445
+ <Modal
2446
+ isVisible={activeModal === MODAL.CIMB_ERROR}
2447
+ onModalHide={runPendingNav}
2448
+ {...modalSafeProps}
2449
+ >
2338
2450
  <SafeAreaView style={{ backgroundColor: '#FFFFFF', borderRadius: 6 }}>
2339
2451
  <View style={{ flexDirection: 'row-reverse' }}>
2340
2452
  <TouchableOpacity
2341
- onPress={() => setShowModal(false)}
2453
+ onPress={() => closeModal()}
2342
2454
  style={{
2343
2455
  width: 40,
2344
2456
  height: 40,
@@ -2419,7 +2531,11 @@ export const Home = observer(function Home() {
2419
2531
  </View>
2420
2532
  </SafeAreaView>
2421
2533
  </Modal>
2422
- <Modal isVisible={showModal2}>
2534
+ <Modal
2535
+ isVisible={activeModal === MODAL.CIMB_APPROVE}
2536
+ onModalHide={runPendingNav}
2537
+ {...modalSafeProps}
2538
+ >
2423
2539
  <SafeAreaView style={{ backgroundColor: '#FFFFFF', borderRadius: 8 }}>
2424
2540
  <View>
2425
2541
  <Image
@@ -2470,11 +2586,12 @@ export const Home = observer(function Home() {
2470
2586
  paddingVertical: 8,
2471
2587
  }}
2472
2588
  onPress={() => {
2473
- setShowModal2(false);
2474
- navigation.push(ScreenNames.AcceptPolicy, {
2475
- loan: loanCimb,
2476
- isCimb: true,
2477
- });
2589
+ closeModalThen(() =>
2590
+ navigation.push(ScreenNames.AcceptPolicy, {
2591
+ loan: loanCimb,
2592
+ isCimb: true,
2593
+ })
2594
+ );
2478
2595
  }}
2479
2596
  >
2480
2597
  <MText style={{ color: color.primary }}>Xác nhận</MText>
@@ -2502,7 +2619,7 @@ export const Home = observer(function Home() {
2502
2619
  });
2503
2620
  if (response.kind === 'ok') {
2504
2621
  if (response.data.meta.errorCode === 200) {
2505
- setShowModal2(false);
2622
+ closeModal();
2506
2623
  const key = `SHOW_${loanCimb.id}`;
2507
2624
  save(key, true);
2508
2625
  Alert.alert(
@@ -2573,11 +2690,12 @@ export const Home = observer(function Home() {
2573
2690
  paddingVertical: 8,
2574
2691
  }}
2575
2692
  onPress={() => {
2576
- setShowModal2(false);
2577
- navigation.push(ScreenNames.AcceptPolicy, {
2578
- loan: loanCimb,
2579
- isCimb: false,
2580
- });
2693
+ closeModalThen(() =>
2694
+ navigation.push(ScreenNames.AcceptPolicy, {
2695
+ loan: loanCimb,
2696
+ isCimb: false,
2697
+ })
2698
+ );
2581
2699
  }}
2582
2700
  >
2583
2701
  <MText style={{ color: color.primary }}>Tiếp tục</MText>
@@ -2588,7 +2706,11 @@ export const Home = observer(function Home() {
2588
2706
  </View>
2589
2707
  </SafeAreaView>
2590
2708
  </Modal>
2591
- <Modal isVisible={showModal3}>
2709
+ <Modal
2710
+ isVisible={activeModal === MODAL.TTS_APPROVE}
2711
+ onModalHide={runPendingNav}
2712
+ {...modalSafeProps}
2713
+ >
2592
2714
  <SafeAreaView style={{ backgroundColor: '#FFFFFF', borderRadius: 6 }}>
2593
2715
  <Image
2594
2716
  source={require('../../assets/tts/Banner.png')}
@@ -2690,10 +2812,9 @@ export const Home = observer(function Home() {
2690
2812
  >
2691
2813
  <TouchableOpacity
2692
2814
  onPress={() => {
2693
- setShowModal3(false);
2694
- setTimeout(() => {
2695
- navigation.push(ScreenNames.LoanInterestRate, { loan });
2696
- }, 300);
2815
+ closeModalThen(() =>
2816
+ navigation.push(ScreenNames.LoanInterestRate, { loan })
2817
+ );
2697
2818
  }}
2698
2819
  >
2699
2820
  <MText
@@ -2711,45 +2832,44 @@ export const Home = observer(function Home() {
2711
2832
  </Modal>
2712
2833
 
2713
2834
  <ModalOptionLoan
2714
- open={showModal4}
2835
+ open={activeModal === MODAL.SUGGEST}
2715
2836
  loan={dataSuggest}
2716
2837
  callback={() => {
2717
- setShowModal4(false);
2718
- setTimeout(() => {
2719
- // onRefresh();
2838
+ closeModalThen(() =>
2720
2839
  navigation.push(ScreenNames.ReviewLoan, {
2721
2840
  loan: loan,
2722
- });
2723
- }, 300);
2841
+ })
2842
+ );
2724
2843
  }}
2725
2844
  onClose={() => {
2726
- setShowModal4(false);
2845
+ closeModal();
2727
2846
  }}
2847
+ onModalHide={runPendingNav}
2728
2848
  />
2729
2849
  <ModalOptionLoanDigital
2730
- open={showModal6}
2850
+ open={activeModal === MODAL.DIGITAL_SUGGEST}
2731
2851
  loan={dataSuggest}
2732
2852
  callback={() => {
2733
- setShowModal6(false);
2734
- setTimeout(() => {
2853
+ closeModalThen(() =>
2735
2854
  navigation.push(ScreenNames.ReviewLoan, {
2736
2855
  loan: loan,
2737
- });
2738
- }, 300);
2856
+ })
2857
+ );
2739
2858
  }}
2740
2859
  onClose={() => {
2741
- setShowModal6(false);
2860
+ closeModal();
2742
2861
  }}
2862
+ onModalHide={runPendingNav}
2743
2863
  />
2744
2864
 
2745
2865
  <AlertLoan
2746
- open={showModal5}
2866
+ open={activeModal === MODAL.ALERT}
2747
2867
  onClose={() => {
2748
- setShowModal5(false);
2868
+ closeModal();
2749
2869
  }}
2750
2870
  />
2751
2871
  </ScrollView>
2752
- <Loading isLoading={isLoading || homeStore.isLoading} />
2872
+ {/* <Loading isLoading={isLoading || homeStore.isLoading} /> */}
2753
2873
  </View>
2754
2874
  );
2755
2875
  });