cuoral-ionic 0.0.6 → 0.0.7

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 (56) hide show
  1. package/android/build/.transforms/bb54161301273cf9b5b94a21c0fb3f23/transformed/classes/classes_dex/classes.dex +0 -0
  2. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$1.dex +0 -0
  3. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$2.dex +0 -0
  4. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$3.dex +0 -0
  5. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$4.dex +0 -0
  6. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$5.dex +0 -0
  7. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$6.dex +0 -0
  8. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$InitiateCallback.dex +0 -0
  9. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$UploadCallback.dex +0 -0
  10. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin.dex +0 -0
  11. package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/desugar_graph.bin +0 -0
  12. package/android/build/intermediates/compile_library_classes_jar/debug/classes.jar +0 -0
  13. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$1.class +0 -0
  14. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$2.class +0 -0
  15. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$3.class +0 -0
  16. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
  17. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
  18. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
  19. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$InitiateCallback.class +0 -0
  20. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$UploadCallback.class +0 -0
  21. package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin.class +0 -0
  22. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$1.class +0 -0
  23. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$2.class +0 -0
  24. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$3.class +0 -0
  25. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
  26. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
  27. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
  28. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$InitiateCallback.class +0 -0
  29. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$UploadCallback.class +0 -0
  30. package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin.class +0 -0
  31. package/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar +0 -0
  32. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  33. package/android/build.gradle +1 -0
  34. package/android/src/main/java/com/cuoral/ionic/CuoralPlugin.java +205 -5
  35. package/dist/cuoral.d.ts +15 -1
  36. package/dist/cuoral.d.ts.map +1 -1
  37. package/dist/cuoral.js +113 -9
  38. package/dist/index.esm.js +159 -19
  39. package/dist/index.esm.js.map +1 -1
  40. package/dist/index.js +159 -19
  41. package/dist/index.js.map +1 -1
  42. package/dist/intelligence.d.ts +4 -0
  43. package/dist/intelligence.d.ts.map +1 -1
  44. package/dist/intelligence.js +19 -0
  45. package/dist/plugin.d.ts +15 -2
  46. package/dist/plugin.d.ts.map +1 -1
  47. package/dist/plugin.js +23 -10
  48. package/dist/types.d.ts +4 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/types.js +4 -0
  51. package/ios/Plugin/CuoralPlugin.swift +249 -13
  52. package/package.json +1 -1
  53. package/src/cuoral.ts +128 -11
  54. package/src/intelligence.ts +23 -0
  55. package/src/plugin.ts +39 -11
  56. package/src/types.ts +4 -0
