@oxyhq/services 5.17.18 → 5.18.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 (238) hide show
  1. package/lib/commonjs/core/mixins/OxyServices.features.js +372 -0
  2. package/lib/commonjs/core/mixins/OxyServices.features.js.map +1 -0
  3. package/lib/commonjs/core/mixins/index.js +3 -2
  4. package/lib/commonjs/core/mixins/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/GroupedItem.js +11 -1
  6. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  7. package/lib/commonjs/ui/components/SettingRow.js +17 -4
  8. package/lib/commonjs/ui/components/SettingRow.js.map +1 -1
  9. package/lib/commonjs/ui/components/feedback/FormInput.js +72 -0
  10. package/lib/commonjs/ui/components/feedback/FormInput.js.map +1 -0
  11. package/lib/commonjs/ui/components/feedback/ProgressIndicator.js +33 -0
  12. package/lib/commonjs/ui/components/feedback/ProgressIndicator.js.map +1 -0
  13. package/lib/commonjs/ui/components/feedback/constants.js +59 -0
  14. package/lib/commonjs/ui/components/feedback/constants.js.map +1 -0
  15. package/lib/commonjs/ui/components/feedback/feedbackStyles.js +262 -0
  16. package/lib/commonjs/ui/components/feedback/feedbackStyles.js.map +1 -0
  17. package/lib/commonjs/ui/components/feedback/index.js +54 -0
  18. package/lib/commonjs/ui/components/feedback/index.js.map +1 -0
  19. package/lib/commonjs/ui/components/feedback/types.js +6 -0
  20. package/lib/commonjs/ui/components/feedback/types.js.map +1 -0
  21. package/lib/commonjs/ui/components/feedback/useFeedbackForm.js +52 -0
  22. package/lib/commonjs/ui/components/feedback/useFeedbackForm.js.map +1 -0
  23. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js +282 -0
  24. package/lib/commonjs/ui/components/modals/DeleteAccountModal.js.map +1 -0
  25. package/lib/commonjs/ui/components/modals/index.js +14 -0
  26. package/lib/commonjs/ui/components/modals/index.js.map +1 -0
  27. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +309 -0
  28. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -0
  29. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js +79 -0
  30. package/lib/commonjs/ui/components/payment/PaymentMethodStep.js.map +1 -0
  31. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js +108 -0
  32. package/lib/commonjs/ui/components/payment/PaymentReviewStep.js.map +1 -0
  33. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js +79 -0
  34. package/lib/commonjs/ui/components/payment/PaymentSuccessStep.js.map +1 -0
  35. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +176 -0
  36. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -0
  37. package/lib/commonjs/ui/components/payment/constants.js +53 -0
  38. package/lib/commonjs/ui/components/payment/constants.js.map +1 -0
  39. package/lib/commonjs/ui/components/payment/index.js +80 -0
  40. package/lib/commonjs/ui/components/payment/index.js.map +1 -0
  41. package/lib/commonjs/ui/components/payment/paymentStyles.js +409 -0
  42. package/lib/commonjs/ui/components/payment/paymentStyles.js.map +1 -0
  43. package/lib/commonjs/ui/components/payment/types.js +6 -0
  44. package/lib/commonjs/ui/components/payment/types.js.map +1 -0
  45. package/lib/commonjs/ui/hooks/index.js +26 -0
  46. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  47. package/lib/commonjs/ui/hooks/useAsyncAction.js +95 -0
  48. package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -0
  49. package/lib/commonjs/ui/hooks/useSettingToggle.js +126 -0
  50. package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -0
  51. package/lib/commonjs/ui/navigation/routes.js +1 -0
  52. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  53. package/lib/commonjs/ui/screens/AccountCenterScreen.js +4 -2
  54. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +33 -30
  56. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/FAQScreen.js +315 -0
  58. package/lib/commonjs/ui/screens/FAQScreen.js.map +1 -0
  59. package/lib/commonjs/ui/screens/FeedbackScreen.js +73 -590
  60. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  61. package/lib/commonjs/ui/screens/HelpSupportScreen.js +8 -7
  62. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  63. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +67 -1395
  64. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  65. package/lib/commonjs/ui/screens/ProfileScreen.js +13 -5
  66. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  67. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +16 -10
  68. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
  69. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +23 -11
  70. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  71. package/lib/module/core/mixins/OxyServices.features.js +369 -0
  72. package/lib/module/core/mixins/OxyServices.features.js.map +1 -0
  73. package/lib/module/core/mixins/index.js +3 -2
  74. package/lib/module/core/mixins/index.js.map +1 -1
  75. package/lib/module/ui/components/GroupedItem.js +11 -1
  76. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  77. package/lib/module/ui/components/SettingRow.js +17 -4
  78. package/lib/module/ui/components/SettingRow.js.map +1 -1
  79. package/lib/module/ui/components/feedback/FormInput.js +67 -0
  80. package/lib/module/ui/components/feedback/FormInput.js.map +1 -0
  81. package/lib/module/ui/components/feedback/ProgressIndicator.js +28 -0
  82. package/lib/module/ui/components/feedback/ProgressIndicator.js.map +1 -0
  83. package/lib/module/ui/components/feedback/constants.js +55 -0
  84. package/lib/module/ui/components/feedback/constants.js.map +1 -0
  85. package/lib/module/ui/components/feedback/feedbackStyles.js +257 -0
  86. package/lib/module/ui/components/feedback/feedbackStyles.js.map +1 -0
  87. package/lib/module/ui/components/feedback/index.js +8 -0
  88. package/lib/module/ui/components/feedback/index.js.map +1 -0
  89. package/lib/module/ui/components/feedback/types.js +4 -0
  90. package/lib/module/ui/components/feedback/types.js.map +1 -0
  91. package/lib/module/ui/components/feedback/useFeedbackForm.js +47 -0
  92. package/lib/module/ui/components/feedback/useFeedbackForm.js.map +1 -0
  93. package/lib/module/ui/components/modals/DeleteAccountModal.js +276 -0
  94. package/lib/module/ui/components/modals/DeleteAccountModal.js.map +1 -0
  95. package/lib/module/ui/components/modals/index.js +4 -0
  96. package/lib/module/ui/components/modals/index.js.map +1 -0
  97. package/lib/module/ui/components/payment/PaymentDetailsStep.js +303 -0
  98. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -0
  99. package/lib/module/ui/components/payment/PaymentMethodStep.js +73 -0
  100. package/lib/module/ui/components/payment/PaymentMethodStep.js.map +1 -0
  101. package/lib/module/ui/components/payment/PaymentReviewStep.js +102 -0
  102. package/lib/module/ui/components/payment/PaymentReviewStep.js.map +1 -0
  103. package/lib/module/ui/components/payment/PaymentSuccessStep.js +73 -0
  104. package/lib/module/ui/components/payment/PaymentSuccessStep.js.map +1 -0
  105. package/lib/module/ui/components/payment/PaymentSummaryStep.js +170 -0
  106. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -0
  107. package/lib/module/ui/components/payment/constants.js +47 -0
  108. package/lib/module/ui/components/payment/constants.js.map +1 -0
  109. package/lib/module/ui/components/payment/index.js +10 -0
  110. package/lib/module/ui/components/payment/index.js.map +1 -0
  111. package/lib/module/ui/components/payment/paymentStyles.js +404 -0
  112. package/lib/module/ui/components/payment/paymentStyles.js.map +1 -0
  113. package/lib/module/ui/components/payment/types.js +4 -0
  114. package/lib/module/ui/components/payment/types.js.map +1 -0
  115. package/lib/module/ui/hooks/index.js +2 -0
  116. package/lib/module/ui/hooks/index.js.map +1 -1
  117. package/lib/module/ui/hooks/useAsyncAction.js +89 -0
  118. package/lib/module/ui/hooks/useAsyncAction.js.map +1 -0
  119. package/lib/module/ui/hooks/useSettingToggle.js +120 -0
  120. package/lib/module/ui/hooks/useSettingToggle.js.map +1 -0
  121. package/lib/module/ui/navigation/routes.js +1 -0
  122. package/lib/module/ui/navigation/routes.js.map +1 -1
  123. package/lib/module/ui/screens/AccountCenterScreen.js +4 -2
  124. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  125. package/lib/module/ui/screens/AccountOverviewScreen.js +33 -30
  126. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  127. package/lib/module/ui/screens/FAQScreen.js +310 -0
  128. package/lib/module/ui/screens/FAQScreen.js.map +1 -0
  129. package/lib/module/ui/screens/FeedbackScreen.js +64 -581
  130. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  131. package/lib/module/ui/screens/HelpSupportScreen.js +8 -7
  132. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  133. package/lib/module/ui/screens/PaymentGatewayScreen.js +67 -1397
  134. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  135. package/lib/module/ui/screens/ProfileScreen.js +13 -5
  136. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  137. package/lib/module/ui/screens/SavesCollectionsScreen.js +16 -10
  138. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
  139. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +23 -11
  140. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  141. package/lib/typescript/core/mixins/OxyServices.features.d.ts +229 -0
  142. package/lib/typescript/core/mixins/OxyServices.features.d.ts.map +1 -0
  143. package/lib/typescript/core/mixins/index.d.ts +71 -1
  144. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  145. package/lib/typescript/ui/components/GroupedItem.d.ts +5 -1
  146. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  147. package/lib/typescript/ui/components/SettingRow.d.ts +6 -0
  148. package/lib/typescript/ui/components/SettingRow.d.ts.map +1 -1
  149. package/lib/typescript/ui/components/feedback/FormInput.d.ts +20 -0
  150. package/lib/typescript/ui/components/feedback/FormInput.d.ts.map +1 -0
  151. package/lib/typescript/ui/components/feedback/ProgressIndicator.d.ts +11 -0
  152. package/lib/typescript/ui/components/feedback/ProgressIndicator.d.ts.map +1 -0
  153. package/lib/typescript/ui/components/feedback/constants.d.ts +5 -0
  154. package/lib/typescript/ui/components/feedback/constants.d.ts.map +1 -0
  155. package/lib/typescript/ui/components/feedback/feedbackStyles.d.ts +280 -0
  156. package/lib/typescript/ui/components/feedback/feedbackStyles.d.ts.map +1 -0
  157. package/lib/typescript/ui/components/feedback/index.d.ts +7 -0
  158. package/lib/typescript/ui/components/feedback/index.d.ts.map +1 -0
  159. package/lib/typescript/ui/components/feedback/types.d.ts +46 -0
  160. package/lib/typescript/ui/components/feedback/types.d.ts.map +1 -0
  161. package/lib/typescript/ui/components/feedback/useFeedbackForm.d.ts +9 -0
  162. package/lib/typescript/ui/components/feedback/useFeedbackForm.d.ts.map +1 -0
  163. package/lib/typescript/ui/components/modals/DeleteAccountModal.d.ts +19 -0
  164. package/lib/typescript/ui/components/modals/DeleteAccountModal.d.ts.map +1 -0
  165. package/lib/typescript/ui/components/modals/index.d.ts +2 -0
  166. package/lib/typescript/ui/components/modals/index.d.ts.map +1 -0
  167. package/lib/typescript/ui/components/payment/PaymentDetailsStep.d.ts +21 -0
  168. package/lib/typescript/ui/components/payment/PaymentDetailsStep.d.ts.map +1 -0
  169. package/lib/typescript/ui/components/payment/PaymentMethodStep.d.ts +14 -0
  170. package/lib/typescript/ui/components/payment/PaymentMethodStep.d.ts.map +1 -0
  171. package/lib/typescript/ui/components/payment/PaymentReviewStep.d.ts +16 -0
  172. package/lib/typescript/ui/components/payment/PaymentReviewStep.d.ts.map +1 -0
  173. package/lib/typescript/ui/components/payment/PaymentSuccessStep.d.ts +10 -0
  174. package/lib/typescript/ui/components/payment/PaymentSuccessStep.d.ts.map +1 -0
  175. package/lib/typescript/ui/components/payment/PaymentSummaryStep.d.ts +15 -0
  176. package/lib/typescript/ui/components/payment/PaymentSummaryStep.d.ts.map +1 -0
  177. package/lib/typescript/ui/components/payment/constants.d.ts +7 -0
  178. package/lib/typescript/ui/components/payment/constants.d.ts.map +1 -0
  179. package/lib/typescript/ui/components/payment/index.d.ts +9 -0
  180. package/lib/typescript/ui/components/payment/index.d.ts.map +1 -0
  181. package/lib/typescript/ui/components/payment/paymentStyles.d.ts +396 -0
  182. package/lib/typescript/ui/components/payment/paymentStyles.d.ts.map +1 -0
  183. package/lib/typescript/ui/components/payment/types.d.ts +40 -0
  184. package/lib/typescript/ui/components/payment/types.d.ts.map +1 -0
  185. package/lib/typescript/ui/hooks/index.d.ts +2 -0
  186. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  187. package/lib/typescript/ui/hooks/useAsyncAction.d.ts +51 -0
  188. package/lib/typescript/ui/hooks/useAsyncAction.d.ts.map +1 -0
  189. package/lib/typescript/ui/hooks/useSettingToggle.d.ts +55 -0
  190. package/lib/typescript/ui/hooks/useSettingToggle.d.ts.map +1 -0
  191. package/lib/typescript/ui/navigation/routes.d.ts +1 -1
  192. package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
  193. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  194. package/lib/typescript/ui/screens/FAQScreen.d.ts +5 -0
  195. package/lib/typescript/ui/screens/FAQScreen.d.ts.map +1 -0
  196. package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
  197. package/lib/typescript/ui/screens/HelpSupportScreen.d.ts.map +1 -1
  198. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts +3 -15
  199. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  200. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  201. package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts.map +1 -1
  202. package/lib/typescript/ui/screens/karma/KarmaCenterScreen.d.ts.map +1 -1
  203. package/package.json +1 -1
  204. package/src/core/mixins/OxyServices.features.ts +428 -0
  205. package/src/core/mixins/index.ts +20 -17
  206. package/src/ui/components/GroupedItem.tsx +19 -1
  207. package/src/ui/components/SettingRow.tsx +26 -4
  208. package/src/ui/components/feedback/FormInput.tsx +84 -0
  209. package/src/ui/components/feedback/ProgressIndicator.tsx +35 -0
  210. package/src/ui/components/feedback/constants.ts +22 -0
  211. package/src/ui/components/feedback/feedbackStyles.ts +247 -0
  212. package/src/ui/components/feedback/index.ts +6 -0
  213. package/src/ui/components/feedback/types.ts +52 -0
  214. package/src/ui/components/feedback/useFeedbackForm.ts +44 -0
  215. package/src/ui/components/modals/DeleteAccountModal.tsx +294 -0
  216. package/src/ui/components/modals/index.ts +1 -0
  217. package/src/ui/components/payment/PaymentDetailsStep.tsx +222 -0
  218. package/src/ui/components/payment/PaymentMethodStep.tsx +89 -0
  219. package/src/ui/components/payment/PaymentReviewStep.tsx +126 -0
  220. package/src/ui/components/payment/PaymentSuccessStep.tsx +71 -0
  221. package/src/ui/components/payment/PaymentSummaryStep.tsx +159 -0
  222. package/src/ui/components/payment/constants.ts +39 -0
  223. package/src/ui/components/payment/index.ts +9 -0
  224. package/src/ui/components/payment/paymentStyles.ts +397 -0
  225. package/src/ui/components/payment/types.ts +45 -0
  226. package/src/ui/hooks/index.ts +3 -1
  227. package/src/ui/hooks/useAsyncAction.ts +129 -0
  228. package/src/ui/hooks/useSettingToggle.ts +147 -0
  229. package/src/ui/navigation/routes.ts +2 -0
  230. package/src/ui/screens/AccountCenterScreen.tsx +2 -2
  231. package/src/ui/screens/AccountOverviewScreen.tsx +35 -37
  232. package/src/ui/screens/FAQScreen.tsx +332 -0
  233. package/src/ui/screens/FeedbackScreen.tsx +91 -626
  234. package/src/ui/screens/HelpSupportScreen.tsx +7 -5
  235. package/src/ui/screens/PaymentGatewayScreen.tsx +96 -1275
  236. package/src/ui/screens/ProfileScreen.tsx +11 -6
  237. package/src/ui/screens/SavesCollectionsScreen.tsx +19 -10
  238. package/src/ui/screens/karma/KarmaCenterScreen.tsx +10 -10
