nx-react-native-cli 2.7.0 → 3.0.0

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 (135) hide show
  1. package/lib/index.cjs +43 -43
  2. package/package.json +1 -1
  3. package/templates/19.7.0/apps/mobile/android/app/src/main/java/com/appsmobile/MainActivity.kt +5 -1
  4. package/templates/19.7.0/apps/mobile/ios/AppsMobile/AppDelegate.mm +6 -0
  5. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x.png +0 -0
  6. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png +0 -0
  7. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@3x.png +0 -0
  8. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png +0 -0
  9. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29.png +0 -0
  10. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x.png +0 -0
  11. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png +0 -0
  12. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@3x.png +0 -0
  13. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png +0 -0
  14. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x.png +0 -0
  15. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png +0 -0
  16. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@3x.png +0 -0
  17. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png +0 -0
  18. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png +0 -0
  19. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png +0 -0
  20. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png +0 -0
  21. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x.png +0 -0
  22. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png +0 -0
  23. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon@3x.png +0 -0
  24. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png +0 -0
  25. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/AppIcon~ipad.png +0 -0
  26. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/AppIcon.appiconset/Contents.json +134 -0
  27. package/templates/19.7.0/apps/mobile/ios/AppsMobile/Images.xcassets/Contents.json +6 -0
  28. package/templates/21.2.2/apps/mobile/android/app/src/main/java/com/mobile/MainActivity.kt +5 -1
  29. package/templates/21.2.2/apps/mobile/ios/Mobile/AppDelegate.mm +6 -0
  30. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x.png +0 -0
  31. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png +0 -0
  32. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20@3x.png +0 -0
  33. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png +0 -0
  34. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29.png +0 -0
  35. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x.png +0 -0
  36. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png +0 -0
  37. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29@3x.png +0 -0
  38. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png +0 -0
  39. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x.png +0 -0
  40. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png +0 -0
  41. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40@3x.png +0 -0
  42. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png +0 -0
  43. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png +0 -0
  44. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png +0 -0
  45. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png +0 -0
  46. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x.png +0 -0
  47. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png +0 -0
  48. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon@3x.png +0 -0
  49. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png +0 -0
  50. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/AppIcon~ipad.png +0 -0
  51. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/AppIcon.appiconset/Contents.json +134 -0
  52. package/templates/21.2.2/apps/mobile/ios/Mobile/Images.xcassets/Contents.json +6 -0
  53. package/templates/shared/apps/mobile/android/app/src/main/AndroidManifest.xml +28 -0
  54. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  55. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  56. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
  57. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  58. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png +0 -0
  59. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  60. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
  61. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  62. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png +0 -0
  63. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  64. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
  65. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  66. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png +0 -0
  67. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  68. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
  69. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  70. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png +0 -0
  71. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  72. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
  73. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  74. package/templates/shared/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +0 -0
  75. package/templates/shared/apps/mobile/android/app/src/main/res/styles.xml +14 -0
  76. package/templates/shared/apps/mobile/run-android.sh +2 -1
  77. package/templates/shared/apps/mobile/scripts/setup-ios-dev-scheme.rb +101 -1
  78. package/templates/shared/apps/mobile/src/app/index.tsx +16 -47
  79. package/templates/shared/apps/mobile/src/app/query-client.ts +40 -0
  80. package/templates/shared/apps/mobile/src/assets/images/logo.png +0 -0
  81. package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert-manager.component.tsx +134 -0
  82. package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert-manager.types.ts +18 -0
  83. package/templates/shared/apps/mobile/src/components/atoms/AlertManager/alert.service.ts +27 -0
  84. package/templates/shared/apps/mobile/src/components/atoms/AlertManager/index.ts +3 -0
  85. package/templates/shared/apps/mobile/src/components/atoms/BottomSheet/bottom-sheet.component.tsx +14 -8
  86. package/templates/shared/apps/mobile/src/components/atoms/Button/button.component.tsx +1 -1
  87. package/templates/shared/apps/mobile/src/components/atoms/DateModalInput/date-modal-input.component.tsx +69 -0
  88. package/templates/shared/apps/mobile/src/components/atoms/DateModalInput/index.ts +1 -0
  89. package/templates/shared/apps/mobile/src/components/atoms/DatePicker/date-picker.component.tsx +44 -0
  90. package/templates/shared/apps/mobile/src/components/atoms/DatePicker/index.ts +1 -0
  91. package/templates/shared/apps/mobile/src/components/atoms/DateTextInput/date-text-input.component.tsx +218 -0
  92. package/templates/shared/apps/mobile/src/components/atoms/DateTextInput/index.ts +1 -0
  93. package/templates/shared/apps/mobile/src/components/atoms/Divider/divider-component.tsx +1 -1
  94. package/templates/shared/apps/mobile/src/components/atoms/GradientBackground/gradient-background.component.tsx +45 -0
  95. package/templates/shared/apps/mobile/src/components/atoms/GradientBackground/index.ts +1 -0
  96. package/templates/shared/apps/mobile/src/components/atoms/InputLayout/input-layout.component.tsx +12 -4
  97. package/templates/shared/apps/mobile/src/components/atoms/KeyboardAccessory/keyboard-accessory.component.tsx +6 -3
  98. package/templates/shared/apps/mobile/src/components/atoms/KeyboardAwareScrollView/keyboard-aware-scroll-view.component.tsx +1 -0
  99. package/templates/shared/apps/mobile/src/components/atoms/Modal/modal.component.tsx +2 -0
  100. package/templates/shared/apps/mobile/src/components/atoms/ScreenLoader/screen-loader.component.tsx +6 -1
  101. package/templates/shared/apps/mobile/src/components/atoms/SelectDropdown/index.ts +1 -0
  102. package/templates/shared/apps/mobile/src/components/atoms/SelectDropdown/select-dropdown.component.tsx +223 -0
  103. package/templates/shared/apps/mobile/src/components/atoms/Skeleton/skeleton.component.tsx +1 -1
  104. package/templates/shared/apps/mobile/src/components/atoms/TextInput/bottom-sheet-text-input.component.tsx +4 -3
  105. package/templates/shared/apps/mobile/src/components/atoms/TextInput/text-input.component.tsx +8 -4
  106. package/templates/shared/apps/mobile/src/components/atoms/ThemeManager/index.ts +1 -0
  107. package/templates/shared/apps/mobile/src/components/atoms/ThemeManager/theme-manager.component.tsx +27 -0
  108. package/templates/shared/apps/mobile/src/components/atoms/ToastManager/index.ts +3 -0
  109. package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast-manager.component.tsx +109 -0
  110. package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast-manager.types.ts +10 -0
  111. package/templates/shared/apps/mobile/src/components/atoms/ToastManager/toast.service.ts +27 -0
  112. package/templates/shared/apps/mobile/src/components/atoms/Typography/typography.component.tsx +1 -1
  113. package/templates/shared/apps/mobile/src/components/atoms/index.ts +8 -0
  114. package/templates/shared/apps/mobile/src/components/molecules/BackButton/back-button.component.tsx +1 -1
  115. package/templates/shared/apps/mobile/src/components/molecules/ScreenContainer/screen-container.component.tsx +4 -24
  116. package/templates/shared/apps/mobile/src/components/molecules/ScreenHeader/screen-header.component.tsx +2 -2
  117. package/templates/shared/apps/mobile/src/hooks/index.ts +1 -0
  118. package/templates/shared/apps/mobile/src/hooks/usePushNotifications.hook.ts +104 -0
  119. package/templates/shared/apps/mobile/src/hooks/useToggleDarkMode.hook.tsx +24 -2
  120. package/templates/shared/apps/mobile/src/icons/alert-triangle.svg +5 -0
  121. package/templates/shared/apps/mobile/src/icons/check-circle.svg +4 -0
  122. package/templates/shared/apps/mobile/src/icons/chevron-down.svg +1 -0
  123. package/templates/shared/apps/mobile/src/icons/chevron-right.svg +1 -0
  124. package/templates/shared/apps/mobile/src/icons/index.ts +18 -1
  125. package/templates/shared/apps/mobile/src/icons/info.svg +5 -0
  126. package/templates/shared/apps/mobile/src/icons/x-circle.svg +5 -0
  127. package/templates/shared/apps/mobile/src/routes/index.tsx +26 -15
  128. package/templates/shared/apps/mobile/src/screens/LandingScreen/landing.screen.tsx +232 -8
  129. package/templates/shared/apps/mobile/src/stores/local-storage.store.ts +9 -5
  130. package/templates/shared/apps/mobile/src/stores/theme.slice.ts +15 -0
  131. package/templates/shared/apps/mobile/src/stores/user.slice.ts +5 -1
  132. package/templates/shared/apps/mobile/src/tailwind/index.ts +3 -3
  133. package/templates/shared/apps/mobile/tailwind.config.js +14 -0
  134. package/templates/shared/patches/react-native-animatable+1.4.0.patch +71 -0
  135. package/templates/shared/apps/mobile/src/assets/images/.gitkeep +0 -0
