cuoral-ionic 0.0.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.
package/dist/bridge.js ADDED
@@ -0,0 +1,159 @@
1
+ import { CuoralMessageType } from './types';
2
+ /**
3
+ * Bridge for bidirectional communication between WebView and Native code
4
+ */
5
+ export class CuoralBridge {
6
+ constructor(config) {
7
+ this.messageHandlers = new Map();
8
+ this.isInitialized = false;
9
+ this.widgetIframe = null;
10
+ this.config = config;
11
+ this.setupMessageListener();
12
+ }
13
+ /**
14
+ * Initialize the bridge
15
+ */
16
+ initialize() {
17
+ if (this.isInitialized) {
18
+ this.log('Bridge already initialized');
19
+ return;
20
+ }
21
+ this.isInitialized = true;
22
+ this.log('Bridge initialized');
23
+ // Notify widget that bridge is ready
24
+ this.sendToWidget({
25
+ type: CuoralMessageType.WIDGET_READY,
26
+ timestamp: Date.now(),
27
+ });
28
+ }
29
+ /**
30
+ * Send message to native code
31
+ */
32
+ sendToNative(message) {
33
+ if (!message.timestamp) {
34
+ message.timestamp = Date.now();
35
+ }
36
+ this.log('Sending to native:', message);
37
+ // For Capacitor - use window.postMessage
38
+ if (window.webkit?.messageHandlers?.cuoral) {
39
+ // iOS WKWebView
40
+ window.webkit.messageHandlers.cuoral.postMessage(message);
41
+ }
42
+ else if (window.CuoralAndroid) {
43
+ // Android JavascriptInterface
44
+ window.CuoralAndroid.postMessage(JSON.stringify(message));
45
+ }
46
+ else {
47
+ // Fallback - use postMessage for any other WebView
48
+ window.postMessage(message, '*');
49
+ }
50
+ }
51
+ /**
52
+ * Send message to widget iframe
53
+ */
54
+ sendToWidget(message) {
55
+ if (!message.timestamp) {
56
+ message.timestamp = Date.now();
57
+ }
58
+ this.log('Sending to widget:', message);
59
+ // Find the iframe element
60
+ const iframe = document.getElementById('cuoral-widget-iframe');
61
+ if (iframe?.contentWindow) {
62
+ try {
63
+ // Use postMessage for cross-origin communication
64
+ iframe.contentWindow.postMessage(message, '*');
65
+ this.log('Message sent via postMessage');
66
+ return;
67
+ }
68
+ catch (error) {
69
+ this.log('Error using postMessage:', error);
70
+ }
71
+ }
72
+ else {
73
+ this.log('Iframe not found or not ready');
74
+ }
75
+ // Fallback: Use localStorage for same-origin
76
+ try {
77
+ const storageKey = `cuoral_message_${Date.now()}`;
78
+ localStorage.setItem(storageKey, JSON.stringify(message));
79
+ // Clean up after 5 seconds
80
+ setTimeout(() => {
81
+ localStorage.removeItem(storageKey);
82
+ }, 5000);
83
+ // Trigger a storage event by updating a counter
84
+ const counter = parseInt(localStorage.getItem('cuoral_message_counter') || '0');
85
+ localStorage.setItem('cuoral_message_counter', (counter + 1).toString());
86
+ }
87
+ catch (error) {
88
+ this.log('Error using localStorage:', error);
89
+ }
90
+ }
91
+ /**
92
+ * Register message handler
93
+ */
94
+ on(type, handler) {
95
+ if (!this.messageHandlers.has(type)) {
96
+ this.messageHandlers.set(type, []);
97
+ }
98
+ const handlers = this.messageHandlers.get(type);
99
+ handlers.push(handler);
100
+ this.log(`Registered handler for ${type}`);
101
+ // Return unsubscribe function
102
+ return () => {
103
+ const index = handlers.indexOf(handler);
104
+ if (index > -1) {
105
+ handlers.splice(index, 1);
106
+ }
107
+ };
108
+ }
109
+ /**
110
+ * Setup window message listener
111
+ */
112
+ setupMessageListener() {
113
+ window.addEventListener('message', (event) => {
114
+ // Only process messages FROM the iframe
115
+ const widgetFrame = document.querySelector('iframe[src*="mobile.html"]');
116
+ if (widgetFrame && event.source === widgetFrame.contentWindow) {
117
+ this.widgetIframe = widgetFrame;
118
+ }
119
+ else if (widgetFrame) {
120
+ return;
121
+ }
122
+ // Validate message structure
123
+ if (!event.data || !event.data.type) {
124
+ return;
125
+ }
126
+ const message = event.data;
127
+ // Check if it's a Cuoral message
128
+ if (!Object.values(CuoralMessageType).includes(message.type)) {
129
+ return;
130
+ }
131
+ this.log('Cuoral message received:', message);
132
+ // Call custom handler if provided
133
+ if (this.config.onMessage) {
134
+ this.config.onMessage(message);
135
+ }
136
+ // Call registered handlers
137
+ const handlers = this.messageHandlers.get(message.type);
138
+ if (handlers) {
139
+ handlers.forEach(handler => handler(message.payload));
140
+ }
141
+ });
142
+ }
143
+ /**
144
+ * Debug logging
145
+ */
146
+ log(...args) {
147
+ if (this.config.debug) {
148
+ console.log('[CuoralBridge]', ...args);
149
+ }
150
+ }
151
+ /**
152
+ * Destroy the bridge
153
+ */
154
+ destroy() {
155
+ this.messageHandlers.clear();
156
+ this.isInitialized = false;
157
+ this.log('Bridge destroyed');
158
+ }
159
+ }
@@ -0,0 +1,51 @@
1
+ export interface CuoralOptions {
2
+ publicKey: string;
3
+ email?: string;
4
+ firstName?: string;
5
+ lastName?: string;
6
+ debug?: boolean;
7
+ widgetBaseUrl?: string;
8
+ showFloatingButton?: boolean;
9
+ useModal?: boolean;
10
+ }
11
+ /**
12
+ * Main Cuoral class - simple API for users
13
+ */
14
+ export declare class Cuoral {
15
+ private bridge;
16
+ private recorder;
17
+ private modal?;
18
+ private options;
19
+ private static readonly PRODUCTION_WIDGET_URL;
20
+ private static readonly DEV_WIDGET_URL;
21
+ constructor(options: CuoralOptions);
22
+ /**
23
+ * Initialize Cuoral
24
+ */
25
+ initialize(): void;
26
+ /**
27
+ * Open the widget modal
28
+ */
29
+ openModal(): void;
30
+ /**
31
+ * Close the widget modal
32
+ */
33
+ closeModal(): void;
34
+ /**
35
+ * Check if modal is open
36
+ */
37
+ isModalOpen(): boolean;
38
+ /**
39
+ * Get the widget URL for iframe embedding
40
+ */
41
+ getWidgetUrl(): string;
42
+ /**
43
+ * Clean up resources
44
+ */
45
+ destroy(): void;
46
+ /**
47
+ * Setup automatic message handlers
48
+ */
49
+ private setupMessageHandlers;
50
+ }
51
+ //# sourceMappingURL=cuoral.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cuoral.d.ts","sourceRoot":"","sources":["../src/cuoral.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAuC;IACpF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAElD,OAAO,EAAE,aAAa;IAsClC;;OAEG;IACI,UAAU,IAAI,IAAI;IASzB;;OAEG;IACI,SAAS,IAAI,IAAI;IAMxB;;OAEG;IACI,UAAU,IAAI,IAAI;IAMzB;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACI,YAAY,IAAI,MAAM;IAe7B;;OAEG;IACI,OAAO,IAAI,IAAI;IAStB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAuE7B"}
package/dist/cuoral.js ADDED
@@ -0,0 +1,173 @@
1
+ import { CuoralBridge } from './bridge';
2
+ import { CuoralRecorder } from './plugin';
3
+ import { CuoralMessageType } from './types';
4
+ import { CuoralModal } from './modal';
5
+ import { Capacitor } from '@capacitor/core';
6
+ /**
7
+ * Main Cuoral class - simple API for users
8
+ */
9
+ export class Cuoral {
10
+ constructor(options) {
11
+ this.options = {
12
+ showFloatingButton: true,
13
+ useModal: true,
14
+ ...options
15
+ };
16
+ // Determine widget base URL
17
+ const baseUrl = options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
18
+ const params = new URLSearchParams({
19
+ auto_start: 'true',
20
+ key: options.publicKey,
21
+ _t: Date.now().toString(),
22
+ });
23
+ if (options.email)
24
+ params.set('email', options.email);
25
+ if (options.firstName)
26
+ params.set('first_name', options.firstName);
27
+ if (options.lastName)
28
+ params.set('last_name', options.lastName);
29
+ const widgetUrl = `${baseUrl}?${params.toString()}`;
30
+ // Initialize modal if enabled
31
+ if (this.options.useModal) {
32
+ this.modal = new CuoralModal(widgetUrl, this.options.showFloatingButton);
33
+ }
34
+ // Initialize bridge and recorder
35
+ this.bridge = new CuoralBridge({
36
+ widgetUrl,
37
+ debug: options.debug || false
38
+ });
39
+ this.recorder = new CuoralRecorder();
40
+ // Setup automatic message handlers
41
+ this.setupMessageHandlers();
42
+ }
43
+ /**
44
+ * Initialize Cuoral
45
+ */
46
+ initialize() {
47
+ this.bridge.initialize();
48
+ // Initialize modal if enabled
49
+ if (this.modal) {
50
+ this.modal.initialize();
51
+ }
52
+ }
53
+ /**
54
+ * Open the widget modal
55
+ */
56
+ openModal() {
57
+ if (this.modal) {
58
+ this.modal.open();
59
+ }
60
+ }
61
+ /**
62
+ * Close the widget modal
63
+ */
64
+ closeModal() {
65
+ if (this.modal) {
66
+ this.modal.close();
67
+ }
68
+ }
69
+ /**
70
+ * Check if modal is open
71
+ */
72
+ isModalOpen() {
73
+ return this.modal?.isModalOpen() || false;
74
+ }
75
+ /**
76
+ * Get the widget URL for iframe embedding
77
+ */
78
+ getWidgetUrl() {
79
+ const baseUrl = this.options.widgetBaseUrl || Cuoral.PRODUCTION_WIDGET_URL;
80
+ const params = new URLSearchParams({
81
+ auto_start: 'true',
82
+ key: this.options.publicKey,
83
+ _t: Date.now().toString(),
84
+ });
85
+ if (this.options.email)
86
+ params.set('email', this.options.email);
87
+ if (this.options.firstName)
88
+ params.set('first_name', this.options.firstName);
89
+ if (this.options.lastName)
90
+ params.set('last_name', this.options.lastName);
91
+ return `${baseUrl}?${params.toString()}`;
92
+ }
93
+ /**
94
+ * Clean up resources
95
+ */
96
+ destroy() {
97
+ this.bridge.destroy();
98
+ // Destroy modal if exists
99
+ if (this.modal) {
100
+ this.modal.destroy();
101
+ }
102
+ }
103
+ /**
104
+ * Setup automatic message handlers
105
+ */
106
+ setupMessageHandlers() {
107
+ // Handle start recording requests from widget
108
+ this.bridge.on(CuoralMessageType.START_RECORDING, async () => {
109
+ const success = await this.recorder.startRecording();
110
+ if (!success) {
111
+ // Error already handled by recorder
112
+ }
113
+ });
114
+ // Handle stop recording requests from widget
115
+ this.bridge.on(CuoralMessageType.STOP_RECORDING, async () => {
116
+ const result = await this.recorder.stopRecording();
117
+ if (result) {
118
+ // Convert file path to web-accessible URL
119
+ const capacitorUrl = result.filePath ? Capacitor.convertFileSrc(result.filePath) : '';
120
+ try {
121
+ // Fetch the video blob from the capacitor URL
122
+ const response = await fetch(capacitorUrl);
123
+ const videoBlob = await response.blob();
124
+ // Convert blob to base64 data URL so it can be serialized and transferred cross-origin
125
+ const reader = new FileReader();
126
+ reader.onloadend = () => {
127
+ const base64data = reader.result;
128
+ // Send the base64 data via bridge to CDN widget
129
+ this.bridge.sendToWidget({
130
+ type: CuoralMessageType.RECORDING_STOPPED,
131
+ payload: {
132
+ videoData: base64data,
133
+ videoType: 'video/mp4',
134
+ originalPath: result.filePath,
135
+ duration: result.duration,
136
+ timestamp: Date.now()
137
+ }
138
+ });
139
+ };
140
+ reader.onerror = (error) => {
141
+ console.error('[Cuoral] Error reading blob:', error);
142
+ this.bridge.sendToWidget({
143
+ type: CuoralMessageType.RECORDING_STOPPED,
144
+ payload: {
145
+ error: 'Failed to process recording',
146
+ duration: result.duration,
147
+ timestamp: Date.now()
148
+ }
149
+ });
150
+ };
151
+ // Start reading the blob as data URL
152
+ reader.readAsDataURL(videoBlob);
153
+ }
154
+ catch (error) {
155
+ console.error('[Cuoral] Error processing video file:', error);
156
+ this.bridge.sendToWidget({
157
+ type: CuoralMessageType.RECORDING_STOPPED,
158
+ payload: {
159
+ error: 'Failed to process recording',
160
+ duration: result.duration,
161
+ timestamp: Date.now()
162
+ }
163
+ });
164
+ }
165
+ }
166
+ else {
167
+ console.error('[Cuoral] No result from stopRecording');
168
+ }
169
+ });
170
+ }
171
+ }
172
+ Cuoral.PRODUCTION_WIDGET_URL = 'https://js.cuoral.com/mobile.html';
173
+ Cuoral.DEV_WIDGET_URL = 'assets/mobile.html';
@@ -0,0 +1,6 @@
1
+ export * from './plugin';
2
+ export * from './bridge';
3
+ export * from './types';
4
+ export * from './cuoral';
5
+ export * from './modal';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}