@@ -0,0 +1,147 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import { toast } from '../../lib/sonner';
3
+
4
+ interface UseSettingToggleOptions {
5
+ /** Initial value of the setting */
6
+ initialValue: boolean;
7
+ /** Function to save the setting to the server */
8
+ onSave: (value: boolean) => Promise<void>;
9
+ /** Success message when saving */
10
+ successMessage?: string;
11
+ /** Error message when save fails */
12
+ errorMessage?: string;
13
+ /** Whether to revert on error (default: true) */
14
+ revertOnError?: boolean;
15
+ /** Whether to show success toast (default: false) */
16
+ showSuccessToast?: boolean;
17
+ }
18
+
19
+ interface UseSettingToggleReturn {
20
+ /** Current value */
21
+ value: boolean;
22
+ /** Whether the setting is being saved */
23
+ isSaving: boolean;
24
+ /** Toggle the setting (optimistic update with revert on error) */
25
+ toggle: () => Promise<void>;
26
+ /** Set the value directly */
27
+ setValue: (value: boolean) => void;
28
+ }
29
+
30
+ /**
31
+ * Hook for handling boolean toggle settings with optimistic updates.
32
+ * Automatically reverts to the previous value if the save fails.
33
+ *
34
+ * @example
35
+ * const { value, toggle, isSaving } = useSettingToggle({
36
+ * initialValue: user.notificationsEnabled,
37
+ * onSave: (value) => api.updateNotifications(value),
38
+ * errorMessage: 'Failed to update notifications',
39
+ * });
40
+ *
41
+ * <Switch value={value} onValueChange={toggle} disabled={isSaving} />
42
+ */
43
+ export function useSettingToggle(options: UseSettingToggleOptions): UseSettingToggleReturn {
44
+ const {
45
+ initialValue,
46
+ onSave,
47
+ successMessage,
48
+ errorMessage = 'Failed to save setting',
49
+ revertOnError = true,
50
+ showSuccessToast = false,
51
+ } = options;
52
+
53
+ const [value, setValue] = useState(initialValue);
54
+ const [isSaving, setIsSaving] = useState(false);
55
+
56
+ // Update value when initialValue changes (e.g., from server)
57
+ useEffect(() => {
58
+ setValue(initialValue);
59
+ }, [initialValue]);
60
+
61
+ const toggle = useCallback(async () => {
62
+ const previousValue = value;
63
+ const newValue = !value;
64
+
65
+ // Optimistic update
66
+ setValue(newValue);
67
+ setIsSaving(true);
68
+
69
+ try {
70
+ await onSave(newValue);
71
+
72
+ if (showSuccessToast && successMessage) {
73
+ toast.success(successMessage);
74
+ }
75
+ } catch (err: any) {
76
+ // Revert on error
77
+ if (revertOnError) {
78
+ setValue(previousValue);
79
+ }
80
+
81
+ toast.error(errorMessage || err?.message || 'An error occurred');
82
+ } finally {
83
+ setIsSaving(false);
84
+ }
85
+ }, [value, onSave, successMessage, errorMessage, revertOnError, showSuccessToast]);
86
+
87
+ return { value, isSaving, toggle, setValue };
88
+ }
89
+
90
+ /**
91
+ * Hook for managing multiple toggle settings at once.
92
+ * Useful when you have several related boolean settings.
93
+ */
94
+ export function useSettingToggles<T extends Record<string, boolean>>(options: {
95
+ initialValues: T;
96
+ onSave: (key: keyof T, value: boolean) => Promise<void>;
97
+ errorMessage?: string | ((key: keyof T) => string);
98
+ revertOnError?: boolean;
99
+ }): {
100
+ values: T;
101
+ savingKeys: Set<keyof T>;
102
+ toggle: (key: keyof T) => Promise<void>;
103
+ setValues: (values: T) => void;
104
+ } {
105
+ const { initialValues, onSave, errorMessage = 'Failed to save setting', revertOnError = true } = options;
106
+
107
+ const [values, setValues] = useState<T>(initialValues);
108
+ const [savingKeys, setSavingKeys] = useState<Set<keyof T>>(new Set());
109
+
110
+ // Update values when initialValues change
111
+ useEffect(() => {
112
+ setValues(initialValues);
113
+ }, [initialValues]);
114
+
115
+ const toggle = useCallback(async (key: keyof T) => {
116
+ const previousValue = values[key];
117
+ const newValue = !previousValue;
118
+
119
+ // Optimistic update
120
+ setValues(prev => ({ ...prev, [key]: newValue }));
121
+ setSavingKeys(prev => new Set(prev).add(key));
122
+
123
+ try {
124
+ await onSave(key, newValue);
125
+ } catch (err: any) {
126
+ // Revert on error
127
+ if (revertOnError) {
128
+ setValues(prev => ({ ...prev, [key]: previousValue }));
129
+ }
130
+
131
+ const message = typeof errorMessage === 'function'
132
+ ? errorMessage(key)
133
+ : errorMessage;
134
+ toast.error(message || err?.message || 'An error occurred');
135
+ } finally {
136
+ setSavingKeys(prev => {
137
+ const next = new Set(prev);
138
+ next.delete(key);
139
+ return next;
140
+ });
141
+ }
142
+ }, [values, onSave, errorMessage, revertOnError]);
143
+
144
+ return { values, savingKeys, toggle, setValues };
145
+ }
146
+
147
+ export default useSettingToggle;
@@ -22,6 +22,7 @@ export type RouteName =
22
22
  | 'SearchSettings'
