@harkenapp/sdk-react-native 0.0.1-alpha.1 → 0.0.1-alpha.2

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 (278) hide show
  1. package/README.md +44 -7
  2. package/app.plugin.cjs +12 -17
  3. package/dist/__mocks__/async-storage.d.ts +16 -0
  4. package/dist/__mocks__/async-storage.d.ts.map +1 -0
  5. package/dist/__mocks__/async-storage.js +39 -0
  6. package/dist/__mocks__/async-storage.js.map +1 -0
  7. package/dist/__mocks__/expo-document-picker.d.ts +26 -0
  8. package/dist/__mocks__/expo-document-picker.d.ts.map +1 -0
  9. package/dist/__mocks__/expo-document-picker.js +25 -0
  10. package/dist/__mocks__/expo-document-picker.js.map +1 -0
  11. package/dist/__mocks__/expo-file-system.d.ts +42 -0
  12. package/dist/__mocks__/expo-file-system.d.ts.map +1 -0
  13. package/dist/__mocks__/expo-file-system.js +37 -0
  14. package/dist/__mocks__/expo-file-system.js.map +1 -0
  15. package/dist/__mocks__/expo-image-picker.d.ts +30 -0
  16. package/dist/__mocks__/expo-image-picker.d.ts.map +1 -0
  17. package/dist/__mocks__/expo-image-picker.js +30 -0
  18. package/dist/__mocks__/expo-image-picker.js.map +1 -0
  19. package/dist/__mocks__/expo-secure-store.d.ts +15 -0
  20. package/dist/__mocks__/expo-secure-store.d.ts.map +1 -0
  21. package/dist/__mocks__/expo-secure-store.js +30 -0
  22. package/dist/__mocks__/expo-secure-store.js.map +1 -0
  23. package/dist/__mocks__/react-native.d.ts +73 -0
  24. package/dist/__mocks__/react-native.d.ts.map +1 -0
  25. package/dist/__mocks__/react-native.js +45 -0
  26. package/dist/__mocks__/react-native.js.map +1 -0
  27. package/dist/api/client.d.ts +8 -8
  28. package/dist/api/client.d.ts.map +1 -1
  29. package/dist/api/client.js +17 -19
  30. package/dist/api/client.js.map +1 -1
  31. package/dist/api/client.test.d.ts +2 -0
  32. package/dist/api/client.test.d.ts.map +1 -0
  33. package/dist/api/client.test.js +417 -0
  34. package/dist/api/client.test.js.map +1 -0
  35. package/dist/api/errors.d.ts +3 -3
  36. package/dist/api/errors.d.ts.map +1 -1
  37. package/dist/api/errors.js +3 -3
  38. package/dist/api/errors.js.map +1 -1
  39. package/dist/api/errors.test.d.ts +2 -0
  40. package/dist/api/errors.test.d.ts.map +1 -0
  41. package/dist/api/errors.test.js +155 -0
  42. package/dist/api/errors.test.js.map +1 -0
  43. package/dist/api/index.d.ts +6 -6
  44. package/dist/api/index.d.ts.map +1 -1
  45. package/dist/api/index.js.map +1 -1
  46. package/dist/api/retry.d.ts +1 -1
  47. package/dist/api/retry.d.ts.map +1 -1
  48. package/dist/api/retry.js.map +1 -1
  49. package/dist/api/retry.test.d.ts +2 -0
  50. package/dist/api/retry.test.d.ts.map +1 -0
  51. package/dist/api/retry.test.js +193 -0
  52. package/dist/api/retry.test.js.map +1 -0
  53. package/dist/attachments/FeedbackSheet.d.ts +36 -13
  54. package/dist/attachments/FeedbackSheet.d.ts.map +1 -1
  55. package/dist/attachments/FeedbackSheet.js +50 -30
  56. package/dist/attachments/FeedbackSheet.js.map +1 -1
  57. package/dist/attachments/index.d.ts +2 -2
  58. package/dist/components/AttachmentGrid.d.ts +12 -4
  59. package/dist/components/AttachmentGrid.d.ts.map +1 -1
  60. package/dist/components/AttachmentGrid.js +44 -34
  61. package/dist/components/AttachmentGrid.js.map +1 -1
  62. package/dist/components/AttachmentPicker.d.ts +3 -3
  63. package/dist/components/AttachmentPicker.d.ts.map +1 -1
  64. package/dist/components/AttachmentPicker.js +34 -36
  65. package/dist/components/AttachmentPicker.js.map +1 -1
  66. package/dist/components/AttachmentPreview.d.ts +10 -4
  67. package/dist/components/AttachmentPreview.d.ts.map +1 -1
  68. package/dist/components/AttachmentPreview.js +48 -34
  69. package/dist/components/AttachmentPreview.js.map +1 -1
  70. package/dist/components/CategorySelector.d.ts +3 -3
  71. package/dist/components/CategorySelector.d.ts.map +1 -1
  72. package/dist/components/CategorySelector.js +21 -27
  73. package/dist/components/CategorySelector.js.map +1 -1
  74. package/dist/components/FeedbackForm.d.ts +3 -3
  75. package/dist/components/FeedbackForm.d.ts.map +1 -1
  76. package/dist/components/FeedbackForm.js +7 -8
  77. package/dist/components/FeedbackForm.js.map +1 -1
  78. package/dist/components/FeedbackSheet.d.ts +34 -11
  79. package/dist/components/FeedbackSheet.d.ts.map +1 -1
  80. package/dist/components/FeedbackSheet.js +46 -28
  81. package/dist/components/FeedbackSheet.js.map +1 -1
  82. package/dist/components/ThemedButton.d.ts +16 -5
  83. package/dist/components/ThemedButton.d.ts.map +1 -1
  84. package/dist/components/ThemedButton.js +38 -29
  85. package/dist/components/ThemedButton.js.map +1 -1
  86. package/dist/components/ThemedText.d.ts +3 -3
  87. package/dist/components/ThemedText.d.ts.map +1 -1
  88. package/dist/components/ThemedText.js +1 -1
  89. package/dist/components/ThemedText.js.map +1 -1
  90. package/dist/components/ThemedTextInput.d.ts +11 -2
  91. package/dist/components/ThemedTextInput.d.ts.map +1 -1
  92. package/dist/components/ThemedTextInput.js +19 -9
  93. package/dist/components/ThemedTextInput.js.map +1 -1
  94. package/dist/components/UploadStatusOverlay.d.ts +11 -3
  95. package/dist/components/UploadStatusOverlay.d.ts.map +1 -1
  96. package/dist/components/UploadStatusOverlay.js +59 -76
  97. package/dist/components/UploadStatusOverlay.js.map +1 -1
  98. package/dist/components/index.d.ts +18 -18
  99. package/dist/components/index.d.ts.map +1 -1
  100. package/dist/components/index.js.map +1 -1
  101. package/dist/context/HarkenContext.d.ts +20 -15
  102. package/dist/context/HarkenContext.d.ts.map +1 -1
  103. package/dist/context/HarkenContext.js +20 -17
  104. package/dist/context/HarkenContext.js.map +1 -1
  105. package/dist/context/index.d.ts +2 -2
  106. package/dist/domain/index.d.ts +2 -2
  107. package/dist/domain/index.d.ts.map +1 -1
  108. package/dist/domain/index.js.map +1 -1
  109. package/dist/hooks/index.d.ts +5 -5
  110. package/dist/hooks/useAnonymousId.js +1 -1
  111. package/dist/hooks/useAnonymousId.test.d.ts +2 -0
  112. package/dist/hooks/useAnonymousId.test.d.ts.map +1 -0
  113. package/dist/hooks/useAnonymousId.test.js +154 -0
  114. package/dist/hooks/useAnonymousId.test.js.map +1 -0
  115. package/dist/hooks/useAttachmentPicker.d.ts +3 -3
  116. package/dist/hooks/useAttachmentPicker.js +7 -7
  117. package/dist/hooks/useAttachmentStatus.d.ts +1 -1
  118. package/dist/hooks/useAttachmentStatus.d.ts.map +1 -1
  119. package/dist/hooks/useAttachmentStatus.js.map +1 -1
  120. package/dist/hooks/useAttachmentUpload.d.ts +2 -2
  121. package/dist/hooks/useAttachmentUpload.d.ts.map +1 -1
  122. package/dist/hooks/useAttachmentUpload.js +5 -5
  123. package/dist/hooks/useAttachmentUpload.js.map +1 -1
  124. package/dist/hooks/useAttachmentUpload.test.d.ts +2 -0
  125. package/dist/hooks/useAttachmentUpload.test.d.ts.map +1 -0
  126. package/dist/hooks/useAttachmentUpload.test.js +542 -0
  127. package/dist/hooks/useAttachmentUpload.test.js.map +1 -0
  128. package/dist/hooks/useFeedback.d.ts +4 -4
  129. package/dist/hooks/useFeedback.d.ts.map +1 -1
  130. package/dist/hooks/useFeedback.js +3 -5
  131. package/dist/hooks/useFeedback.js.map +1 -1
  132. package/dist/hooks/useFeedback.test.d.ts +2 -0
  133. package/dist/hooks/useFeedback.test.d.ts.map +1 -0
  134. package/dist/hooks/useFeedback.test.js +299 -0
  135. package/dist/hooks/useFeedback.test.js.map +1 -0
  136. package/dist/hooks/useHarkenContext.d.ts +1 -1
  137. package/dist/hooks/useHarkenContext.js +1 -1
  138. package/dist/hooks/useHarkenTheme.d.ts +27 -3
  139. package/dist/hooks/useHarkenTheme.d.ts.map +1 -1
  140. package/dist/hooks/useHarkenTheme.js +26 -2
  141. package/dist/hooks/useHarkenTheme.js.map +1 -1
  142. package/dist/index.d.ts +28 -28
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/services/index.d.ts +3 -3
  146. package/dist/services/index.d.ts.map +1 -1
  147. package/dist/services/index.js.map +1 -1
  148. package/dist/services/uploadQueueService.d.ts +2 -2
  149. package/dist/services/uploadQueueService.d.ts.map +1 -1
  150. package/dist/services/uploadQueueService.js +16 -17
  151. package/dist/services/uploadQueueService.js.map +1 -1
  152. package/dist/services/uploadQueueService.test.d.ts +2 -0
  153. package/dist/services/uploadQueueService.test.d.ts.map +1 -0
  154. package/dist/services/uploadQueueService.test.js +426 -0
  155. package/dist/services/uploadQueueService.test.js.map +1 -0
  156. package/dist/services/uploadQueueStorage.d.ts +1 -1
  157. package/dist/services/uploadQueueStorage.d.ts.map +1 -1
  158. package/dist/services/uploadQueueStorage.js +4 -4
  159. package/dist/services/uploadQueueStorage.js.map +1 -1
  160. package/dist/services/uploadQueueStorage.test.d.ts +2 -0
  161. package/dist/services/uploadQueueStorage.test.d.ts.map +1 -0
  162. package/dist/services/uploadQueueStorage.test.js +200 -0
  163. package/dist/services/uploadQueueStorage.test.js.map +1 -0
  164. package/dist/storage/IdentityStore.d.ts +1 -1
  165. package/dist/storage/IdentityStore.d.ts.map +1 -1
  166. package/dist/storage/IdentityStore.js.map +1 -1
  167. package/dist/storage/IdentityStore.test.d.ts +2 -0
  168. package/dist/storage/IdentityStore.test.d.ts.map +1 -0
  169. package/dist/storage/IdentityStore.test.js +176 -0
  170. package/dist/storage/IdentityStore.test.js.map +1 -0
  171. package/dist/storage/SecureStoreAdapter.d.ts +1 -1
  172. package/dist/storage/SecureStoreAdapter.test.d.ts +2 -0
  173. package/dist/storage/SecureStoreAdapter.test.d.ts.map +1 -0
  174. package/dist/storage/SecureStoreAdapter.test.js +114 -0
  175. package/dist/storage/SecureStoreAdapter.test.js.map +1 -0
  176. package/dist/storage/defaultStorage.d.ts +1 -1
  177. package/dist/storage/defaultStorage.js +4 -4
  178. package/dist/storage/defaultStorage.test.d.ts +2 -0
  179. package/dist/storage/defaultStorage.test.d.ts.map +1 -0
  180. package/dist/storage/defaultStorage.test.js +159 -0
  181. package/dist/storage/defaultStorage.test.js.map +1 -0
  182. package/dist/storage/index.d.ts +5 -5
  183. package/dist/storage/types.js +1 -1
  184. package/dist/theme/defaults.d.ts +14 -3
  185. package/dist/theme/defaults.d.ts.map +1 -1
  186. package/dist/theme/defaults.js +58 -43
  187. package/dist/theme/defaults.js.map +1 -1
  188. package/dist/theme/index.d.ts +3 -2
  189. package/dist/theme/index.d.ts.map +1 -1
  190. package/dist/theme/index.js +4 -1
  191. package/dist/theme/index.js.map +1 -1
  192. package/dist/theme/resolver.d.ts +16 -0
  193. package/dist/theme/resolver.d.ts.map +1 -0
  194. package/dist/theme/resolver.js +375 -0
  195. package/dist/theme/resolver.js.map +1 -0
  196. package/dist/theme/resolver.test.d.ts +2 -0
  197. package/dist/theme/resolver.test.d.ts.map +1 -0
  198. package/dist/theme/resolver.test.js +344 -0
  199. package/dist/theme/resolver.test.js.map +1 -0
  200. package/dist/theme/types.d.ts +378 -5
  201. package/dist/theme/types.d.ts.map +1 -1
  202. package/dist/types/config.d.ts +4 -4
  203. package/dist/types/index.d.ts +2 -2
  204. package/dist/utils/index.d.ts +1 -1
  205. package/dist/utils/uuid.d.ts.map +1 -1
  206. package/dist/utils/uuid.js +4 -5
  207. package/dist/utils/uuid.js.map +1 -1
  208. package/dist/utils/uuid.test.d.ts +2 -0
  209. package/dist/utils/uuid.test.d.ts.map +1 -0
  210. package/dist/utils/uuid.test.js +78 -0
  211. package/dist/utils/uuid.test.js.map +1 -0
  212. package/package.json +21 -13
  213. package/src/@types/expo-file-system-legacy.d.ts +3 -3
  214. package/src/__mocks__/async-storage.ts +46 -0
  215. package/src/__mocks__/expo-document-picker.ts +41 -0
  216. package/src/__mocks__/expo-file-system.ts +62 -0
  217. package/src/__mocks__/expo-image-picker.ts +48 -0
  218. package/src/__mocks__/expo-secure-store.ts +29 -0
  219. package/src/__mocks__/react-native.ts +46 -0
  220. package/src/api/client.test.ts +515 -0
  221. package/src/api/client.ts +45 -64
  222. package/src/api/errors.test.ts +193 -0
  223. package/src/api/errors.ts +7 -11
  224. package/src/api/index.ts +6 -10
  225. package/src/api/retry.test.ts +251 -0
  226. package/src/api/retry.ts +3 -6
  227. package/src/attachments/FeedbackSheet.tsx +100 -80
  228. package/src/attachments/index.ts +2 -2
  229. package/src/components/AttachmentGrid.tsx +54 -45
  230. package/src/components/AttachmentPicker.tsx +43 -54
  231. package/src/components/AttachmentPreview.tsx +51 -47
  232. package/src/components/CategorySelector.tsx +29 -35
  233. package/src/components/FeedbackForm.tsx +23 -35
  234. package/src/components/FeedbackSheet.tsx +89 -68
  235. package/src/components/ThemedButton.tsx +49 -47
  236. package/src/components/ThemedText.tsx +7 -10
  237. package/src/components/ThemedTextInput.tsx +23 -13
  238. package/src/components/UploadStatusOverlay.tsx +66 -89
  239. package/src/components/index.ts +18 -21
  240. package/src/context/HarkenContext.tsx +29 -28
  241. package/src/context/index.ts +2 -2
  242. package/src/domain/index.ts +2 -5
  243. package/src/domain/upload-queue.ts +5 -5
  244. package/src/hooks/index.ts +5 -5
  245. package/src/hooks/useAnonymousId.test.ts +189 -0
  246. package/src/hooks/useAnonymousId.ts +3 -3
  247. package/src/hooks/useAttachmentPicker.ts +12 -12
  248. package/src/hooks/useAttachmentStatus.ts +12 -16
  249. package/src/hooks/useAttachmentUpload.test.ts +632 -0
  250. package/src/hooks/useAttachmentUpload.ts +45 -54
  251. package/src/hooks/useFeedback.test.ts +376 -0
  252. package/src/hooks/useFeedback.ts +12 -14
  253. package/src/hooks/useHarkenContext.ts +4 -4
  254. package/src/hooks/useHarkenTheme.ts +30 -6
  255. package/src/index.ts +28 -52
  256. package/src/services/index.ts +3 -9
  257. package/src/services/uploadQueueService.test.ts +489 -0
  258. package/src/services/uploadQueueService.ts +40 -56
  259. package/src/services/uploadQueueStorage.test.ts +243 -0
  260. package/src/services/uploadQueueStorage.ts +7 -9
  261. package/src/storage/IdentityStore.test.ts +173 -0
  262. package/src/storage/IdentityStore.ts +4 -5
  263. package/src/storage/SecureStoreAdapter.test.ts +147 -0
  264. package/src/storage/SecureStoreAdapter.ts +1 -1
  265. package/src/storage/defaultStorage.test.ts +159 -0
  266. package/src/storage/defaultStorage.ts +6 -6
  267. package/src/storage/index.ts +5 -5
  268. package/src/storage/types.ts +1 -1
  269. package/src/theme/defaults.ts +75 -46
  270. package/src/theme/index.ts +15 -2
  271. package/src/theme/resolver.test.ts +411 -0
  272. package/src/theme/resolver.ts +446 -0
  273. package/src/theme/types.ts +453 -15
  274. package/src/types/config.ts +4 -4
  275. package/src/types/index.ts +2 -2
  276. package/src/utils/index.ts +1 -1
  277. package/src/utils/uuid.test.ts +85 -0
  278. package/src/utils/uuid.ts +4 -7
