core-outline 1.1.3 → 1.1.5
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/index.es.js +2336 -24
- package/dist/index.js +2340 -24
- package/package.json +3 -2
- package/src/components/CoreOutline/CoreOutline.js +330 -5
- package/src/components/CoreOutline/socket.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "core-outline",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "A React component for Core&Outline",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"react-error-boundary": "^5.0.0",
|
|
48
48
|
"react-tracking": "^9.3.2",
|
|
49
|
-
"rrweb": "^2.0.0-alpha.4"
|
|
49
|
+
"rrweb": "^2.0.0-alpha.4",
|
|
50
|
+
"socket.io-client": "^4.8.1"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -1,15 +1,118 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { record } from 'rrweb';
|
|
3
|
+
import { v4 } from 'uuid';
|
|
4
|
+
import socket from './socket';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
import {
|
|
7
|
+
EventType,
|
|
8
|
+
eventWithTime,
|
|
9
|
+
IncrementalSource,
|
|
10
|
+
MouseInteractions,
|
|
11
|
+
} from 'rrweb';
|
|
12
|
+
|
|
13
|
+
const CoreOutline = ({ children, data_source_id, data_source_secret }) => {
|
|
5
14
|
const [events, setEvents] = useState([]);
|
|
15
|
+
const [replayEvents, setReplayEvents] = useState([]);
|
|
6
16
|
const recorderActive = useRef(false);
|
|
7
17
|
const [currentPage, setCurrentPage] = useState(window.location.href);
|
|
18
|
+
const [location, setLocation] = useState({ latitude: null, longitude: null });
|
|
19
|
+
const [browser, setBrowser] = useState(getBrowserInfo());
|
|
20
|
+
const [appToken, setAppToken] = useState('');
|
|
21
|
+
const [sessionId, setSessionId] = useState('');
|
|
22
|
+
|
|
23
|
+
async function authorizeApp(data_source_id, data_source_secret) {
|
|
24
|
+
const requestOptions = {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
data_source_id: data_source_id,
|
|
31
|
+
data_source_secret: data_source_secret,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
const response = fetch(
|
|
35
|
+
`http://localhost:4000/data-source/authorize`,
|
|
36
|
+
requestOptions
|
|
37
|
+
)
|
|
38
|
+
.then((response) => response.json())
|
|
39
|
+
.then((data) => {
|
|
40
|
+
setAppToken(data.app_token);
|
|
41
|
+
return data;
|
|
42
|
+
})
|
|
43
|
+
.catch((error) => {
|
|
44
|
+
console.error('Error:', error);
|
|
45
|
+
return error;
|
|
46
|
+
});
|
|
47
|
+
return response;
|
|
48
|
+
}
|
|
49
|
+
useEffect(async () => {
|
|
50
|
+
await authorizeApp(data_source_id, data_source_secret);
|
|
51
|
+
setSessionId(v4());
|
|
52
|
+
localStorage.setItem('coreOutlineSessionId', sessionId);
|
|
53
|
+
localStorage.setItem('coreOutlineDataSourceIdId', data_source_id);
|
|
54
|
+
console.log('Session', sessionId);
|
|
55
|
+
socket.send({
|
|
56
|
+
topic: 'session-data',
|
|
57
|
+
data_source_id: data_source_id,
|
|
58
|
+
session_id: sessionId,
|
|
59
|
+
start_date: new Date().toLocaleString(),
|
|
60
|
+
latitude: location.latitude,
|
|
61
|
+
longitude: location.longitude,
|
|
62
|
+
browser: browser,
|
|
63
|
+
event_id: 'SESSION_START',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
socket.send({
|
|
68
|
+
topic: 'session-data',
|
|
69
|
+
data_source_id: data_source_id,
|
|
70
|
+
session_id: sessionId,
|
|
71
|
+
start_date: new Date().toLocaleString(),
|
|
72
|
+
latitude: location.latitude,
|
|
73
|
+
longitude: location.longitude,
|
|
74
|
+
browser: browser,
|
|
75
|
+
event_id: 'SESSION_END',
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
8
79
|
|
|
9
80
|
useEffect(() => {
|
|
10
|
-
|
|
81
|
+
const navigationEvent = {
|
|
82
|
+
type: 4,
|
|
83
|
+
data: {
|
|
84
|
+
href: window.location.href,
|
|
85
|
+
width: 1536,
|
|
86
|
+
height: 730,
|
|
87
|
+
},
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
};
|
|
90
|
+
const startTime = performance.now();
|
|
91
|
+
const observer = new MutationObserver(() => {
|
|
92
|
+
const endTime = performance.now();
|
|
93
|
+
const loadTime = endTime - startTime;
|
|
94
|
+
navigationEvent.data.loadTime = loadTime;
|
|
95
|
+
const hours = Math.floor(loadTime / 3600000);
|
|
96
|
+
const minutes = Math.floor((loadTime % 3600000) / 60000);
|
|
97
|
+
const seconds = Math.floor((loadTime % 60000) / 1000);
|
|
98
|
+
navigationEvent.data.loadTimeFormatted = `${hours} hours ${minutes} minutes ${seconds} seconds`;
|
|
99
|
+
observer.disconnect();
|
|
100
|
+
});
|
|
101
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
102
|
+
setEvents((prevEvents) => [...prevEvents, navigationEvent]);
|
|
11
103
|
const handleNavigation = () => {
|
|
12
104
|
setCurrentPage(window.location.href);
|
|
105
|
+
socket.send({
|
|
106
|
+
topic: 'session-data',
|
|
107
|
+
data_source_id: data_source_id,
|
|
108
|
+
session_id: sessionId,
|
|
109
|
+
start_date: new Date().toLocaleString(),
|
|
110
|
+
latitude: location.latitude,
|
|
111
|
+
longitude: location.longitude,
|
|
112
|
+
browser: browser,
|
|
113
|
+
page_name: window.location.href,
|
|
114
|
+
event_id: 'PAGE_NAVIGATION',
|
|
115
|
+
});
|
|
13
116
|
saveEvents(events);
|
|
14
117
|
};
|
|
15
118
|
|
|
@@ -26,13 +129,60 @@ const CoreOutline = ({ children }) => {
|
|
|
26
129
|
};
|
|
27
130
|
}, [currentPage]);
|
|
28
131
|
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
console.log('Location', location);
|
|
134
|
+
getLocation();
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
29
137
|
useEffect(() => {
|
|
30
138
|
let stopFn;
|
|
31
139
|
if (!recorderActive.current) {
|
|
32
140
|
stopFn = record({
|
|
33
141
|
emit(event) {
|
|
142
|
+
setReplayEvents((prevEvents) => [...prevEvents, event]);
|
|
143
|
+
},
|
|
144
|
+
maskAllInputs: false,
|
|
145
|
+
maskTextSelector: null,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
recorderActive.current = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return () => {
|
|
152
|
+
if (stopFn) {
|
|
153
|
+
stopFn();
|
|
154
|
+
saveEvents(events);
|
|
155
|
+
|
|
156
|
+
recorderActive.current = false;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
let stopFn;
|
|
163
|
+
if (!recorderActive.current) {
|
|
164
|
+
stopFn = record({
|
|
165
|
+
emit(event) {
|
|
166
|
+
console.log('Event:', event);
|
|
167
|
+
if (event.type == 3 && event.data.type == 2) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (event.type == 4) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (event.type == 3 && event.data.source == 0) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (event.type == 3 && event.data.source == 1) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (event.type == 3 && event.data.type == 3) {
|
|
180
|
+
console.log('Contextmenu Event:', event);
|
|
181
|
+
}
|
|
34
182
|
setEvents((prevEvents) => [...prevEvents, event]);
|
|
35
183
|
},
|
|
184
|
+
maskAllInputs: false,
|
|
185
|
+
maskTextSelector: null,
|
|
36
186
|
});
|
|
37
187
|
|
|
38
188
|
recorderActive.current = true;
|
|
@@ -48,6 +198,74 @@ const CoreOutline = ({ children }) => {
|
|
|
48
198
|
};
|
|
49
199
|
}, []);
|
|
50
200
|
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
const handleClick = (event) => {
|
|
203
|
+
console.log('Inner text:', event.target.innerText);
|
|
204
|
+
const clickEvent = {
|
|
205
|
+
type: EventType.IncrementalSnapshot,
|
|
206
|
+
data: {
|
|
207
|
+
source: IncrementalSource.MouseInteraction,
|
|
208
|
+
type: MouseInteractions.Click,
|
|
209
|
+
id: event.target.id,
|
|
210
|
+
x: event.clientX,
|
|
211
|
+
y: event.clientY,
|
|
212
|
+
},
|
|
213
|
+
timestamp: Date.now(),
|
|
214
|
+
};
|
|
215
|
+
const clickedElement = document.elementFromPoint(
|
|
216
|
+
clickEvent.data.x,
|
|
217
|
+
clickEvent.data.y
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
if (clickedElement) {
|
|
221
|
+
clickEvent.data.metadata = {
|
|
222
|
+
label:
|
|
223
|
+
clickedElement.getAttribute('data-label') ||
|
|
224
|
+
clickedElement.innerText ||
|
|
225
|
+
null,
|
|
226
|
+
value:
|
|
227
|
+
clickedElement.getAttribute('value') ||
|
|
228
|
+
clickedElement.innerText ||
|
|
229
|
+
null,
|
|
230
|
+
id:
|
|
231
|
+
clickedElement.getAttribute('id') ||
|
|
232
|
+
clickedElement.innerText ||
|
|
233
|
+
null,
|
|
234
|
+
name:
|
|
235
|
+
clickedElement.getAttribute('name') ||
|
|
236
|
+
clickedElement.innerText ||
|
|
237
|
+
null,
|
|
238
|
+
class:
|
|
239
|
+
clickedElement.getAttribute('class') ||
|
|
240
|
+
clickedElement.innerText ||
|
|
241
|
+
null,
|
|
242
|
+
tag: clickedElement.tagName,
|
|
243
|
+
html: String(clickedElement.getHTML()),
|
|
244
|
+
innerText: event.target.innerText,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
setEvents((prevEvents) => [...prevEvents, clickEvent]);
|
|
248
|
+
|
|
249
|
+
socket.send({
|
|
250
|
+
topic: 'session-data',
|
|
251
|
+
data_source_id: data_source_id,
|
|
252
|
+
session_id: sessionId,
|
|
253
|
+
start_date: new Date().toLocaleString(),
|
|
254
|
+
latitude: location.latitude,
|
|
255
|
+
longitude: location.longitude,
|
|
256
|
+
browser: browser,
|
|
257
|
+
event_id: 'ITEM_CLICKED',
|
|
258
|
+
// item_clicked: event.target,
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
document.addEventListener('click', handleClick);
|
|
263
|
+
|
|
264
|
+
return () => {
|
|
265
|
+
document.removeEventListener('click', handleClick);
|
|
266
|
+
};
|
|
267
|
+
}, []);
|
|
268
|
+
|
|
51
269
|
function saveEvents(events) {
|
|
52
270
|
const requestOptions = {
|
|
53
271
|
method: 'PUT',
|
|
@@ -56,8 +274,9 @@ const CoreOutline = ({ children }) => {
|
|
|
56
274
|
},
|
|
57
275
|
body: JSON.stringify(events),
|
|
58
276
|
};
|
|
59
|
-
|
|
60
|
-
|
|
277
|
+
fetch(
|
|
278
|
+
`http://localhost:5000/generate-s3-url/${data_source_id}/${sessionId}`
|
|
279
|
+
)
|
|
61
280
|
.then((response) => response.json())
|
|
62
281
|
.then((data) => {
|
|
63
282
|
const uploadURL = data.signed_url;
|
|
@@ -65,7 +284,113 @@ const CoreOutline = ({ children }) => {
|
|
|
65
284
|
});
|
|
66
285
|
}
|
|
67
286
|
|
|
68
|
-
|
|
287
|
+
function saveEventsLocally(type = 'formatted' || 'raw') {
|
|
288
|
+
let blob = '';
|
|
289
|
+
if (type === 'formatted') {
|
|
290
|
+
blob = new Blob([JSON.stringify(events, null, 2)], {
|
|
291
|
+
type: 'application/json',
|
|
292
|
+
});
|
|
293
|
+
} else if (type === 'raw') {
|
|
294
|
+
blob = new Blob([JSON.stringify(replayEvents, null, 2)], {
|
|
295
|
+
type: 'application/json',
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const url = URL.createObjectURL(blob);
|
|
299
|
+
const a = document.createElement('a');
|
|
300
|
+
a.href = url;
|
|
301
|
+
a.download = `${type}_events.json`;
|
|
302
|
+
a.click();
|
|
303
|
+
URL.revokeObjectURL(url);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function getBrowserInfo() {
|
|
307
|
+
const userAgent = navigator.userAgent;
|
|
308
|
+
let browserName = 'Unknown';
|
|
309
|
+
|
|
310
|
+
if (userAgent.indexOf('Firefox') > -1) {
|
|
311
|
+
browserName = 'Firefox';
|
|
312
|
+
} else if (
|
|
313
|
+
userAgent.indexOf('Opera') > -1 ||
|
|
314
|
+
userAgent.indexOf('OPR') > -1
|
|
315
|
+
) {
|
|
316
|
+
browserName = 'Opera';
|
|
317
|
+
} else if (userAgent.indexOf('Chrome') > -1) {
|
|
318
|
+
browserName = 'Chrome';
|
|
319
|
+
} else if (userAgent.indexOf('Safari') > -1) {
|
|
320
|
+
browserName = 'Safari';
|
|
321
|
+
} else if (
|
|
322
|
+
userAgent.indexOf('MSIE') > -1 ||
|
|
323
|
+
userAgent.indexOf('Trident/') > -1
|
|
324
|
+
) {
|
|
325
|
+
browserName = 'Internet Explorer';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return browserName;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function getLocation() {
|
|
332
|
+
if (navigator.geolocation) {
|
|
333
|
+
navigator.geolocation.getCurrentPosition(
|
|
334
|
+
(position) => {
|
|
335
|
+
setLocation({
|
|
336
|
+
latitude: position.coords.latitude,
|
|
337
|
+
longitude: position.coords.longitude,
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
(error) => {
|
|
341
|
+
console.error('Error getting location:', error);
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
} else {
|
|
345
|
+
console.error('Geolocation is not supported by this browser.');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div>
|
|
351
|
+
<button
|
|
352
|
+
onClick={() => {
|
|
353
|
+
saveEventsLocally('formatted');
|
|
354
|
+
saveEventsLocally('raw');
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
Save Events Locally
|
|
358
|
+
</button>
|
|
359
|
+
{children}
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
export const flag_item_clicked = (item_id) => {
|
|
365
|
+
const sessionId = localStorage.getItem('coreOutlineSessionId');
|
|
366
|
+
const dataSourceId = localStorage.getItem('coreOutlineDataSourceIdId');
|
|
367
|
+
console.log('Session', sessionId);
|
|
368
|
+
console.log('Data Source', dataSourceId);
|
|
369
|
+
socket.send({
|
|
370
|
+
topic: 'session-data',
|
|
371
|
+
data_source_id: dataSourceId,
|
|
372
|
+
session_id: sessionId,
|
|
373
|
+
start_date: new Date().toLocaleString(),
|
|
374
|
+
event_id: 'PRODUCT_CLICKED',
|
|
375
|
+
item_clicked: item_id,
|
|
376
|
+
});
|
|
377
|
+
return { status: 'success', message: 'Item click flagged successfully.' };
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
export const flag_item_purchased = (item_id) => {
|
|
381
|
+
const sessionId = localStorage.getItem('coreOutlineSessionId');
|
|
382
|
+
const dataSourceId = localStorage.getItem('coreOutlineDataSourceIdId');
|
|
383
|
+
console.log('Session', sessionId);
|
|
384
|
+
console.log('Data Source', dataSourceId);
|
|
385
|
+
socket.send({
|
|
386
|
+
topic: 'session-data',
|
|
387
|
+
data_source_id: dataSourceId,
|
|
388
|
+
session_id: sessionId,
|
|
389
|
+
start_date: new Date().toLocaleString(),
|
|
390
|
+
event_id: 'PRODUCT_PURCHASED',
|
|
391
|
+
item_clicked: item_id,
|
|
392
|
+
});
|
|
393
|
+
return { status: 'success', message: 'Item purchase flagged successfully.' };
|
|
69
394
|
};
|
|
70
395
|
|
|
71
396
|
export default CoreOutline;
|