react-native-acoustic-connect-beta 18.0.14 → 18.0.16
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 +4 -5
- package/android/build.gradle +2 -2
- package/android/src/main/assets/ConnectAdvancedConfig.json +1 -1
- package/android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt +787 -490
- package/ios/HybridAcousticConnectRN.swift +75 -0
- package/lib/commonjs/TLTRN.js +69 -0
- package/lib/commonjs/TLTRN.js.map +1 -1
- package/lib/commonjs/components/Connect.js +5 -1
- 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 +5 -1
- 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 +1 -0
- 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 +6 -1
- 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
package/src/TLTRN.ts
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import MessageQueue from "react-native/Libraries/BatchedBridge/MessageQueue.js";
|
|
13
13
|
import { Platform } from "react-native";
|
|
14
14
|
import KeyboardListener from "./utils/KeyboardListener";
|
|
15
|
+
import DialogListener from "./utils/DialogListener";
|
|
16
|
+
import type { DialogEvent, DialogButtonClickEvent, DialogDismissEvent } from "./utils/DialogListener";
|
|
15
17
|
import AcousticConnectRN from './index';
|
|
16
18
|
|
|
17
19
|
// @ts-ignore
|
|
@@ -153,6 +155,79 @@ class TLTRN {
|
|
|
153
155
|
return result;
|
|
154
156
|
};
|
|
155
157
|
|
|
158
|
+
// New dialog event logging methods
|
|
159
|
+
static logDialogShowEvent = async (dialogId: string, dialogTitle: string, dialogType: string) => {
|
|
160
|
+
let result = false
|
|
161
|
+
try {
|
|
162
|
+
result = AcousticConnectRN.logDialogShowEvent(dialogId, dialogTitle, dialogType);
|
|
163
|
+
} catch (error: Error | any) {
|
|
164
|
+
console.log('LogDialogShowEvent error: ', error.message);
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
static logDialogDismissEvent = async (dialogId: string, dismissReason: string) => {
|
|
170
|
+
let result = false
|
|
171
|
+
try {
|
|
172
|
+
result = AcousticConnectRN.logDialogDismissEvent(dialogId, dismissReason);
|
|
173
|
+
} catch (error: Error | any) {
|
|
174
|
+
console.log('LogDialogDismissEvent error: ', error.message);
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
static logDialogButtonClickEvent = async (dialogId: string, buttonText: string, buttonIndex: number) => {
|
|
180
|
+
let result = false
|
|
181
|
+
try {
|
|
182
|
+
result = AcousticConnectRN.logDialogButtonClickEvent(dialogId, buttonText, buttonIndex);
|
|
183
|
+
} catch (error: Error | any) {
|
|
184
|
+
console.log('LogDialogButtonClickEvent error: ', error.message);
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
static logDialogCustomEvent = async (dialogId: string, eventName: string, values: Record<string, string | number | boolean>) => {
|
|
190
|
+
let result = false
|
|
191
|
+
try {
|
|
192
|
+
result = AcousticConnectRN.logDialogCustomEvent(dialogId, eventName, values);
|
|
193
|
+
} catch (error: Error | any) {
|
|
194
|
+
console.log('LogDialogCustomEvent error: ', error.message);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Dialog event interceptor methods
|
|
200
|
+
static eventListenerRegistered = false;
|
|
201
|
+
static eventListenerUnsubscribe: (() => void) | null = null;
|
|
202
|
+
|
|
203
|
+
static interceptDialogEvents = (enable: boolean) => {
|
|
204
|
+
const dialogListener = DialogListener.getInstance();
|
|
205
|
+
|
|
206
|
+
if (enable && !TLTRN.eventListenerRegistered) {
|
|
207
|
+
dialogListener.startIntercepting();
|
|
208
|
+
TLTRN.eventListenerUnsubscribe = dialogListener.addEventListener((event: DialogEvent | DialogButtonClickEvent | DialogDismissEvent) => {
|
|
209
|
+
if ('buttonText' in event) {
|
|
210
|
+
// This is a button click event
|
|
211
|
+
TLTRN.logDialogButtonClickEvent(event.dialogId, event.buttonText, event.buttonIndex);
|
|
212
|
+
} else if ('dismissReason' in event) {
|
|
213
|
+
// This is a dialog dismiss event
|
|
214
|
+
TLTRN.logDialogDismissEvent(event.dialogId, event.dismissReason);
|
|
215
|
+
} else {
|
|
216
|
+
// This is a dialog show event
|
|
217
|
+
TLTRN.logDialogShowEvent(event.dialogId, event.dialogTitle, event.dialogType);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
TLTRN.eventListenerRegistered = true;
|
|
221
|
+
} else if (!enable && TLTRN.eventListenerRegistered) {
|
|
222
|
+
dialogListener.stopIntercepting();
|
|
223
|
+
if (TLTRN.eventListenerUnsubscribe) {
|
|
224
|
+
TLTRN.eventListenerUnsubscribe();
|
|
225
|
+
TLTRN.eventListenerUnsubscribe = null;
|
|
226
|
+
}
|
|
227
|
+
TLTRN.eventListenerRegistered = false;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
156
231
|
static listenToBridge = (message: any) => {
|
|
157
232
|
if (TLTRN.displayDebug) {
|
|
158
233
|
console.log(
|
|
@@ -15,9 +15,10 @@ import TLTRN from '../TLTRN';
|
|
|
15
15
|
interface ConnectProps {
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
captureKeyboardEvents: boolean;
|
|
18
|
+
captureDialogEvents?: boolean; // New prop for dialog event capture
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const Connect: React.FC<ConnectProps> = ({ children, captureKeyboardEvents }) => {
|
|
21
|
+
const Connect: React.FC<ConnectProps> = ({ children, captureKeyboardEvents, captureDialogEvents = false }) => {
|
|
21
22
|
const navigation = (children as any).ref; // Type assertion for children with ref
|
|
22
23
|
const currentRoute = useRef<string | undefined>(undefined);
|
|
23
24
|
const initial = useRef<boolean>(false);
|
|
@@ -26,6 +27,10 @@ const Connect: React.FC<ConnectProps> = ({ children, captureKeyboardEvents }) =>
|
|
|
26
27
|
TLTRN.interceptKeyboardEvents(captureKeyboardEvents);
|
|
27
28
|
}, [captureKeyboardEvents]);
|
|
28
29
|
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
TLTRN.interceptDialogEvents(captureDialogEvents);
|
|
32
|
+
}, [captureDialogEvents]);
|
|
33
|
+
|
|
29
34
|
useEffect(() => {
|
|
30
35
|
if (
|
|
31
36
|
!navigation ||
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Dialog Event Tracking
|
|
2
|
+
|
|
3
|
+
This document describes the dialog event tracking functionality added to the Acoustic Connect React Native SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The dialog tracking system provides automatic and manual tracking of dialog popup events and user interactions. It supports:
|
|
8
|
+
|
|
9
|
+
- **Automatic tracking** of React Native's `Alert.alert()` calls
|
|
10
|
+
- **Manual tracking** of custom dialog components
|
|
11
|
+
- **Button click tracking** for both automatic and custom dialogs
|
|
12
|
+
- **Event logging** to the Acoustic Connect backend
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
### 1. Automatic Alert.alert Interception
|
|
17
|
+
|
|
18
|
+
The system automatically intercepts and tracks all `Alert.alert()` calls without requiring any code changes:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// This will be automatically tracked
|
|
22
|
+
Alert.alert(
|
|
23
|
+
'Confirmation',
|
|
24
|
+
'Are you sure?',
|
|
25
|
+
[
|
|
26
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
27
|
+
{ text: 'OK', onPress: () => console.log('OK pressed') }
|
|
28
|
+
]
|
|
29
|
+
);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Manual Custom Dialog Tracking
|
|
33
|
+
|
|
34
|
+
For custom dialog components, you can manually track events:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { useDialogTracking } from 'react-native-acoustic-connect-beta';
|
|
38
|
+
|
|
39
|
+
const MyComponent = () => {
|
|
40
|
+
const { generateDialogId, trackDialogShow, trackDialogDismiss } = useDialogTracking();
|
|
41
|
+
|
|
42
|
+
const showDialog = () => {
|
|
43
|
+
const dialogId = generateDialogId();
|
|
44
|
+
trackDialogShow(dialogId, 'My Custom Dialog', [
|
|
45
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
46
|
+
{ text: 'OK' }
|
|
47
|
+
]);
|
|
48
|
+
// Show your custom dialog
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const hideDialog = () => {
|
|
52
|
+
const dialogId = generateDialogId(); // Store this in your component state
|
|
53
|
+
trackDialogDismiss(dialogId, 'user_dismiss');
|
|
54
|
+
// Hide your custom dialog
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Button Click Tracking
|
|
60
|
+
|
|
61
|
+
Track button clicks in custom dialogs:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const { createTrackedButton } = useDialogTracking();
|
|
65
|
+
|
|
66
|
+
const showDialogWithTrackedButtons = () => {
|
|
67
|
+
const dialogId = generateDialogId();
|
|
68
|
+
const originalButtons = [
|
|
69
|
+
{ text: 'No', style: 'cancel' },
|
|
70
|
+
{ text: 'Yes', onPress: () => console.log('Yes pressed') }
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
// Create tracked buttons
|
|
74
|
+
const trackedButtons = originalButtons.map((button, index) =>
|
|
75
|
+
createTrackedButton(dialogId, button, index)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
trackDialogShow(dialogId, 'Confirmation', trackedButtons);
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. HOC-Based Automatic Tracking
|
|
83
|
+
|
|
84
|
+
For the easiest integration, use the HOC (Higher-Order Component) approach:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { withAcousticAutoDialog } from 'react-native-acoustic-connect-beta';
|
|
88
|
+
import { Dialog } from 'react-native-paper';
|
|
89
|
+
|
|
90
|
+
// Create tracked version of your dialog component
|
|
91
|
+
const TrackedDialog = withAcousticAutoDialog(Dialog);
|
|
92
|
+
|
|
93
|
+
// Use normally - all events automatically tracked!
|
|
94
|
+
<TrackedDialog
|
|
95
|
+
visible={dialogVisible}
|
|
96
|
+
onDismiss={hideDialog}
|
|
97
|
+
title="My Dialog"
|
|
98
|
+
>
|
|
99
|
+
<Dialog.Content>
|
|
100
|
+
<Text>Dialog content</Text>
|
|
101
|
+
</Dialog.Content>
|
|
102
|
+
<Dialog.Actions>
|
|
103
|
+
<Button onPress={hideDialog}>Cancel</Button>
|
|
104
|
+
<Button onPress={confirmAction}>Confirm</Button>
|
|
105
|
+
</Dialog.Actions>
|
|
106
|
+
</TrackedDialog>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The HOC automatically tracks:
|
|
110
|
+
- Dialog show events
|
|
111
|
+
- Dialog dismiss events
|
|
112
|
+
- Button click events within the dialog
|
|
113
|
+
- Recursively scans dialog children for buttons
|
|
114
|
+
|
|
115
|
+
## Setup
|
|
116
|
+
|
|
117
|
+
### 1. Enable Dialog Tracking in Connect Component
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { Connect } from 'react-native-acoustic-connect-beta';
|
|
121
|
+
|
|
122
|
+
<Connect
|
|
123
|
+
captureKeyboardEvents={true}
|
|
124
|
+
captureDialogEvents={true} // Enable dialog tracking
|
|
125
|
+
>
|
|
126
|
+
<NavigationContainer ref={navigationRef}>
|
|
127
|
+
{/* Your app content */}
|
|
128
|
+
</NavigationContainer>
|
|
129
|
+
</Connect>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Use the useDialogTracking Hook
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { useDialogTracking } from 'react-native-acoustic-connect-beta';
|
|
136
|
+
|
|
137
|
+
const MyComponent = () => {
|
|
138
|
+
const dialogTracking = useDialogTracking();
|
|
139
|
+
|
|
140
|
+
// Use dialogTracking methods...
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## API Reference
|
|
145
|
+
|
|
146
|
+
### Connect Component Props
|
|
147
|
+
|
|
148
|
+
| Prop | Type | Default | Description |
|
|
149
|
+
|------|------|---------|-------------|
|
|
150
|
+
| `captureDialogEvents` | `boolean` | `false` | Enable automatic dialog event tracking |
|
|
151
|
+
|
|
152
|
+
### useDialogTracking Hook
|
|
153
|
+
|
|
154
|
+
Returns an object with the following methods:
|
|
155
|
+
|
|
156
|
+
#### `generateDialogId(): string`
|
|
157
|
+
Generates a unique dialog identifier.
|
|
158
|
+
|
|
159
|
+
#### `trackDialogShow(dialogId: string, title: string, buttons?: AlertButton[]): void`
|
|
160
|
+
Tracks a dialog show event.
|
|
161
|
+
|
|
162
|
+
#### `trackDialogDismiss(dialogId: string, reason: string): void`
|
|
163
|
+
Tracks a dialog dismiss event.
|
|
164
|
+
|
|
165
|
+
#### `trackDialogButtonClick(dialogId: string, buttonText: string, buttonIndex: number): void`
|
|
166
|
+
Tracks a dialog button click event.
|
|
167
|
+
|
|
168
|
+
#### `trackDialogCustomEvent(dialogId: string, eventName: string, values: Record<string, string \| number \| boolean>): void`
|
|
169
|
+
Tracks a custom dialog event.
|
|
170
|
+
|
|
171
|
+
#### `createTrackedButton(dialogId: string, button: AlertButton, buttonIndex: number): AlertButton`
|
|
172
|
+
Creates a button with automatic click tracking.
|
|
173
|
+
|
|
174
|
+
#### `cleanup(): void`
|
|
175
|
+
Cleans up dialog tracking state.
|
|
176
|
+
|
|
177
|
+
### DialogListener Class
|
|
178
|
+
|
|
179
|
+
For advanced usage, you can directly use the DialogListener class:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { DialogListener } from 'react-native-acoustic-connect-beta';
|
|
183
|
+
|
|
184
|
+
const dialogListener = DialogListener.getInstance();
|
|
185
|
+
dialogListener.startIntercepting();
|
|
186
|
+
dialogListener.addEventListener((event) => {
|
|
187
|
+
console.log('Dialog event:', event);
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Event Types
|
|
192
|
+
|
|
193
|
+
### DialogEvent
|
|
194
|
+
```typescript
|
|
195
|
+
interface DialogEvent {
|
|
196
|
+
dialogId: string;
|
|
197
|
+
dialogTitle: string;
|
|
198
|
+
dialogType: 'alert' | 'custom' | 'modal';
|
|
199
|
+
timestamp: number;
|
|
200
|
+
buttons?: AlertButton[];
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### DialogButtonClickEvent
|
|
205
|
+
```typescript
|
|
206
|
+
interface DialogButtonClickEvent {
|
|
207
|
+
dialogId: string;
|
|
208
|
+
buttonText: string;
|
|
209
|
+
buttonIndex: number;
|
|
210
|
+
timestamp: number;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Native Interface Methods
|
|
215
|
+
|
|
216
|
+
The following methods are added to the AcousticConnectRN interface:
|
|
217
|
+
|
|
218
|
+
- `logDialogShowEvent(dialogId: string, dialogTitle: string, dialogType: string): boolean`
|
|
219
|
+
- `logDialogDismissEvent(dialogId: string, dismissReason: string): boolean`
|
|
220
|
+
- `logDialogButtonClickEvent(dialogId: string, buttonText: string, buttonIndex: number): boolean`
|
|
221
|
+
- `logDialogCustomEvent(dialogId: string, eventName: string, values: Record<string, string \| number \| boolean>): boolean`
|
|
222
|
+
|
|
223
|
+
## Best Practices
|
|
224
|
+
|
|
225
|
+
1. **Store Dialog IDs**: When manually tracking custom dialogs, store the dialog ID in your component state to properly track dismiss events.
|
|
226
|
+
|
|
227
|
+
2. **Use Consistent Naming**: Use descriptive dialog titles and button text for better analytics.
|
|
228
|
+
|
|
229
|
+
3. **Handle Errors**: Wrap dialog tracking calls in try-catch blocks to prevent app crashes.
|
|
230
|
+
|
|
231
|
+
4. **Clean Up**: Call the cleanup method when components unmount to prevent memory leaks.
|
|
232
|
+
|
|
233
|
+
## Example Implementation
|
|
234
|
+
|
|
235
|
+
See `src/examples/DialogTrackingExample.tsx` for a complete example showing all features.
|
|
236
|
+
|
|
237
|
+
## Troubleshooting
|
|
238
|
+
|
|
239
|
+
### Dialog events not being tracked
|
|
240
|
+
1. Ensure `captureDialogEvents={true}` is set on the Connect component
|
|
241
|
+
2. Check that the DialogListener is properly initialized
|
|
242
|
+
3. Verify that the native AcousticConnectRN interface is available
|
|
243
|
+
|
|
244
|
+
### Custom dialog tracking not working
|
|
245
|
+
1. Make sure you're calling `trackDialogShow` before showing the dialog
|
|
246
|
+
2. Store the dialog ID and use it consistently for dismiss events
|
|
247
|
+
3. Use `createTrackedButton` for automatic button click tracking
|
|
248
|
+
|
|
249
|
+
### Performance considerations
|
|
250
|
+
- Dialog tracking is lightweight and shouldn't impact app performance
|
|
251
|
+
- The system uses efficient event delegation and cleanup
|
|
252
|
+
- Dialog IDs are generated using a simple algorithm to avoid external dependencies
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Native Implementation for Dialog Event Tracking
|
|
2
|
+
|
|
3
|
+
This document summarizes the native implementation of the dialog event tracking functionality for both Android and iOS platforms.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The dialog event tracking system has been implemented across all three layers:
|
|
8
|
+
1. **TypeScript Interface** (`src/specs/react-native-acoustic-connect.nitro.ts`)
|
|
9
|
+
2. **Android Implementation** (`android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt`)
|
|
10
|
+
3. **iOS Implementation** (`ios/HybridAcousticConnectRN.swift`)
|
|
11
|
+
|
|
12
|
+
## Interface Methods Added
|
|
13
|
+
|
|
14
|
+
Four new methods were added to the `AcousticConnectRN` interface:
|
|
15
|
+
|
|
16
|
+
### 1. `logDialogShowEvent`
|
|
17
|
+
- **Purpose**: Logs when a dialog is shown
|
|
18
|
+
- **Parameters**:
|
|
19
|
+
- `dialogId`: Unique identifier for the dialog
|
|
20
|
+
- `dialogTitle`: The title of the dialog
|
|
21
|
+
- `dialogType`: The type of dialog (alert, custom, modal)
|
|
22
|
+
- **Returns**: `Boolean` indicating success/failure
|
|
23
|
+
|
|
24
|
+
### 2. `logDialogDismissEvent`
|
|
25
|
+
- **Purpose**: Logs when a dialog is dismissed
|
|
26
|
+
- **Parameters**:
|
|
27
|
+
- `dialogId`: Unique identifier for the dialog
|
|
28
|
+
- `dismissReason`: The reason for dismissing the dialog
|
|
29
|
+
- **Returns**: `Boolean` indicating success/failure
|
|
30
|
+
|
|
31
|
+
### 3. `logDialogButtonClickEvent`
|
|
32
|
+
- **Purpose**: Logs when a button in a dialog is clicked
|
|
33
|
+
- **Parameters**:
|
|
34
|
+
- `dialogId`: Unique identifier for the dialog
|
|
35
|
+
- `buttonText`: The text of the clicked button
|
|
36
|
+
- `buttonIndex`: The index of the clicked button
|
|
37
|
+
- **Returns**: `Boolean` indicating success/failure
|
|
38
|
+
|
|
39
|
+
### 4. `logDialogCustomEvent`
|
|
40
|
+
- **Purpose**: Logs custom dialog events with additional data
|
|
41
|
+
- **Parameters**:
|
|
42
|
+
- `dialogId`: Unique identifier for the dialog
|
|
43
|
+
- `eventName`: The name of the custom event
|
|
44
|
+
- `values`: A map of values associated with the event
|
|
45
|
+
- **Returns**: `Boolean` indicating success/failure
|
|
46
|
+
|
|
47
|
+
## Android Implementation
|
|
48
|
+
|
|
49
|
+
### File: `android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt`
|
|
50
|
+
|
|
51
|
+
All four methods have been implemented using the existing `Connect.logCustomEvent()` API:
|
|
52
|
+
|
|
53
|
+
```kotlin
|
|
54
|
+
override fun logDialogShowEvent(dialogId: String, dialogTitle: String, dialogType: String): Boolean {
|
|
55
|
+
var result = false
|
|
56
|
+
try {
|
|
57
|
+
val values = HashMap<String?, String?>()
|
|
58
|
+
values["dialogId"] = dialogId
|
|
59
|
+
values["dialogTitle"] = dialogTitle
|
|
60
|
+
values["dialogType"] = dialogType
|
|
61
|
+
values["eventType"] = "dialog_show"
|
|
62
|
+
values["timestamp"] = System.currentTimeMillis().toString()
|
|
63
|
+
|
|
64
|
+
result = Connect.logCustomEvent("DialogShowEvent", values, EOMonitoringLevel.kEOMonitoringLevelInfo.value)
|
|
65
|
+
} catch (e: Exception) {
|
|
66
|
+
println("Error logging dialog show event: ${e.message}")
|
|
67
|
+
result = false
|
|
68
|
+
}
|
|
69
|
+
return result
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Key Features:
|
|
74
|
+
- **Error Handling**: All methods include try-catch blocks for robust error handling
|
|
75
|
+
- **Consistent Logging**: Uses the same `Connect.logCustomEvent()` API as other events
|
|
76
|
+
- **Timestamp**: Includes millisecond timestamps for event tracking
|
|
77
|
+
- **Event Type**: Each event includes an `eventType` field for easy filtering
|
|
78
|
+
- **Monitoring Level**: Uses `EOMonitoringLevel.kEOMonitoringLevelInfo.value` for consistent logging level
|
|
79
|
+
|
|
80
|
+
## iOS Implementation
|
|
81
|
+
|
|
82
|
+
### File: `ios/HybridAcousticConnectRN.swift`
|
|
83
|
+
|
|
84
|
+
All four methods have been implemented using the existing `ConnectCustomEvent().logEvent()` API:
|
|
85
|
+
|
|
86
|
+
```swift
|
|
87
|
+
func logDialogShowEvent(dialogId: String, dialogTitle: String, dialogType: String) throws -> Bool {
|
|
88
|
+
let values: [String: Any] = [
|
|
89
|
+
"dialogId": dialogId,
|
|
90
|
+
"dialogTitle": dialogTitle,
|
|
91
|
+
"dialogType": dialogType,
|
|
92
|
+
"eventType": "dialog_show",
|
|
93
|
+
"timestamp": String(Int(Date().timeIntervalSince1970 * 1000))
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
let result = ConnectCustomEvent().logEvent("DialogShowEvent", values: values, level: kConnectMonitoringLevelType.connectMonitoringLevelInfo)
|
|
97
|
+
return result
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Key Features:
|
|
102
|
+
- **Error Handling**: Uses Swift's throwing mechanism for error handling
|
|
103
|
+
- **Consistent Logging**: Uses the same `ConnectCustomEvent().logEvent()` API as other events
|
|
104
|
+
- **Timestamp**: Includes millisecond timestamps for event tracking
|
|
105
|
+
- **Event Type**: Each event includes an `eventType` field for easy filtering
|
|
106
|
+
- **Monitoring Level**: Uses `kConnectMonitoringLevelType.connectMonitoringLevelInfo` for consistent logging level
|
|
107
|
+
- **Type Safety**: Leverages Swift's strong typing for parameter validation
|
|
108
|
+
|
|
109
|
+
## Generated Spec Files
|
|
110
|
+
|
|
111
|
+
The Nitro framework automatically generated the interface specifications:
|
|
112
|
+
|
|
113
|
+
### Android: `nitrogen/generated/android/kotlin/com/margelo/nitro/acousticconnectrn/HybridAcousticConnectRNSpec.kt`
|
|
114
|
+
- Contains abstract method declarations for all four dialog event methods
|
|
115
|
+
- Properly annotated with `@DoNotStrip` and `@Keep` for ProGuard compatibility
|
|
116
|
+
|
|
117
|
+
### iOS: `nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec.swift`
|
|
118
|
+
- Contains protocol method declarations for all four dialog event methods
|
|
119
|
+
- Includes proper Swift documentation comments
|
|
120
|
+
|
|
121
|
+
## Data Structure
|
|
122
|
+
|
|
123
|
+
All dialog events include consistent metadata:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"dialogId": "unique_dialog_identifier",
|
|
128
|
+
"eventType": "dialog_show|dialog_dismiss|dialog_button_click|dialog_custom_event",
|
|
129
|
+
"timestamp": "1703123456789",
|
|
130
|
+
// Additional fields specific to each event type
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Error Handling Strategy
|
|
135
|
+
|
|
136
|
+
### Android:
|
|
137
|
+
- Uses try-catch blocks to prevent app crashes
|
|
138
|
+
- Logs error messages to console for debugging
|
|
139
|
+
- Returns `false` on any exception
|
|
140
|
+
|
|
141
|
+
### iOS:
|
|
142
|
+
- Uses Swift's throwing mechanism
|
|
143
|
+
- Errors are propagated up the call stack
|
|
144
|
+
- Caller can handle errors appropriately
|
|
145
|
+
|
|
146
|
+
## Performance Considerations
|
|
147
|
+
|
|
148
|
+
- **Lightweight**: All methods are lightweight and don't perform heavy operations
|
|
149
|
+
- **Async Safe**: Methods can be called from any thread
|
|
150
|
+
- **Memory Efficient**: Uses simple data structures and minimal memory allocation
|
|
151
|
+
- **Network Efficient**: Events are batched and sent according to existing Connect SDK policies
|
|
152
|
+
|
|
153
|
+
## Testing
|
|
154
|
+
|
|
155
|
+
The implementation can be tested using:
|
|
156
|
+
|
|
157
|
+
1. **Unit Tests**: Test individual method calls
|
|
158
|
+
2. **Integration Tests**: Test with actual dialog components
|
|
159
|
+
3. **End-to-End Tests**: Verify events appear in Acoustic Connect dashboard
|
|
160
|
+
|
|
161
|
+
## Future Enhancements
|
|
162
|
+
|
|
163
|
+
Potential improvements for future versions:
|
|
164
|
+
|
|
165
|
+
1. **Event Batching**: Batch multiple dialog events for better performance
|
|
166
|
+
2. **Custom Event Types**: Allow developers to define custom dialog event types
|
|
167
|
+
3. **Event Filtering**: Add configuration options to filter specific dialog events
|
|
168
|
+
4. **Analytics Integration**: Direct integration with analytics platforms
|
|
169
|
+
5. **Performance Metrics**: Track dialog performance metrics (show time, interaction time)
|
|
170
|
+
|
|
171
|
+
## Compatibility
|
|
172
|
+
|
|
173
|
+
- **Backward Compatible**: No breaking changes to existing functionality
|
|
174
|
+
- **Platform Agnostic**: Consistent API across Android and iOS
|
|
175
|
+
- **Framework Compatible**: Works with existing Connect SDK infrastructure
|
|
176
|
+
- **Version Safe**: Compatible with current React Native versions
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
|
|
11
|
+
import React, { useState } from 'react';
|
|
12
|
+
import { View, Text, Button, Alert, StyleSheet } from 'react-native';
|
|
13
|
+
import { useDialogTracking } from '../utils/useDialogTracking';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Example component demonstrating dialog event tracking
|
|
17
|
+
* Shows how to use both automatic Alert.alert interception and manual custom dialog tracking
|
|
18
|
+
*/
|
|
19
|
+
const DialogTrackingExample: React.FC = () => {
|
|
20
|
+
const [customDialogVisible, setCustomDialogVisible] = useState(false);
|
|
21
|
+
const {
|
|
22
|
+
generateDialogId,
|
|
23
|
+
trackDialogShow,
|
|
24
|
+
trackDialogDismiss,
|
|
25
|
+
createTrackedButton
|
|
26
|
+
} = useDialogTracking();
|
|
27
|
+
|
|
28
|
+
// Example 1: Automatic Alert.alert tracking (no additional code needed)
|
|
29
|
+
const showAlertDialog = () => {
|
|
30
|
+
Alert.alert(
|
|
31
|
+
'Confirmation',
|
|
32
|
+
'Are you sure you want to proceed?',
|
|
33
|
+
[
|
|
34
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
35
|
+
{ text: 'OK', onPress: () => console.log('OK pressed') }
|
|
36
|
+
]
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Example 2: Manual custom dialog tracking
|
|
41
|
+
const showCustomDialog = () => {
|
|
42
|
+
const dialogId = generateDialogId();
|
|
43
|
+
const title = 'Custom Dialog';
|
|
44
|
+
const buttons = [
|
|
45
|
+
{ text: 'Cancel', style: 'cancel' as const },
|
|
46
|
+
{ text: 'Confirm', onPress: () => console.log('Confirm pressed') }
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Track dialog show event
|
|
50
|
+
trackDialogShow(dialogId, title, buttons);
|
|
51
|
+
setCustomDialogVisible(true);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const hideCustomDialog = () => {
|
|
55
|
+
const dialogId = generateDialogId(); // In real app, you'd store this
|
|
56
|
+
trackDialogDismiss(dialogId, 'user_dismiss');
|
|
57
|
+
setCustomDialogVisible(false);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Example 3: Custom dialog with tracked buttons
|
|
61
|
+
const showTrackedCustomDialog = () => {
|
|
62
|
+
const dialogId = generateDialogId();
|
|
63
|
+
const title = 'Tracked Custom Dialog';
|
|
64
|
+
const originalButtons = [
|
|
65
|
+
{ text: 'No', style: 'cancel' as const },
|
|
66
|
+
{ text: 'Yes', onPress: () => console.log('Yes pressed') }
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Create tracked buttons
|
|
70
|
+
const trackedButtons = originalButtons.map((button, index) =>
|
|
71
|
+
createTrackedButton(dialogId, button, index)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
trackDialogShow(dialogId, title, trackedButtons);
|
|
75
|
+
setCustomDialogVisible(true);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<View style={styles.container}>
|
|
80
|
+
<Text style={styles.title}>Dialog Tracking Examples</Text>
|
|
81
|
+
|
|
82
|
+
<View style={styles.buttonContainer}>
|
|
83
|
+
<Button
|
|
84
|
+
title="Show Alert Dialog (Auto-tracked)"
|
|
85
|
+
onPress={showAlertDialog}
|
|
86
|
+
/>
|
|
87
|
+
</View>
|
|
88
|
+
|
|
89
|
+
<View style={styles.buttonContainer}>
|
|
90
|
+
<Button
|
|
91
|
+
title="Show Custom Dialog (Manual tracking)"
|
|
92
|
+
onPress={showCustomDialog}
|
|
93
|
+
/>
|
|
94
|
+
</View>
|
|
95
|
+
|
|
96
|
+
<View style={styles.buttonContainer}>
|
|
97
|
+
<Button
|
|
98
|
+
title="Show Tracked Custom Dialog"
|
|
99
|
+
onPress={showTrackedCustomDialog}
|
|
100
|
+
/>
|
|
101
|
+
</View>
|
|
102
|
+
|
|
103
|
+
{customDialogVisible && (
|
|
104
|
+
<View style={styles.customDialog}>
|
|
105
|
+
<Text style={styles.dialogTitle}>Custom Dialog</Text>
|
|
106
|
+
<Text style={styles.dialogMessage}>
|
|
107
|
+
This is a custom dialog with manual tracking
|
|
108
|
+
</Text>
|
|
109
|
+
<View style={styles.dialogButtons}>
|
|
110
|
+
<Button title="Cancel" onPress={hideCustomDialog} />
|
|
111
|
+
<Button title="OK" onPress={hideCustomDialog} />
|
|
112
|
+
</View>
|
|
113
|
+
</View>
|
|
114
|
+
)}
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const styles = StyleSheet.create({
|
|
120
|
+
container: {
|
|
121
|
+
flex: 1,
|
|
122
|
+
padding: 20,
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
},
|
|
125
|
+
title: {
|
|
126
|
+
fontSize: 24,
|
|
127
|
+
fontWeight: 'bold',
|
|
128
|
+
textAlign: 'center',
|
|
129
|
+
marginBottom: 30,
|
|
130
|
+
},
|
|
131
|
+
buttonContainer: {
|
|
132
|
+
marginVertical: 10,
|
|
133
|
+
},
|
|
134
|
+
customDialog: {
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
top: '50%',
|
|
137
|
+
left: 20,
|
|
138
|
+
right: 20,
|
|
139
|
+
backgroundColor: 'white',
|
|
140
|
+
borderRadius: 10,
|
|
141
|
+
padding: 20,
|
|
142
|
+
elevation: 5,
|
|
143
|
+
shadowColor: '#000',
|
|
144
|
+
shadowOffset: { width: 0, height: 2 },
|
|
145
|
+
shadowOpacity: 0.25,
|
|
146
|
+
shadowRadius: 3.84,
|
|
147
|
+
},
|
|
148
|
+
dialogTitle: {
|
|
149
|
+
fontSize: 18,
|
|
150
|
+
fontWeight: 'bold',
|
|
151
|
+
marginBottom: 10,
|
|
152
|
+
},
|
|
153
|
+
dialogMessage: {
|
|
154
|
+
fontSize: 16,
|
|
155
|
+
marginBottom: 20,
|
|
156
|
+
},
|
|
157
|
+
dialogButtons: {
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
justifyContent: 'space-around',
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export default DialogTrackingExample;
|