@oxyhq/services 5.3.11 → 5.4.1

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 (213) hide show
  1. package/README.md +21 -0
  2. package/lib/commonjs/assets/assets/icons/OxyServices.tsx +67 -0
  3. package/lib/commonjs/assets/assets/icons/logo_OxyServices.svg +1 -0
  4. package/lib/commonjs/assets/icons/OxyServices.js +53 -0
  5. package/lib/commonjs/assets/icons/OxyServices.js.map +1 -0
  6. package/lib/commonjs/assets/icons/logo_OxyServices.svg +1 -0
  7. package/lib/commonjs/core/index.js +119 -23
  8. package/lib/commonjs/core/index.js.map +1 -1
  9. package/lib/commonjs/index.js +2 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/lib/sonner.js +15 -11
  12. package/lib/commonjs/lib/sonner.js.map +1 -1
  13. package/lib/commonjs/node/index.js +2 -0
  14. package/lib/commonjs/node/index.js.map +1 -1
  15. package/lib/commonjs/ui/components/GroupedItem.js +109 -0
  16. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -0
  17. package/lib/commonjs/ui/components/GroupedSection.js +33 -0
  18. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -0
  19. package/lib/commonjs/ui/components/OxyProvider.js +95 -112
  20. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  21. package/lib/commonjs/ui/components/ProfileCard.js +124 -0
  22. package/lib/commonjs/ui/components/ProfileCard.js.map +1 -0
  23. package/lib/commonjs/ui/components/QuickActions.js +87 -0
  24. package/lib/commonjs/ui/components/QuickActions.js.map +1 -0
  25. package/lib/commonjs/ui/components/Section.js +36 -0
  26. package/lib/commonjs/ui/components/Section.js.map +1 -0
  27. package/lib/commonjs/ui/components/SectionTitle.js +35 -0
  28. package/lib/commonjs/ui/components/SectionTitle.js.map +1 -0
  29. package/lib/commonjs/ui/components/bottomSheet/index.js +6 -6
  30. package/lib/commonjs/ui/components/index.js +97 -0
  31. package/lib/commonjs/ui/components/index.js.map +1 -0
  32. package/lib/commonjs/ui/navigation/OxyRouter.js +20 -3
  33. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  34. package/lib/commonjs/ui/screens/AccountCenterScreen.js +190 -207
  35. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/AccountManagementDemo.js +299 -0
  37. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -0
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +669 -401
  39. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  40. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +695 -498
  41. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  42. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +451 -488
  43. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  44. package/lib/commonjs/ui/screens/AppInfoScreen.js +498 -185
  45. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  46. package/lib/commonjs/ui/screens/BillingManagementScreen.js +636 -0
  47. package/lib/commonjs/ui/screens/BillingManagementScreen.js.map +1 -0
  48. package/lib/commonjs/ui/screens/FileManagementScreen.js +2497 -0
  49. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -0
  50. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +1620 -0
  51. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  52. package/lib/commonjs/ui/screens/ProfileScreen.js +117 -13
  53. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  54. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
  56. package/lib/commonjs/ui/screens/SignUpScreen.js +1 -1
  57. package/lib/commonjs/utils/polyfills.js +42 -0
  58. package/lib/commonjs/utils/polyfills.js.map +1 -0
  59. package/lib/module/assets/assets/icons/OxyServices.tsx +67 -0
  60. package/lib/module/assets/assets/icons/logo_OxyServices.svg +1 -0
  61. package/lib/module/assets/icons/OxyServices.js +46 -0
  62. package/lib/module/assets/icons/OxyServices.js.map +1 -0
  63. package/lib/module/assets/icons/logo_OxyServices.svg +1 -0
  64. package/lib/module/core/index.js +119 -23
  65. package/lib/module/core/index.js.map +1 -1
  66. package/lib/module/index.js +3 -0
  67. package/lib/module/index.js.map +1 -1
  68. package/lib/module/lib/sonner.js +13 -1
  69. package/lib/module/lib/sonner.js.map +1 -1
  70. package/lib/module/node/index.js +3 -0
  71. package/lib/module/node/index.js.map +1 -1
  72. package/lib/module/ui/components/GroupedItem.js +104 -0
  73. package/lib/module/ui/components/GroupedItem.js.map +1 -0
  74. package/lib/module/ui/components/GroupedSection.js +28 -0
  75. package/lib/module/ui/components/GroupedSection.js.map +1 -0
  76. package/lib/module/ui/components/OxyProvider.js +97 -114
  77. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  78. package/lib/module/ui/components/ProfileCard.js +119 -0
  79. package/lib/module/ui/components/ProfileCard.js.map +1 -0
  80. package/lib/module/ui/components/QuickActions.js +82 -0
  81. package/lib/module/ui/components/QuickActions.js.map +1 -0
  82. package/lib/module/ui/components/Section.js +31 -0
  83. package/lib/module/ui/components/Section.js.map +1 -0
  84. package/lib/module/ui/components/SectionTitle.js +30 -0
  85. package/lib/module/ui/components/SectionTitle.js.map +1 -0
  86. package/lib/module/ui/components/bottomSheet/index.js +2 -5
  87. package/lib/module/ui/components/bottomSheet/index.js.map +1 -1
  88. package/lib/module/ui/components/index.js +18 -0
  89. package/lib/module/ui/components/index.js.map +1 -0
  90. package/lib/module/ui/navigation/OxyRouter.js +20 -3
  91. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  92. package/lib/module/ui/screens/AccountCenterScreen.js +191 -208
  93. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  94. package/lib/module/ui/screens/AccountManagementDemo.js +296 -0
  95. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -0
  96. package/lib/module/ui/screens/AccountOverviewScreen.js +671 -403
  97. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  98. package/lib/module/ui/screens/AccountSettingsScreen.js +698 -501
  99. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  100. package/lib/module/ui/screens/AccountSwitcherScreen.js +450 -488
  101. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  102. package/lib/module/ui/screens/AppInfoScreen.js +498 -186
  103. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  104. package/lib/module/ui/screens/BillingManagementScreen.js +631 -0
  105. package/lib/module/ui/screens/BillingManagementScreen.js.map +1 -0
  106. package/lib/module/ui/screens/FileManagementScreen.js +2492 -0
  107. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -0
  108. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +1615 -0
  109. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  110. package/lib/module/ui/screens/ProfileScreen.js +118 -14
  111. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  112. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  113. package/lib/module/ui/screens/SignInScreen.js +1 -1
  114. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  115. package/lib/module/ui/screens/SignUpScreen.js +1 -1
  116. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  117. package/lib/module/utils/polyfills.js +36 -0
  118. package/lib/module/utils/polyfills.js.map +1 -0
  119. package/lib/typescript/assets/icons/OxyServices.d.ts +29 -0
  120. package/lib/typescript/assets/icons/OxyServices.d.ts.map +1 -0
  121. package/lib/typescript/core/index.d.ts +26 -1
  122. package/lib/typescript/core/index.d.ts.map +1 -1
  123. package/lib/typescript/index.d.ts +1 -0
  124. package/lib/typescript/index.d.ts.map +1 -1
  125. package/lib/typescript/lib/sonner.d.ts +5 -1
  126. package/lib/typescript/lib/sonner.d.ts.map +1 -1
  127. package/lib/typescript/models/interfaces.d.ts +1 -2
  128. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  129. package/lib/typescript/node/index.d.ts +1 -0
  130. package/lib/typescript/node/index.d.ts.map +1 -1
  131. package/lib/typescript/ui/components/GroupedItem.d.ts +17 -0
  132. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -0
  133. package/lib/typescript/ui/components/GroupedSection.d.ts +19 -0
  134. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -0
  135. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  136. package/lib/typescript/ui/components/ProfileCard.d.ts +20 -0
  137. package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -0
  138. package/lib/typescript/ui/components/QuickActions.d.ts +15 -0
  139. package/lib/typescript/ui/components/QuickActions.d.ts.map +1 -0
  140. package/lib/typescript/ui/components/Section.d.ts +11 -0
  141. package/lib/typescript/ui/components/Section.d.ts.map +1 -0
  142. package/lib/typescript/ui/components/SectionTitle.d.ts +9 -0
  143. package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -0
  144. package/lib/typescript/ui/components/bottomSheet/index.d.ts +3 -2
  145. package/lib/typescript/ui/components/bottomSheet/index.d.ts.map +1 -1
  146. package/lib/typescript/ui/components/index.d.ts +13 -0
  147. package/lib/typescript/ui/components/index.d.ts.map +1 -0
  148. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  149. package/lib/typescript/ui/navigation/types.d.ts +8 -0
  150. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  151. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  152. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts +8 -0
  153. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts.map +1 -0
  154. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  155. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -4
  156. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  157. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  158. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  159. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts +5 -0
  160. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts.map +1 -0
  161. package/lib/typescript/ui/screens/FileManagementScreen.d.ts +8 -0
  162. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -0
  163. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts +5 -0
  164. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -0
  165. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  166. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  167. package/lib/typescript/utils/polyfills.d.ts +6 -0
  168. package/lib/typescript/utils/polyfills.d.ts.map +1 -0
  169. package/package.json +11 -3
  170. package/src/__tests__/polyfills.test.ts +30 -0
  171. package/src/__tests__/setup.ts +43 -0
  172. package/src/__tests__/ui/screens/AccountSettingsScreen.test.tsx +8 -8
  173. package/src/assets/icons/OxyServices.tsx +67 -0
  174. package/src/assets/icons/logo_OxyServices.svg +1 -0
  175. package/src/core/index.ts +127 -19
  176. package/src/index.ts +3 -0
  177. package/src/lib/sonner.ts +10 -1
  178. package/src/models/interfaces.ts +1 -2
  179. package/src/node/index.ts +3 -0
  180. package/src/ui/components/GroupedItem.tsx +118 -0
  181. package/src/ui/components/GroupedSection.tsx +45 -0
  182. package/src/ui/components/OxyProvider.tsx +95 -120
  183. package/src/ui/components/ProfileCard.tsx +129 -0
  184. package/src/ui/components/QuickActions.tsx +90 -0
  185. package/src/ui/components/Section.tsx +37 -0
  186. package/src/ui/components/SectionTitle.tsx +31 -0
  187. package/src/ui/components/bottomSheet/index.tsx +13 -11
  188. package/src/ui/components/index.ts +15 -0
  189. package/src/ui/navigation/OxyRouter.tsx +20 -3
  190. package/src/ui/navigation/types.ts +10 -1
  191. package/src/ui/screens/AccountCenterScreen.tsx +188 -159
  192. package/src/ui/screens/AccountManagementDemo.tsx +297 -0
  193. package/src/ui/screens/AccountOverviewScreen.tsx +474 -310
  194. package/src/ui/screens/AccountSettingsScreen.tsx +648 -463
  195. package/src/ui/screens/AccountSwitcherScreen.tsx +385 -449
  196. package/src/ui/screens/AppInfoScreen.tsx +571 -140
  197. package/src/ui/screens/BillingManagementScreen.tsx +589 -0
  198. package/src/ui/screens/FileManagementScreen.tsx +2513 -0
  199. package/src/ui/screens/PremiumSubscriptionScreen.tsx +1628 -0
  200. package/src/ui/screens/ProfileScreen.tsx +101 -7
  201. package/src/ui/screens/SessionManagementScreen.tsx +1 -0
  202. package/src/ui/screens/SignInScreen.tsx +1 -1
  203. package/src/ui/screens/SignUpScreen.tsx +1 -1
  204. package/src/utils/polyfills.ts +34 -0
  205. package/lib/commonjs/lib/sonner.web.js +0 -17
  206. package/lib/commonjs/lib/sonner.web.js.map +0 -1
  207. package/lib/module/lib/sonner.web.js +0 -4
  208. package/lib/module/lib/sonner.web.js.map +0 -1
  209. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts +0 -2
  210. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts.map +0 -1
  211. package/lib/typescript/lib/sonner.web.d.ts +0 -2
  212. package/lib/typescript/lib/sonner.web.d.ts.map +0 -1
  213. package/src/lib/sonner.web.ts +0 -1
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+
5
+ interface GroupedItemProps {
6
+ icon?: string;
7
+ iconColor?: string;
8
+ title: string;
9
+ subtitle?: string;
10
+ theme: 'light' | 'dark';
11
+ onPress?: () => void;
12
+ isFirst?: boolean;
13
+ isLast?: boolean;
14
+ showChevron?: boolean;
15
+ disabled?: boolean;
16
+ customContent?: React.ReactNode;
17
+ }
18
+
19
+ const GroupedItem: React.FC<GroupedItemProps> = ({
20
+ icon,
21
+ iconColor = '#007AFF',
22
+ title,
23
+ subtitle,
24
+ theme,
25
+ onPress,
26
+ isFirst = false,
27
+ isLast = false,
28
+ showChevron = true,
29
+ disabled = false,
30
+ customContent,
31
+ }) => {
32
+ const isDarkTheme = theme === 'dark';
33
+ const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
34
+ const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#FFFFFF';
35
+
36
+ const itemStyles = [
37
+ styles.groupedItem,
38
+ isFirst && styles.firstGroupedItem,
39
+ isLast && styles.lastGroupedItem,
40
+ { backgroundColor: secondaryBackgroundColor },
41
+ ];
42
+
43
+ const content = (
44
+ <View style={styles.groupedItemContent}>
45
+ {icon && (
46
+ <Ionicons name={icon as any} size={20} color={iconColor} style={styles.actionIcon} />
47
+ )}
48
+ <View style={styles.actionTextContainer}>
49
+ <Text style={[styles.actionButtonText, { color: textColor }]}>{title}</Text>
50
+ {subtitle && (
51
+ <Text style={[styles.actionButtonSubtext, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
52
+ {subtitle}
53
+ </Text>
54
+ )}
55
+ </View>
56
+ {customContent}
57
+ {showChevron && (
58
+ <Ionicons name="chevron-forward" size={16} color={isDarkTheme ? '#666666' : '#999999'} />
59
+ )}
60
+ </View>
61
+ );
62
+
63
+ if (onPress && !disabled) {
64
+ return (
65
+ <TouchableOpacity style={itemStyles} onPress={onPress}>
66
+ {content}
67
+ </TouchableOpacity>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <View style={itemStyles}>
73
+ {content}
74
+ </View>
75
+ );
76
+ };
77
+
78
+ const styles = StyleSheet.create({
79
+ groupedItem: {
80
+ flexDirection: 'row',
81
+ alignItems: 'center',
82
+ justifyContent: 'space-between',
83
+ marginBottom: 2,
84
+ overflow: 'hidden',
85
+ },
86
+ firstGroupedItem: {
87
+ borderTopLeftRadius: 24,
88
+ borderTopRightRadius: 24,
89
+ },
90
+ lastGroupedItem: {
91
+ borderBottomLeftRadius: 24,
92
+ borderBottomRightRadius: 24,
93
+ marginBottom: 8,
94
+ },
95
+ groupedItemContent: {
96
+ flexDirection: 'row',
97
+ alignItems: 'center',
98
+ padding: 16,
99
+ width: '100%',
100
+ },
101
+ actionIcon: {
102
+ marginRight: 12,
103
+ },
104
+ actionTextContainer: {
105
+ flex: 1,
106
+ },
107
+ actionButtonText: {
108
+ fontSize: 16,
109
+ fontWeight: '500',
110
+ marginBottom: 2,
111
+ },
112
+ actionButtonSubtext: {
113
+ fontSize: 13,
114
+ lineHeight: 18,
115
+ },
116
+ });
117
+
118
+ export default GroupedItem;
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import GroupedItem from './GroupedItem';
4
+
5
+ interface GroupedSectionItem {
6
+ id: string;
7
+ icon?: string;
8
+ iconColor?: string;
9
+ title: string;
10
+ subtitle?: string;
11
+ onPress?: () => void;
12
+ showChevron?: boolean;
13
+ disabled?: boolean;
14
+ customContent?: React.ReactNode;
15
+ }
16
+
17
+ interface GroupedSectionProps {
18
+ items: GroupedSectionItem[];
19
+ theme: 'light' | 'dark';
20
+ }
21
+
22
+ const GroupedSection: React.FC<GroupedSectionProps> = ({ items, theme }) => {
23
+ return (
24
+ <View>
25
+ {items.map((item, index) => (
26
+ <GroupedItem
27
+ key={item.id}
28
+ icon={item.icon}
29
+ iconColor={item.iconColor}
30
+ title={item.title}
31
+ subtitle={item.subtitle}
32
+ theme={theme}
33
+ onPress={item.onPress}
34
+ isFirst={index === 0}
35
+ isLast={index === items.length - 1}
36
+ showChevron={item.showChevron}
37
+ disabled={item.disabled}
38
+ customContent={item.customContent}
39
+ />
40
+ ))}
41
+ </View>
42
+ );
43
+ };
44
+
45
+ export default GroupedSection;
@@ -1,27 +1,20 @@
1
- // filepath: /home/nate/OxyServicesandApi/OxyHQServices/src/ui/components/OxyProvider.tsx
2
1
  import React, { useCallback, useRef, useState, useEffect } from 'react';
3
2
  import { View, Text, StyleSheet, Dimensions, Platform, Animated, StatusBar, Keyboard, KeyboardEvent } from 'react-native';
4
3
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
5
4
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
6
- import { OxyServices } from '../../core';
7
5
  import { OxyProviderProps } from '../navigation/types';
8
- import SignInScreen from '../screens/SignInScreen';
9
- import SignUpScreen from '../screens/SignUpScreen';
10
- import AccountCenterScreen from '../screens/AccountCenterScreen';
11
6
  import { OxyContextProvider, useOxy } from '../context/OxyContext';
12
7
  import OxyRouter from '../navigation/OxyRouter';
13
8
  import { FontLoader, setupFonts } from './FontLoader';
14
9
  import { Toaster } from '../../lib/sonner';
15
10
 
16
11
  // Import bottom sheet components directly - no longer a peer dependency
17
- import { BottomSheetModal, BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetModalProvider, BottomSheetView } from './bottomSheet';
18
- import { BottomSheetScrollView } from '@gorhom/bottom-sheet';
12
+ import { BottomSheetModal, BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetModalProvider, BottomSheetView, BottomSheetScrollView } from './bottomSheet';
13
+ import type { BottomSheetModalRef } from './bottomSheet';
19
14
 
20
15
  // Initialize fonts automatically
21
16
  setupFonts();
22
17
 
23
- const { height, width } = Dimensions.get('window');
24
-
25
18
  /**
26
19
  * Enhanced OxyProvider component
27
20
  *
@@ -36,11 +29,12 @@ const OxyProvider: React.FC<OxyProviderProps> = (props) => {
36
29
  contextOnly = false,
37
30
  onAuthStateChange,
38
31
  storageKeyPrefix,
32
+ showInternalToaster = true,
39
33
  ...bottomSheetProps
40
34
  } = props;
41
35
 
42
36
  // Create internal bottom sheet ref
43
- const internalBottomSheetRef = useRef<BottomSheetModal>(null);
37
+ const internalBottomSheetRef = useRef<BottomSheetModalRef>(null);
44
38
 
45
39
  // If contextOnly is true, we just provide the context without the bottom sheet UI
46
40
  if (contextOnly) {
@@ -72,10 +66,12 @@ const OxyProvider: React.FC<OxyProviderProps> = (props) => {
72
66
  {children}
73
67
  </SafeAreaProvider>
74
68
  </BottomSheetModalProvider>
75
- {/* Move Toaster outside BottomSheetModalProvider to ensure it appears above the modal backdrop */}
76
- <View style={styles.toasterContainer}>
77
- <Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
78
- </View>
69
+ {/* Global Toaster for app-wide notifications outside of Modal contexts - only show if internal toaster is disabled */}
70
+ {!showInternalToaster && (
71
+ <View style={styles.toasterContainer}>
72
+ <Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
73
+ </View>
74
+ )}
79
75
  </GestureHandlerRootView>
80
76
  </FontLoader>
81
77
  </OxyContextProvider>
@@ -97,15 +93,17 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
97
93
  customStyles = {},
98
94
  bottomSheetRef,
99
95
  autoPresent = false,
96
+ showInternalToaster = true,
100
97
  }) => {
101
98
  // Use the internal ref (which is passed as a prop from OxyProvider)
102
- const modalRef = useRef<BottomSheetModal>(null);
99
+ const modalRef = useRef<BottomSheetModalRef>(null);
103
100
 
104
101
  // Create a ref to store the navigation function from OxyRouter
105
102
  const navigationRef = useRef<((screen: string, props?: Record<string, any>) => void) | null>(null);
106
103
 
107
104
  // Track content height for dynamic sizing
108
105
  const [contentHeight, setContentHeight] = useState<number>(0);
106
+ const [containerWidth, setContainerWidth] = useState<number>(800); // Track actual container width
109
107
  const screenHeight = Dimensions.get('window').height;
110
108
 
111
109
  // Set up effect to sync the internal ref with our modal ref
@@ -148,14 +146,14 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
148
146
  } else {
149
147
  // For React Native - use the global variable approach
150
148
  console.log('Using React Native global navigation');
151
- (global as any).oxyNavigateEvent = { screen: screenName, props };
149
+ (globalThis as any).oxyNavigateEvent = { screen: screenName, props };
152
150
  }