@@ -0,0 +1,27 @@
1
+ import { useEffect } from 'react';
2
+ import { StatusBar } from 'react-native';
3
+ import { useDeviceContext } from 'twrnc';
4
+
5
+ import CONFIG from '@/config';
6
+ import { useLocalStorageStore } from '@/stores';
7
+ import { tw } from '@/tailwind';
8
+
9
+ export function ThemeManager() {
10
+ const storedScheme = useLocalStorageStore((s) => s.colorScheme);
11
+ useDeviceContext(tw, {
12
+ initialColorScheme: storedScheme,
13
+ observeDeviceColorSchemeChanges: false,
14
+ });
15
+
16
+ const isDark = storedScheme === 'dark';
17
+
18
+ useEffect(() => {
19
+ StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content');
20
+ if (CONFIG.IS_ANDROID) {
21
+ StatusBar.setBackgroundColor('transparent');
22
+ StatusBar.setTranslucent(true);
23
+ }
24
+ }, [isDark]);
25
+
26
+ return null;
27
+ }
@@ -0,0 +1,3 @@
1
+ export * from './toast-manager.component';
2
+ export * from './toast-manager.types';
3
+ export * from './toast.service';
@@ -0,0 +1,109 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { Pressable, View } from 'react-native';
3
+ import Animated, { SlideInDown, SlideOutDown } from 'react-native-reanimated';
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
+ import { SvgProps } from 'react-native-svg';
6
+
7
+ import { ToastManagerRef, ToastOptions, ToastVariant } from './toast-manager.types';
8
+ import { Toast } from './toast.service';
9
+
10
+ import { Typography } from '@/components/atoms/Typography';
11
+ import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XCircleIcon } from '@/icons';
12
+ import { colors, tw } from '@/tailwind';
13
+
14
+ type QueuedToast = ToastOptions & {
15
+ variant: ToastVariant;
16
+ };
17
+
18
+ type VariantConfig = {
19
+ color: string;
20
+ icon: React.FC<SvgProps>;
21
+ };
22
+
23
+ const ANIMATION_DURATION = 300;
24
+ const DEFAULT_DURATION = 3000;
25
+ const ICON_SIZE = 20;
26
+ const BOTTOM_PADDING_OFFSET = 16;
27
+
28
+ const VARIANT_CONFIG: Record<ToastVariant, VariantConfig> = {
29
+ error: { color: colors.error, icon: XCircleIcon },
30
+ info: { color: colors.secondary[500], icon: InfoIcon },
31
+ success: { color: colors.success, icon: CheckCircleIcon },
32
+ warning: { color: colors.primary[500], icon: AlertTriangleIcon },
33
+ };
34
+
35
+ export function ToastManager() {
36
+ const [queue, setQueue] = useState<QueuedToast[]>([]);
37
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
38
+ const insets = useSafeAreaInsets();
39
+
40
+ const currentToast = queue[0];
41
+
42
+ const handleDismiss = useCallback(() => {
43
+ if (timerRef.current) {
44
+ clearTimeout(timerRef.current);
45
+ timerRef.current = null;
46
+ }
47
+ setQueue((prev) => prev.slice(1));
48
+ }, []);
49
+
50
+ useEffect(() => {
51
+ const ref: ToastManagerRef = {
52
+ show: (variant: ToastVariant, options: ToastOptions) => {
53
+ setQueue((prev) => [...prev, { ...options, variant }]);
54
+ },
55
+ };
56
+ Toast.setRef(ref);
57
+ }, []);
58
+
59
+ useEffect(() => {
60
+ if (!currentToast) {
61
+ return;
62
+ }
63
+
64
+ const duration = currentToast.duration ?? DEFAULT_DURATION;
65
+ timerRef.current = setTimeout(() => {
66
+ setQueue((prev) => prev.slice(1));
67
+ }, duration);
68
+
69
+ return () => {
70
+ if (timerRef.current) {
71
+ clearTimeout(timerRef.current);
72
+ timerRef.current = null;
73
+ }
74
+ };
75
+ }, [currentToast]);
76
+
77
+ if (!currentToast) {
78
+ return null;
79
+ }
80
+
81
+ const config = VARIANT_CONFIG[currentToast.variant];
82
+ const IconComponent = config.icon;
83
+
84
+ return (
85
+ <View pointerEvents="box-none" style={tw`absolute bottom-10 left-6 right-6 z-50 items-center`}>
86
+ <Animated.View
87
+ key={`${currentToast.variant}-${currentToast.message}`}
88
+ entering={SlideInDown.duration(ANIMATION_DURATION)}
89
+ exiting={SlideOutDown.duration(ANIMATION_DURATION)}
90
+ style={[tw`mx-4 w-full max-w-sm`, { paddingBottom: insets.bottom + BOTTOM_PADDING_OFFSET }]}
91
+ >
92
+ <Pressable
93
+ style={tw`dark:border-divider dark:bg-sheet flex-row items-center rounded-xl border border-gray-200 bg-white px-4 py-3.5 shadow-sm`}
94
+ onPress={handleDismiss}
95
+ >
96
+ <View style={tw`mr-3`}>
97
+ <IconComponent color={config.color} height={ICON_SIZE} width={ICON_SIZE} />
98
+ </View>
99
+ <Typography
100
+ numberOfLines={2}
101
+ style={tw`dark:text-foreground flex-1 text-sm font-medium text-gray-800`}
102
+ >
103
+ {currentToast.message}
104
+ </Typography>
105
+ </Pressable>
106
+ </Animated.View>
107
+ </View>
108
+ );
109
+ }
@@ -0,0 +1,10 @@
1
+ export type ToastVariant = 'error' | 'info' | 'success' | 'warning';
2
+
3
+ export type ToastOptions = {
4
+ duration?: number;
5
+ message: string;
6
+ };
7
+
8
+ export type ToastManagerRef = {
9
+ show: (variant: ToastVariant, options: ToastOptions) => void;
10
+ };
@@ -0,0 +1,27 @@
1
+ import { ToastManagerRef, ToastOptions } from './toast-manager.types';
2
+
3
+ class ToastService {
4
+ private static ref: ToastManagerRef | null = null;
5
+
6
+ static error(message: string, options?: Omit<ToastOptions, 'message'>) {
7
+ ToastService.ref?.show('error', { message, ...options });
8
+ }
9
+
10
+ static info(message: string, options?: Omit<ToastOptions, 'message'>) {
11
+ ToastService.ref?.show('info', { message, ...options });
12
+ }
13
+
14
+ static setRef(ref: ToastManagerRef) {
15
+ ToastService.ref = ref;
16
+ }
17
+
18
+ static success(message: string, options?: Omit<ToastOptions, 'message'>) {
19
+ ToastService.ref?.show('success', { message, ...options });
20
+ }
21
+
22
+ static warning(message: string, options?: Omit<ToastOptions, 'message'>) {
23
+ ToastService.ref?.show('warning', { message, ...options });
24
+ }
25
+ }
26
+
27
+ export { ToastService as Toast };
@@ -16,7 +16,7 @@ export function Typography(props: Props): JSX.Element {
16
16
  return (
17
17
  <RNText
18
18
  {...shouldTruncateTextProps}
19
- style={[tw`font-sans text-base font-normal text-black`, style]}
19
+ style={[tw`dark:text-foreground font-sans text-base font-normal text-gray-900`, style]}
20
20
  {...extraProps}
21
21
  />
22
22
  );
@@ -1,13 +1,21 @@
1
+ export * from './AlertManager';
1
2
  export * from './BottomSheet';
2
3
  export * from './Button';
4
+ export * from './DateModalInput';
5
+ export * from './DatePicker';
6
+ export * from './DateTextInput';
3
7
  export * from './Divider';
4
8
  export * from './ExcludedEdges';
9
+ export * from './GradientBackground';
5
10
  export * from './InputLayout';
6
11
  export * from './KeyboardAccessory';
7
12
  export * from './KeyboardAwareScrollView';
8
13
  export * from './ListLoadingItem';
9
14
  export * from './Modal';
10
15
  export * from './ScreenLoader';
16
+ export * from './SelectDropdown';
11
17
  export * from './Skeleton';
12
18
  export * from './TextInput';
19
+ export * from './ThemeManager';
20
+ export * from './ToastManager';
13
21
  export * from './Typography';
@@ -51,7 +51,7 @@ export function BackButton(props: Props) {
51
51
  onPress={handleOnPress}
52
52
  >
53
53
  <View style={[tw`flex-1 items-center justify-center rounded-full`, style]}>
54
- <ArrowLeftIcon style={tw`text-gray-950`} />
54
+ <ArrowLeftIcon style={tw`dark:text-foreground text-gray-900`} />
55
55
  </View>
56
56
  </TouchableOpacity>
57
57
  );
@@ -1,12 +1,9 @@
1
- import { useFocusEffect } from '@react-navigation/native';
2
1
  import React, { ReactElement } from 'react';
3
2
  import {
4
3
  NativeScrollEvent,
5
4
  NativeSyntheticEvent,
6
5
  Platform,
7
6
  RefreshControlProps,
8
- StatusBar,
9
- StatusBarStyle,
10
7
  StyleProp,
11
8
  View,
12
9
  ViewStyle,
@@ -15,20 +12,15 @@ import { ScrollView as RNScrollView } from 'react-native-gesture-handler';
15
12
  import { Edge, SafeAreaProviderProps, SafeAreaView } from 'react-native-safe-area-context';
16
13
 
17
14
  import { KeyboardAwareScrollView } from '@/components';
18
- import CONFIG from '@/config';
19
15
  import { tw } from '@/tailwind';
20
16
 
21
17
  type Props = SafeAreaProviderProps & {
22
- barStyle?: StatusBarStyle;
23
18
  scrollViewRef?: React.RefObject<RNScrollView | null>;
24
19
  containerStyle?: StyleProp<ViewStyle>;
25
20
  excludedEdges?: Edge[];
26
21
  extraBottomPadding?: number;
27
22
  hasScroll?: boolean;
28
23
  refreshControl?: ReactElement;
29
- shouldShowStatusBar?: boolean;
30
- shouldBeTranslucent?: boolean;
31
- statusBarColor?: string;
32
24
  useSafeAreaView?: boolean;
33
25
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
34
26
  };
@@ -43,7 +35,6 @@ const safeAreaViewEdges: Edge[] = Platform.select({
43
35
 
44
36
  export function ScreenContainer(props: Props) {
45
37
  const {
46
- barStyle = 'dark-content',
47
38
  children,
48
39
  containerStyle,
49
40
  excludedEdges = [],
@@ -52,20 +43,9 @@ export function ScreenContainer(props: Props) {
52
43
  onScroll,
53
44
  refreshControl,
54
45
  scrollViewRef,
55
- shouldBeTranslucent = false,
56
- shouldShowStatusBar = true,
57
- statusBarColor = 'transparent',
58
46
  style,
59
47
  useSafeAreaView = true,
60
48
  } = props;
61
- useFocusEffect(() => {
62
- StatusBar.setHidden(!shouldShowStatusBar);
63
- if (CONFIG.IS_ANDROID) {
64
- StatusBar.setBackgroundColor(statusBarColor);
65
- StatusBar.setTranslucent(!shouldBeTranslucent);
66
- StatusBar.setBarStyle(barStyle);
67
- }
68
- });
69
49
 
70
50
  const defaultContainerStyle = [
71
51
  defaultStyle,
@@ -76,12 +56,12 @@ export function ScreenContainer(props: Props) {
76
56
 
77
57
  if (!useSafeAreaView) {
78
58
  return (
79
- <View style={[tw`flex-1 bg-white`, style]}>
59
+ <View style={[tw`dark:bg-background flex-1 bg-white`, style]}>
80
60
  {hasScroll ? (
81
61
  <KeyboardAwareScrollView
82
62
  containerStyle={defaultContainerStyle}
83
63
  refreshControl={refreshControl as ReactElement<RefreshControlProps>}
84
- scrollViewRef={scrollViewRef}
64
+ scrollViewRef={scrollViewRef as React.RefObject<RNScrollView>}
85
65
  onScroll={onScroll}
86
66
  >
87
67
  {children}
@@ -99,12 +79,12 @@ export function ScreenContainer(props: Props) {
99
79
  : safeAreaViewEdges;
100
80
 
101
81
  return (
102
- <SafeAreaView edges={edges} style={[tw`flex-1 bg-white`, style]}>
82
+ <SafeAreaView edges={edges} style={[tw`dark:bg-background flex-1 bg-white`, style]}>
103
83
  {hasScroll ? (
104
84
  <KeyboardAwareScrollView
105
85
  containerStyle={defaultContainerStyle}
106
86
  refreshControl={refreshControl as ReactElement<RefreshControlProps>}
107
- scrollViewRef={scrollViewRef}
87
+ scrollViewRef={scrollViewRef as React.RefObject<RNScrollView>}
108
88
  onScroll={onScroll}
109
89
  >
110
90
  {children}
@@ -39,7 +39,7 @@ export function ScreenHeader(props: Props) {
39
39
  {extraActionComponent ? (
40
40
  extraActionComponent
41
41
  ) : (
42
- <GearIcon height={25} style={tw`text-black`} width={25} />
42
+ <GearIcon height={25} style={tw`dark:text-foreground text-gray-900`} width={25} />
43
43
  )}
44
44
  </View>
45
45
  </TouchableOpacity>
@@ -51,7 +51,7 @@ export function ScreenHeader(props: Props) {
51
51
  {hasBackButton && <BackButton style={tw`z-10`} onPress={onBackPress} />}
52
52
  <Typography
53
53
  style={[
54
- tw`text-primary-700 absolute inset-x-0 top-4 text-center text-xl font-medium`,
54
+ tw`dark:text-foreground absolute inset-x-0 top-4 text-center text-xl font-medium text-gray-900`,
55
55
  titleStyle,
56
56
  ]}
57
57
  >
@@ -4,6 +4,7 @@ export * from './useDebounce.hook';
4
4
  export * from './useGetLayoutHeight.hook';
5
5
  export * from './useGetLayoutWidth.hook';
6
6
  export * from './useNavigation.hook';
7
+ export * from './usePushNotifications.hook';
7
8
  export * from './useShakeAnimation.hook';
8
9
  export * from './useTextInputChangeFocus.hook';
9
10
  export * from './useThrottle.hook';
@@ -0,0 +1,104 @@
1
+ import { useEffect } from 'react';
2
+ import { AppState, AppStateStatus, Linking } from 'react-native';
3
+ import { checkNotifications, requestNotifications, RESULTS } from 'react-native-permissions';
4
+
5
+ import { Alert } from '@/components/atoms';
6
+ import { useLocalStorageStore } from '@/stores/local-storage.store';
7
+ import { devLog } from '@/utils/log.util';
8
+
9
+ function showEnableNotificationsAlert() {
10
+ Alert.info({
11
+ actions: [
12
+ { label: 'Later', variant: 'cancel' },
13
+ { label: 'Open Settings', onPress: () => Linking.openSettings() },
14
+ ],
15
+ message: 'Enable notifications to stay updated with important alerts and messages.',
16
+ title: 'Notifications Disabled',
17
+ });
18
+ }
19
+
20
+ function isGranted(status: string): boolean {
21
+ return status === RESULTS.GRANTED || status === RESULTS.LIMITED;
22
+ }
23
+
24
+ // TODO: Implement once @react-native-firebase/messaging is installed
25
+ async function registerFcmToken() {
26
+ devLog('registerFcmToken: Firebase messaging not configured yet');
27
+ }
28
+
29
+ // TODO: Implement once @react-native-firebase/messaging is installed
30
+ async function removeFcmToken() {
31
+ devLog('removeFcmToken: Firebase messaging not configured yet');
32
+ }
33
+
34
+ function syncPushNotificationState(status: string) {
35
+ useLocalStorageStore.getState().setPushNotificationsEnabled(isGranted(status));
36
+ }
37
+
38
+ export async function requestPushNotification() {
39
+ const { status } = await checkNotifications();
40
+ syncPushNotificationState(status);
41
+
42
+ if (isGranted(status)) {
43
+ await registerFcmToken();
44
+
45
+ return;
46
+ }
47
+
48
+ const { status: requestStatus } = await requestNotifications([]);
49
+
50
+ if (isGranted(requestStatus)) {
51
+ syncPushNotificationState(requestStatus);
52
+ await registerFcmToken();
53
+
54
+ return;
55
+ }
56
+
57
+ const isBlocked = status === RESULTS.BLOCKED || requestStatus === RESULTS.BLOCKED;
58
+ if (isBlocked) {
59
+ showEnableNotificationsAlert();
60
+ }
61
+ }
62
+
63
+ async function handleAppForeground() {
64
+ const { status } = await checkNotifications();
65
+ syncPushNotificationState(status);
66
+
67
+ if (!isGranted(status)) {
68
+ return;
69
+ }
70
+
71
+ await registerFcmToken();
72
+ }
73
+
74
+ export async function removeCurrentFcmToken(): Promise<void> {
75
+ try {
76
+ await removeFcmToken();
77
+ } catch (error) {
78
+ devLog('Failed to remove FCM token:', error);
79
+ }
80
+ }
81
+
82
+ export function usePushNotifications(isAuthenticated: boolean) {
83
+ useEffect(() => {
84
+ if (!isAuthenticated) {
85
+ return;
86
+ }
87
+
88
+ const handleAppStateChange = (nextState: AppStateStatus) => {
89
+ if (nextState !== 'active') {
90
+ return;
91
+ }
92
+
93
+ handleAppForeground().catch((error) => {
94
+ devLog('Failed to check notification permission on foreground:', error);
95
+ });
96
+ };
97
+
98
+ const subscription = AppState.addEventListener('change', handleAppStateChange);
99
+
100
+ return () => {
101
+ subscription.remove();
102
+ };
103
+ }, [isAuthenticated]);
104
+ }
@@ -1,9 +1,31 @@
1
+ import { useCallback } from 'react';
1
2
  import { useAppColorScheme } from 'twrnc';
2
3
 
4
+ import { useLocalStorageStore } from '@/stores';
5
+ import { ColorScheme } from '@/stores/theme.slice';
3
6
  import { tw } from '@/tailwind';
4
7
 
5
8
  export function useToggleDarkMode() {
6
- const [, toggleColorScheme] = useAppColorScheme(tw);
9
+ const [colorScheme, , setTwColorScheme] = useAppColorScheme(tw);
10
+ const setStoredColorScheme = useLocalStorageStore((s) => s.setColorScheme);
7
11
 
8
- return toggleColorScheme;
12
+ const toggleColorScheme = useCallback(() => {
13
+ const next: ColorScheme = colorScheme === 'dark' ? 'light' : 'dark';
14
+ setTwColorScheme(next);
15
+ setStoredColorScheme(next);
16
+ }, [colorScheme, setTwColorScheme, setStoredColorScheme]);
17
+
18
+ const setColorScheme = useCallback(
19
+ (scheme: ColorScheme) => {
20
+ setTwColorScheme(scheme);
21
+ setStoredColorScheme(scheme);
22
+ },
23
+ [setTwColorScheme, setStoredColorScheme],
24
+ );
25
+
26
+ return {
27
+ colorScheme: (colorScheme ?? 'light') as ColorScheme,
28
+ setColorScheme,
29
+ toggleColorScheme,
30
+ };
9
31
  }
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
3
+ <path d="M12 9v4"/>
4
+ <path d="M12 17h.01"/>
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
3
+ <path d="m9 11 3 3L22 4"/>
4
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
@@ -1,6 +1,23 @@
1
+ import AlertTriangleIcon from './alert-triangle.svg';
1
2
  import ArrowLeftIcon from './arrow-left.svg';
3
+ import CheckCircleIcon from './check-circle.svg';
4
+ import ChevronDownIcon from './chevron-down.svg';
5
+ import ChevronRightIcon from './chevron-right.svg';
2
6
  import CrossIcon from './cross.svg';
3
7
  import GearIcon from './gear.svg';
8
+ import InfoIcon from './info.svg';
4
9
  import KeyboardHideIcon from './keyboard-hide.svg';
10
+ import XCircleIcon from './x-circle.svg';
5
11
 
6
- export { ArrowLeftIcon, CrossIcon, GearIcon, KeyboardHideIcon };
12
+ export {
13
+ AlertTriangleIcon,
14
+ ArrowLeftIcon,
15
+ CheckCircleIcon,
16
+ ChevronDownIcon,
17
+ ChevronRightIcon,
18
+ CrossIcon,
19
+ GearIcon,
20
+ InfoIcon,
21
+ KeyboardHideIcon,
22
+ XCircleIcon,
23
+ };
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <circle cx="12" cy="12" r="10"/>
3
+ <path d="M12 16v-4"/>
4
+ <path d="M12 8h.01"/>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <circle cx="12" cy="12" r="10"/>
3
+ <path d="m15 9-6 6"/>
4
+ <path d="m9 9 6 6"/>
5
+ </svg>
@@ -1,13 +1,13 @@
1
- import { DefaultTheme, NavigationContainer, Theme } from '@react-navigation/native';
1
+ import { DarkTheme, DefaultTheme, NavigationContainer, Theme } from '@react-navigation/native';
2
2
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
3
- import React, { useEffect } from 'react';
4
- import { StatusBar } from 'react-native';
3
+ import React, { useMemo } from 'react';
4
+ import BootSplash from 'react-native-bootsplash';
5
5
 
6
- import CONFIG from '@/config';
7
6
  import { Routes } from '@/routes';
8
7
  import PrivateRoutes from '@/routes/privateRoutes';
9
8
  import PublicRoutes from '@/routes/publicRoutes';
10
9
  import { screenOptions } from '@/routes/screen-options';
10
+ import { useLocalStorageStore } from '@/stores';
11
11
  import { colors } from '@/tailwind';
12
12
 
13
13
  const RootStack = createNativeStackNavigator();
@@ -16,11 +16,19 @@ export const noAnimation = {
16
16
  animationEnabled: false,
17
17
  };
18
18
 
19
- const navigationTheme: Theme = {
19
+ const lightNavigationTheme: Theme = {
20
20
  ...DefaultTheme,
21
21
  colors: {
22
22
  ...DefaultTheme.colors,
23
- background: colors.gray[50],
23
+ background: colors.white,
24
+ },
25
+ };
26
+
27
+ const darkNavigationTheme: Theme = {
28
+ ...DarkTheme,
29
+ colors: {
30
+ ...DarkTheme.colors,
31
+ background: colors.background,
24
32
  },
25
33
  };
26
34
 
@@ -29,15 +37,13 @@ export default function ApplicationRoutes() {
29
37
  // const isUserAuthenticated = useLocalStorageState((state) => !!state.accessToken);
30
38
  const isUserAuthenticated = false;
31
39
  const initialRouteName = isUserAuthenticated ? Routes.PRIVATE : Routes.PUBLIC;
40
+ const colorScheme = useLocalStorageStore((s) => s.colorScheme);
41
+ const isDark = colorScheme === 'dark';
32
42
 
33
- useEffect(() => {
34
- StatusBar.setHidden(false);
35
- if (CONFIG.IS_ANDROID) {
36
- StatusBar.setBackgroundColor('transparent');
37
- StatusBar.setTranslucent(true);
38
- StatusBar.setBarStyle('dark-content');
39
- }
40
- }, []);
43
+ const navigationTheme = useMemo(
44
+ () => (isDark ? darkNavigationTheme : lightNavigationTheme),
45
+ [isDark],
46
+ );
41
47
 
42
48
  // if (isLoading) {
43
49
  // return <ScreenLoader />;
@@ -50,7 +56,12 @@ export default function ApplicationRoutes() {
50
56
  );
51
57
 
52
58
  return (
53
- <NavigationContainer theme={navigationTheme}>
59
+ <NavigationContainer
60
+ theme={navigationTheme}
61
+ onReady={() => {
62
+ BootSplash.hide();
63
+ }}
64
+ >
54
65
  <RootStack.Navigator initialRouteName={initialRouteName} screenOptions={screenOptions}>
55
66
  {screens}
56
67
  </RootStack.Navigator>