package/dist/plugin.d.ts CHANGED
@@ -12,10 +12,17 @@ export interface CuoralPluginInterface {
12
12
  /**
13
13
  * Stop screen recording
14
14
  */
15
- stopRecording(): Promise<{
15
+ stopRecording(options?: {
16
+ autoUpload?: boolean;
17
+ sessionId?: string;
18
+ publicKey?: string;
19
+ customerId?: string;
20
+ }): Promise<{
16
21
  success: boolean;
17
22
  filePath?: string;
18
23
  duration?: number;
24
+ uploaded?: boolean;
25
+ uploadError?: string;
19
26
  }>;
20
27
  /**
21
28
  * Get recording state
@@ -56,9 +63,15 @@ export declare class CuoralRecorder {
56
63
  /**
57
64
  * Stop recording
58
65
  */
59
- stopRecording(): Promise<{
66
+ stopRecording(options?: {
67
+ autoUpload?: boolean;
68
+ sessionId?: string;
69
+ publicKey?: string;
70
+ customerId?: string;
71
+ }): Promise<{
60
72
  filePath?: string;
61
73
  duration?: number;
74
+ uploaded?: boolean;
62
75
  } | null>;
63
76
  /**
64
77
  * Take screenshot
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAErF;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE7C;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAErE;;OAEG;IACH,oBAAoB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAExD;;OAEG;IACH,kBAAkB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,QAAA,MAAM,YAAY,uBAAwD,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAS;IAEpC;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAyDlE;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAiD/E;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqBjF;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC;;OAEG;IACH,OAAO,CAAC,WAAW;CAKpB"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IAEH;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE7C;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAErE;;OAEG;IACH,oBAAoB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAExD;;OAEG;IACH,kBAAkB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,QAAA,MAAM,YAAY,uBAAwD,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAS;IAEpC;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAyDlE;;OAEG;IACG,aAAa,CAAC,OAAO,CAAC,EAAE;QAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IA6DhF;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqBjF;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC;;OAEG;IACH,OAAO,CAAC,WAAW;CAKpB"}
package/dist/plugin.js CHANGED
@@ -68,7 +68,7 @@ export class CuoralRecorder {
68
68
  /**
69
69
  * Stop recording
70
70
  */
71
- async stopRecording() {
71
+ async stopRecording(options) {
72
72
  try {
73
73
  if (!this.isRecording) {
74
74
  // Send error message to widget so it can exit "stopping" state
@@ -78,23 +78,36 @@ export class CuoralRecorder {
78
78
  });
79
79
  return null;
80
80
  }
81
- const result = await CuoralPlugin.stopRecording();
81
+ const result = await CuoralPlugin.stopRecording(options);
82
82
  if (result.success) {
83
83
  this.isRecording = false;
84
84
  const duration = this.recordingStartTime
85
85
  ? Math.floor((Date.now() - this.recordingStartTime) / 1000)
86
86
  : 0;
87
- // Post message to widget
88
- this.postMessage({
89
- type: CuoralMessageType.RECORDING_STOPPED,
90
- payload: {
91
- filePath: result.filePath,
92
- duration: result.duration || duration,
93
- },
94
- });
87
+ // If uploaded, notify widget differently
88
+ if (result.uploaded) {
89
+ this.postMessage({
90
+ type: CuoralMessageType.RECORDING_UPLOADED,
91
+ payload: {
92
+ duration: result.duration || duration,
93
+ uploaded: true
94
+ },
95
+ });
96
+ }
97
+ else {
98
+ // Post message to widget (old behavior)
99
+ this.postMessage({
100
+ type: CuoralMessageType.RECORDING_STOPPED,
101
+ payload: {
102
+ filePath: result.filePath,
103
+ duration: result.duration || duration,
104
+ },
105
+ });
106
+ }
95
107
  return {
96
108
  filePath: result.filePath,
97
109
  duration: result.duration || duration,
110
+ uploaded: result.uploaded,
98
111
  };
99
112
  }
100
113
  // If result.success is false, send error to widget
package/dist/types.d.ts CHANGED
@@ -6,12 +6,16 @@ export declare enum CuoralMessageType {
6
6
  STOP_RECORDING = "CUORAL_STOP_RECORDING",
7
7
  RECORDING_STARTED = "CUORAL_RECORDING_STARTED",
8
8
  RECORDING_STOPPED = "CUORAL_RECORDING_STOPPED",
9
+ RECORDING_UPLOADED = "CUORAL_RECORDING_UPLOADED",
9
10
  RECORDING_ERROR = "CUORAL_RECORDING_ERROR",
10
11
  TAKE_SCREENSHOT = "CUORAL_TAKE_SCREENSHOT",
11
12
  SCREENSHOT_TAKEN = "CUORAL_SCREENSHOT_TAKEN",
12
13
  SCREENSHOT_ERROR = "CUORAL_SCREENSHOT_ERROR",
13
14
  WIDGET_READY = "CUORAL_WIDGET_READY",
14
15
  WIDGET_CLOSED = "CUORAL_WIDGET_CLOSED",
16
+ SESSION_UPDATED = "CUORAL_SESSION_UPDATED",
17
+ REQUEST_SESSION_ID = "CUORAL_REQUEST_SESSION_ID",
18
+ SESSION_ID_RESPONSE = "CUORAL_SESSION_ID_RESPONSE",
15
19
  UPLOAD_FILE = "CUORAL_UPLOAD_FILE",
16
20
  UPLOAD_PROGRESS = "CUORAL_UPLOAD_PROGRESS",
17
21
  UPLOAD_COMPLETE = "CUORAL_UPLOAD_COMPLETE",
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,iBAAiB;IAE3B,eAAe,2BAA2B;IAC1C,cAAc,0BAA0B;IACxC,iBAAiB,6BAA6B;IAC9C,iBAAiB,6BAA6B;IAC9C,eAAe,2BAA2B;IAG1C,eAAe,2BAA2B;IAC1C,gBAAgB,4BAA4B;IAC5C,gBAAgB,4BAA4B;IAG5C,YAAY,wBAAwB;IACpC,aAAa,yBAAyB;IAGtC,WAAW,uBAAuB;IAClC,eAAe,2BAA2B;IAC1C,eAAe,2BAA2B;IAC1C,YAAY,wBAAwB;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAE7C;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,iBAAiB;IAE3B,eAAe,2BAA2B;IAC1C,cAAc,0BAA0B;IACxC,iBAAiB,6BAA6B;IAC9C,iBAAiB,6BAA6B;IAC9C,kBAAkB,8BAA8B;IAChD,eAAe,2BAA2B;IAG1C,eAAe,2BAA2B;IAC1C,gBAAgB,4BAA4B;IAC5C,gBAAgB,4BAA4B;IAG5C,YAAY,wBAAwB;IACpC,aAAa,yBAAyB;IACtC,eAAe,2BAA2B;IAC1C,kBAAkB,8BAA8B;IAChD,mBAAmB,+BAA+B;IAGlD,WAAW,uBAAuB;IAClC,eAAe,2BAA2B;IAC1C,eAAe,2BAA2B;IAC1C,YAAY,wBAAwB;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAE7C;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACzB"}
package/dist/types.js CHANGED
@@ -8,6 +8,7 @@ export var CuoralMessageType;
8
8
  CuoralMessageType["STOP_RECORDING"] = "CUORAL_STOP_RECORDING";
9
9
  CuoralMessageType["RECORDING_STARTED"] = "CUORAL_RECORDING_STARTED";
10
10
  CuoralMessageType["RECORDING_STOPPED"] = "CUORAL_RECORDING_STOPPED";
11
+ CuoralMessageType["RECORDING_UPLOADED"] = "CUORAL_RECORDING_UPLOADED";
11
12
  CuoralMessageType["RECORDING_ERROR"] = "CUORAL_RECORDING_ERROR";
12
13
  // Screenshot
13
14
  CuoralMessageType["TAKE_SCREENSHOT"] = "CUORAL_TAKE_SCREENSHOT";
@@ -16,6 +17,9 @@ export var CuoralMessageType;
16
17
  // Widget Communication
17
18
  CuoralMessageType["WIDGET_READY"] = "CUORAL_WIDGET_READY";
18
19
  CuoralMessageType["WIDGET_CLOSED"] = "CUORAL_WIDGET_CLOSED";
20
+ CuoralMessageType["SESSION_UPDATED"] = "CUORAL_SESSION_UPDATED";
21
+ CuoralMessageType["REQUEST_SESSION_ID"] = "CUORAL_REQUEST_SESSION_ID";
22
+ CuoralMessageType["SESSION_ID_RESPONSE"] = "CUORAL_SESSION_ID_RESPONSE";
19
23
  // File Upload
20
24
  CuoralMessageType["UPLOAD_FILE"] = "CUORAL_UPLOAD_FILE";
21
25
  CuoralMessageType["UPLOAD_PROGRESS"] = "CUORAL_UPLOAD_PROGRESS";
@@ -110,10 +110,19 @@ public class CuoralPlugin: CAPPlugin {
110
110
  do {
111
111
  assetWriter = try AVAssetWriter(url: outputURL, fileType: .mp4)
112
112
 
113
+ // Reduced quality settings to prevent huge file sizes
114
+ // 2.5 Mbps bitrate = ~18MB per minute (down from ~600MB per minute)
115
+ let compressionProperties: [String: Any] = [
116
+ AVVideoAverageBitRateKey: 2_500_000, // 2.5 Mbps
117
+ AVVideoMaxKeyFrameIntervalKey: 30,
118
+ AVVideoProfileLevelKey: AVVideoProfileLevelH264BaselineAutoLevel
119
+ ]
120
+
113
121
  let videoSettings: [String: Any] = [
114
122
  AVVideoCodecKey: AVVideoCodecType.h264,
115
123
  AVVideoWidthKey: UIScreen.main.bounds.width * UIScreen.main.scale,
116
- AVVideoHeightKey: UIScreen.main.bounds.height * UIScreen.main.scale
124
+ AVVideoHeightKey: UIScreen.main.bounds.height * UIScreen.main.scale,
125
+ AVVideoCompressionPropertiesKey: compressionProperties
117
126
  ]
118
127
 
119
128
  videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
@@ -184,6 +193,12 @@ public class CuoralPlugin: CAPPlugin {
184
193
 
185
194
  isRecording = false
186
195
 
196
+ // Get upload parameters
197
+ let shouldUpload = call.getBool("autoUpload") ?? true
198
+ let sessionId = call.getString("sessionId") ?? ""
199
+ let publicKey = call.getString("publicKey") ?? ""
200
+ let customerId = call.getString("customerId") ?? ""
201
+
187
202
  recorder.stopCapture { [weak self] error in
188
203
  guard let self = self else { return }
189
204
 
@@ -208,20 +223,241 @@ public class CuoralPlugin: CAPPlugin {
208
223
  let duration = self.recordingStartTime.map { Date().timeIntervalSince($0) } ?? 0
209
224
  let filePath = self.videoOutputURL?.path ?? ""
210
225
 
211
- call.resolve([
212
- "success": true,
213
- "filePath": filePath,
214
- "duration": Int(duration)
215
- ])
216
-
217
- // Clean up
218
- self.assetWriter = nil
219
- self.videoInput = nil
220
- self.videoOutputURL = nil
221
- self.recordingStartTime = nil
222
- self.firstFrameTime = nil
226
+ // If autoUpload is enabled, upload the video
227
+ if shouldUpload && !sessionId.isEmpty && !publicKey.isEmpty {
228
+ self.uploadRecording(
229
+ filePath: filePath,
230
+ sessionId: sessionId,
231
+ publicKey: publicKey,
232
+ customerId: customerId,
233
+ duration: Int(duration)
234
+ ) { success, error in
235
+ if success {
236
+ call.resolve([
237
+ "success": true,
238
+ "filePath": filePath,
239
+ "duration": Int(duration),
240
+ "uploaded": true
241
+ ])
242
+ } else {
243
+ call.resolve([
244
+ "success": true,
245
+ "filePath": filePath,
246
+ "duration": Int(duration),
247
+ "uploaded": false,
248
+ "uploadError": error ?? "Unknown error"
249
+ ])
250
+ }
251
+
252
+ // Clean up
253
+ self.assetWriter = nil
254
+ self.videoInput = nil
255
+ self.videoOutputURL = nil
256
+ self.recordingStartTime = nil
257
+ self.firstFrameTime = nil
258
+ }
259
+ } else {
260
+ // No upload, just return file path
261
+ call.resolve([
262
+ "success": true,
263
+ "filePath": filePath,
264
+ "duration": Int(duration),
265
+ "uploaded": false
266
+ ])
267
+
268
+ // Clean up
269
+ self.assetWriter = nil
270
+ self.videoInput = nil
271
+ self.videoOutputURL = nil
272
+ self.recordingStartTime = nil
273
+ self.firstFrameTime = nil
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Upload recording to Cuoral backend
281
+ */
282
+ private func uploadRecording(
283
+ filePath: String,
284
+ sessionId: String,
285
+ publicKey: String,
286
+ customerId: String,
287
+ duration: Int,
288
+ completion: @escaping (Bool, String?) -> Void
289
+ ) {
290
+ guard let fileURL = URL(string: "file://\(filePath)"),
291
+ FileManager.default.fileExists(atPath: filePath) else {
292
+ completion(false, "File not found")
293
+ return
294
+ }
295
+
296
+ // Read video data
297
+ guard let videoData = try? Data(contentsOf: fileURL) else {
298
+ completion(false, "Failed to read video file")
299
+ return
300
+ }
301
+
302
+ // Step 1: Initiate recording to get record_id
303
+ initiateRecording(sessionId: sessionId, customerId: customerId, publicKey: publicKey) { recordId, error in
304
+ if let error = error {
305
+ completion(false, "Failed to initiate recording: \(error)")
306
+ return
223
307
  }
308
+
309
+ guard let recordId = recordId else {
310
+ completion(false, "No record ID returned")
311
+ return
312
+ }
313
+
314
+ // Step 2: Upload video with the record_id
315
+ self.uploadVideo(
316
+ videoData: videoData,
317
+ recordId: recordId,
318
+ publicKey: publicKey,
319
+ customerId: customerId,
320
+ duration: duration,
321
+ completion: completion
322
+ )
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Step 1: Initiate recording session
328
+ */
329
+ private func initiateRecording(
330
+ sessionId: String,
331
+ customerId: String,
332
+ publicKey: String,
333
+ completion: @escaping (String?, String?) -> Void
334
+ ) {
335
+ let url = URL(string: "https://api.cuoral.com/customer-intelligence/initiate/record")!
336
+ var request = URLRequest(url: url)
337
+ request.httpMethod = "POST"
338
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
339
+ request.setValue("*/*", forHTTPHeaderField: "Accept")
340
+ request.setValue(publicKey, forHTTPHeaderField: "x-org-id")
341
+
342
+ // API needs both session_id and customer_id (matching widget.js)
343
+ let body: [String: String] = [
344
+ "session_id": sessionId,
345
+ "customer_id": customerId
346
+ ]
347
+
348
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: body) else {
349
+ completion(nil, "Failed to serialize JSON")
350
+ return
224
351
  }
352
+
353
+ request.httpBody = jsonData
354
+ request.timeoutInterval = 30.0
355
+
356
+ URLSession.shared.dataTask(with: request) { data, response, error in
357
+ if let error = error {
358
+ completion(nil, error.localizedDescription)
359
+ return
360
+ }
361
+
362
+ guard let httpResponse = response as? HTTPURLResponse else {
363
+ completion(nil, "Invalid response")
364
+ return
365
+ }
366
+
367
+ // API returns JSON: { status: 'success', record_id: '...' }
368
+ guard httpResponse.statusCode == 200, let data = data else {
369
+ completion(nil, "Failed to initiate recording (status \(httpResponse.statusCode))")
370
+ return
371
+ }
372
+
373
+ // Parse JSON response
374
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
375
+ let status = json["status"] as? String,
376
+ status == "success",
377
+ let recordId = json["record_id"] as? String else {
378
+ completion(nil, "Invalid response format")
379
+ return
380
+ }
381
+
382
+ completion(recordId, nil)
383
+ }.resume()
384
+ }
385
+
386
+ /**
387
+ * Step 2: Upload video to the initiated recording
388
+ */
389
+ private func uploadVideo(
390
+ videoData: Data,
391
+ recordId: String,
392
+ publicKey: String,
393
+ customerId: String,
394
+ duration: Int,
395
+ completion: @escaping (Bool, String?) -> Void
396
+ ) {
397
+ // Create multipart form data
398
+ let boundary = "Boundary-\(UUID().uuidString)"
399
+ var body = Data()
400
+
401
+ // Add form fields (API spec: only record_id is required)
402
+ let parameters: [String: String] = [
403
+ "record_id": recordId,
404
+ "console_error": "[]",
405
+ "api_response_log": "[]",
406
+ "page_view": "[]"
407
+ ]
408
+
409
+ for (key, value) in parameters {
410
+ body.append("--\(boundary)\r\n".data(using: .utf8)!)
411
+ body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
412
+ body.append("\(value)\r\n".data(using: .utf8)!)
413
+ }
414
+
415
+ // Add video file
416
+ body.append("--\(boundary)\r\n".data(using: .utf8)!)
417
+ body.append("Content-Disposition: form-data; name=\"record_media\"; filename=\"recording.mp4\"\r\n".data(using: .utf8)!)
418
+ body.append("Content-Type: video/mp4\r\n\r\n".data(using: .utf8)!)
419
+ body.append(videoData)
420
+ body.append("\r\n".data(using: .utf8)!)
421
+ body.append("--\(boundary)--\r\n".data(using: .utf8)!)
422
+
423
+ // Create request
424
+ let url = URL(string: "https://api.cuoral.com/customer-intelligence/update/record")!
425
+ var request = URLRequest(url: url)
426
+ request.httpMethod = "POST"
427
+ request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
428
+ request.setValue("*/*", forHTTPHeaderField: "Accept")
429
+ // Note: widget.js doesn't send x-org-id for update endpoint
430
+ request.httpBody = body
431
+ request.timeoutInterval = 60.0 // 60 seconds for upload
432
+
433
+ // Send request
434
+ URLSession.shared.dataTask(with: request) { data, response, error in
435
+ if let error = error {
436
+ completion(false, error.localizedDescription)
437
+ return
438
+ }
439
+
440
+ guard let httpResponse = response as? HTTPURLResponse else {
441
+ completion(false, "Invalid response")
442
+ return
443
+ }
444
+
445
+ // API returns JSON: { status: 'success' }
446
+ guard httpResponse.statusCode == 200, let data = data else {
447
+ completion(false, "Upload failed with status \(httpResponse.statusCode)")
448
+ return
449
+ }
450
+
451
+ // Parse JSON response
452
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
453
+ let status = json["status"] as? String,
454
+ status == "success" else {
455
+ completion(false, "Upload failed")
456
+ return
457
+ }
458
+
459
+ completion(true, nil)
460
+ }.resume()
225
461
  }
226
462
 
227
463
  @objc func getRecordingState(_ call: CAPPluginCall) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cuoral-ionic",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Cuoral Ionic Framework Library - Proactive customer success platform with support ticketing, customer intelligence, screen recording, and engagement tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/cuoral.ts CHANGED
@@ -61,6 +61,12 @@ export class Cuoral {
61
61
  _t: Date.now().toString(),
62
62
  });
63
63
 
64
+ // Add session_id if available (for widget to use existing session)
65
+ const existingSessionId = localStorage.getItem('__x_loadID');
66
+ if (existingSessionId) {
67
+ params.set('cuoral_mobile_session_id', existingSessionId);
68
+ }
69
+
64
70
  if (options.email) params.set('email', options.email);
65
71
  if (options.firstName) params.set('first_name', options.firstName);
66
72
  if (options.lastName) params.set('last_name', options.lastName);
@@ -88,6 +94,14 @@ export class Cuoral {
88
94
  * Initialize Cuoral
89
95
  */
90
96
  public async initialize(): Promise<void> {
97
+ // Fetch session configuration and initialize intelligence if enabled by backend
98
+ await this.initializeIntelligence();
99
+
100
+ console.log('[Cuoral] Initialize - Session ID:', localStorage.getItem('__x_loadID'));
101
+
102
+ // Setup localStorage listener to detect when widget changes session
103
+ this.setupStorageListener();
104
+
91
105
  this.bridge.initialize();
92
106
 
93
107
  // Recreate modal if it was destroyed (e.g., after clearSession)
@@ -96,13 +110,12 @@ export class Cuoral {
96
110
  this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton);
97
111
  }
98
112
 
99
- // Initialize modal if enabled
113
+ // Update modal URL with session ID
100
114
  if (this.modal) {
115
+ const widgetUrl = this.getWidgetUrl();
116
+ this.modal.updateWidgetUrl(widgetUrl);
101
117
  this.modal.initialize();
102
118
  }
103
-
104
- // Fetch session configuration and initialize intelligence if enabled by backend
105
- await this.initializeIntelligence();
106
119
  }
107
120
 
108
121
  /**
@@ -139,8 +152,11 @@ export class Cuoral {
139
152
 
140
153
  // Only initialize intelligence if customer_intelligence is enabled in backend
141
154
  if (config && config.customer_intelligence === true) {
155
+ console.log('[Cuoral] Initializing intelligence with session:', sessionId);
142
156
  this.intelligence = new CuoralIntelligence(sessionId);
143
157
  this.intelligence.init();
158
+ } else {
159
+ console.log('[Cuoral] Intelligence not enabled or no config for session:', sessionId);
144
160
  }
145
161
  } catch (error) {
146
162
  console.warn('[Cuoral] Failed to initialize intelligence:', error);
@@ -205,6 +221,54 @@ export class Cuoral {
205
221
  }
206
222
  }
207
223
 
224
+ /**
225
+ * Update user profile for the current session
226
+ * Call this after user logs in to update the intelligence session with their profile
227
+ * @param email - User's email address
228
+ * @param name - User's full name
229
+ */
230
+ public async updateUserProfile(email: string, name: string): Promise<boolean> {
231
+ try {
232
+ const sessionId = localStorage.getItem('__x_loadID');
233
+ if (!sessionId) {
234
+ console.warn('[Cuoral] No session ID found, cannot update profile');
235
+ return false;
236
+ }
237
+
238
+ console.log('[Cuoral] Updating user profile for session:', sessionId);
239
+
240
+ const response = await fetch('https://api.cuoral.com/conversation/set-profile', {
241
+ method: 'POST',
242
+ headers: { 'Content-Type': 'application/json' },
243
+ body: JSON.stringify({
244
+ session_id: sessionId,
245
+ email: email,
246
+ name: name,
247
+ }),
248
+ });
249
+
250
+ if (!response.ok) {
251
+ console.error('[Cuoral] Failed to update profile:', response.statusText);
252
+ return false;
253
+ }
254
+
255
+ console.log('[Cuoral] ✓ User profile updated successfully for session:', sessionId);
256
+
257
+ // Store user info locally
258
+ this.options.email = email;
259
+ const nameParts = name.split(' ');
260
+ if (nameParts.length > 0) {
261
+ this.options.firstName = nameParts[0];
262
+ this.options.lastName = nameParts.slice(1).join(' ');
263
+ }
264
+
265
+ return true;
266
+ } catch (error) {
267
+ console.error('[Cuoral] Error updating user profile:', error);
268
+ return false;
269
+ }
270
+ }
271
+
208
272
  /**
209
273
  * Start native screen recording programmatically
210
274
  * @returns Promise<boolean> - true if recording started successfully
@@ -220,11 +284,20 @@ export class Cuoral {
220
284
 
221
285
  /**
222
286
  * Stop native screen recording programmatically
223
- * @returns Promise<{filePath?: string; duration?: number} | null> - Recording result or null if failed
287
+ * Recording will be automatically uploaded to the portal
288
+ * @returns Promise<{filePath?: string; duration?: number; uploaded?: boolean} | null> - Recording result or null if failed
224
289
  */
225
- public async stopRecording(): Promise<{filePath?: string; duration?: number} | null> {
290
+ public async stopRecording(): Promise<{filePath?: string; duration?: number; uploaded?: boolean} | null> {
226
291
  try {
227
- return await this.recorder.stopRecording();
292
+ const sessionId = localStorage.getItem('__x_loadID');
293
+ const customerId = localStorage.getItem('cuoralCustomerId');
294
+
295
+ return await this.recorder.stopRecording({
296
+ autoUpload: true,
297
+ sessionId: sessionId || undefined,
298
+ publicKey: this.options.publicKey,
299
+ customerId: customerId || undefined,
300
+ });
228
301
  } catch (error) {
229
302
  console.error('[Cuoral] Failed to stop recording:', error);
230
303
  return null;
@@ -274,7 +347,7 @@ export class Cuoral {
274
347
  // Add session_id if available
275
348
  const sessionId = localStorage.getItem('__x_loadID');
276
349
  if (sessionId) {
277
- params.set('session_id', sessionId);
350
+ params.set('cuoral_mobile_session_id', sessionId);
278
351
  }
279
352
 
280
353
  if (this.options.email) params.set('email', this.options.email);
@@ -333,6 +406,32 @@ export class Cuoral {
333
406
  }
334
407
  }
335
408
 
409
+ /**
410
+ * Setup localStorage listener for session changes
411
+ * Widget updates localStorage when creating new session, SDK detects and syncs
412
+ */
413
+ private setupStorageListener(): void {
414
+ // Poll localStorage every 2 seconds to detect session changes
415
+ // (storage event doesn't fire for same-window changes)
416
+ let lastKnownSession = localStorage.getItem('__x_loadID');
417
+
418
+ setInterval(() => {
419
+ const currentSession = localStorage.getItem('__x_loadID');
420
+ if (currentSession && currentSession !== lastKnownSession) {
421
+ console.log('[Cuoral] 🔄 Session changed in localStorage');
422
+ console.log('[Cuoral] Old session:', lastKnownSession);
423
+ console.log('[Cuoral] New session:', currentSession);
424
+
425
+ lastKnownSession = currentSession;
426
+
427
+ // Update intelligence to use new session
428
+ if (this.intelligence) {
429
+ this.intelligence.updateSessionId(currentSession);
430
+ }
431
+ }
432
+ }, 2000);
433
+ }
434
+
336
435
  /**
337
436
  * Clean up resources
338
437
  */
@@ -439,10 +538,28 @@ export class Cuoral {
439
538
 
440
539
  // Handle stop recording requests from widget
441
540
  this.bridge.on(CuoralMessageType.STOP_RECORDING, async () => {
442
- const result = await this.recorder.stopRecording();
541
+ const sessionId = localStorage.getItem('__x_loadID');
542
+ const customerId = localStorage.getItem('cuoralCustomerId');
543
+
544
+ const result = await this.recorder.stopRecording({
545
+ autoUpload: true,
546
+ sessionId: sessionId || undefined,
547
+ publicKey: this.options.publicKey,
548
+ customerId: customerId || undefined,
549
+ });
443
550
 
444
- if (result) {
445
- // Convert file path to web-accessible URL
551
+ if (result && result.uploaded) {
552
+ // Video was automatically uploaded, just notify widget
553
+ this.bridge.sendToWidget({
554
+ type: CuoralMessageType.RECORDING_UPLOADED,
555
+ payload: {
556
+ duration: result.duration,
557
+ uploaded: true,
558
+ timestamp: Date.now()
559
+ }
560
+ });
561
+ } else if (result) {
562
+ // Upload failed or was disabled, send video data to widget (old behavior)
446
563
  const capacitorUrl = result.filePath ? Capacitor.convertFileSrc(result.filePath) : '';
447
564
 
448
565
  try {