23
23
  | 'FileManagement'
24
24
  | 'HelpSupport'
25
+ | 'FAQ'
25
26
  | 'Feedback'
26
27
  | 'LegalDocuments'
27
28
  | 'AppInfo'
@@ -59,6 +60,7 @@ const screenLoaders: Record<RouteName, () => ComponentType<BaseScreenProps>> = {
59
60
  SearchSettings: () => require('../screens/SearchSettingsScreen').default,
60
61
  FileManagement: () => require('../screens/FileManagementScreen').default,
61
62
  HelpSupport: () => require('../screens/HelpSupportScreen').default,
63
+ FAQ: () => require('../screens/FAQScreen').default,
62
64
  Feedback: () => require('../screens/FeedbackScreen').default,
63
65
  LegalDocuments: () => require('../screens/LegalDocumentsScreen').default,
64
66
  AppInfo: () => require('../screens/AppInfoScreen').default,
@@ -226,7 +226,7 @@ const AccountCenterScreen: React.FC<BaseScreenProps> = ({
226
226
  iconColor: colors.iconStorage,
227
227
  title: t('accountCenter.items.notifications.title') || 'Notifications',
228
228
  subtitle: t('accountCenter.items.notifications.subtitle') || 'Manage notification settings',
229
- onPress: () => toast.info(t('accountCenter.items.notifications.coming') || 'Notifications feature coming soon!'),
229
+ onPress: () => navigate?.('EditProfile', { activeTab: 'notifications' }),
230
230
  }] : []),
