@harkenapp/sdk-react-native 0.0.1-alpha.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 (235) hide show
  1. package/README.md +67 -0
  2. package/app.plugin.cjs +135 -0
  3. package/app.plugin.js +1 -0
  4. package/dist/api/client.d.ts +67 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +163 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/api/errors.d.ts +46 -0
  9. package/dist/api/errors.d.ts.map +1 -0
  10. package/dist/api/errors.js +72 -0
  11. package/dist/api/errors.js.map +1 -0
  12. package/dist/api/index.d.ts +7 -0
  13. package/dist/api/index.d.ts.map +1 -0
  14. package/dist/api/index.js +20 -0
  15. package/dist/api/index.js.map +1 -0
  16. package/dist/api/retry.d.ts +29 -0
  17. package/dist/api/retry.d.ts.map +1 -0
  18. package/dist/api/retry.js +74 -0
  19. package/dist/api/retry.js.map +1 -0
  20. package/dist/attachments/FeedbackSheet.d.ts +88 -0
  21. package/dist/attachments/FeedbackSheet.d.ts.map +1 -0
  22. package/dist/attachments/FeedbackSheet.js +250 -0
  23. package/dist/attachments/FeedbackSheet.js.map +1 -0
  24. package/dist/attachments/index.d.ts +20 -0
  25. package/dist/attachments/index.d.ts.map +1 -0
  26. package/dist/attachments/index.js +40 -0
  27. package/dist/attachments/index.js.map +1 -0
  28. package/dist/components/AttachmentGrid.d.ts +94 -0
  29. package/dist/components/AttachmentGrid.d.ts.map +1 -0
  30. package/dist/components/AttachmentGrid.js +132 -0
  31. package/dist/components/AttachmentGrid.js.map +1 -0
  32. package/dist/components/AttachmentPicker.d.ts +98 -0
  33. package/dist/components/AttachmentPicker.d.ts.map +1 -0
  34. package/dist/components/AttachmentPicker.js +297 -0
  35. package/dist/components/AttachmentPicker.js.map +1 -0
  36. package/dist/components/AttachmentPreview.d.ts +78 -0
  37. package/dist/components/AttachmentPreview.d.ts.map +1 -0
  38. package/dist/components/AttachmentPreview.js +133 -0
  39. package/dist/components/AttachmentPreview.js.map +1 -0
  40. package/dist/components/CategorySelector.d.ts +77 -0
  41. package/dist/components/CategorySelector.d.ts.map +1 -0
  42. package/dist/components/CategorySelector.js +117 -0
  43. package/dist/components/CategorySelector.js.map +1 -0
  44. package/dist/components/FeedbackForm.d.ts +50 -0
  45. package/dist/components/FeedbackForm.d.ts.map +1 -0
  46. package/dist/components/FeedbackForm.js +141 -0
  47. package/dist/components/FeedbackForm.js.map +1 -0
  48. package/dist/components/FeedbackSheet.d.ts +75 -0
  49. package/dist/components/FeedbackSheet.d.ts.map +1 -0
  50. package/dist/components/FeedbackSheet.js +215 -0
  51. package/dist/components/FeedbackSheet.js.map +1 -0
  52. package/dist/components/ThemedButton.d.ts +23 -0
  53. package/dist/components/ThemedButton.d.ts.map +1 -0
  54. package/dist/components/ThemedButton.js +77 -0
  55. package/dist/components/ThemedButton.js.map +1 -0
  56. package/dist/components/ThemedText.d.ts +16 -0
  57. package/dist/components/ThemedText.d.ts.map +1 -0
  58. package/dist/components/ThemedText.js +44 -0
  59. package/dist/components/ThemedText.js.map +1 -0
  60. package/dist/components/ThemedTextInput.d.ts +13 -0
  61. package/dist/components/ThemedTextInput.d.ts.map +1 -0
  62. package/dist/components/ThemedTextInput.js +76 -0
  63. package/dist/components/ThemedTextInput.js.map +1 -0
  64. package/dist/components/UploadStatusOverlay.d.ts +82 -0
  65. package/dist/components/UploadStatusOverlay.d.ts.map +1 -0
  66. package/dist/components/UploadStatusOverlay.js +319 -0
  67. package/dist/components/UploadStatusOverlay.js.map +1 -0
  68. package/dist/components/index.d.ts +19 -0
  69. package/dist/components/index.d.ts.map +1 -0
  70. package/dist/components/index.js +28 -0
  71. package/dist/components/index.js.map +1 -0
  72. package/dist/context/HarkenContext.d.ts +62 -0
  73. package/dist/context/HarkenContext.d.ts.map +1 -0
  74. package/dist/context/HarkenContext.js +128 -0
  75. package/dist/context/HarkenContext.js.map +1 -0
  76. package/dist/context/index.d.ts +3 -0
  77. package/dist/context/index.d.ts.map +1 -0
  78. package/dist/context/index.js +7 -0
  79. package/dist/context/index.js.map +1 -0
  80. package/dist/domain/index.d.ts +3 -0
  81. package/dist/domain/index.d.ts.map +1 -0
  82. package/dist/domain/index.js +7 -0
  83. package/dist/domain/index.js.map +1 -0
  84. package/dist/domain/upload-queue.d.ts +116 -0
  85. package/dist/domain/upload-queue.d.ts.map +1 -0
  86. package/dist/domain/upload-queue.js +34 -0
  87. package/dist/domain/upload-queue.js.map +1 -0
  88. package/dist/hooks/index.d.ts +6 -0
  89. package/dist/hooks/index.d.ts.map +1 -0
  90. package/dist/hooks/index.js +16 -0
  91. package/dist/hooks/index.js.map +1 -0
  92. package/dist/hooks/useAnonymousId.d.ts +28 -0
  93. package/dist/hooks/useAnonymousId.d.ts.map +1 -0
  94. package/dist/hooks/useAnonymousId.js +59 -0
  95. package/dist/hooks/useAnonymousId.js.map +1 -0
  96. package/dist/hooks/useAttachmentPicker.d.ts +84 -0
  97. package/dist/hooks/useAttachmentPicker.d.ts.map +1 -0
  98. package/dist/hooks/useAttachmentPicker.js +181 -0
  99. package/dist/hooks/useAttachmentPicker.js.map +1 -0
  100. package/dist/hooks/useAttachmentStatus.d.ts +51 -0
  101. package/dist/hooks/useAttachmentStatus.d.ts.map +1 -0
  102. package/dist/hooks/useAttachmentStatus.js +69 -0
  103. package/dist/hooks/useAttachmentStatus.js.map +1 -0
  104. package/dist/hooks/useAttachmentUpload.d.ts +101 -0
  105. package/dist/hooks/useAttachmentUpload.d.ts.map +1 -0
  106. package/dist/hooks/useAttachmentUpload.js +293 -0
  107. package/dist/hooks/useAttachmentUpload.js.map +1 -0
  108. package/dist/hooks/useFeedback.d.ts +55 -0
  109. package/dist/hooks/useFeedback.d.ts.map +1 -0
  110. package/dist/hooks/useFeedback.js +96 -0
  111. package/dist/hooks/useFeedback.js.map +1 -0
  112. package/dist/hooks/useHarkenContext.d.ts +25 -0
  113. package/dist/hooks/useHarkenContext.d.ts.map +1 -0
  114. package/dist/hooks/useHarkenContext.js +35 -0
  115. package/dist/hooks/useHarkenContext.js.map +1 -0
  116. package/dist/hooks/useHarkenTheme.d.ts +26 -0
  117. package/dist/hooks/useHarkenTheme.d.ts.map +1 -0
  118. package/dist/hooks/useHarkenTheme.js +36 -0
  119. package/dist/hooks/useHarkenTheme.js.map +1 -0
  120. package/dist/index.d.ts +49 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +91 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/services/index.d.ts +4 -0
  125. package/dist/services/index.d.ts.map +1 -0
  126. package/dist/services/index.js +9 -0
  127. package/dist/services/index.js.map +1 -0
  128. package/dist/services/uploadQueueService.d.ts +193 -0
  129. package/dist/services/uploadQueueService.d.ts.map +1 -0
  130. package/dist/services/uploadQueueService.js +623 -0
  131. package/dist/services/uploadQueueService.js.map +1 -0
  132. package/dist/services/uploadQueueStorage.d.ts +30 -0
  133. package/dist/services/uploadQueueStorage.d.ts.map +1 -0
  134. package/dist/services/uploadQueueStorage.js +77 -0
  135. package/dist/services/uploadQueueStorage.js.map +1 -0
  136. package/dist/storage/IdentityStore.d.ts +38 -0
  137. package/dist/storage/IdentityStore.d.ts.map +1 -0
  138. package/dist/storage/IdentityStore.js +83 -0
  139. package/dist/storage/IdentityStore.js.map +1 -0
  140. package/dist/storage/SecureStoreAdapter.d.ts +28 -0
  141. package/dist/storage/SecureStoreAdapter.d.ts.map +1 -0
  142. package/dist/storage/SecureStoreAdapter.js +52 -0
  143. package/dist/storage/SecureStoreAdapter.js.map +1 -0
  144. package/dist/storage/defaultStorage.d.ts +20 -0
  145. package/dist/storage/defaultStorage.d.ts.map +1 -0
  146. package/dist/storage/defaultStorage.js +131 -0
  147. package/dist/storage/defaultStorage.js.map +1 -0
  148. package/dist/storage/index.d.ts +6 -0
  149. package/dist/storage/index.d.ts.map +1 -0
  150. package/dist/storage/index.js +13 -0
  151. package/dist/storage/index.js.map +1 -0
  152. package/dist/storage/types.d.ts +32 -0
  153. package/dist/storage/types.d.ts.map +1 -0
  154. package/dist/storage/types.js +11 -0
  155. package/dist/storage/types.js.map +1 -0
  156. package/dist/theme/defaults.d.ts +43 -0
  157. package/dist/theme/defaults.d.ts.map +1 -0
  158. package/dist/theme/defaults.js +128 -0
  159. package/dist/theme/defaults.js.map +1 -0
  160. package/dist/theme/index.d.ts +3 -0
  161. package/dist/theme/index.d.ts.map +1 -0
  162. package/dist/theme/index.js +14 -0
  163. package/dist/theme/index.js.map +1 -0
  164. package/dist/theme/types.d.ts +136 -0
  165. package/dist/theme/types.d.ts.map +1 -0
  166. package/dist/theme/types.js +3 -0
  167. package/dist/theme/types.js.map +1 -0
  168. package/dist/types/config.d.ts +100 -0
  169. package/dist/types/config.d.ts.map +1 -0
  170. package/dist/types/config.js +3 -0
  171. package/dist/types/config.js.map +1 -0
  172. package/dist/types/index.d.ts +3 -0
  173. package/dist/types/index.d.ts.map +1 -0
  174. package/dist/types/index.js +3 -0
  175. package/dist/types/index.js.map +1 -0
  176. package/dist/types/openapi.d.ts +601 -0
  177. package/dist/types/openapi.d.ts.map +1 -0
  178. package/dist/types/openapi.js +7 -0
  179. package/dist/types/openapi.js.map +1 -0
  180. package/dist/utils/index.d.ts +2 -0
  181. package/dist/utils/index.d.ts.map +1 -0
  182. package/dist/utils/index.js +6 -0
  183. package/dist/utils/index.js.map +1 -0
  184. package/dist/utils/uuid.d.ts +10 -0
  185. package/dist/utils/uuid.d.ts.map +1 -0
  186. package/dist/utils/uuid.js +60 -0
  187. package/dist/utils/uuid.js.map +1 -0
  188. package/package.json +124 -0
  189. package/src/@types/expo-file-system-legacy.d.ts +13 -0
  190. package/src/api/client.ts +250 -0
  191. package/src/api/errors.ts +84 -0
  192. package/src/api/index.ts +15 -0
  193. package/src/api/retry.ts +99 -0
  194. package/src/attachments/FeedbackSheet.tsx +400 -0
  195. package/src/attachments/index.ts +70 -0
  196. package/src/components/AttachmentGrid.tsx +247 -0
  197. package/src/components/AttachmentPicker.tsx +391 -0
  198. package/src/components/AttachmentPreview.tsx +210 -0
  199. package/src/components/CategorySelector.tsx +174 -0
  200. package/src/components/FeedbackForm.tsx +216 -0
  201. package/src/components/FeedbackSheet.tsx +321 -0
  202. package/src/components/ThemedButton.tsx +127 -0
  203. package/src/components/ThemedText.tsx +65 -0
  204. package/src/components/ThemedTextInput.tsx +65 -0
  205. package/src/components/UploadStatusOverlay.tsx +440 -0
  206. package/src/components/index.ts +39 -0
  207. package/src/context/HarkenContext.tsx +129 -0
  208. package/src/context/index.ts +2 -0
  209. package/src/domain/index.ts +12 -0
  210. package/src/domain/upload-queue.ts +131 -0
  211. package/src/hooks/index.ts +10 -0
  212. package/src/hooks/useAnonymousId.ts +68 -0
  213. package/src/hooks/useAttachmentPicker.ts +243 -0
  214. package/src/hooks/useAttachmentStatus.ts +86 -0
  215. package/src/hooks/useAttachmentUpload.ts +370 -0
  216. package/src/hooks/useFeedback.ts +139 -0
  217. package/src/hooks/useHarkenContext.ts +35 -0
  218. package/src/hooks/useHarkenTheme.ts +36 -0
  219. package/src/index.ts +168 -0
  220. package/src/services/index.ts +11 -0
  221. package/src/services/uploadQueueService.ts +727 -0
  222. package/src/services/uploadQueueStorage.ts +78 -0
  223. package/src/storage/IdentityStore.ts +89 -0
  224. package/src/storage/SecureStoreAdapter.ts +59 -0
  225. package/src/storage/defaultStorage.ts +109 -0
  226. package/src/storage/index.ts +5 -0
  227. package/src/storage/types.ts +34 -0
  228. package/src/theme/defaults.ts +151 -0
  229. package/src/theme/index.ts +23 -0
  230. package/src/theme/types.ts +157 -0
  231. package/src/types/config.ts +112 -0
  232. package/src/types/index.ts +10 -0
  233. package/src/types/openapi.ts +601 -0
  234. package/src/utils/index.ts +1 -0
  235. package/src/utils/uuid.ts +77 -0
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * Hook for subscribing to a single attachment's upload status.
4
+ *
5
+ * Useful for components that display a single attachment with progress indicator.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.useAttachmentStatus = useAttachmentStatus;
9
+ const react_1 = require("react");
10
+ const services_1 = require("../services");
11
+ /**
12
+ * Hook to subscribe to a single attachment's upload status.
13
+ *
14
+ * Returns null if the attachment is not found in the queue.
15
+ *
16
+ * @param attachmentId - Server-assigned attachment ID
17
+ * @returns Current status or null if not found
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * function AttachmentThumbnail({ attachmentId, uri }: Props) {
22
+ * const status = useAttachmentStatus(attachmentId);
23
+ *
24
+ * if (!status) return null;
25
+ *
26
+ * return (
27
+ * <View>
28
+ * <Image source={{ uri }} />
29
+ * {status.phase === 'uploading' && (
30
+ * <ProgressBar progress={status.progress} />
31
+ * )}
32
+ * {status.phase === 'failed' && (
33
+ * <Text>Error: {status.error}</Text>
34
+ * )}
35
+ * {status.phase === 'completed' && (
36
+ * <Icon name="checkmark" />
37
+ * )}
38
+ * </View>
39
+ * );
40
+ * }
41
+ * ```
42
+ */
43
+ function useAttachmentStatus(attachmentId) {
44
+ const [status, setStatus] = (0, react_1.useState)(() => {
45
+ // Initialize with current state from queue
46
+ const item = services_1.uploadQueueService.getItemByAttachmentId(attachmentId);
47
+ if (!item)
48
+ return null;
49
+ return {
50
+ phase: item.phase,
51
+ progress: item.progress,
52
+ error: item.lastError,
53
+ };
54
+ });
55
+ (0, react_1.useEffect)(() => {
56
+ const unsubscribe = services_1.uploadQueueService.onProgress((progress) => {
57
+ if (progress.attachmentId !== attachmentId)
58
+ return;
59
+ setStatus({
60
+ phase: progress.phase,
61
+ progress: progress.progress,
62
+ error: progress.error,
63
+ });
64
+ });
65
+ return unsubscribe;
66
+ }, [attachmentId]);
67
+ return status;
68
+ }
69
+ //# sourceMappingURL=useAttachmentStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAttachmentStatus.js","sourceRoot":"","sources":["../../src/hooks/useAttachmentStatus.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAkDH,kDA+BC;AA/ED,iCAA4C;AAC5C,0CAAiD;AAejD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,mBAAmB,CACjC,YAAoB;IAEpB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAA0B,GAAG,EAAE;QACjE,2CAA2C;QAC3C,MAAM,IAAI,GAAG,6BAAkB,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,IAAI,CAAC,SAAS;SACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,6BAAkB,CAAC,UAAU,CAC/C,CAAC,QAAwB,EAAE,EAAE;YAC3B,IAAI,QAAQ,CAAC,YAAY,KAAK,YAAY;gBAAE,OAAO;YAEnD,SAAS,CAAC;gBACR,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;aACtB,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Hook for managing attachment uploads.
3
+ *
4
+ * Provides methods for picking images/documents and tracking upload progress.
5
+ * Uploads happen in background via the singleton uploadQueueService.
6
+ */
7
+ import { UploadPhase } from '../domain';
8
+ /**
9
+ * State for a single attachment.
10
+ */
11
+ export interface AttachmentState {
12
+ /** Server-assigned attachment ID */
13
+ attachmentId: string;
14
+ /** Local file URI for preview */
15
+ localUri: string;
16
+ /** Original filename */
17
+ fileName: string;
18
+ /** MIME type */
19
+ mimeType: string;
20
+ /** Current upload phase */
21
+ phase: UploadPhase;
22
+ /** Upload progress (0.0 - 1.0) */
23
+ progress: number;
24
+ /** Error message if failed */
25
+ error?: string;
26
+ }
27
+ /**
28
+ * Return type for useAttachmentUpload hook.
29
+ */
30
+ export interface UseAttachmentUploadResult {
31
+ /** All current attachments */
32
+ attachments: AttachmentState[];
33
+ /** Pick image from camera or library */
34
+ pickImage: (source: 'camera' | 'library') => Promise<AttachmentState | null>;
35
+ /** Pick document (images or PDFs) */
36
+ pickDocument: () => Promise<AttachmentState | null>;
37
+ /** Add attachment from existing local URI */
38
+ addAttachment: (params: {
39
+ uri: string;
40
+ mimeType: string;
41
+ fileName: string;
42
+ fileSize: number;
43
+ }) => Promise<AttachmentState>;
44
+ /** Retry a failed upload */
45
+ retryAttachment: (attachmentId: string) => Promise<void>;
46
+ /** Remove attachment (cancels if uploading) */
47
+ removeAttachment: (attachmentId: string) => Promise<void>;
48
+ /** Get all attachment IDs for feedback submission */
49
+ getAttachmentIds: () => string[];
50
+ /** True if any uploads are in progress */
51
+ hasActiveUploads: boolean;
52
+ /** Clear all completed attachments */
53
+ clearCompleted: () => void;
54
+ /** Clear all failed attachments */
55
+ clearFailed: () => void;
56
+ }
57
+ /**
58
+ * Hook for managing attachment uploads with background support.
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * function FeedbackForm() {
63
+ * const {
64
+ * attachments,
65
+ * pickImage,
66
+ * removeAttachment,
67
+ * getAttachmentIds,
68
+ * hasActiveUploads,
69
+ * } = useAttachmentUpload();
70
+ *
71
+ * const handleAddPhoto = async () => {
72
+ * await pickImage('library');
73
+ * };
74
+ *
75
+ * const handleSubmit = async () => {
76
+ * // Can submit even if uploads are still in progress!
77
+ * await submitFeedback({
78
+ * message: 'Bug report',
79
+ * attachmentIds: getAttachmentIds(),
80
+ * });
81
+ * };
82
+ *
83
+ * return (
84
+ * <View>
85
+ * {attachments.map(att => (
86
+ * <AttachmentPreview
87
+ * key={att.attachmentId}
88
+ * uri={att.localUri}
89
+ * progress={att.progress}
90
+ * phase={att.phase}
91
+ * />
92
+ * ))}
93
+ * <Button onPress={handleAddPhoto} title="Add Photo" />
94
+ * <Button onPress={handleSubmit} title="Submit" />
95
+ * </View>
96
+ * );
97
+ * }
98
+ * ```
99
+ */
100
+ export declare function useAttachmentUpload(): UseAttachmentUploadResult;
101
+ //# sourceMappingURL=useAttachmentUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAttachmentUpload.d.ts","sourceRoot":"","sources":["../../src/hooks/useAttachmentUpload.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,EAAE,WAAW,EAAkB,MAAM,WAAW,CAAC;AAGxD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,8BAA8B;IAC9B,WAAW,EAAE,eAAe,EAAE,CAAC;IAE/B,wCAAwC;IACxC,SAAS,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAE7E,qCAAqC;IACrC,YAAY,EAAE,MAAM,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAEpD,6CAA6C;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE;QACtB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAE/B,4BAA4B;IAC5B,eAAe,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD,+CAA+C;IAC/C,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,MAAM,EAAE,CAAC;IAEjC,0CAA0C;IAC1C,gBAAgB,EAAE,OAAO,CAAC;IAE1B,sCAAsC;IACtC,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,mCAAmC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,mBAAmB,IAAI,yBAAyB,CA2P/D"}
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ /**
3
+ * Hook for managing attachment uploads.
4
+ *
5
+ * Provides methods for picking images/documents and tracking upload progress.
6
+ * Uploads happen in background via the singleton uploadQueueService.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.useAttachmentUpload = useAttachmentUpload;
43
+ const react_1 = require("react");
44
+ const ImagePicker = __importStar(require("expo-image-picker"));
45
+ const DocumentPicker = __importStar(require("expo-document-picker"));
46
+ const FileSystem = __importStar(require("expo-file-system/legacy"));
47
+ const services_1 = require("../services");
48
+ const domain_1 = require("../domain");
49
+ const useHarkenContext_1 = require("./useHarkenContext");
50
+ /**
51
+ * Hook for managing attachment uploads with background support.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * function FeedbackForm() {
56
+ * const {
57
+ * attachments,
58
+ * pickImage,
59
+ * removeAttachment,
60
+ * getAttachmentIds,
61
+ * hasActiveUploads,
62
+ * } = useAttachmentUpload();
63
+ *
64
+ * const handleAddPhoto = async () => {
65
+ * await pickImage('library');
66
+ * };
67
+ *
68
+ * const handleSubmit = async () => {
69
+ * // Can submit even if uploads are still in progress!
70
+ * await submitFeedback({
71
+ * message: 'Bug report',
72
+ * attachmentIds: getAttachmentIds(),
73
+ * });
74
+ * };
75
+ *
76
+ * return (
77
+ * <View>
78
+ * {attachments.map(att => (
79
+ * <AttachmentPreview
80
+ * key={att.attachmentId}
81
+ * uri={att.localUri}
82
+ * progress={att.progress}
83
+ * phase={att.phase}
84
+ * />
85
+ * ))}
86
+ * <Button onPress={handleAddPhoto} title="Add Photo" />
87
+ * <Button onPress={handleSubmit} title="Submit" />
88
+ * </View>
89
+ * );
90
+ * }
91
+ * ```
92
+ */
93
+ function useAttachmentUpload() {
94
+ const { client, config } = (0, useHarkenContext_1.useHarkenContext)();
95
+ const [attachments, setAttachments] = (0, react_1.useState)(new Map());
96
+ // Track which attachment IDs this hook instance is managing
97
+ const attachmentIdsRef = (0, react_1.useRef)(new Set());
98
+ // Initialize upload queue service on first use
99
+ (0, react_1.useEffect)(() => {
100
+ if (!services_1.uploadQueueService.initialized) {
101
+ void services_1.uploadQueueService.initialize({
102
+ client,
103
+ debug: config.debug,
104
+ });
105
+ }
106
+ }, [client, config.debug]);
107
+ // Subscribe to progress updates from the queue service
108
+ (0, react_1.useEffect)(() => {
109
+ // Guard against service not being initialized
110
+ if (!services_1.uploadQueueService) {
111
+ return;
112
+ }
113
+ const unsubProgress = services_1.uploadQueueService.onProgress((progress) => {
114
+ // Only track attachments we added
115
+ if (!attachmentIdsRef.current.has(progress.attachmentId))
116
+ return;
117
+ setAttachments((prev) => {
118
+ const existing = prev.get(progress.attachmentId);
119
+ if (!existing)
120
+ return prev;
121
+ const next = new Map(prev);
122
+ next.set(progress.attachmentId, {
123
+ ...existing,
124
+ phase: progress.phase,
125
+ progress: progress.progress,
126
+ error: progress.error,
127
+ });
128
+ return next;
129
+ });
130
+ });
131
+ return () => {
132
+ unsubProgress();
133
+ };
134
+ }, []);
135
+ /**
136
+ * Add an attachment from a local URI.
137
+ */
138
+ const addAttachment = (0, react_1.useCallback)(async (params) => {
139
+ const { attachmentId } = await services_1.uploadQueueService.enqueue({
140
+ localUri: params.uri,
141
+ mimeType: params.mimeType,
142
+ fileName: params.fileName,
143
+ fileSize: params.fileSize,
144
+ });
145
+ const state = {
146
+ attachmentId,
147
+ localUri: params.uri,
148
+ fileName: params.fileName,
149
+ mimeType: params.mimeType,
150
+ phase: domain_1.UploadPhase.QUEUED,
151
+ progress: 0,
152
+ };
153
+ attachmentIdsRef.current.add(attachmentId);
154
+ setAttachments((prev) => new Map(prev).set(attachmentId, state));
155
+ return state;
156
+ }, []);
157
+ /**
158
+ * Pick an image from camera or photo library.
159
+ */
160
+ const pickImage = (0, react_1.useCallback)(async (source) => {
161
+ const options = {
162
+ mediaTypes: ['images'],
163
+ quality: 0.8,
164
+ };
165
+ const result = source === 'camera'
166
+ ? await ImagePicker.launchCameraAsync(options)
167
+ : await ImagePicker.launchImageLibraryAsync(options);
168
+ if (result.canceled || !result.assets[0]) {
169
+ return null;
170
+ }
171
+ const asset = result.assets[0];
172
+ const fileName = asset.fileName ?? `image_${Date.now()}.jpg`;
173
+ const mimeType = asset.mimeType ?? 'image/jpeg';
174
+ // Get file size - use asset.fileSize if available, otherwise query filesystem
175
+ let fileSize = asset.fileSize;
176
+ if (fileSize === undefined || fileSize === null) {
177
+ const fileInfo = await FileSystem.getInfoAsync(asset.uri);
178
+ fileSize = fileInfo.exists && fileInfo.size ? fileInfo.size : 0;
179
+ }
180
+ return addAttachment({
181
+ uri: asset.uri,
182
+ mimeType,
183
+ fileName,
184
+ fileSize,
185
+ });
186
+ }, [addAttachment]);
187
+ /**
188
+ * Pick a document (images or PDFs).
189
+ */
190
+ const pickDocument = (0, react_1.useCallback)(async () => {
191
+ const result = await DocumentPicker.getDocumentAsync({
192
+ type: ['image/*', 'application/pdf'],
193
+ copyToCacheDirectory: true,
194
+ });
195
+ if (result.canceled || !result.assets[0]) {
196
+ return null;
197
+ }
198
+ const asset = result.assets[0];
199
+ // Get file size - use asset.size if available, otherwise query filesystem
200
+ let fileSize = asset.size;
201
+ if (fileSize === undefined || fileSize === null) {
202
+ const fileInfo = await FileSystem.getInfoAsync(asset.uri);
203
+ fileSize = fileInfo.exists && fileInfo.size ? fileInfo.size : 0;
204
+ }
205
+ return addAttachment({
206
+ uri: asset.uri,
207
+ mimeType: asset.mimeType ?? 'application/octet-stream',
208
+ fileName: asset.name,
209
+ fileSize,
210
+ });
211
+ }, [addAttachment]);
212
+ /**
213
+ * Retry a failed attachment upload.
214
+ */
215
+ const retryAttachment = (0, react_1.useCallback)(async (attachmentId) => {
216
+ await services_1.uploadQueueService.retryItem(attachmentId);
217
+ }, []);
218
+ /**
219
+ * Remove an attachment (cancels upload if in progress).
220
+ */
221
+ const removeAttachment = (0, react_1.useCallback)(async (attachmentId) => {
222
+ await services_1.uploadQueueService.cancelItem(attachmentId);
223
+ attachmentIdsRef.current.delete(attachmentId);
224
+ setAttachments((prev) => {
225
+ const next = new Map(prev);
226
+ next.delete(attachmentId);
227
+ return next;
228
+ });
229
+ }, []);
230
+ /**
231
+ * Get all attachment IDs for feedback submission.
232
+ */
233
+ const getAttachmentIds = (0, react_1.useCallback)(() => {
234
+ return Array.from(attachments.values()).map((a) => a.attachmentId);
235
+ }, [attachments]);
236
+ /**
237
+ * Clear all completed attachments from both local state and queue service.
238
+ */
239
+ const clearCompleted = (0, react_1.useCallback)(() => {
240
+ // Clear from queue service (persisted storage)
241
+ void services_1.uploadQueueService.clearCompleted();
242
+ // Clear from local state
243
+ setAttachments((prev) => {
244
+ const next = new Map();
245
+ for (const [id, att] of prev) {
246
+ if (att.phase !== domain_1.UploadPhase.COMPLETED) {
247
+ next.set(id, att);
248
+ }
249
+ else {
250
+ attachmentIdsRef.current.delete(id);
251
+ }
252
+ }
253
+ return next;
254
+ });
255
+ }, []);
256
+ /**
257
+ * Clear all failed attachments from both local state and queue service.
258
+ */
259
+ const clearFailed = (0, react_1.useCallback)(() => {
260
+ // Clear from queue service (persisted storage)
261
+ void services_1.uploadQueueService.clearFailed();
262
+ // Clear from local state
263
+ setAttachments((prev) => {
264
+ const next = new Map();
265
+ for (const [id, att] of prev) {
266
+ if (att.phase !== domain_1.UploadPhase.FAILED) {
267
+ next.set(id, att);
268
+ }
269
+ else {
270
+ attachmentIdsRef.current.delete(id);
271
+ }
272
+ }
273
+ return next;
274
+ });
275
+ }, []);
276
+ // Compute whether any uploads are in progress
277
+ const hasActiveUploads = Array.from(attachments.values()).some((a) => a.phase === domain_1.UploadPhase.QUEUED ||
278
+ a.phase === domain_1.UploadPhase.UPLOADING ||
279
+ a.phase === domain_1.UploadPhase.CONFIRMING);
280
+ return {
281
+ attachments: Array.from(attachments.values()),
282
+ pickImage,
283
+ pickDocument,
284
+ addAttachment,
285
+ retryAttachment,
286
+ removeAttachment,
287
+ getAttachmentIds,
288
+ hasActiveUploads,
289
+ clearCompleted,
290
+ clearFailed,
291
+ };
292
+ }
293
+ //# sourceMappingURL=useAttachmentUpload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAttachmentUpload.js","sourceRoot":"","sources":["../../src/hooks/useAttachmentUpload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { components } from '../types/index.js';
2
+ import { HarkenApiError, HarkenNetworkError } from '../api/errors';
3
+ import type { FeedbackCategory, DeviceMetadata } from '../types';
4
+ type FeedbackSubmissionResponse = components['schemas']['FeedbackSubmissionResponse'];
5
+ export interface SubmitFeedbackParams {
6
+ /** Feedback message content */
7
+ message: string;
8
+ /** Feedback category */
9
+ category: FeedbackCategory;
10
+ /** Optional title/subject */
11
+ title?: string;
12
+ /** Additional device metadata (merged with auto-collected metadata) */
13
+ metadata?: Partial<DeviceMetadata>;
14
+ /** Attachment IDs from presigned uploads */
15
+ attachments?: string[];
16
+ }
17
+ export interface UseFeedbackResult {
18
+ /** Submit feedback to Harken */
19
+ submitFeedback: (params: SubmitFeedbackParams) => Promise<FeedbackSubmissionResponse>;
20
+ /** True while a submission is in progress */
21
+ isSubmitting: boolean;
22
+ /** Last error from submission attempt */
23
+ error: HarkenApiError | HarkenNetworkError | null;
24
+ /** Clear the error state */
25
+ clearError: () => void;
26
+ /** True if anonymous ID is still loading */
27
+ isInitializing: boolean;
28
+ }
29
+ /**
30
+ * Hook for submitting feedback through the Harken API.
31
+ *
32
+ * Automatically includes the anonymous ID and device metadata.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * function FeedbackScreen() {
37
+ * const { submitFeedback, isSubmitting, error } = useFeedback();
38
+ *
39
+ * const handleSubmit = async () => {
40
+ * try {
41
+ * await submitFeedback({
42
+ * message: 'Great app!',
43
+ * category: 'idea',
44
+ * });
45
+ * // Success!
46
+ * } catch (e) {
47
+ * // Error is also available in `error` state
48
+ * }
49
+ * };
50
+ * }
51
+ * ```
52
+ */
53
+ export declare function useFeedback(): UseFeedbackResult;
54
+ export {};
55
+ //# sourceMappingURL=useFeedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFeedback.d.ts","sourceRoot":"","sources":["../../src/hooks/useFeedback.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEjE,KAAK,0BAA0B,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,4BAA4B,CAAC,CAAC;AAEtF,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACnC,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,cAAc,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACtF,6CAA6C;IAC7C,YAAY,EAAE,OAAO,CAAC;IACtB,yCAAyC;IACzC,KAAK,EAAE,cAAc,GAAG,kBAAkB,GAAG,IAAI,CAAC;IAClD,4BAA4B;IAC5B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,4CAA4C;IAC5C,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CA6E/C"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFeedback = useFeedback;
4
+ const react_1 = require("react");
5
+ const react_native_1 = require("react-native");
6
+ const useHarkenContext_1 = require("./useHarkenContext");
7
+ const useAnonymousId_1 = require("./useAnonymousId");
8
+ const client_1 = require("../api/client");
9
+ const errors_1 = require("../api/errors");
10
+ /**
11
+ * Hook for submitting feedback through the Harken API.
12
+ *
13
+ * Automatically includes the anonymous ID and device metadata.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * function FeedbackScreen() {
18
+ * const { submitFeedback, isSubmitting, error } = useFeedback();
19
+ *
20
+ * const handleSubmit = async () => {
21
+ * try {
22
+ * await submitFeedback({
23
+ * message: 'Great app!',
24
+ * category: 'idea',
25
+ * });
26
+ * // Success!
27
+ * } catch (e) {
28
+ * // Error is also available in `error` state
29
+ * }
30
+ * };
31
+ * }
32
+ * ```
33
+ */
34
+ function useFeedback() {
35
+ const { config } = (0, useHarkenContext_1.useHarkenContext)();
36
+ const { anonymousId, isLoading: isInitializing } = (0, useAnonymousId_1.useAnonymousId)();
37
+ const [isSubmitting, setIsSubmitting] = (0, react_1.useState)(false);
38
+ const [error, setError] = (0, react_1.useState)(null);
39
+ // Create client instance (memoized)
40
+ const client = (0, react_1.useMemo)(() => {
41
+ return new client_1.HarkenClient({
42
+ publishableKey: config.publishableKey,
43
+ userToken: config.userToken,
44
+ baseUrl: config.apiBaseUrl,
45
+ });
46
+ }, [config.publishableKey, config.userToken, config.apiBaseUrl]);
47
+ const clearError = (0, react_1.useCallback)(() => {
48
+ setError(null);
49
+ }, []);
50
+ const submitFeedback = (0, react_1.useCallback)(async (params) => {
51
+ if (!anonymousId) {
52
+ throw new Error('Anonymous ID not yet initialized. Wait for isInitializing to be false.');
53
+ }
54
+ setIsSubmitting(true);
55
+ setError(null);
56
+ try {
57
+ // Collect device metadata
58
+ // Only set platform if it's a known value (ios, android)
59
+ // Other platforms (web, windows, macos) should be passed via metadata
60
+ const detectedPlatform = react_native_1.Platform.OS === 'ios' ? 'ios' :
61
+ react_native_1.Platform.OS === 'android' ? 'android' :
62
+ undefined;
63
+ const deviceMetadata = {
64
+ ...(detectedPlatform && { platform: detectedPlatform }),
65
+ ...params.metadata,
66
+ };
67
+ const response = await client.submitFeedback({
68
+ message: params.message,
69
+ category: params.category,
70
+ title: params.title,
71
+ anon_id: anonymousId,
72
+ metadata: deviceMetadata,
73
+ attachments: params.attachments,
74
+ });
75
+ return response;
76
+ }
77
+ catch (e) {
78
+ const harkenError = e instanceof errors_1.HarkenApiError || e instanceof errors_1.HarkenNetworkError
79
+ ? e
80
+ : new errors_1.HarkenNetworkError(e instanceof Error ? e.message : 'Unknown error', e instanceof Error ? e : undefined);
81
+ setError(harkenError);
82
+ throw harkenError;
83
+ }
84
+ finally {
85
+ setIsSubmitting(false);
86
+ }
87
+ }, [anonymousId, client]);
88
+ return {
89
+ submitFeedback,
90
+ isSubmitting,
91
+ error,
92
+ clearError,
93
+ isInitializing,
94
+ };
95
+ }
96
+ //# sourceMappingURL=useFeedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFeedback.js","sourceRoot":"","sources":["../../src/hooks/useFeedback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { HarkenContextValue } from '../context';
2
+ /**
3
+ * Hook to access the full Harken context.
4
+ *
5
+ * Provides access to theme, config, and SDK state.
6
+ * Must be used within a HarkenProvider.
7
+ *
8
+ * @returns The full HarkenContextValue
9
+ * @throws Error if used outside of HarkenProvider
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * function MyComponent() {
14
+ * const { theme, isDarkMode, config } = useHarkenContext();
15
+ *
16
+ * return (
17
+ * <View>
18
+ * <Text>Dark mode: {isDarkMode ? 'on' : 'off'}</Text>
19
+ * </View>
20
+ * );
21
+ * }
22
+ * ```
23
+ */
24
+ export declare function useHarkenContext(): HarkenContextValue;
25
+ //# sourceMappingURL=useHarkenContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHarkenContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useHarkenContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAQrD"}