core-outline 1.1.4 → 1.1.6
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.
|
@@ -1,48 +1,109 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from
|
|
2
|
-
import { record } from
|
|
3
|
-
import { v4 } from
|
|
4
|
-
import
|
|
5
|
-
const socket = io("http://localhost:4000");
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { record } from 'rrweb';
|
|
3
|
+
import { v4 } from 'uuid';
|
|
4
|
+
import socket from './socket';
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
EventType,
|
|
8
|
+
eventWithTime,
|
|
9
|
+
IncrementalSource,
|
|
10
|
+
MouseInteractions,
|
|
11
|
+
} from 'rrweb';
|
|
12
|
+
|
|
13
|
+
const CoreOutline = ({ children, data_source_id, data_source_secret }) => {
|
|
8
14
|
const [events, setEvents] = useState([]);
|
|
15
|
+
const [replayEvents, setReplayEvents] = useState([]);
|
|
9
16
|
const recorderActive = useRef(false);
|
|
10
17
|
const [currentPage, setCurrentPage] = useState(window.location.href);
|
|
11
18
|
const [location, setLocation] = useState({ latitude: null, longitude: null });
|
|
12
19
|
const [browser, setBrowser] = useState(getBrowserInfo());
|
|
13
|
-
const
|
|
20
|
+
const [appToken, setAppToken] = useState('');
|
|
21
|
+
const [sessionId, setSessionId] = useState('');
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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',
|
|
18
57
|
data_source_id: data_source_id,
|
|
19
58
|
session_id: sessionId,
|
|
20
59
|
start_date: new Date().toLocaleString(),
|
|
21
60
|
latitude: location.latitude,
|
|
22
61
|
longitude: location.longitude,
|
|
23
62
|
browser: browser,
|
|
24
|
-
event_id:
|
|
63
|
+
event_id: 'SESSION_START',
|
|
25
64
|
});
|
|
26
65
|
|
|
27
66
|
return () => {
|
|
28
|
-
socket.
|
|
29
|
-
topic:
|
|
67
|
+
socket.send({
|
|
68
|
+
topic: 'session-data',
|
|
30
69
|
data_source_id: data_source_id,
|
|
31
70
|
session_id: sessionId,
|
|
32
71
|
start_date: new Date().toLocaleString(),
|
|
33
72
|
latitude: location.latitude,
|
|
34
73
|
longitude: location.longitude,
|
|
35
74
|
browser: browser,
|
|
36
|
-
event_id:
|
|
75
|
+
event_id: 'SESSION_END',
|
|
37
76
|
});
|
|
38
77
|
};
|
|
39
78
|
}, []);
|
|
40
79
|
|
|
41
80
|
useEffect(() => {
|
|
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]);
|
|
42
103
|
const handleNavigation = () => {
|
|
43
104
|
setCurrentPage(window.location.href);
|
|
44
|
-
socket.
|
|
45
|
-
topic:
|
|
105
|
+
socket.send({
|
|
106
|
+
topic: 'session-data',
|
|
46
107
|
data_source_id: data_source_id,
|
|
47
108
|
session_id: sessionId,
|
|
48
109
|
start_date: new Date().toLocaleString(),
|
|
@@ -50,12 +111,12 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
50
111
|
longitude: location.longitude,
|
|
51
112
|
browser: browser,
|
|
52
113
|
page_name: window.location.href,
|
|
53
|
-
event_id:
|
|
114
|
+
event_id: 'PAGE_NAVIGATION',
|
|
54
115
|
});
|
|
55
116
|
saveEvents(events);
|
|
56
117
|
};
|
|
57
118
|
|
|
58
|
-
window.addEventListener(
|
|
119
|
+
window.addEventListener('popstate', handleNavigation);
|
|
59
120
|
const originalPushState = window.history.pushState;
|
|
60
121
|
window.history.pushState = function (...args) {
|
|
61
122
|
originalPushState.apply(this, args);
|
|
@@ -63,13 +124,13 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
63
124
|
};
|
|
64
125
|
|
|
65
126
|
return () => {
|
|
66
|
-
window.removeEventListener(
|
|
127
|
+
window.removeEventListener('popstate', handleNavigation);
|
|
67
128
|
window.history.pushState = originalPushState;
|
|
68
129
|
};
|
|
69
130
|
}, [currentPage]);
|
|
70
131
|
|
|
71
132
|
useEffect(() => {
|
|
72
|
-
console.log(
|
|
133
|
+
console.log('Location', location);
|
|
73
134
|
getLocation();
|
|
74
135
|
}, []);
|
|
75
136
|
|
|
@@ -78,8 +139,50 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
78
139
|
if (!recorderActive.current) {
|
|
79
140
|
stopFn = record({
|
|
80
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
|
+
}
|
|
81
182
|
setEvents((prevEvents) => [...prevEvents, event]);
|
|
82
183
|
},
|
|
184
|
+
maskAllInputs: false,
|
|
185
|
+
maskTextSelector: null,
|
|
83
186
|
});
|
|
84
187
|
|
|
85
188
|
recorderActive.current = true;
|
|
@@ -97,38 +200,83 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
97
200
|
|
|
98
201
|
useEffect(() => {
|
|
99
202
|
const handleClick = (event) => {
|
|
100
|
-
console.log(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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',
|
|
104
251
|
data_source_id: data_source_id,
|
|
105
252
|
session_id: sessionId,
|
|
106
253
|
start_date: new Date().toLocaleString(),
|
|
107
254
|
latitude: location.latitude,
|
|
108
255
|
longitude: location.longitude,
|
|
109
256
|
browser: browser,
|
|
110
|
-
event_id:
|
|
111
|
-
item_clicked: event.target,
|
|
257
|
+
event_id: 'ITEM_CLICKED',
|
|
258
|
+
// item_clicked: event.target,
|
|
112
259
|
});
|
|
113
260
|
};
|
|
114
261
|
|
|
115
|
-
document.addEventListener(
|
|
262
|
+
document.addEventListener('click', handleClick);
|
|
116
263
|
|
|
117
264
|
return () => {
|
|
118
|
-
document.removeEventListener(
|
|
265
|
+
document.removeEventListener('click', handleClick);
|
|
119
266
|
};
|
|
120
267
|
}, []);
|
|
121
268
|
|
|
122
269
|
function saveEvents(events) {
|
|
123
270
|
const requestOptions = {
|
|
124
|
-
method:
|
|
271
|
+
method: 'PUT',
|
|
125
272
|
headers: {
|
|
126
|
-
|
|
273
|
+
'Content-Type': 'application/json',
|
|
127
274
|
},
|
|
128
275
|
body: JSON.stringify(events),
|
|
129
276
|
};
|
|
130
|
-
|
|
131
|
-
|
|
277
|
+
fetch(
|
|
278
|
+
`http://localhost:5000/generate-s3-url/${data_source_id}/${sessionId}`
|
|
279
|
+
)
|
|
132
280
|
.then((response) => response.json())
|
|
133
281
|
.then((data) => {
|
|
134
282
|
const uploadURL = data.signed_url;
|
|
@@ -136,31 +284,50 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
136
284
|
});
|
|
137
285
|
}
|
|
138
286
|
|
|
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
|
+
|
|
139
306
|
function getBrowserInfo() {
|
|
140
307
|
const userAgent = navigator.userAgent;
|
|
141
|
-
let browserName =
|
|
142
|
-
|
|
143
|
-
if (userAgent.indexOf(
|
|
144
|
-
browserName =
|
|
145
|
-
} else if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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';
|
|
153
326
|
}
|
|
154
327
|
|
|
155
328
|
return browserName;
|
|
156
329
|
}
|
|
157
330
|
|
|
158
|
-
useEffect(() => {
|
|
159
|
-
if (events.length > 0) {
|
|
160
|
-
socket.emit("record-event", { sessionId, events });
|
|
161
|
-
}
|
|
162
|
-
}, [events]);
|
|
163
|
-
|
|
164
331
|
function getLocation() {
|
|
165
332
|
if (navigator.geolocation) {
|
|
166
333
|
navigator.geolocation.getCurrentPosition(
|
|
@@ -171,15 +338,59 @@ const CoreOutline = ({ children, data_source_id }) => {
|
|
|
171
338
|
});
|
|
172
339
|
},
|
|
173
340
|
(error) => {
|
|
174
|
-
console.error(
|
|
341
|
+
console.error('Error getting location:', error);
|
|
175
342
|
}
|
|
176
343
|
);
|
|
177
344
|
} else {
|
|
178
|
-
console.error(
|
|
345
|
+
console.error('Geolocation is not supported by this browser.');
|
|
179
346
|
}
|
|
180
347
|
}
|
|
181
348
|
|
|
182
|
-
return
|
|
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.' };
|
|
183
394
|
};
|
|
184
395
|
|
|
185
396
|
export default CoreOutline;
|