153
151
  };
154
152
  }
155
153
  }, [bottomSheetRef, modalRef]);
156
154
 
157
155
  // Use percentage-based snap points for better cross-platform compatibility
158
- const [snapPoints, setSnapPoints] = useState<(string | number)[]>(['60%', '85%']);
156
+ const [snapPoints, setSnapPoints] = useState<(string | number)[]>(['60%', '90%']);
159
157
 
160
158
  // Animation values - we'll use these for content animations
161
159
  // Start with opacity 1 on Android to avoid visibility issues
@@ -176,15 +174,21 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
176
174
  const keyboardWillShowListener = Keyboard.addListener(
177
175
  Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
178
176
  (event: KeyboardEvent) => {
179
- setKeyboardVisible(true);
180
- // Get keyboard height from event
181
- const keyboardHeightValue = event.endCoordinates.height;
182
- setKeyboardHeight(keyboardHeightValue);
183
-
184
- // Ensure the bottom sheet remains visible when keyboard opens
185
- // by adjusting to the highest snap point
186
- if (modalRef.current) {
187
- modalRef.current.snapToIndex(1);
177
+ // Debounce rapid keyboard events
178
+ if (!keyboardVisible) {
179
+ setKeyboardVisible(true);
180
+ // Get keyboard height from event
181
+ const keyboardHeightValue = event.endCoordinates.height;
182
+ setKeyboardHeight(keyboardHeightValue);
183
+
184
+ // Ensure the bottom sheet remains visible when keyboard opens
185
+ // by adjusting to the highest snap point
186
+ if (modalRef.current) {
187
+ // Use requestAnimationFrame to avoid conflicts
188
+ requestAnimationFrame(() => {
189
+ modalRef.current?.snapToIndex(1);
190
+ });
191
+ }
188
192
  }
189
193
  }
190
194
  );
@@ -192,8 +196,10 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
192
196
  const keyboardWillHideListener = Keyboard.addListener(
193
197
  Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide',
194
198
  () => {
195
- setKeyboardVisible(false);
196
- setKeyboardHeight(0);
199
+ if (keyboardVisible) {
200
+ setKeyboardVisible(false);
201
+ setKeyboardHeight(0);
202
+ }
197
203
  }
198
204
  );
199
205
 
@@ -202,7 +208,7 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
202
208
  keyboardWillShowListener.remove();
203
209
  keyboardWillHideListener.remove();
204
210
  };
