fix(player) - tracker (#1264)

* fix(tracker): fix time inputs capturing

* fix(player): clear selection manger on clicks; display frustrations row on xray by default

* fix(player): add option todisable network in iframes

* cherry-pick 383b5dd6405d8f47b9d794c123390e91b4154663 conflicts

* Revert "cherry-pick 383b5dd6405d8f47b9d794c123390e91b4154663 conflicts"

This reverts commit 364b62fda0.

* fix(player): fix selection manager styles and reset

---------

Co-authored-by: nick-delirium <nikita@openreplay.com>
This commit is contained in:
Shekar Siri 2023-05-17 18:08:26 +02:00 committed by GitHub
parent 29e9b71cd1
commit d7d2884113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 72 additions and 23 deletions

View file

@ -18,6 +18,7 @@ function OverviewPanel({ issuesList }: { issuesList: Record<string, any>[] }) {
const [dataLoaded, setDataLoaded] = React.useState(false);
const [selectedFeatures, setSelectedFeatures] = React.useState([
'PERFORMANCE',
'FRUSTRATIONS',
'ERRORS',
'NETWORK',
]);

View file

@ -8,6 +8,7 @@ export default class Cursor {
private tagElement: HTMLDivElement;
private coords = { x: 0, y: 0 };
private isMoving = false;
private onClick: () => void;
constructor(overlay: HTMLDivElement, isMobile: boolean) {
this.cursor = document.createElement('div');
@ -75,13 +76,14 @@ export default class Cursor {
click() {
const styleList = this.isMobile ? styles.clickedMobile : styles.clicked
this.cursor.classList.add(styleList)
this.onClick?.()
setTimeout(() => {
this.cursor.classList.remove(styleList)
}, 600)
}
// TODO (to keep on a different playing speed):
// transition
// setTransitionSpeed()
setOnClickHook(callback: () => void) {
this.onClick = callback
}
}

View file

@ -58,6 +58,7 @@ function isIframe(el: Element): el is HTMLIFrameElement {
export default class Screen {
readonly overlay: HTMLDivElement
readonly cursor: Cursor
private selectionTargets: { start?: HTMLDivElement, end?: HTMLDivElement } = { start: undefined, end: undefined }
private readonly iframe: HTMLIFrameElement;
private readonly screen: HTMLDivElement;
@ -253,4 +254,27 @@ export default class Screen {
setOnUpdate(cb: any) {
this.onUpdateHook = cb
}
public createSelection(start: HTMLDivElement, end: HTMLDivElement) {
this.selectionTargets = { start, end }
this.overlay.appendChild(start);
this.overlay.appendChild(end);
setTimeout(() => {
start.className = styles.highlightoff
end.className = styles.highlightoff
}, 750)
}
public clearSelection() {
if (this.selectionTargets.start && this.selectionTargets.end) {
this.overlay.removeChild(this.selectionTargets.start);
this.overlay.removeChild(this.selectionTargets.end);
this.selectionTargets.start.remove()
this.selectionTargets.end.remove()
this.selectionTargets = { start: undefined, end: undefined }
}
}
}

View file

@ -20,3 +20,8 @@
bottom: 0;
z-index: 10;
}
.highlightoff {
opacity: 0;
transition: all 0.25s cubic-bezier(0, 0, 0.4, 1.0);
}

View file

@ -61,6 +61,10 @@ export default class DOMManager extends ListWalker<Message> {
this.stringDict = stringDict
}
public clearSelectionManager() {
this.selectionManager.clearSelection()
}
append(m: Message): void {
if (m.tp === MType.SetNodeScroll) {
let scrollManager = this.nodeScrollManagers.get(m.id)

View file

@ -9,15 +9,12 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
}
private selected: [{ id: number, node: Element } | null, { id: number, node: Element } | null] = [null, null];
private markers: Element[] = []
clearSelection() {
this.selected[0] && this.screen.overlay.removeChild(this.selected[0].node) && this.selected[0].node.remove();
this.selected[1] && this.screen.overlay.removeChild(this.selected[1].node) && this.selected[1].node.remove();
this.markers.forEach(marker => marker.remove())
public clearSelection = () => {
if (this.selected[0] === null && this.selected[1] === null) return;
this.screen.clearSelection()
this.selected = [null, null];
this.markers = [];
}
move(t: number) {
@ -25,6 +22,7 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
if (!msg) {
return;
}
// in theory: empty selection or selection removed
if (msg.selectionStart <= 0) {
this.clearSelection()
@ -48,26 +46,22 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
Object.assign(endPointer.style, {
top: endCoords.top + 'px',
left: (endCoords.left + (endCoords.width / 2) + 3) + 'px',
width: (endCoords.width / 2) + 'px',
right: (endCoords.right) + 'px',
width: (endCoords.width) + 'px',
height: endCoords.height + 'px',
borderRight: '2px solid blue',
position: 'absolute',
boxShadow: '1px 4px 1px -2px blue',
});
Object.assign(startPointer.style, {
top: startCoords.top + 'px',
left: (startCoords.left - 3) + 'px',
width: (startCoords.width / 2 ) + 'px',
left: (startCoords.left) + 'px',
width: (startCoords.width) + 'px',
height: startCoords.height + 'px',
borderLeft: '2px solid blue',
position: 'absolute',
boxShadow: '1px 4px 1px -2px blue',
});
this.markers.push(startPointer, endPointer);
this.screen.overlay.appendChild(startPointer);
this.screen.overlay.appendChild(endPointer);
this.screen.createSelection(startPointer, endPointer);
this.selected = [{ id: msg.selectionStart, node: startPointer }, { id: msg.selectionEnd, node: endPointer }];
}

View file

@ -59,6 +59,7 @@ export default class PagesManager extends ListWalker<DOMManager> {
moveReady(t: number): Promise<void> {
const requiredPage = this.moveGetLast(t)
if (requiredPage != null) {
this.currentPage?.clearSelectionManager()
this.currentPage = requiredPage
this.currentPage.reset() // Otherwise it won't apply create_document
}

View file

@ -1,3 +1,8 @@
# 7.0.1
- fix time inputs capturing
- add option to disable network tracking inside iframes
# 7.0.0
- **[breaking]** added gzip compression to large messages

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "7.0.0",
"version": "7.0.1",
"keywords": [
"logging",
"replay"
@ -40,7 +40,7 @@
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
"replace-in-files": "^2.0.3",
"rollup": "^2.17.0",
"rollup": "^2.59.0",
"rollup-plugin-terser": "^6.1.0",
"semver": "^6.3.0",
"ts-jest": "^29.0.3",

View file

@ -3,7 +3,17 @@ import { normSpaces, IN_BROWSER, getLabelAttribute, now } from '../utils.js'
import { hasTag } from '../app/guards.js'
import { InputChange, SetInputValue, SetInputChecked } from '../app/messages.gen.js'
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date', 'tel']
const INPUT_TYPES = [
'text',
'password',
'email',
'search',
'number',
'range',
'date',
'tel',
'time',
]
// TODO: take into consideration "contenteditable" attribute
type TextFieldElement = HTMLInputElement | HTMLTextAreaElement

View file

@ -79,6 +79,7 @@ export interface Options {
failuresOnly: boolean
ignoreHeaders: Array<string> | boolean
capturePayload: boolean
captureInIframes: boolean
sanitizer?: Sanitizer
}
@ -89,6 +90,7 @@ export default function (app: App, opts: Partial<Options> = {}) {
ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
capturePayload: false,
sessionTokenHeader: false,
captureInIframes: true,
},
opts,
)
@ -312,7 +314,6 @@ export default function (app: App, opts: Partial<Options> = {}) {
) {
const rdo = getXHRRequestDataObject(this)
rdo.body = body
// @ts-ignore ??? this -> XMLHttpRequest
return nativeSend.apply(this, arguments)
}
@ -336,5 +337,7 @@ export default function (app: App, opts: Partial<Options> = {}) {
patchWindow(window)
app.observer.attachContextCallback(app.safe(patchWindow))
if (options.captureInIframes) {
app.observer.attachContextCallback(app.safe(patchWindow))
}
}