@@ -1,9 +1,9 @@
1
- import React from 'react';
2
- import { Text } from 'react-native';
3
- import type { TextProps, TextStyle } from 'react-native';
4
- import { useHarkenTheme } from '../hooks';
1
+ import React from "react";
2
+ import { Text } from "react-native";
3
+ import type { TextProps, TextStyle } from "react-native";
4
+ import { useHarkenTheme } from "../hooks";
5
5
 
6
- export type TextVariant = 'title' | 'body' | 'label' | 'caption';
6
+ export type TextVariant = "title" | "body" | "label" | "caption";
7
7
 
8
8
  export interface ThemedTextProps extends TextProps {
9
9
  /** Text variant determining size and weight */
@@ -18,7 +18,7 @@ export interface ThemedTextProps extends TextProps {
18
18
  * Themed text component that uses Harken theme typography.
19
19
  */
20
20
  export function ThemedText({
21
- variant = 'body',
21
+ variant = "body",
22
22
  color,
23
23
  secondary = false,
24
24
  style,
@@ -55,10 +55,7 @@ export function ThemedText({
55
55
  const textColor = color ?? (secondary ? theme.colors.textSecondary : theme.colors.text);
56
56
 
57
57
  return (
58
- <Text
59
- style={[variantStyles[variant], { color: textColor }, style]}
60
- {...props}
61
- >
58
+ <Text style={[variantStyles[variant], { color: textColor }, style]} {...props}>
62
59
  {children}
63
60
  </Text>
64
61
  );
@@ -1,7 +1,7 @@
1
- import React, { useState } from 'react';
2
- import { TextInput, View } from 'react-native';
3
- import type { TextInputProps, ViewStyle, TextStyle, StyleProp } from 'react-native';
4
- import { useHarkenTheme } from '../hooks';
1
+ import React, { useState } from "react";
2
+ import { TextInput, View } from "react-native";
3
+ import type { TextInputProps, ViewStyle, TextStyle, StyleProp } from "react-native";
4
+ import { useHarkenTheme } from "../hooks";
5
5
 
6
6
  export interface ThemedTextInputProps extends TextInputProps {
7
7
  /** Error state */
@@ -12,6 +12,15 @@ export interface ThemedTextInputProps extends TextInputProps {
12
12
 
13
13
  /**
14
14
  * Themed text input component with Harken styling.
15
+ *
16
+ * Uses the following theme tokens:
17
+ * - `colors.inputBackground` for background
18
+ * - `colors.inputBorder`, `inputBorderFocused`, `inputBorderError` for border states
19
+ * - `colors.inputText` for text color
20
+ * - `colors.inputPlaceholder` for placeholder
21
+ * - `spacing.inputPadding` for padding
22
+ * - `radii.input` for border radius
23
+ * - `sizing.inputMinHeight` for minimum height
15
24
  */
16
25
  export function ThemedTextInput({
17
26
  error = false,
@@ -22,34 +31,35 @@ export function ThemedTextInput({
22
31
  ...props
23
32
  }: ThemedTextInputProps): React.JSX.Element {
24
33
  const theme = useHarkenTheme();
34
+ const { input } = theme.components;
25
35
  const [isFocused, setIsFocused] = useState(false);
26
36
 
27
37
  const getBorderColor = () => {
28
- if (error) return theme.colors.error;
29
- if (isFocused) return theme.colors.borderFocused;
30
- return theme.colors.border;
38
+ if (error) return input.borderError;
39
+ if (isFocused) return input.borderFocused;
40
+ return input.border;
31
41
  };
32
42
 
33
43
  const inputStyle: TextStyle = {
34
44
  fontSize: theme.typography.bodySize,
35
45
  fontFamily: theme.typography.fontFamily,
36
- color: theme.colors.text,
37
- padding: theme.spacing.md,
38
- minHeight: 44,
46
+ color: input.text,
47
+ padding: input.padding,
48
+ minHeight: input.minHeight,
39
49
  };
40
50
 
41
51
  const containerStyles: ViewStyle = {
42
- backgroundColor: theme.colors.backgroundSecondary,
52
+ backgroundColor: input.background,
43
53
  borderWidth: 1,
44
54
  borderColor: getBorderColor(),
45
- borderRadius: theme.radii.md,
55
+ borderRadius: input.radius,
46
56
  };
47
57
 
48
58
  return (
49
59
  <View style={[containerStyles, containerStyle]}>
50
60
  <TextInput
51
61
  style={[inputStyle, style]}
52
- placeholderTextColor={theme.colors.textPlaceholder}
62
+ placeholderTextColor={input.placeholder}
53
63
  onFocus={(e) => {
54
64
  setIsFocused(true);
55
65
  onFocus?.(e);
@@ -1,9 +1,9 @@
1
- import React from 'react';
2
- import { View, Pressable, ActivityIndicator, StyleSheet } from 'react-native';
3
- import type { ViewStyle, StyleProp } from 'react-native';
4
- import { useHarkenTheme } from '../hooks';
5
- import { ThemedText } from './ThemedText';
6
- import { UploadPhase } from '../domain';
1
+ import React from "react";
2
+ import { View, Pressable, ActivityIndicator, StyleSheet } from "react-native";
3
+ import type { ViewStyle, StyleProp } from "react-native";
4
+ import { useHarkenTheme } from "../hooks";
5
+ import { ThemedText } from "./ThemedText";
6
+ import { UploadPhase } from "../domain";
7
7
 
8
8
  /**
9
9
  * Customizable labels for upload status states.
@@ -41,11 +41,7 @@ export interface UploadStatusOverlayProps {
41
41
  /** Custom progress renderer */
42
42
  renderProgress?: (progress: number) => React.ReactNode;
43
43
  /** Custom error renderer */
44
- renderError?: (
45
- error: string,
46
- onRetry?: () => void,
47
- onRemove?: () => void
48
- ) => React.ReactNode;
44
+ renderError?: (error: string, onRetry?: () => void, onRemove?: () => void) => React.ReactNode;
49
45
  /** Custom success/completed renderer */
50
46
  renderSuccess?: (onRemove?: () => void) => React.ReactNode;
51
47
  }
@@ -59,6 +55,14 @@ export interface UploadStatusOverlayProps {
59
55
  * - Checkmark when complete
60
56
  * - Error with retry button when failed
61
57
  *
58
+ * Uses the following theme tokens:
59
+ * - `colors.uploadOverlay` for overlay background
60
+ * - `colors.uploadOverlayError` for error overlay background
61
+ * - `colors.uploadProgressTrack` for progress bar track
62
+ * - `colors.uploadProgressFill` for progress bar fill
63
+ * - `colors.uploadBadgeSuccess` for success badge
64
+ * - `colors.uploadText` for overlay text
65
+ *
62
66
  * @example
63
67
  * ```tsx
64
68
  * // Basic usage
@@ -101,23 +105,22 @@ export function UploadStatusOverlay({
101
105
  renderSuccess,
102
106
  }: UploadStatusOverlayProps): React.JSX.Element | null {
103
107
  const theme = useHarkenTheme();
108
+ const { upload } = theme.components;
104
109
 
105
110
  // Merge labels with defaults
106
111
  const resolvedLabels: Required<UploadStatusLabels> = {
107
- retry: labels?.retry ?? 'Retry',
108
- remove: labels?.remove ?? 'Remove',
109
- cancel: labels?.cancel ?? 'Cancel',
110
- confirming: labels?.confirming ?? 'Confirming...',
111
- waiting: labels?.waiting ?? 'Waiting...',
112
- uploadFailed: labels?.uploadFailed ?? 'Upload failed',
112
+ retry: labels?.retry ?? "Retry",
113
+ remove: labels?.remove ?? "Remove",
114
+ cancel: labels?.cancel ?? "Cancel",
115
+ confirming: labels?.confirming ?? "Confirming...",
116
+ waiting: labels?.waiting ?? "Waiting...",
117
+ uploadFailed: labels?.uploadFailed ?? "Upload failed",
113
118
  };
114
119
 
115
120
  // Completed state - just show a subtle checkmark
116
121
  if (phase === UploadPhase.COMPLETED) {
117
122
  if (renderSuccess) {
118
- return (
119
- <View style={[styles.overlay, style]}>{renderSuccess(onRemove)}</View>
120
- );
123
+ return <View style={[styles.overlay, style]}>{renderSuccess(onRemove)}</View>;
121
124
  }
122
125
 
123
126
  return (
@@ -126,12 +129,12 @@ export function UploadStatusOverlay({
126
129
  style={[
127
130
  styles.badge,
128
131
  {
129
- backgroundColor: theme.colors.success,
132
+ backgroundColor: upload.badgeSuccess,
130
133
  borderRadius: theme.radii.full,
131
134
  },
132
135
  ]}
133
136
  >
134
- <ThemedText style={styles.badgeIcon}>✓</ThemedText>
137
+ <ThemedText style={[styles.badgeIcon, { color: upload.text }]}>✓</ThemedText>
135
138
  </View>
136
139
  {onRemove && (
137
140
  <Pressable
@@ -157,9 +160,7 @@ export function UploadStatusOverlay({
157
160
 
158
161
  if (renderError) {
159
162
  return (
160
- <View style={[styles.overlay, style]}>
161
- {renderError(errorMessage, onRetry, onRemove)}
162
- </View>
163
+ <View style={[styles.overlay, style]}>{renderError(errorMessage, onRetry, onRemove)}</View>
163
164
  );
164
165
  }
165
166
 
@@ -168,12 +169,12 @@ export function UploadStatusOverlay({
168
169
  style={[
169
170
  styles.overlay,
170
171
  styles.fullOverlay,
171
- { backgroundColor: theme.colors.overlayDark },
172
+ { backgroundColor: upload.overlayError },
172
173
  style,
173
174
  ]}
174
175
  >
175
176
  <ThemedText style={styles.errorIcon}>⚠️</ThemedText>
176
- <ThemedText style={styles.errorText} numberOfLines={2}>
177
+ <ThemedText style={[styles.errorText, { color: upload.text }]} numberOfLines={2}>
177
178
  {errorMessage}
178
179
  </ThemedText>
179
180
  <View style={styles.buttonRow}>
@@ -188,7 +189,7 @@ export function UploadStatusOverlay({
188
189
  },
189
190
  ]}
190
191
  >
191
- <ThemedText style={styles.actionButtonText}>
192
+ <ThemedText style={[styles.actionButtonText, { color: upload.text }]}>
192
193
  {resolvedLabels.retry}
193
194
  </ThemedText>
194
195
  </Pressable>
@@ -204,7 +205,7 @@ export function UploadStatusOverlay({
204
205
  },
205
206
  ]}
206
207
  >
207
- <ThemedText style={styles.actionButtonText}>
208
+ <ThemedText style={[styles.actionButtonText, { color: upload.text }]}>
208
209
  {resolvedLabels.remove}
209
210
  </ThemedText>
210
211
  </Pressable>
@@ -221,12 +222,7 @@ export function UploadStatusOverlay({
221
222
  if (renderProgress) {
222
223
  return (
223
224
  <View
224
- style={[
225
- styles.overlay,
226
- styles.fullOverlay,
227
- { backgroundColor: theme.colors.overlay },
228
- style,
229
- ]}
225
+ style={[styles.overlay, styles.fullOverlay, { backgroundColor: upload.overlay }, style]}
230
226
  >
231
227
  {renderProgress(progress)}
232
228
  </View>
@@ -235,19 +231,16 @@ export function UploadStatusOverlay({
235
231
 
236
232
  return (
237
233
  <View
238
- style={[
239
- styles.overlay,
240
- styles.fullOverlay,
241
- { backgroundColor: theme.colors.overlay },
242
- style,
243
- ]}
234
+ style={[styles.overlay, styles.fullOverlay, { backgroundColor: upload.overlay }, style]}
244
235
  >
245
- <ThemedText style={styles.progressText}>{progressPercent}%</ThemedText>
236
+ <ThemedText style={[styles.progressText, { color: upload.text }]}>
237
+ {progressPercent}%
238
+ </ThemedText>
246
239
  <View
247
240
  style={[
248
241
  styles.progressBarContainer,
249
242
  {
250
- backgroundColor: 'rgba(255,255,255,0.3)',
243
+ backgroundColor: upload.progressTrack,
251
244
  borderRadius: theme.radii.sm,
252
245
  },
253
246
  ]}
@@ -257,7 +250,7 @@ export function UploadStatusOverlay({
257
250
  styles.progressBarFill,
258
251
  {
259
252
  width: `${progressPercent}%`,
260
- backgroundColor: theme.colors.primary,
253
+ backgroundColor: upload.progressFill,
261
254
  borderRadius: theme.radii.sm,
262
255
  },
263
256
  ]}
@@ -269,11 +262,12 @@ export function UploadStatusOverlay({
269
262
  style={[
270
263
  styles.cancelButton,
271
264
  {
265
+ backgroundColor: upload.progressTrack,
272
266
  borderRadius: theme.radii.sm,
273
267
  },
274
268
  ]}
275
269
  >
276
- <ThemedText style={styles.cancelText}>
270
+ <ThemedText style={[styles.cancelText, { color: upload.text }]}>
277
271
  {resolvedLabels.cancel}
278
272
  </ThemedText>
279
273
  </Pressable>
@@ -286,15 +280,10 @@ export function UploadStatusOverlay({
286
280
  if (phase === UploadPhase.CONFIRMING) {
287
281
  return (
288
282
  <View
289
- style={[
290
- styles.overlay,
291
- styles.fullOverlay,
292
- { backgroundColor: theme.colors.overlay },
293
- style,
294
- ]}
283
+ style={[styles.overlay, styles.fullOverlay, { backgroundColor: upload.overlay }, style]}
295
284
  >
296
- <ActivityIndicator color="#fff" size="small" />
297
- <ThemedText style={styles.confirmingText}>
285
+ <ActivityIndicator color={upload.text} size="small" />
286
+ <ThemedText style={[styles.confirmingText, { color: upload.text }]}>
298
287
  {resolvedLabels.confirming}
299
288
  </ThemedText>
300
289
  </View>
@@ -305,15 +294,10 @@ export function UploadStatusOverlay({
305
294
  if (phase === UploadPhase.QUEUED) {
306
295
  return (
307
296
  <View
308
- style={[
309
- styles.overlay,
310
- styles.fullOverlay,
311
- { backgroundColor: theme.colors.overlay },
312
- style,
313
- ]}
297
+ style={[styles.overlay, styles.fullOverlay, { backgroundColor: upload.overlay }, style]}
314
298
  >
315
- <ActivityIndicator color="#fff" size="small" />
316
- <ThemedText style={styles.queuedText}>
299
+ <ActivityIndicator color={upload.text} size="small" />
300
+ <ThemedText style={[styles.queuedText, { color: upload.text }]}>
317
301
  {resolvedLabels.waiting}
318
302
  </ThemedText>
319
303
  {onRemove && (
@@ -322,11 +306,12 @@ export function UploadStatusOverlay({
322
306
  style={[
323
307
  styles.cancelButton,
324
308
  {
309
+ backgroundColor: upload.progressTrack,
325
310
  borderRadius: theme.radii.sm,
326
311
  },
327
312
  ]}
328
313
  >
329
- <ThemedText style={styles.cancelText}>
314
+ <ThemedText style={[styles.cancelText, { color: upload.text }]}>
330
315
  {resolvedLabels.cancel}
331
316
  </ThemedText>
332
317
  </Pressable>
@@ -343,33 +328,32 @@ const styles = StyleSheet.create({
343
328
  ...StyleSheet.absoluteFillObject,
344
329
  },
345
330
  fullOverlay: {
346
- alignItems: 'center',
347
- justifyContent: 'center',
331
+ alignItems: "center",
332
+ justifyContent: "center",
348
333
  padding: 8,
349
334
  },
350
335
  completedOverlay: {
351
- alignItems: 'flex-end',
352
- justifyContent: 'space-between',
353
- flexDirection: 'row',
336
+ alignItems: "flex-end",
337
+ justifyContent: "space-between",
338
+ flexDirection: "row",
354
339
  padding: 4,
355
340
  },
356
341
  badge: {
357
342
  width: 24,
358
343
  height: 24,
359
- alignItems: 'center',
360
- justifyContent: 'center',
344
+ alignItems: "center",
345
+ justifyContent: "center",
361
346
  },
362
347
  badgeIcon: {
363
- color: '#fff',
364
348
  fontSize: 14,
365
- fontWeight: '700',
349
+ fontWeight: "700",
366
350
  },
367
351
  removeButton: {
368
352
  width: 24,
369
353
  height: 24,
370
- alignItems: 'center',
371
- justifyContent: 'center',
372
- shadowColor: '#000',
354
+ alignItems: "center",
355
+ justifyContent: "center",
356
+ shadowColor: "#000",
373
357
  shadowOffset: { width: 0, height: 1 },
374
358
  shadowOpacity: 0.2,
375
359
  shadowRadius: 2,
@@ -377,40 +361,35 @@ const styles = StyleSheet.create({
377
361
  },
378
362
  removeIcon: {
379
363
  fontSize: 18,
380
- fontWeight: '700',
364
+ fontWeight: "700",
381
365
  lineHeight: 20,
382
366
  },
383
367
  progressText: {
384
- color: '#fff',
385
368
  fontSize: 16,
386
- fontWeight: '600',
369
+ fontWeight: "600",
387
370
  marginBottom: 8,
388
371
  },
389
372
  progressBarContainer: {
390
- width: '80%',
373
+ width: "80%",
391
374
  height: 6,
392
- overflow: 'hidden',
375
+ overflow: "hidden",
393
376
  },
394
377
  progressBarFill: {
395
- height: '100%',
378
+ height: "100%",
396
379
  },
397
380
  cancelButton: {
398
381
  marginTop: 8,
399
382
  paddingHorizontal: 12,
400
383
  paddingVertical: 4,
401
- backgroundColor: 'rgba(255,255,255,0.2)',
402
384
  },
403
385
  cancelText: {
404
- color: '#fff',
405
386
  fontSize: 12,
406
387
  },
407
388
  confirmingText: {
408
- color: '#fff',
409
389
  fontSize: 12,
410
390
  marginTop: 4,
411
391
  },
412
392
  queuedText: {
413
- color: '#fff',
414
393
  fontSize: 12,
415
394
  marginTop: 4,
416
395
  },
@@ -419,13 +398,12 @@ const styles = StyleSheet.create({
419
398
  marginBottom: 4,
420
399
  },
421
400
  errorText: {
422
- color: '#fff',
423
401
  fontSize: 11,
424
- textAlign: 'center',
402
+ textAlign: "center",
425
403
  marginBottom: 8,
426
404
  },
427
405
  buttonRow: {
428
- flexDirection: 'row',
406
+ flexDirection: "row",
429
407
  gap: 8,
430
408
  },
431
409
  actionButton: {
@@ -433,8 +411,7 @@ const styles = StyleSheet.create({
433
411
  paddingVertical: 6,
434
412
  },
435
413
  actionButtonText: {
436
- color: '#fff',
437
414
  fontSize: 12,
438
- fontWeight: '600',
415
+ fontWeight: "600",
439
416
  },
440
417
  });
@@ -1,39 +1,36 @@
1
1
  // Base themed components
2
- export { ThemedText } from './ThemedText';
3
- export type { ThemedTextProps, TextVariant } from './ThemedText';
2
+ export { ThemedText } from "./ThemedText";
3
+ export type { ThemedTextProps, TextVariant } from "./ThemedText";
4
4
 
5
- export { ThemedTextInput } from './ThemedTextInput';
6
- export type { ThemedTextInputProps } from './ThemedTextInput';
5
+ export { ThemedTextInput } from "./ThemedTextInput";
6
+ export type { ThemedTextInputProps } from "./ThemedTextInput";
7
7
 
8
- export { ThemedButton } from './ThemedButton';
9
- export type { ThemedButtonProps, ButtonVariant } from './ThemedButton';
8
+ export { ThemedButton } from "./ThemedButton";
9
+ export type { ThemedButtonProps, ButtonVariant } from "./ThemedButton";
10
10
 
11
11
  // Feedback components
12
- export { CategorySelector, DEFAULT_CATEGORIES } from './CategorySelector';
13
- export type { CategorySelectorProps, CategoryOption } from './CategorySelector';
12
+ export { CategorySelector, DEFAULT_CATEGORIES } from "./CategorySelector";
13
+ export type { CategorySelectorProps, CategoryOption } from "./CategorySelector";
14
14
 
15
- export { FeedbackForm } from './FeedbackForm';
16
- export type { FeedbackFormProps, FeedbackFormData } from './FeedbackForm';
15
+ export { FeedbackForm } from "./FeedbackForm";
16
+ export type { FeedbackFormProps, FeedbackFormData } from "./FeedbackForm";
17
17
 
18
18
  // Note: FeedbackSheet is exported from the main entry point (comes from attachments module)
19
19
  // to provide full attachment support by default.
20
20
 
21
21
  // Attachment components
22
- export { AttachmentPicker } from './AttachmentPicker';
22
+ export { AttachmentPicker } from "./AttachmentPicker";
23
23
  export type {
24
24
  AttachmentPickerProps,
25
25
  AttachmentSource,
26
26
  PickerOptionConfig,
27
- } from './AttachmentPicker';
27
+ } from "./AttachmentPicker";
28
28
 
29
- export { UploadStatusOverlay } from './UploadStatusOverlay';
30
- export type {
31
- UploadStatusOverlayProps,
32
- UploadStatusLabels,
33
- } from './UploadStatusOverlay';
29
+ export { UploadStatusOverlay } from "./UploadStatusOverlay";
30
+ export type { UploadStatusOverlayProps, UploadStatusLabels } from "./UploadStatusOverlay";
34
31
 
35
- export { AttachmentPreview } from './AttachmentPreview';
36
- export type { AttachmentPreviewProps } from './AttachmentPreview';
32
+ export { AttachmentPreview } from "./AttachmentPreview";
33
+ export type { AttachmentPreviewProps } from "./AttachmentPreview";
37
34
 
38
- export { AttachmentGrid } from './AttachmentGrid';
39
- export type { AttachmentGridProps } from './AttachmentGrid';
35
+ export { AttachmentGrid } from "./AttachmentGrid";
36
+ export type { AttachmentGridProps } from "./AttachmentGrid";
@@ -1,17 +1,17 @@
1
- import React, { createContext, useMemo } from 'react';
2
- import { useColorScheme } from 'react-native';
3
- import type { HarkenTheme, ThemeMode } from '../theme';
4
- import { lightTheme, darkTheme, createTheme } from '../theme';
5
- import type { HarkenConfig, HarkenProviderProps } from '../types';
6
- import { IdentityStore, createDefaultStorage } from '../storage';
7
- import { HarkenClient } from '../api/client';
1
+ import React, { createContext, useMemo } from "react";
2
+ import { useColorScheme } from "react-native";
3
+ import type { ResolvedHarkenTheme, ThemeMode } from "../theme";
4
+ import { lightTheme, darkTheme, resolveTheme } from "../theme";
5
+ import type { HarkenConfig, HarkenProviderProps } from "../types";
6
+ import { IdentityStore, createDefaultStorage } from "../storage";
7
+ import { HarkenClient } from "../api/client";
8
8
 
9
9
  /**
10
10
  * Context value provided by HarkenProvider.
11
11
  */
12
12
  export interface HarkenContextValue {
13
- /** The resolved theme based on mode and overrides */
14
- theme: HarkenTheme;
13
+ /** The resolved theme with all fallbacks applied */
14
+ theme: ResolvedHarkenTheme;
15
15
  /** Current theme mode */
16
16
  themeMode: ThemeMode;
17
17
  /** Whether dark mode is currently active */
@@ -52,20 +52,25 @@ export const HarkenContext = createContext<HarkenContextValue | null>(null);
52
52
  *
53
53
  * @example
54
54
  * ```tsx
55
- * // With custom storage implementation
56
- * import { HarkenProvider, createSecureStoreAdapter } from '@harkenapp/sdk-react-native';
57
- * import * as SecureStore from 'expo-secure-store';
58
- *
59
- * const storage = createSecureStoreAdapter(SecureStore);
60
- *
61
- * <HarkenProvider config={{ publishableKey: 'pk_live_xxxx' }} storage={storage}>
62
- * <YourApp />
55
+ * // With custom theme for modal embedding
56
+ * <HarkenProvider
57
+ * config={{ publishableKey: 'pk_live_xxxx' }}
58
+ * themeMode="dark"
59
+ * darkTheme={{
60
+ * colors: {
61
+ * surface: '#2d2d2d',
62
+ * chipBackground: '#3d3d3d',
63
+ * formBackground: 'transparent',
64
+ * },
65
+ * }}
66
+ * >
67
+ * <FeedbackSheet layout="auto" />
63
68
  * </HarkenProvider>
64
69
  * ```
65
70
  */
66
71
  export function HarkenProvider({
67
72
  config,
68
- themeMode = 'system',
73
+ themeMode = "system",
69
74
  lightTheme: lightOverrides,
70
75
  darkTheme: darkOverrides,
71
76
  storage,
@@ -76,17 +81,17 @@ export function HarkenProvider({
76
81
 
77
82
  // Determine if dark mode should be active
78
83
  const isDarkMode = useMemo(() => {
79
- if (themeMode === 'dark') return true;
80
- if (themeMode === 'light') return false;
84
+ if (themeMode === "dark") return true;
85
+ if (themeMode === "light") return false;
81
86
  // 'system' mode - follow device preference
82
- return systemColorScheme === 'dark';
87
+ return systemColorScheme === "dark";
83
88
  }, [themeMode, systemColorScheme]);
84
89
 
85
- // Build the resolved theme
90
+ // Build the resolved theme using the centralized resolver
86
91
  const theme = useMemo(() => {
87
92
  const baseTheme = isDarkMode ? darkTheme : lightTheme;
88
93
  const overrides = isDarkMode ? darkOverrides : lightOverrides;
89
- return createTheme(baseTheme, overrides);
94
+ return resolveTheme(baseTheme, overrides);
90
95
  }, [isDarkMode, lightOverrides, darkOverrides]);
91
96
 
92
97
  // Create identity store (memoized to persist across re-renders)
@@ -121,9 +126,5 @@ export function HarkenProvider({
121
126
  [theme, themeMode, isDarkMode, config, identityStore, client]
122
127
  );
123
128
 
124
- return (
125
- <HarkenContext.Provider value={contextValue}>
126
- {children}
127
- </HarkenContext.Provider>
128
- );
129
+ return <HarkenContext.Provider value={contextValue}>{children}</HarkenContext.Provider>;
129
130
  }
@@ -1,2 +1,2 @@
1
- export { HarkenProvider, HarkenContext } from './HarkenContext';
2
- export type { HarkenContextValue } from './HarkenContext';
1
+ export { HarkenProvider, HarkenContext } from "./HarkenContext";
2
+ export type { HarkenContextValue } from "./HarkenContext";
@@ -1,7 +1,4 @@
1
- export {
2
- UploadPhase,
3
- DEFAULT_UPLOAD_RETRY_CONFIG,
4
- } from './upload-queue';
1
+ export { UploadPhase, DEFAULT_UPLOAD_RETRY_CONFIG } from "./upload-queue";
5
2
 
6
3
  export type {
7
4
  QueueItem,
@@ -9,4 +6,4 @@ export type {
9
6
  UploadProgress,
10
7
  PersistedQueue,
11
8
  UploadRetryConfig,
12
- } from './upload-queue';
9
+ } from "./upload-queue";
@@ -9,15 +9,15 @@
9
9
  */
10
10
  export enum UploadPhase {
11
11
  /** Waiting in queue to be processed */
12
- QUEUED = 'queued',
12
+ QUEUED = "queued",
13
13
  /** Currently uploading to storage */
14
- UPLOADING = 'uploading',
14
+ UPLOADING = "uploading",
15
15
  /** Upload complete, confirming with server */
16
- CONFIRMING = 'confirming',
16
+ CONFIRMING = "confirming",
17
17
  /** Successfully uploaded and confirmed */
18
- COMPLETED = 'completed',
18
+ COMPLETED = "completed",
19
19
  /** Failed after max retries */
20
- FAILED = 'failed',
20
+ FAILED = "failed",
21
21
  }
22
22
 
23
23
  /**