205
- }, []);
211
+ }, [keyboardVisible]);
206
212
 
207
213
  // Present the modal when component mounts, but only if autoPresent is true
208
214
  useEffect(() => {
@@ -229,27 +235,6 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
229
235
  }),
230
236
  ]).start();
231
237
  };
232
-
233
- // Override present to also handle animations
234
- // @ts-ignore - Dynamic method assignment
235
- bottomSheetRef.current.present = () => {
236
- modalRef.current?.present();
237
-
238
- // Start content animations after presenting
239
- Animated.parallel([
240
- Animated.timing(fadeAnim, {
241
- toValue: 1,
242
- duration: 300,
243
- useNativeDriver: Platform.OS === 'ios', // Only use native driver on iOS
244
- }),
245
- Animated.spring(slideAnim, {
246
- toValue: 0,
247
- friction: 8,
248
- tension: 40,
249
- useNativeDriver: Platform.OS === 'ios', // Only use native driver on iOS
250
- }),
251
- ]).start();
252
- };
253
238
  }
254
239
 
255
240
  // Auto-present if the autoPresent prop is true
@@ -346,20 +331,26 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
346
331
  // If we have content height, use it as a constraint
347
332
  if (contentHeight > 0) {
348
333
  // Calculate content height as percentage of screen (plus some padding)
349
- const contentHeightPercent = Math.min(Math.ceil((contentHeight + 40) / screenHeight * 100), 90) + '%';
334
+ // Clamp to ensure we don't exceed 90% to leave space for UI elements
335
+ const contentHeightPercent = Math.min(Math.ceil((contentHeight) / screenHeight * 100), 90);
336
+ const contentHeightPercentStr = `${contentHeightPercent}%`;
350
337
  // Use content height for first snap point if it's taller than the default
351
- const firstPoint = contentHeight / screenHeight > 0.6 ? contentHeightPercent : points[0];
352
- setSnapPoints([firstPoint, points[1]]);
338
+ const firstPoint = contentHeight / screenHeight > 0.6 ? contentHeightPercentStr : points[0];
339
+ setSnapPoints([firstPoint, points[1] || '90%']);
353
340
  } else {
354
341
  setSnapPoints(points);
355
342
  }
