react-native-acoustic-connect-beta 18.0.13 → 18.0.15
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/Examples/SampleUI/.detoxrc.js +83 -0
- package/Examples/SampleUI/ConnectConfig.json +2 -2
- package/Examples/SampleUI/android/app/build.gradle +3 -0
- package/Examples/SampleUI/android/app/src/main/java/com/sampleui/MainActivity.kt +0 -35
- package/Examples/SampleUI/android/settings.gradle +1 -1
- package/Examples/SampleUI/package.json +8 -6
- package/Examples/SampleUI/scripts/integration-test-android.sh +292 -0
- package/Examples/SampleUI/src/Examples/DialogExample.tsx +88 -2
- package/Examples/SampleUI/src/Examples/Dialogs/DialogTrackingTest.tsx +307 -0
- package/Examples/SampleUI/src/Examples/Dialogs/index.tsx +37 -0
- package/Examples/SampleUI/src/index.native.tsx +5 -5
- package/android/build.gradle +2 -2
- package/android/src/main/assets/ConnectAdvancedConfig.json +1 -1
- package/android/src/main/assets/TealeafAdvancedConfig.json +1 -1
- package/android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt +787 -490
- package/ios/HybridAcousticConnectRN.swift +79 -4
- package/lib/commonjs/TLTRN.js +69 -0
- package/lib/commonjs/TLTRN.js.map +1 -1
- package/lib/commonjs/components/Connect.js +31 -29
- package/lib/commonjs/components/Connect.js.map +1 -1
- package/lib/commonjs/docs/DialogTracking.md +252 -0
- package/lib/commonjs/docs/NativeImplementation.md +176 -0
- package/lib/commonjs/examples/DialogTrackingExample.js +175 -0
- package/lib/commonjs/examples/DialogTrackingExample.js.map +1 -0
- package/lib/commonjs/examples/HOCDialogExample.js +296 -0
- package/lib/commonjs/examples/HOCDialogExample.js.map +1 -0
- package/lib/commonjs/index.js +28 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/DialogDebugger.js +216 -0
- package/lib/commonjs/utils/DialogDebugger.js.map +1 -0
- package/lib/commonjs/utils/DialogListener.js +203 -0
- package/lib/commonjs/utils/DialogListener.js.map +1 -0
- package/lib/commonjs/utils/useDialogTracking.js +107 -0
- package/lib/commonjs/utils/useDialogTracking.js.map +1 -0
- package/lib/commonjs/utils/withAcousticAutoDialog.js +282 -0
- package/lib/commonjs/utils/withAcousticAutoDialog.js.map +1 -0
- package/lib/module/TLTRN.js +69 -0
- package/lib/module/TLTRN.js.map +1 -1
- package/lib/module/components/Connect.js +32 -31
- package/lib/module/components/Connect.js.map +1 -1
- package/lib/module/docs/DialogTracking.md +252 -0
- package/lib/module/docs/NativeImplementation.md +176 -0
- package/lib/module/examples/DialogTrackingExample.js +172 -0
- package/lib/module/examples/DialogTrackingExample.js.map +1 -0
- package/lib/module/examples/HOCDialogExample.js +292 -0
- package/lib/module/examples/HOCDialogExample.js.map +1 -0
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/DialogDebugger.js +211 -0
- package/lib/module/utils/DialogDebugger.js.map +1 -0
- package/lib/module/utils/DialogListener.js +199 -0
- package/lib/module/utils/DialogListener.js.map +1 -0
- package/lib/module/utils/useDialogTracking.js +102 -0
- package/lib/module/utils/useDialogTracking.js.map +1 -0
- package/lib/module/utils/withAcousticAutoDialog.js +275 -0
- package/lib/module/utils/withAcousticAutoDialog.js.map +1 -0
- package/lib/typescript/src/TLTRN.d.ts +7 -0
- package/lib/typescript/src/TLTRN.d.ts.map +1 -1
- package/lib/typescript/src/components/Connect.d.ts +2 -2
- package/lib/typescript/src/components/Connect.d.ts.map +1 -1
- package/lib/typescript/src/examples/DialogTrackingExample.d.ts +17 -0
- package/lib/typescript/src/examples/DialogTrackingExample.d.ts.map +1 -0
- package/lib/typescript/src/examples/HOCDialogExample.d.ts +21 -0
- package/lib/typescript/src/examples/HOCDialogExample.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts +4 -0
- package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts.map +1 -1
- package/lib/typescript/src/utils/DialogDebugger.d.ts +58 -0
- package/lib/typescript/src/utils/DialogDebugger.d.ts.map +1 -0
- package/lib/typescript/src/utils/DialogListener.d.ts +85 -0
- package/lib/typescript/src/utils/DialogListener.d.ts.map +1 -0
- package/lib/typescript/src/utils/useDialogTracking.d.ts +25 -0
- package/lib/typescript/src/utils/useDialogTracking.d.ts.map +1 -0
- package/lib/typescript/src/utils/withAcousticAutoDialog.d.ts +37 -0
- package/lib/typescript/src/utils/withAcousticAutoDialog.d.ts.map +1 -0
- package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.cpp +26 -0
- package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.hpp +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/acousticconnectrn/HybridAcousticConnectRNSpec.kt +16 -0
- package/nitrogen/generated/ios/c++/HybridAcousticConnectRNSpecSwift.hpp +32 -0
- package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec.swift +4 -0
- package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec_cxx.swift +71 -0
- package/nitrogen/generated/shared/c++/HybridAcousticConnectRNSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridAcousticConnectRNSpec.hpp +4 -0
- package/package.json +1 -1
- package/scripts/ConnectConfig.json +1 -1
- package/src/TLTRN.ts +75 -0
- package/src/components/Connect.tsx +92 -101
- package/src/docs/DialogTracking.md +252 -0
- package/src/docs/NativeImplementation.md +176 -0
- package/src/examples/DialogTrackingExample.tsx +163 -0
- package/src/examples/HOCDialogExample.tsx +253 -0
- package/src/index.ts +5 -1
- package/src/specs/react-native-acoustic-connect.nitro.ts +5 -0
- package/src/utils/DialogDebugger.ts +224 -0
- package/src/utils/DialogListener.ts +238 -0
- package/src/utils/useDialogTracking.ts +102 -0
- package/src/utils/withAcousticAutoDialog.tsx +312 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/********************************************************************************************
|
|
2
|
+
* Copyright (C) 2025 Acoustic, L.P. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* NOTICE: This file contains material that is confidential and proprietary to
|
|
5
|
+
* Acoustic, L.P. and/or other developers. No license is granted under any intellectual or
|
|
6
|
+
* industrial property rights of Acoustic, L.P. except as may be provided in an agreement with
|
|
7
|
+
* Acoustic, L.P. Any unauthorized copying or distribution of content from this file is
|
|
8
|
+
* prohibited.
|
|
9
|
+
*
|
|
10
|
+
* Created by Omar Hernandez on 5/9/25.
|
|
11
|
+
*
|
|
12
|
+
********************************************************************************************/
|
|
13
|
+
|
|
14
|
+
import React, { forwardRef, useImperativeHandle, useRef, useEffect, useCallback } from 'react';
|
|
15
|
+
import { Alert } from 'react-native';
|
|
16
|
+
import { useDialogTracking } from './useDialogTracking';
|
|
17
|
+
import AcousticConnectRN from '../index';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* HOC Wrapper that automatically tracks dialog show/dismiss events and button clicks
|
|
21
|
+
* Works with any dialog component that has 'visible' and 'onDismiss' props
|
|
22
|
+
*
|
|
23
|
+
* Supported Dialog Patterns:
|
|
24
|
+
* - react-native-paper Dialog components
|
|
25
|
+
* - Custom modal components with visible/onDismiss props
|
|
26
|
+
* - Components with different prop names (show/hide, open/close, etc.)
|
|
27
|
+
* - Components with custom button implementations
|
|
28
|
+
* - Components with nested dialog structures
|
|
29
|
+
*/
|
|
30
|
+
export function withAcousticAutoDialog(
|
|
31
|
+
DialogComponent: React.ComponentType<any>
|
|
32
|
+
): React.ComponentType<any> {
|
|
33
|
+
return forwardRef<any, any>((props, ref) => {
|
|
34
|
+
const { generateDialogId } = useDialogTracking();
|
|
35
|
+
const dialogIdRef = useRef<string | null>(null);
|
|
36
|
+
const isVisibleRef = useRef(false);
|
|
37
|
+
|
|
38
|
+
// Track button click event
|
|
39
|
+
const trackButtonClick = useCallback((buttonText: string, buttonIndex: number) => {
|
|
40
|
+
if (dialogIdRef.current) {
|
|
41
|
+
AcousticConnectRN.logDialogButtonClickEvent(dialogIdRef.current, buttonText, buttonIndex);
|
|
42
|
+
console.log(`🔍 withAcousticAutoDialog: Button clicked - ${buttonText} (${dialogIdRef.current})`);
|
|
43
|
+
}
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
// Wrap button onPress handlers to track clicks
|
|
47
|
+
const wrapButtonOnPress = useCallback((originalOnPress: (() => void) | undefined, buttonText: string, buttonIndex: number) => {
|
|
48
|
+
return () => {
|
|
49
|
+
trackButtonClick(buttonText, buttonIndex);
|
|
50
|
+
if (originalOnPress) {
|
|
51
|
+
originalOnPress();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}, [trackButtonClick]);
|
|
55
|
+
|
|
56
|
+
// Recursively wrap buttons in children
|
|
57
|
+
const wrapButtonsInChildren = useCallback((children: any, buttonIndex: number = 0): any => {
|
|
58
|
+
if (!children) return children;
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(children)) {
|
|
61
|
+
return children.map((child, index) => wrapButtonsInChildren(child, buttonIndex + index));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (React.isValidElement(children)) {
|
|
65
|
+
const childProps = children.props as any;
|
|
66
|
+
|
|
67
|
+
// Check if this is a button component (supports various button types)
|
|
68
|
+
const isButton =
|
|
69
|
+
(typeof children.type === 'function' &&
|
|
70
|
+
((children.type as any).displayName === 'Button' ||
|
|
71
|
+
(children.type as any).name === 'Button' ||
|
|
72
|
+
(children.type as any).displayName === 'DialogAction' ||
|
|
73
|
+
(children.type as any).name === 'DialogAction' ||
|
|
74
|
+
(children.type as any).displayName === 'TouchableOpacity' ||
|
|
75
|
+
(children.type as any).name === 'TouchableOpacity' ||
|
|
76
|
+
(children.type as any).displayName === 'Pressable' ||
|
|
77
|
+
(children.type as any).name === 'Pressable')) ||
|
|
78
|
+
(childProps && (childProps.onPress || childProps.onPressIn || childProps.onPressOut || childProps.onTouchEnd));
|
|
79
|
+
|
|
80
|
+
if (isButton && childProps?.onPress) {
|
|
81
|
+
const buttonText = childProps.children || childProps.title || 'Button';
|
|
82
|
+
return React.cloneElement(children, {
|
|
83
|
+
...childProps,
|
|
84
|
+
onPress: wrapButtonOnPress(childProps.onPress, buttonText, buttonIndex)
|
|
85
|
+
} as any);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Recursively wrap buttons in nested children
|
|
89
|
+
if (childProps?.children) {
|
|
90
|
+
return React.cloneElement(children, {
|
|
91
|
+
...childProps,
|
|
92
|
+
children: wrapButtonsInChildren(childProps.children, buttonIndex)
|
|
93
|
+
} as any);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return children;
|
|
98
|
+
}, [wrapButtonOnPress]);
|
|
99
|
+
|
|
100
|
+
// Track dialog show event when visible changes to true
|
|
101
|
+
// Supports various visibility prop names: visible, show, open, isOpen, isVisible
|
|
102
|
+
const isVisible = props.visible || props.show || props.open || props.isOpen || props.isVisible;
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (isVisible && !isVisibleRef.current) {
|
|
106
|
+
const dialogId = generateDialogId();
|
|
107
|
+
dialogIdRef.current = dialogId;
|
|
108
|
+
isVisibleRef.current = true;
|
|
109
|
+
|
|
110
|
+
// Get dialog title from various possible sources
|
|
111
|
+
const getDialogTitle = (): string => {
|
|
112
|
+
// Try to get title from props (supports various prop names)
|
|
113
|
+
if (props.title) return props.title;
|
|
114
|
+
if (props.dialogTitle) return props.dialogTitle;
|
|
115
|
+
if (props.header) return props.header;
|
|
116
|
+
if (props.heading) return props.heading;
|
|
117
|
+
if (props.name) return props.name;
|
|
118
|
+
|
|
119
|
+
// Try to get title from children (for react-native-paper Dialog)
|
|
120
|
+
if (props.children) {
|
|
121
|
+
console.log(`🔍 withAcousticAutoDialog: Starting title extraction from children`);
|
|
122
|
+
console.log(`🔍 withAcousticAutoDialog: Initial children:`, props.children);
|
|
123
|
+
|
|
124
|
+
const extractTitleFromChildren = (children: any, depth: number = 0): string | null => {
|
|
125
|
+
if (!children) return null;
|
|
126
|
+
|
|
127
|
+
if (Array.isArray(children)) {
|
|
128
|
+
for (const child of children) {
|
|
129
|
+
const title = extractTitleFromChildren(child, depth + 1);
|
|
130
|
+
if (title) return title;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (React.isValidElement(children)) {
|
|
136
|
+
const childProps = children.props as any;
|
|
137
|
+
|
|
138
|
+
// Debug: Log component info for debugging
|
|
139
|
+
console.log(`🔍 withAcousticAutoDialog: Processing component at depth ${depth}:`, {
|
|
140
|
+
type: children.type,
|
|
141
|
+
displayName: (children.type as any)?.displayName,
|
|
142
|
+
name: (children.type as any)?.name,
|
|
143
|
+
hasChildren: !!childProps?.children,
|
|
144
|
+
childrenType: typeof childProps?.children
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Check if this is a Dialog component from react-native-paper
|
|
148
|
+
if (children.type &&
|
|
149
|
+
typeof children.type === 'function' &&
|
|
150
|
+
((children.type as any).displayName === 'Dialog' ||
|
|
151
|
+
(children.type as any).name === 'Dialog')) {
|
|
152
|
+
console.log(`🔍 withAcousticAutoDialog: Found Dialog component at depth ${depth}`);
|
|
153
|
+
// Extract title from Dialog children
|
|
154
|
+
if (childProps?.children) {
|
|
155
|
+
return extractTitleFromChildren(childProps.children, depth + 1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if this is a Portal component (common wrapper for dialogs)
|
|
160
|
+
if (children.type &&
|
|
161
|
+
typeof children.type === 'function' &&
|
|
162
|
+
((children.type as any).displayName === 'Portal' ||
|
|
163
|
+
(children.type as any).name === 'Portal')) {
|
|
164
|
+
console.log(`🔍 withAcousticAutoDialog: Found Portal component at depth ${depth}`);
|
|
165
|
+
// Extract title from Portal children
|
|
166
|
+
if (childProps?.children) {
|
|
167
|
+
return extractTitleFromChildren(childProps.children, depth + 1);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check if this is a DialogTitle component (supports various title component types)
|
|
172
|
+
if (children.type &&
|
|
173
|
+
typeof children.type === 'function' &&
|
|
174
|
+
((children.type as any).displayName === 'DialogTitle' ||
|
|
175
|
+
(children.type as any).name === 'DialogTitle' ||
|
|
176
|
+
(children.type as any).displayName === 'Title' ||
|
|
177
|
+
(children.type as any).name === 'Title' ||
|
|
178
|
+
(children.type as any).displayName === 'Header' ||
|
|
179
|
+
(children.type as any).name === 'Header' ||
|
|
180
|
+
(children.type as any).displayName === 'Heading' ||
|
|
181
|
+
(children.type as any).name === 'Heading')) {
|
|
182
|
+
if (childProps?.children && typeof childProps.children === 'string') {
|
|
183
|
+
console.log(`🔍 withAcousticAutoDialog: Found title in title component: "${childProps.children}"`);
|
|
184
|
+
return childProps.children;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check if this component has title content
|
|
189
|
+
if (childProps?.title) {
|
|
190
|
+
return childProps.title;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Recursively search in nested children
|
|
194
|
+
if (childProps?.children) {
|
|
195
|
+
return extractTitleFromChildren(childProps.children, depth + 1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return null;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const title = extractTitleFromChildren(props.children);
|
|
203
|
+
if (title) {
|
|
204
|
+
console.log(`🔍 withAcousticAutoDialog: Found title from children: "${title}"`);
|
|
205
|
+
return title;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return 'Dialog';
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const title = getDialogTitle();
|
|
213
|
+
|
|
214
|
+
// Log dialog show event
|
|
215
|
+
AcousticConnectRN.logDialogShowEvent(dialogId, title, 'custom');
|
|
216
|
+
console.log(`🔍 withAcousticAutoDialog: Dialog shown - ${title} (${dialogId})`);
|
|
217
|
+
} else if (!isVisible && isVisibleRef.current) {
|
|
218
|
+
// Track dialog dismiss event when visible changes to false
|
|
219
|
+
if (dialogIdRef.current) {
|
|
220
|
+
AcousticConnectRN.logDialogDismissEvent(dialogIdRef.current, 'user_action');
|
|
221
|
+
console.log(`🔍 withAcousticAutoDialog: Dialog dismissed - ${dialogIdRef.current}`);
|
|
222
|
+
dialogIdRef.current = null;
|
|
223
|
+
}
|
|
224
|
+
isVisibleRef.current = false;
|
|
225
|
+
}
|
|
226
|
+
}, [isVisible, generateDialogId]);
|
|
227
|
+
|
|
228
|
+
// Handle dismiss events (supports various dismiss prop names)
|
|
229
|
+
const handleDismiss = () => {
|
|
230
|
+
if (dialogIdRef.current) {
|
|
231
|
+
AcousticConnectRN.logDialogDismissEvent(dialogIdRef.current, 'user_action');
|
|
232
|
+
console.log(`🔍 withAcousticAutoDialog: Dialog dismissed via dismiss handler - ${dialogIdRef.current}`);
|
|
233
|
+
dialogIdRef.current = null;
|
|
234
|
+
}
|
|
235
|
+
isVisibleRef.current = false;
|
|
236
|
+
|
|
237
|
+
// Call original dismiss handler if it exists (supports various prop names)
|
|
238
|
+
if (props.onDismiss) {
|
|
239
|
+
props.onDismiss();
|
|
240
|
+
} else if (props.onClose) {
|
|
241
|
+
props.onClose();
|
|
242
|
+
} else if (props.onHide) {
|
|
243
|
+
props.onHide();
|
|
244
|
+
} else if (props.close) {
|
|
245
|
+
props.close();
|
|
246
|
+
} else if (props.hide) {
|
|
247
|
+
props.hide();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Forward ref to the wrapped component
|
|
252
|
+
useImperativeHandle(ref, () => {
|
|
253
|
+
return {
|
|
254
|
+
...ref,
|
|
255
|
+
// Add any additional methods if needed
|
|
256
|
+
};
|
|
257
|
+
}, [ref]);
|
|
258
|
+
|
|
259
|
+
// Wrap buttons in children to track clicks
|
|
260
|
+
const wrappedChildren = wrapButtonsInChildren(props.children);
|
|
261
|
+
|
|
262
|
+
// Determine which dismiss prop to use based on what the original component expects
|
|
263
|
+
const dismissProps: any = {};
|
|
264
|
+
if (props.onDismiss) {
|
|
265
|
+
dismissProps.onDismiss = handleDismiss;
|
|
266
|
+
} else if (props.onClose) {
|
|
267
|
+
dismissProps.onClose = handleDismiss;
|
|
268
|
+
} else if (props.onHide) {
|
|
269
|
+
dismissProps.onHide = handleDismiss;
|
|
270
|
+
} else if (props.close) {
|
|
271
|
+
dismissProps.close = handleDismiss;
|
|
272
|
+
} else if (props.hide) {
|
|
273
|
+
dismissProps.hide = handleDismiss;
|
|
274
|
+
} else {
|
|
275
|
+
// Default to onDismiss if no dismiss prop is found
|
|
276
|
+
dismissProps.onDismiss = handleDismiss;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<DialogComponent
|
|
281
|
+
{...props}
|
|
282
|
+
{...dismissProps}
|
|
283
|
+
ref={ref}
|
|
284
|
+
children={wrappedChildren}
|
|
285
|
+
/>
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Convenience function to create tracked versions of common dialog components
|
|
292
|
+
*/
|
|
293
|
+
export function createTrackedDialogComponents() {
|
|
294
|
+
// Note: These would need to be imported in the consuming app
|
|
295
|
+
// This is just a template for how to use the HOC
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
// Example usage (uncomment when react-native-paper is available):
|
|
299
|
+
// TrackedDialog: withAcousticAutoDialog(require('react-native-paper').Dialog),
|
|
300
|
+
// TrackedPortal: withAcousticAutoDialog(require('react-native-paper').Portal),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Hook to get tracked dialog components
|
|
306
|
+
*/
|
|
307
|
+
export function useTrackedDialogs() {
|
|
308
|
+
return {
|
|
309
|
+
withAcousticAutoDialog,
|
|
310
|
+
createTrackedDialogComponents,
|
|
311
|
+
};
|
|
312
|
+
}
|