cuoral-ionic 0.0.6 → 0.0.8
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.
- package/android/build/.transforms/bb54161301273cf9b5b94a21c0fb3f23/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$1.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$2.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$3.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$4.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$5.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$6.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$InitiateCallback.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin$UploadCallback.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/debug_dex/com/cuoral/ionic/CuoralPlugin.dex +0 -0
- package/android/build/.transforms/f1aabffcd8b03aa664e77a79b3e1de5d/transformed/debug/desugar_graph.bin +0 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/classes.jar +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$1.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$2.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$3.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$InitiateCallback.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin$UploadCallback.class +0 -0
- package/android/build/intermediates/javac/debug/classes/com/cuoral/ionic/CuoralPlugin.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$2.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$3.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$4.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$5.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$6.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$InitiateCallback.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin$UploadCallback.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/com/cuoral/ionic/CuoralPlugin.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{CuoralPlugin$1.class.uniqueId1 → CuoralPlugin$1.class.uniqueId2} +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{CuoralPlugin$2.class.uniqueId2 → CuoralPlugin$2.class.uniqueId5} +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$3.class.uniqueId3 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$4.class.uniqueId6 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$5.class.uniqueId4 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$6.class.uniqueId8 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$InitiateCallback.class.uniqueId0 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin$UploadCallback.class.uniqueId7 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin.class.uniqueId1 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/cuoral/ionic/CuoralPlugin.java +209 -6
- package/dist/bridge.d.ts.map +1 -1
- package/dist/cuoral.d.ts +15 -1
- package/dist/cuoral.d.ts.map +1 -1
- package/dist/cuoral.js +141 -12
- package/dist/index.esm.js +218 -46
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +218 -46
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +4 -0
- package/dist/intelligence.d.ts.map +1 -1
- package/dist/intelligence.js +19 -0
- package/dist/plugin.d.ts +16 -3
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +53 -36
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/ios/Plugin/CuoralPlugin.swift +249 -13
- package/package.json +1 -1
- package/src/bridge.ts +1 -0
- package/src/cuoral.ts +158 -14
- package/src/intelligence.ts +23 -0
- package/src/plugin.ts +70 -37
- package/src/types.ts +4 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/CuoralPlugin.class.uniqueId0 +0 -0
package/src/cuoral.ts
CHANGED
|
@@ -42,6 +42,8 @@ export class Cuoral {
|
|
|
42
42
|
private static readonly DEV_WIDGET_URL = 'assets/mobile.html';
|
|
43
43
|
|
|
44
44
|
constructor(options: CuoralOptions) {
|
|
45
|
+
console.log('[Cuoral] Constructor called with options:', options);
|
|
46
|
+
|
|
45
47
|
// Check if running on a mobile platform
|
|
46
48
|
if (!Capacitor.isNativePlatform()) {
|
|
47
49
|
throw new Error('Cuoral Ionic library only works on native mobile platforms (iOS/Android). Web is not supported.');
|
|
@@ -53,6 +55,8 @@ export class Cuoral {
|
|
|
53
55
|
...options
|
|
54
56
|
};
|
|
55
57
|
|
|
58
|
+
console.log('[Cuoral] Merged options:', this.options);
|
|
59
|
+
|
|
56
60
|
// Determine widget base URL
|
|
57
61
|
const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
|
|
58
62
|
const params = new URLSearchParams({
|
|
@@ -61,6 +65,12 @@ export class Cuoral {
|
|
|
61
65
|
_t: Date.now().toString(),
|
|
62
66
|
});
|
|
63
67
|
|
|
68
|
+
// Add session_id if available (for widget to use existing session)
|
|
69
|
+
const existingSessionId = localStorage.getItem('__x_loadID');
|
|
70
|
+
if (existingSessionId) {
|
|
71
|
+
params.set('cuoral_mobile_session_id', existingSessionId);
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
if (options.email) params.set('email', options.email);
|
|
65
75
|
if (options.firstName) params.set('first_name', options.firstName);
|
|
66
76
|
if (options.lastName) params.set('last_name', options.lastName);
|
|
@@ -80,14 +90,29 @@ export class Cuoral {
|
|
|
80
90
|
|
|
81
91
|
this.recorder = new CuoralRecorder();
|
|
82
92
|
|
|
93
|
+
// Expose bridge on window for widget detection and debugging
|
|
94
|
+
(window as any).CuoralCapacitorBridge = true;
|
|
95
|
+
(window as any).__cuoralBridge = this.bridge; // For debugging
|
|
96
|
+
(window as any).__cuoralRecorder = this.recorder; // For debugging
|
|
97
|
+
console.log('[Cuoral] Bridge and recorder created, exposed on window');
|
|
98
|
+
|
|
83
99
|
// Setup automatic message handlers
|
|
84
100
|
this.setupMessageHandlers();
|
|
101
|
+
console.log('[Cuoral] Constructor complete');
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
/**
|
|
88
105
|
* Initialize Cuoral
|
|
89
106
|
*/
|
|
90
107
|
public async initialize(): Promise<void> {
|
|
108
|
+
// Fetch session configuration and initialize intelligence if enabled by backend
|
|
109
|
+
await this.initializeIntelligence();
|
|
110
|
+
|
|
111
|
+
console.log('[Cuoral] Initialize - Session ID:', localStorage.getItem('__x_loadID'));
|
|
112
|
+
|
|
113
|
+
// Setup localStorage listener to detect when widget changes session
|
|
114
|
+
this.setupStorageListener();
|
|
115
|
+
|
|
91
116
|
this.bridge.initialize();
|
|
92
117
|
|
|
93
118
|
// Recreate modal if it was destroyed (e.g., after clearSession)
|
|
@@ -96,13 +121,12 @@ export class Cuoral {
|
|
|
96
121
|
this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton);
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
//
|
|
124
|
+
// Update modal URL with session ID
|
|
100
125
|
if (this.modal) {
|
|
126
|
+
const widgetUrl = this.getWidgetUrl();
|
|
127
|
+
this.modal.updateWidgetUrl(widgetUrl);
|
|
101
128
|
this.modal.initialize();
|
|
102
129
|
}
|
|
103
|
-
|
|
104
|
-
// Fetch session configuration and initialize intelligence if enabled by backend
|
|
105
|
-
await this.initializeIntelligence();
|
|
106
130
|
}
|
|
107
131
|
|
|
108
132
|
/**
|
|
@@ -139,8 +163,11 @@ export class Cuoral {
|
|
|
139
163
|
|
|
140
164
|
// Only initialize intelligence if customer_intelligence is enabled in backend
|
|
141
165
|
if (config && config.customer_intelligence === true) {
|
|
166
|
+
console.log('[Cuoral] Initializing intelligence with session:', sessionId);
|
|
142
167
|
this.intelligence = new CuoralIntelligence(sessionId);
|
|
143
168
|
this.intelligence.init();
|
|
169
|
+
} else {
|
|
170
|
+
console.log('[Cuoral] Intelligence not enabled or no config for session:', sessionId);
|
|
144
171
|
}
|
|
145
172
|
} catch (error) {
|
|
146
173
|
console.warn('[Cuoral] Failed to initialize intelligence:', error);
|
|
@@ -205,6 +232,54 @@ export class Cuoral {
|
|
|
205
232
|
}
|
|
206
233
|
}
|
|
207
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Update user profile for the current session
|
|
237
|
+
* Call this after user logs in to update the intelligence session with their profile
|
|
238
|
+
* @param email - User's email address
|
|
239
|
+
* @param name - User's full name
|
|
240
|
+
*/
|
|
241
|
+
public async updateUserProfile(email: string, name: string): Promise<boolean> {
|
|
242
|
+
try {
|
|
243
|
+
const sessionId = localStorage.getItem('__x_loadID');
|
|
244
|
+
if (!sessionId) {
|
|
245
|
+
console.warn('[Cuoral] No session ID found, cannot update profile');
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log('[Cuoral] Updating user profile for session:', sessionId);
|
|
250
|
+
|
|
251
|
+
const response = await fetch('https://api.cuoral.com/conversation/set-profile', {
|
|
252
|
+
method: 'POST',
|
|
253
|
+
headers: { 'Content-Type': 'application/json' },
|
|
254
|
+
body: JSON.stringify({
|
|
255
|
+
session_id: sessionId,
|
|
256
|
+
email: email,
|
|
257
|
+
name: name,
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
console.error('[Cuoral] Failed to update profile:', response.statusText);
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log('[Cuoral] ✓ User profile updated successfully for session:', sessionId);
|
|
267
|
+
|
|
268
|
+
// Store user info locally
|
|
269
|
+
this.options.email = email;
|
|
270
|
+
const nameParts = name.split(' ');
|
|
271
|
+
if (nameParts.length > 0) {
|
|
272
|
+
this.options.firstName = nameParts[0];
|
|
273
|
+
this.options.lastName = nameParts.slice(1).join(' ');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return true;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error('[Cuoral] Error updating user profile:', error);
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
208
283
|
/**
|
|
209
284
|
* Start native screen recording programmatically
|
|
210
285
|
* @returns Promise<boolean> - true if recording started successfully
|
|
@@ -220,11 +295,20 @@ export class Cuoral {
|
|
|
220
295
|
|
|
221
296
|
/**
|
|
222
297
|
* Stop native screen recording programmatically
|
|
223
|
-
*
|
|
298
|
+
* Recording will be automatically uploaded to the portal
|
|
299
|
+
* @returns Promise<{filePath?: string; duration?: number; uploaded?: boolean} | null> - Recording result or null if failed
|
|
224
300
|
*/
|
|
225
|
-
public async stopRecording(): Promise<{filePath?: string; duration?: number} | null> {
|
|
301
|
+
public async stopRecording(): Promise<{filePath?: string; duration?: number; uploaded?: boolean} | null> {
|
|
226
302
|
try {
|
|
227
|
-
|
|
303
|
+
const sessionId = localStorage.getItem('__x_loadID');
|
|
304
|
+
const customerId = localStorage.getItem('cuoralCustomerId');
|
|
305
|
+
|
|
306
|
+
return await this.recorder.stopRecording({
|
|
307
|
+
autoUpload: true,
|
|
308
|
+
sessionId: sessionId || undefined,
|
|
309
|
+
publicKey: this.options.publicKey,
|
|
310
|
+
customerId: customerId || undefined,
|
|
311
|
+
});
|
|
228
312
|
} catch (error) {
|
|
229
313
|
console.error('[Cuoral] Failed to stop recording:', error);
|
|
230
314
|
return null;
|
|
@@ -274,7 +358,7 @@ export class Cuoral {
|
|
|
274
358
|
// Add session_id if available
|
|
275
359
|
const sessionId = localStorage.getItem('__x_loadID');
|
|
276
360
|
if (sessionId) {
|
|
277
|
-
params.set('
|
|
361
|
+
params.set('cuoral_mobile_session_id', sessionId);
|
|
278
362
|
}
|
|
279
363
|
|
|
280
364
|
if (this.options.email) params.set('email', this.options.email);
|
|
@@ -333,6 +417,32 @@ export class Cuoral {
|
|
|
333
417
|
}
|
|
334
418
|
}
|
|
335
419
|
|
|
420
|
+
/**
|
|
421
|
+
* Setup localStorage listener for session changes
|
|
422
|
+
* Widget updates localStorage when creating new session, SDK detects and syncs
|
|
423
|
+
*/
|
|
424
|
+
private setupStorageListener(): void {
|
|
425
|
+
// Poll localStorage every 2 seconds to detect session changes
|
|
426
|
+
// (storage event doesn't fire for same-window changes)
|
|
427
|
+
let lastKnownSession = localStorage.getItem('__x_loadID');
|
|
428
|
+
|
|
429
|
+
setInterval(() => {
|
|
430
|
+
const currentSession = localStorage.getItem('__x_loadID');
|
|
431
|
+
if (currentSession && currentSession !== lastKnownSession) {
|
|
432
|
+
console.log('[Cuoral] 🔄 Session changed in localStorage');
|
|
433
|
+
console.log('[Cuoral] Old session:', lastKnownSession);
|
|
434
|
+
console.log('[Cuoral] New session:', currentSession);
|
|
435
|
+
|
|
436
|
+
lastKnownSession = currentSession;
|
|
437
|
+
|
|
438
|
+
// Update intelligence to use new session
|
|
439
|
+
if (this.intelligence) {
|
|
440
|
+
this.intelligence.updateSessionId(currentSession);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}, 2000);
|
|
444
|
+
}
|
|
445
|
+
|
|
336
446
|
/**
|
|
337
447
|
* Clean up resources
|
|
338
448
|
*/
|
|
@@ -431,18 +541,52 @@ export class Cuoral {
|
|
|
431
541
|
private setupMessageHandlers(): void {
|
|
432
542
|
// Handle start recording requests from widget
|
|
433
543
|
this.bridge.on(CuoralMessageType.START_RECORDING, async () => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
544
|
+
// Pass sendMessages: false to prevent duplicate messages
|
|
545
|
+
const success = await this.recorder.startRecording(undefined, false);
|
|
546
|
+
if (success) {
|
|
547
|
+
// Send RECORDING_STARTED back to widget
|
|
548
|
+
this.bridge.sendToWidget({
|
|
549
|
+
type: CuoralMessageType.RECORDING_STARTED,
|
|
550
|
+
payload: {
|
|
551
|
+
timestamp: Date.now()
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
} else {
|
|
555
|
+
this.bridge.sendToWidget({
|
|
556
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
557
|
+
payload: {
|
|
558
|
+
error: 'Failed to start recording'
|
|
559
|
+
}
|
|
560
|
+
});
|
|
437
561
|
}
|
|
438
562
|
});
|
|
439
563
|
|
|
440
564
|
// Handle stop recording requests from widget
|
|
441
565
|
this.bridge.on(CuoralMessageType.STOP_RECORDING, async () => {
|
|
442
|
-
const
|
|
566
|
+
const sessionId = localStorage.getItem('__x_loadID');
|
|
567
|
+
const customerId = localStorage.getItem('cuoralCustomerId');
|
|
443
568
|
|
|
444
|
-
|
|
445
|
-
|
|
569
|
+
// Pass sendMessages: false to prevent duplicate messages
|
|
570
|
+
const result = await this.recorder.stopRecording({
|
|
571
|
+
autoUpload: true,
|
|
572
|
+
sessionId: sessionId || undefined,
|
|
573
|
+
publicKey: this.options.publicKey,
|
|
574
|
+
customerId: customerId || undefined,
|
|
575
|
+
}, false);
|
|
576
|
+
|
|
577
|
+
if (result && result.uploaded) {
|
|
578
|
+
// Video was automatically uploaded, notify widget with RECORDING_STOPPED
|
|
579
|
+
this.bridge.sendToWidget({
|
|
580
|
+
type: CuoralMessageType.RECORDING_STOPPED,
|
|
581
|
+
payload: {
|
|
582
|
+
duration: result.duration,
|
|
583
|
+
uploaded: true,
|
|
584
|
+
uploadedToBackend: true,
|
|
585
|
+
timestamp: Date.now()
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
} else if (result) {
|
|
589
|
+
// Upload failed or was disabled, send video data to widget (old behavior)
|
|
446
590
|
const capacitorUrl = result.filePath ? Capacitor.convertFileSrc(result.filePath) : '';
|
|
447
591
|
|
|
448
592
|
try {
|
package/src/intelligence.ts
CHANGED
|
@@ -117,6 +117,27 @@ export class CuoralIntelligence {
|
|
|
117
117
|
this.sessionId = sessionId;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Update the session ID (e.g., when user logs in)
|
|
122
|
+
*/
|
|
123
|
+
public updateSessionId(newSessionId: string): void {
|
|
124
|
+
console.log('[Cuoral Intelligence] Updating session ID from', this.sessionId, 'to', newSessionId);
|
|
125
|
+
this.sessionId = newSessionId;
|
|
126
|
+
|
|
127
|
+
// Flush any pending events with the old session before switching
|
|
128
|
+
this.flush();
|
|
129
|
+
|
|
130
|
+
// Update native error capture with new session ID
|
|
131
|
+
if (Capacitor.isNativePlatform()) {
|
|
132
|
+
CuoralPlugin.setupNativeErrorCapture({
|
|
133
|
+
backendUrl: this.config.consoleErrorBackendUrl,
|
|
134
|
+
sessionId: newSessionId
|
|
135
|
+
}).catch(error => {
|
|
136
|
+
console.warn('[Cuoral Intelligence] Failed to update native error capture:', error);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
120
141
|
/**
|
|
121
142
|
* Initialize intelligence tracking
|
|
122
143
|
*/
|
|
@@ -482,6 +503,7 @@ export class CuoralIntelligence {
|
|
|
482
503
|
data,
|
|
483
504
|
};
|
|
484
505
|
|
|
506
|
+
console.log(`[Intelligence] Enqueuing ${type} event with session:`, this.sessionId);
|
|
485
507
|
queue.push(event);
|
|
486
508
|
|
|
487
509
|
// Flush immediately for critical errors
|
|
@@ -506,6 +528,7 @@ export class CuoralIntelligence {
|
|
|
506
528
|
return;
|
|
507
529
|
}
|
|
508
530
|
|
|
531
|
+
|
|
509
532
|
// Clear timer
|
|
510
533
|
if (this.batchTimers[eventType]) {
|
|
511
534
|
clearTimeout(this.batchTimers[eventType]);
|
package/src/plugin.ts
CHANGED
|
@@ -19,7 +19,18 @@ export interface CuoralPluginInterface {
|
|
|
19
19
|
/**
|
|
20
20
|
* Stop screen recording
|
|
21
21
|
*/
|
|
22
|
-
stopRecording(
|
|
22
|
+
stopRecording(options?: {
|
|
23
|
+
autoUpload?: boolean;
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
publicKey?: string;
|
|
26
|
+
customerId?: string;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
filePath?: string;
|
|
30
|
+
duration?: number;
|
|
31
|
+
uploaded?: boolean;
|
|
32
|
+
uploadError?: string;
|
|
33
|
+
}>;
|
|
23
34
|
|
|
24
35
|
/**
|
|
25
36
|
* Get recording state
|
|
@@ -59,7 +70,7 @@ export class CuoralRecorder {
|
|
|
59
70
|
/**
|
|
60
71
|
* Start recording with automatic permission handling
|
|
61
72
|
*/
|
|
62
|
-
async startRecording(options?: RecordingOptions): Promise<boolean> {
|
|
73
|
+
async startRecording(options?: RecordingOptions, sendMessages: boolean = true): Promise<boolean> {
|
|
63
74
|
try {
|
|
64
75
|
// Check if already recording
|
|
65
76
|
if (this.isRecording) {
|
|
@@ -86,20 +97,24 @@ export class CuoralRecorder {
|
|
|
86
97
|
this.isRecording = true;
|
|
87
98
|
this.recordingStartTime = Date.now();
|
|
88
99
|
|
|
89
|
-
// Post message to widget
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
// Post message to widget only if enabled (disabled when called from widget handler)
|
|
101
|
+
if (sendMessages) {
|
|
102
|
+
this.postMessage({
|
|
103
|
+
type: CuoralMessageType.RECORDING_STARTED,
|
|
104
|
+
payload: { timestamp: this.recordingStartTime },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
94
107
|
} else {
|
|
95
108
|
// Recording failed - reset state and notify widget
|
|
96
109
|
this.isRecording = false;
|
|
97
110
|
this.recordingStartTime = undefined;
|
|
98
111
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
if (sendMessages) {
|
|
113
|
+
this.postMessage({
|
|
114
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
115
|
+
payload: { error: 'Failed to start recording' },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
return result.success;
|
|
@@ -108,10 +123,12 @@ export class CuoralRecorder {
|
|
|
108
123
|
this.isRecording = false;
|
|
109
124
|
this.recordingStartTime = undefined;
|
|
110
125
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
if (sendMessages) {
|
|
127
|
+
this.postMessage({
|
|
128
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
129
|
+
payload: { error: (error as Error).message },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
115
132
|
return false;
|
|
116
133
|
}
|
|
117
134
|
}
|
|
@@ -119,51 +136,67 @@ export class CuoralRecorder {
|
|
|
119
136
|
/**
|
|
120
137
|
* Stop recording
|
|
121
138
|
*/
|
|
122
|
-
async stopRecording(
|
|
139
|
+
async stopRecording(options?: {
|
|
140
|
+
autoUpload?: boolean;
|
|
141
|
+
sessionId?: string;
|
|
142
|
+
publicKey?: string;
|
|
143
|
+
customerId?: string;
|
|
144
|
+
}, sendMessages: boolean = true): Promise<{ filePath?: string; duration?: number; uploaded?: boolean } | null> {
|
|
123
145
|
try {
|
|
124
146
|
if (!this.isRecording) {
|
|
125
147
|
// Send error message to widget so it can exit "stopping" state
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
if (sendMessages) {
|
|
149
|
+
this.postMessage({
|
|
150
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
151
|
+
payload: { error: 'Not recording' },
|
|
152
|
+
});
|
|
153
|
+
}
|
|
130
154
|
return null;
|
|
131
155
|
}
|
|
132
156
|
|
|
133
|
-
const result = await CuoralPlugin.stopRecording();
|
|
157
|
+
const result = await CuoralPlugin.stopRecording(options);
|
|
134
158
|
if (result.success) {
|
|
135
159
|
this.isRecording = false;
|
|
136
160
|
const duration = this.recordingStartTime
|
|
137
161
|
? Math.floor((Date.now() - this.recordingStartTime) / 1000)
|
|
138
162
|
: 0;
|
|
139
163
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
// Send RECORDING_STOPPED message only if enabled
|
|
165
|
+
if (sendMessages) {
|
|
166
|
+
this.postMessage({
|
|
167
|
+
type: CuoralMessageType.RECORDING_STOPPED,
|
|
168
|
+
payload: {
|
|
169
|
+
filePath: result.filePath,
|
|
170
|
+
duration: result.duration || duration,
|
|
171
|
+
uploaded: result.uploaded,
|
|
172
|
+
uploadedToBackend: result.uploaded,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
148
176
|
|
|
149
177
|
return {
|
|
150
178
|
filePath: result.filePath,
|
|
151
179
|
duration: result.duration || duration,
|
|
180
|
+
uploaded: result.uploaded,
|
|
152
181
|
};
|
|
153
182
|
}
|
|
154
183
|
|
|
155
184
|
// If result.success is false, send error to widget
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
185
|
+
if (sendMessages) {
|
|
186
|
+
this.postMessage({
|
|
187
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
188
|
+
payload: { error: 'Failed to stop recording' },
|
|
189
|
+
});
|
|
190
|
+
}
|
|
160
191
|
return null;
|
|
161
192
|
} catch (error) {
|
|
162
193
|
console.error('[Cuoral] Failed to stop recording:', error);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
194
|
+
if (sendMessages) {
|
|
195
|
+
this.postMessage({
|
|
196
|
+
type: CuoralMessageType.RECORDING_ERROR,
|
|
197
|
+
payload: { error: (error as Error).message },
|
|
198
|
+
});
|
|
199
|
+
}
|
|
167
200
|
return null;
|
|
168
201
|
}
|
|
169
202
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,7 @@ export enum CuoralMessageType {
|
|
|
7
7
|
STOP_RECORDING = 'CUORAL_STOP_RECORDING',
|
|
8
8
|
RECORDING_STARTED = 'CUORAL_RECORDING_STARTED',
|
|
9
9
|
RECORDING_STOPPED = 'CUORAL_RECORDING_STOPPED',
|
|
10
|
+
RECORDING_UPLOADED = 'CUORAL_RECORDING_UPLOADED',
|
|
10
11
|
RECORDING_ERROR = 'CUORAL_RECORDING_ERROR',
|
|
11
12
|
|
|
12
13
|
// Screenshot
|
|
@@ -17,6 +18,9 @@ export enum CuoralMessageType {
|
|
|
17
18
|
// Widget Communication
|
|
18
19
|
WIDGET_READY = 'CUORAL_WIDGET_READY',
|
|
19
20
|
WIDGET_CLOSED = 'CUORAL_WIDGET_CLOSED',
|
|
21
|
+
SESSION_UPDATED = 'CUORAL_SESSION_UPDATED',
|
|
22
|
+
REQUEST_SESSION_ID = 'CUORAL_REQUEST_SESSION_ID',
|
|
23
|
+
SESSION_ID_RESPONSE = 'CUORAL_SESSION_ID_RESPONSE',
|
|
20
24
|
|
|
21
25
|
// File Upload
|
|
22
26
|
UPLOAD_FILE = 'CUORAL_UPLOAD_FILE',
|