356
343
  }
357
344
  }, [keyboardVisible, contentHeight, screenHeight]);
358
345
 
359
- // Handle content layout changes to measure height
346
+ // Handle content layout changes to measure height and width
360
347
  const handleContentLayout = useCallback((event: any) => {
361
- const layoutHeight = event.nativeEvent.layout.height;
348
+ const { height: layoutHeight, width: layoutWidth } = event.nativeEvent.layout;
362
349
  setContentHeight(layoutHeight);
350
+ setContainerWidth(layoutWidth);
351
+
352
+ // Debug: log container dimensions
353
+ console.log('[OxyProvider] Container dimensions (full):', { width: layoutWidth, height: layoutHeight });
363
354
 
364
355
  // Update snap points based on new content height
365
356
  if (keyboardVisible) {
@@ -368,8 +359,10 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
368
359
  setSnapPoints([highestPoint, highestPoint]);
369
360
  } else {
370
361
  if (layoutHeight > 0) {
371
- const contentHeightPercent = Math.min(Math.ceil((layoutHeight + 40) / screenHeight * 100), 90) + '%';
372
- const firstPoint = layoutHeight / screenHeight > 0.6 ? contentHeightPercent : snapPoints[0];
362
+ // Add padding and clamp to reasonable limits
363
+ const contentHeightPercent = Math.min(Math.ceil((layoutHeight + 40) / screenHeight * 100), 90);
364
+ const contentHeightPercentStr = `${contentHeightPercent}%`;
365
+ const firstPoint = layoutHeight / screenHeight > 0.6 ? contentHeightPercentStr : snapPoints[0];
373
366
  setSnapPoints([firstPoint, snapPoints[1]]);
374
367
  }
375
368
  }
@@ -395,27 +388,6 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
395
388
 
396
389
  // Handle sheet index changes
397
390
  const handleSheetChanges = useCallback((index: number) => {
398
- if (index === -1 && onClose) {
399
- onClose();
400
- } else if (index === 1) {
401
- // Pulse animation when expanded to full height
402
- Animated.sequence([
403
- Animated.timing(handleScaleAnim, {
404
- toValue: 1.2,
405
- duration: 200,
406
- useNativeDriver: Platform.OS === 'ios', // Only use native driver on iOS
407
- }),
408
- Animated.timing(handleScaleAnim, {
409
- toValue: 1,
410
- duration: 200,
411
- useNativeDriver: Platform.OS === 'ios', // Only use native driver on iOS
412
- }),
413
- ]).start();
414
- } else if (index === 0 && keyboardVisible) {
415
- // If keyboard is visible and user tries to go to a smaller snap point,
416
- // force the sheet to stay at the highest point for better visibility
417
- modalRef.current?.snapToIndex(1);
418
- }
419
391
  }, [onClose, handleScaleAnim, keyboardVisible]);
420
392
 
421
393
  return (
@@ -425,23 +397,6 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
425
397
  snapPoints={snapPoints}
426
398
  enablePanDownToClose
427
399
  backdropComponent={renderBackdrop}
428
- // Remove enableDynamicSizing as we're implementing our own solution
429
- handleComponent={() => (
430
- <Animated.View
431
- style={{
432
- alignItems: 'center',
433
- paddingVertical: 8,
434
- ...(Platform.OS === 'ios' ? {
435
- transform: [{ scale: handleScaleAnim }]
436
- } : {})
437
- }}
438
- >
439
- <View style={[
440
- styles.indicator,
441
- { backgroundColor: customStyles.handleColor || (theme === 'light' ? '#CCCCCC' : '#444444') }
442
- ]} />
443
- </Animated.View>
444
- )}
445
400
  backgroundStyle={[
446
401
  getBackgroundStyle(),
447
402
  {
@@ -455,6 +410,7 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
455
410
  height: 4,
456
411
  }}
457
412
  onChange={handleSheetChanges}
413
+ style={styles.bottomSheetContainer}
458
414
  // Adding additional props to improve layout behavior
459
415
  keyboardBehavior="interactive"
460
416
  keyboardBlurBehavior="restore"
@@ -477,38 +433,57 @@ const OxyBottomSheet: React.FC<OxyProviderProps> = ({
477
433
  ]}
478
434
  onLayout={handleContentLayout}
479
435
  >
480
- <Animated.View
481
- style={[
482
- styles.animatedContent,
483
- // Apply animations - conditionally for Android
484
- Platform.OS === 'android' ?
485
- {
486
- opacity: 1, // No fade animation on Android
487
- } : {
488
- opacity: fadeAnim,
489
- transform: [{ translateY: slideAnim }]
490
- }
491
- ]}
492
- >
493
- <OxyRouter
494
- oxyServices={oxyServices}
495
- initialScreen={initialScreen}
496
- onClose={handleClose}
497
- onAuthenticated={handleAuthenticated}
498
- theme={theme}
499
- adjustSnapPoints={adjustSnapPoints}
500
- navigationRef={navigationRef}
501
- />
502
- </Animated.View>
436
+ <View style={styles.centeredContentWrapper}>
437
+ <Animated.View
438
+ style={[
439
+ styles.animatedContent,
440
+ // Apply animations - conditionally for Android
441
+ Platform.OS === 'android' ?
442
+ {
443
+ opacity: 1, // No fade animation on Android
444
+ } : {
445
+ opacity: fadeAnim,
446
+ transform: [{ translateY: slideAnim }]
447
+ }
448
+ ]}
449
+ >
450
+ <OxyRouter
451
+ oxyServices={oxyServices}
452
+ initialScreen={initialScreen}
453
+ onClose={handleClose}
454
+ onAuthenticated={handleAuthenticated}
455
+ theme={theme}
456
+ adjustSnapPoints={adjustSnapPoints}
457
+ navigationRef={navigationRef}
458
+ containerWidth={containerWidth}
459
+ />
460
+ </Animated.View>
461
+ </View>
503
462
  </BottomSheetScrollView>
463
+ {/* Internal toaster - only show if showInternalToaster is true */}
464
+ {showInternalToaster && (
465
+ <View style={styles.toasterContainer}>
466
+ <Toaster position="top-center" swipeToDismissDirection="left" />
467
+ </View>
468
+ )}
504
469
  </BottomSheetModal>
505
470
  );
506
471
  };
507
472
 
508
473
  const styles = StyleSheet.create({
474
+ bottomSheetContainer: {
475
+ maxWidth: 800,
476
+ width: '100%',
477
+ marginHorizontal: 'auto',
478
+ },
509
479
  contentContainer: {
510
480
  width: '100%',
511
- backgroundColor: 'transparent', // Make this transparent to let the bottom sheet background show through
481
+ borderTopLeftRadius: 35,
482
+ borderTopRightRadius: 35,
483
+ },
484
+ centeredContentWrapper: {
485
+ width: '100%',
486
+ marginHorizontal: 'auto',
512
487
  },
513
488
  animatedContent: {
514
489
  width: '100%',
@@ -516,7 +491,6 @@ const styles = StyleSheet.create({
516
491
  indicator: {
517
492
  width: 40,
518
493
  height: 4,
519
- alignSelf: 'center',
520
494
  marginTop: 8,
521
495
  marginBottom: 8,
522
496
  borderRadius: 35,
@@ -531,6 +505,7 @@ const styles = StyleSheet.create({
531
505
  position: 'relative',
532
506
  backgroundColor: 'transparent',
533
507
  ...Platform.select({
508
+
534
509
  android: {
535
510
  height: '100%',
536
511
  width: '100%',
@@ -0,0 +1,129 @@
1
+ import React from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+ import Avatar from './Avatar';
5
+ import { fontFamilies } from '../styles/fonts';
6
+
7
+ interface ProfileCardProps {
8
+ user: {
9
+ username: string;
10
+ email?: string;
11
+ name?: {
12
+ full?: string;
13
+ };
14
+ avatar?: {
15
+ url?: string;
16
+ };
17
+ };
18
+ theme: 'light' | 'dark';
19
+ onEditPress?: () => void;
20
+ onClosePress?: () => void;
21
+ showCloseButton?: boolean;
22
+ }
23
+
24
+ const ProfileCard: React.FC<ProfileCardProps> = ({
25
+ user,
26
+ theme,
27
+ onEditPress,
28
+ onClosePress,
29
+ showCloseButton = false,
30
+ }) => {
31
+ const isDarkTheme = theme === 'dark';
32
+ const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
33
+ const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#FFFFFF';
34
+ const primaryColor = '#0066CC';
35
+
36
+ return (
37
+ <View style={styles.headerSection}>
38
+ <View style={[
39
+ styles.profileCard,
40
+ styles.firstGroupedItem,
41
+ styles.lastGroupedItem,
42
+ { backgroundColor: secondaryBackgroundColor }
43
+ ]}>
44
+ <View style={styles.userProfile}>
45
+ <Avatar
46
+ uri={user?.avatar?.url}
47
+ name={user?.name?.full || user?.username}
48
+ size={60}
49
+ theme={theme}
50
+ />
51
+ <View style={styles.userInfo}>
52
+ <Text style={[styles.userName, { color: textColor }]}>{user.username}</Text>
53
+ {user.email && (
54
+ <Text style={[styles.userEmail, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
55
+ {user.email}
56
+ </Text>
57
+ )}
58
+ {onEditPress && (
59
+ <TouchableOpacity
60
+ style={styles.editProfileButton}
61
+ onPress={onEditPress}
62
+ >
63
+ <Text style={[styles.editProfileText, { color: primaryColor }]}>Edit Profile</Text>
64
+ </TouchableOpacity>
65
+ )}
66
+ </View>
67
+ </View>
68
+ {showCloseButton && onClosePress && (
69
+ <TouchableOpacity style={styles.closeButton} onPress={onClosePress}>
70
+ <Ionicons name="close" size={24} color={textColor} />
71
+ </TouchableOpacity>
72
+ )}
73
+ </View>
74
+ </View>
75
+ );
76
+ };
77
+
78
+ const styles = StyleSheet.create({
79
+ headerSection: {
80
+ padding: 16,
81
+ paddingTop: 20,
82
+ },
83
+ profileCard: {
84
+ padding: 20,
85
+ flexDirection: 'row',
86
+ alignItems: 'center',
87
+ justifyContent: 'space-between',
88
+ },
89
+ firstGroupedItem: {
90
+ borderTopLeftRadius: 24,
91
+ borderTopRightRadius: 24,
92
+ },
93
+ lastGroupedItem: {
94
+ borderBottomLeftRadius: 24,
95
+ borderBottomRightRadius: 24,
96
+ marginBottom: 8,
97
+ },
98
+ userProfile: {
99
+ flexDirection: 'row',
100
+ alignItems: 'center',
101
+ flex: 1,
102
+ },
103
+ userInfo: {
104
+ marginLeft: 16,
105
+ flex: 1,
106
+ },
107
+ userName: {
108
+ fontSize: 22,
109
+ fontWeight: 'bold',
110
+ fontFamily: fontFamilies.phuduBold,
111
+ marginBottom: 4,
112
+ },
113
+ userEmail: {
114
+ fontSize: 14,
115
+ marginBottom: 8,
116
+ },
117
+ editProfileButton: {
118
+ alignSelf: 'flex-start',
119
+ },
120
+ editProfileText: {
121
+ fontSize: 14,
122
+ fontWeight: '600',
123
+ },
124
+ closeButton: {
125
+ padding: 8,
126
+ },
127
+ });
128
+
129
+ export default ProfileCard;