231
231
  {
232
232
  id: 'language',
@@ -242,7 +242,7 @@ const AccountCenterScreen: React.FC<BaseScreenProps> = ({
242
242
  iconColor: colors.iconSecurity,
243
243
  title: t('accountOverview.items.help.title') || 'Help & Support',
244
244
  subtitle: t('accountOverview.items.help.subtitle') || 'Get help and contact support',
245
- onPress: () => toast.info(t('accountOverview.items.help.coming') || 'Help & Support feature coming soon!'),
245
+ onPress: () => navigate?.('HelpSupport'),
246
246
  },
247
247
  {
248
248
  id: 'appinfo',
@@ -34,6 +34,7 @@ import {
34
34
  HEADER_PADDING_TOP_OVERVIEW,
35
35
  createScreenContentStyle,
36
36
  } from '../constants/spacing';
37
+ import { DeleteAccountModal } from '../components/modals';
37
38
 
38
39
  // Optional Lottie import - gracefully handle if not available
39
40
  let LottieView: any = null;
@@ -83,6 +84,7 @@ const AccountOverviewScreen: React.FC<BaseScreenProps> = ({
83
84
  const [showMoreAccounts, setShowMoreAccounts] = useState(false);
84
85
  const [additionalAccountsData, setAdditionalAccountsData] = useState<any[]>([]);
85
86
  const [loadingAdditionalAccounts, setLoadingAdditionalAccounts] = useState(false);
87
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
86
88
  const lottieRef = useRef<any>(null);
87
89
  const hasPlayedRef = useRef(false);
88
90
  const insets = useSafeAreaInsets();
@@ -289,45 +291,22 @@ const AccountOverviewScreen: React.FC<BaseScreenProps> = ({
289
291
  toast.error(t('accountOverview.items.deleteAccount.error') || 'User not available');
290
292
  return;
291
293
  }
294
+ setShowDeleteModal(true);
295
+ }, [user, t]);
292
296
 
293
- confirmAction(
294
- t('accountOverview.items.deleteAccount.confirmMessage') || `This action cannot be undone. This will permanently delete your account and all associated data.\n\nAre you sure you want to delete your account?`,
295
- async () => {
296
- // For React Native, we'd need a separate modal for password/confirmation input
297
- // For now, we'll use a simplified confirmation
298
- // In production, you'd want to create a modal with password and username confirmation fields
299
- if (!oxyServices) {
300
- toast.error(t('accountOverview.items.deleteAccount.error') || 'Service not available');
301
- return;
302
- }
297
+ const handleConfirmDelete = useCallback(async (password: string) => {
298
+ if (!oxyServices || !user) {
299
+ throw new Error(t('accountOverview.items.deleteAccount.error') || 'Service not available');
300
+ }
303
301
 
304
- Alert.alert(
305
- t('accountOverview.items.deleteAccount.confirmTitle') || 'Delete Account',
306
- t('accountOverview.items.deleteAccount.finalConfirm') || `This is your final warning. Your account will be permanently deleted and cannot be recovered. Type "${user.username}" to confirm.`,
307
- [
308
- {
309
- text: t('common.cancel') || 'Cancel',
310
- style: 'cancel',
311
- },
312
- {
313
- text: t('accountOverview.items.deleteAccount.confirm') || 'Delete Forever',
314
- style: 'destructive',
315
- onPress: async () => {
316
- try {
317
- // Note: In a production app, you'd want to show a modal with password and username confirmation fields
318
- // For now, we'll require the user to enter these via a custom modal or prompt
319
- toast.error(t('accountOverview.items.deleteAccount.passwordRequired') || 'Password confirmation required. This feature needs a modal with password input.');
320
- } catch (error: any) {
321
- console.error('Failed to delete account:', error);
322
- toast.error(error?.message || t('accountOverview.items.deleteAccount.error') || 'Failed to delete account');
323
- }
324
- },
325
- },
326
- ]
327
- );
328
- }
329
- );
330
- }, [user, oxyServices, logout, onClose, t]);
302
+ await oxyServices.deleteAccount(password);
303
+ toast.success(t('accountOverview.items.deleteAccount.success') || 'Account deleted successfully');
304
+ setShowDeleteModal(false);
305
+ await logout();
306
+ if (onClose) {
307
+ onClose();
308
+ }
309
+ }, [oxyServices, user, logout, onClose, t]);
331
310
 
332
311
  if (!isAuthenticated) {
333
312
  return (
@@ -703,6 +682,25 @@ const AccountOverviewScreen: React.FC<BaseScreenProps> = ({
703
682
  />
704
683
  </Section>
705
684
  </ScrollView>
685
+
686
+ {/* Delete Account Modal */}
687
+ {user && (
688
+ <DeleteAccountModal
689
+ visible={showDeleteModal}
690
+ username={user.username || ''}
691
+ onClose={() => setShowDeleteModal(false)}
692
+ onDelete={handleConfirmDelete}
693
+ colors={{
694
+ background: themeStyles.backgroundColor,
695
+ text: themeStyles.textColor,
696
+ secondaryText: themeStyles.isDarkTheme ? '#888888' : '#666666',
697
+ border: themeStyles.borderColor,
698
+ danger: '#FF3B30',
699
+ inputBackground: themeStyles.isDarkTheme ? '#333333' : '#F5F5F5',
700
+ }}
701
+ t={t}
702
+ />
703
+ )}
706
704
  </View>
707
705
  );
708
706
  };
@@ -0,0 +1,332 @@
1
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ StyleSheet,
7
+ ScrollView,
8
+ TextInput,
9
+ Platform,
10
+ } from 'react-native';
11
+ import { Ionicons } from '@expo/vector-icons';
12
+ import type { BaseScreenProps } from '../types/navigation';
13
+ import { toast } from '../../lib/sonner';
14
+ import { Header, LoadingState, EmptyState } from '../components';
15
+ import { useI18n } from '../hooks/useI18n';
16
+ import { useThemeStyles } from '../hooks/useThemeStyles';
17
+ import { useOxy } from '../context/OxyContext';
18
+
19
+ interface FAQ {
20
+ id: string;
21
+ question: string;
22
+ answer: string;
23
+ category: string;
24
+ }
25
+
26
+ const FAQScreen: React.FC<BaseScreenProps> = ({
27
+ onClose,
28
+ theme,
29
+ goBack,
30
+ }) => {
31
+ const { oxyServices } = useOxy();
32
+ const { t } = useI18n();
33
+ const themeStyles = useThemeStyles(theme || 'light');
34
+
35
+ const [faqs, setFaqs] = useState<FAQ[]>([]);
36
+ const [isLoading, setIsLoading] = useState(true);
37
+ const [searchQuery, setSearchQuery] = useState('');
38
+ const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
39
+ const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
40
+
41
+ // Load FAQs from API
42
+ useEffect(() => {
43
+ const loadFAQs = async () => {
44
+ try {
45
+ setIsLoading(true);
46
+ const data = await oxyServices.getFAQs();
47
+ setFaqs(data);
48
+ } catch (error) {
49
+ toast.error(t('faq.loadError') || 'Failed to load FAQs');
50
+ } finally {
51
+ setIsLoading(false);
52
+ }
53
+ };
54
+
55
+ loadFAQs();
56
+ }, [oxyServices, t]);
57
+
58
+ // Get unique categories
59
+ const categories = useMemo(() => {
60
+ const cats = [...new Set(faqs.map(f => f.category))];
61
+ return cats.sort();
62
+ }, [faqs]);
63
+
64
+ // Filter FAQs based on search and category
65
+ const filteredFaqs = useMemo(() => {
66
+ let result = faqs;
67
+
68
+ if (selectedCategory) {
69
+ result = result.filter(f => f.category === selectedCategory);
70
+ }
71
+
72
+ if (searchQuery.trim()) {
73
+ const query = searchQuery.toLowerCase();
74
+ result = result.filter(f =>
75
+ f.question.toLowerCase().includes(query) ||
76
+ f.answer.toLowerCase().includes(query)
77
+ );
78
+ }
79
+
80
+ return result;
81
+ }, [faqs, searchQuery, selectedCategory]);
82
+
83
+ const toggleExpanded = useCallback((id: string) => {
84
+ setExpandedIds(prev => {
85
+ const next = new Set(prev);
86
+ if (next.has(id)) {
87
+ next.delete(id);
88
+ } else {
89
+ next.add(id);
90
+ }
91
+ return next;
92
+ });
93
+ }, []);
94
+
95
+ const styles = useMemo(() => createStyles(themeStyles), [themeStyles]);
96
+
97
+ return (
98
+ <View style={[styles.container, { backgroundColor: themeStyles.backgroundColor }]}>
99
+ <Header
100
+ title={t('faq.title') || 'FAQ'}
101
+ onBack={goBack || onClose}
102
+ variant="minimal"
103
+ elevation="subtle"
104
+ />
105
+
106
+ {/* Search bar */}
107
+ <View style={styles.searchContainer}>
108
+ <View style={[styles.searchInputWrapper, { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor: themeStyles.borderColor }]}>
109
+ <Ionicons name="search" size={20} color={themeStyles.mutedTextColor} style={styles.searchIcon} />
110
+ <TextInput
111
+ style={[styles.searchInput, { color: themeStyles.textColor }]}
112
+ placeholder={t('faq.searchPlaceholder') || 'Search FAQs...'}
113
+ placeholderTextColor={themeStyles.mutedTextColor}
114
+ value={searchQuery}
115
+ onChangeText={setSearchQuery}
116
+ accessibilityLabel="Search FAQs"
117
+ />
118
+ {searchQuery.length > 0 && (
119
+ <TouchableOpacity
120
+ onPress={() => setSearchQuery('')}
121
+ accessibilityRole="button"
122
+ accessibilityLabel="Clear search"
123
+ >
124
+ <Ionicons name="close-circle" size={20} color={themeStyles.mutedTextColor} />
125
+ </TouchableOpacity>
126
+ )}
127
+ </View>
128
+ </View>
129
+
130
+ {/* Category filters */}
131
+ {categories.length > 0 && (
132
+ <ScrollView
133
+ horizontal
134
+ showsHorizontalScrollIndicator={false}
135
+ style={styles.categoriesContainer}
136
+ contentContainerStyle={styles.categoriesContent}
137
+ >
138
+ <TouchableOpacity
139
+ style={[
140
+ styles.categoryChip,
141
+ !selectedCategory && styles.categoryChipActive,
142
+ { backgroundColor: !selectedCategory ? themeStyles.primaryColor : themeStyles.secondaryBackgroundColor }
143
+ ]}
144
+ onPress={() => setSelectedCategory(null)}
145
+ accessibilityRole="button"
146
+ accessibilityLabel="Show all categories"
147
+ accessibilityState={{ selected: !selectedCategory }}
148
+ >
149
+ <Text style={[
150
+ styles.categoryChipText,
151
+ { color: !selectedCategory ? '#FFFFFF' : themeStyles.textColor }
152
+ ]}>
153
+ {t('faq.allCategories') || 'All'}
154
+ </Text>
155
+ </TouchableOpacity>
156
+ {categories.map(cat => (
157
+ <TouchableOpacity
158
+ key={cat}
159
+ style={[
160
+ styles.categoryChip,
161
+ selectedCategory === cat && styles.categoryChipActive,
162
+ { backgroundColor: selectedCategory === cat ? themeStyles.primaryColor : themeStyles.secondaryBackgroundColor }
163
+ ]}
164
+ onPress={() => setSelectedCategory(cat)}
165
+ accessibilityRole="button"
166
+ accessibilityLabel={`Filter by ${cat}`}
167
+ accessibilityState={{ selected: selectedCategory === cat }}
168
+ >
169
+ <Text style={[
170
+ styles.categoryChipText,
171
+ { color: selectedCategory === cat ? '#FFFFFF' : themeStyles.textColor }
172
+ ]}>
173
+ {cat}
174
+ </Text>
175
+ </TouchableOpacity>
176
+ ))}
177
+ </ScrollView>
178
+ )}
179
+
180
+ <ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
181
+ {isLoading ? (
182
+ <LoadingState
183
+ message={t('faq.loading') || 'Loading FAQs...'}
184
+ color={themeStyles.textColor}
185
+ />
186
+ ) : filteredFaqs.length === 0 ? (
187
+ <EmptyState
188
+ message={searchQuery ? (t('faq.noResults') || 'No FAQs match your search') : (t('faq.empty') || 'No FAQs available')}
189
+ textColor={themeStyles.textColor}
190
+ />
191
+ ) : (
192
+ filteredFaqs.map((faq, index) => {
193
+ const isExpanded = expandedIds.has(faq.id);
194
+ return (
195
+ <View
196
+ key={faq.id}
197
+ style={[
198
+ styles.faqItem,
199
+ { backgroundColor: themeStyles.secondaryBackgroundColor, borderColor: themeStyles.borderColor },
200
+ index === 0 && styles.faqItemFirst,
201
+ ]}
202
+ >
203
+ <TouchableOpacity
204
+ style={styles.faqQuestion}
205
+ onPress={() => toggleExpanded(faq.id)}
206
+ accessibilityRole="button"
207
+ accessibilityLabel={faq.question}
208
+ accessibilityHint={isExpanded ? 'Collapse answer' : 'Expand answer'}
209
+ accessibilityState={{ expanded: isExpanded }}
210
+ >
211
+ <Text style={[styles.faqQuestionText, { color: themeStyles.textColor }]}>
212
+ {faq.question}
213
+ </Text>
214
+ <Ionicons
215
+ name={isExpanded ? 'chevron-up' : 'chevron-down'}
216
+ size={20}
217
+ color={themeStyles.mutedTextColor}
218
+ />
219
+ </TouchableOpacity>
220
+ {isExpanded && (
221
+ <View style={[styles.faqAnswer, { borderTopColor: themeStyles.borderColor }]}>
222
+ <Text style={[styles.faqAnswerText, { color: themeStyles.mutedTextColor }]}>
223
+ {faq.answer}
224
+ </Text>
225
+ <View style={styles.faqCategory}>
226
+ <Ionicons name="pricetag-outline" size={14} color={themeStyles.primaryColor} />
227
+ <Text style={[styles.faqCategoryText, { color: themeStyles.primaryColor }]}>
228
+ {faq.category}
229
+ </Text>
230
+ </View>
231
+ </View>
232
+ )}
233
+ </View>
234
+ );
235
+ })
236
+ )}
237
+ </ScrollView>
238
+ </View>
239
+ );
240
+ };
241
+
242
+ const createStyles = (themeStyles: any) => StyleSheet.create({
243
+ container: {
244
+ flex: 1,
245
+ },
246
+ searchContainer: {
247
+ paddingHorizontal: 16,
248
+ paddingVertical: 12,
249
+ },
250
+ searchInputWrapper: {
251
+ flexDirection: 'row',
252
+ alignItems: 'center',
253
+ borderRadius: 12,
254
+ borderWidth: 1,
255
+ paddingHorizontal: 12,
256
+ height: 44,
257
+ },
258
+ searchIcon: {
259
+ marginRight: 8,
260
+ },
261
+ searchInput: {
262
+ flex: 1,
263
+ fontSize: 16,
264
+ ...Platform.select({
265
+ web: { outlineStyle: 'none' as any },
266
+ }),
267
+ },
268
+ categoriesContainer: {
269
+ maxHeight: 50,
270
+ },
271
+ categoriesContent: {
272
+ paddingHorizontal: 16,
273
+ gap: 8,
274
+ },
275
+ categoryChip: {
276
+ paddingHorizontal: 16,
277
+ paddingVertical: 8,
278
+ borderRadius: 20,
279
+ marginRight: 8,
280
+ },
281
+ categoryChipActive: {},
282
+ categoryChipText: {
283
+ fontSize: 14,
284
+ fontWeight: '500',
285
+ },
286
+ content: {
287
+ flex: 1,
288
+ padding: 16,
289
+ },
290
+ faqItem: {
291
+ borderRadius: 12,
292
+ borderWidth: 1,
293
+ marginBottom: 12,
294
+ overflow: 'hidden',
295
+ },
296
+ faqItemFirst: {
297
+ marginTop: 0,
298
+ },
299
+ faqQuestion: {
300
+ flexDirection: 'row',
301
+ alignItems: 'center',
302
+ justifyContent: 'space-between',
303
+ padding: 16,
304
+ },
305
+ faqQuestionText: {
306
+ flex: 1,
307
+ fontSize: 16,
308
+ fontWeight: '600',
309
+ marginRight: 12,
310
+ },
311
+ faqAnswer: {
312
+ padding: 16,
313
+ paddingTop: 12,
314
+ borderTopWidth: 1,
315
+ },
316
+ faqAnswerText: {
317
+ fontSize: 14,
318
+ lineHeight: 22,
319
+ },
320
+ faqCategory: {
321
+ flexDirection: 'row',
322
+ alignItems: 'center',
323
+ marginTop: 12,
324
+ },
325
+ faqCategoryText: {
326
+ fontSize: 12,
327
+ marginLeft: 6,
328
+ fontWeight: '500',
329
+ },
330
+ });
331
+
332
+ export default FAQScreen;