fix(tracker): uxt fixes

This commit is contained in:
nick-delirium 2023-11-30 15:49:10 +01:00
parent bca4b25e64
commit f394d08ec3
5 changed files with 103 additions and 49 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "@openreplay/tracker", "name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package", "description": "The OpenReplay tracker main package",
"version": "11.0.0", "version": "11.0.1-9",
"keywords": [ "keywords": [
"logging", "logging",
"replay" "replay"

View file

@ -51,6 +51,7 @@ interface OnStartInfo {
} }
const CANCELED = 'canceled' as const const CANCELED = 'canceled' as const
const uxtStorageKey = 'or_uxt_active'
const START_ERROR = ':(' as const const START_ERROR = ':(' as const
type SuccessfulStart = OnStartInfo & { success: true } type SuccessfulStart = OnStartInfo & { success: true }
type UnsuccessfulStart = { type UnsuccessfulStart = {
@ -145,11 +146,6 @@ export default class App {
private readonly contextId private readonly contextId
public attributeSender: AttributeSender public attributeSender: AttributeSender
private canvasRecorder: CanvasRecorder | null = null private canvasRecorder: CanvasRecorder | null = null
private canvasOptions = {
canvasEnabled: false,
canvasQuality: 'medium',
canvasFPS: 1,
}
private uxtManager: UserTestManager private uxtManager: UserTestManager
constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>) { constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>) {
@ -311,7 +307,7 @@ export default class App {
} }
} }
this.uxtManager = new UserTestManager(this) this.uxtManager = new UserTestManager(this, uxtStorageKey)
} }
private _debug(context: string, e: any) { private _debug(context: string, e: any) {
@ -712,13 +708,19 @@ export default class App {
} }
this.restartAttempts = 0 this.restartAttempts = 0
let uxtId: number | undefined
const savedUxtTag = this.localStorage.getItem(uxtStorageKey)
if (savedUxtTag) {
uxtId = parseInt(savedUxtTag, 10)
}
if (location?.search) { if (location?.search) {
const query = new URLSearchParams(location.search) const query = new URLSearchParams(location.search)
if (query.has('oruxt')) { if (query.has('oruxt')) {
const testId = query.get('oruxt') const qId = query.get('oruxt')
if (testId) this.uxtManager.getTest(parseInt(testId, 10), token) uxtId = qId ? parseInt(qId, 10) : undefined
} }
} }
if (uxtId) this.uxtManager.getTest(uxtId, token, Boolean(savedUxtTag))
return SuccessfulStart(onStartInfo) return SuccessfulStart(onStartInfo)
}) })

View file

@ -32,22 +32,11 @@ export default function attachDND(element, dragTarget) {
document.addEventListener('mousemove', onMouseMove) document.addEventListener('mousemove', onMouseMove)
dragTarget.onmouseup = function () { const clearAll = () => {
document.removeEventListener('mousemove', onMouseMove) document.removeEventListener('mousemove', onMouseMove)
dragTarget.onmouseup = null document.removeEventListener('mouseup', clearAll)
} }
document.addEventListener('mouseup', clearAll)
// dragTarget.onmouseleave = function () {
// document.removeEventListener('mousemove', onMouseMove)
// dragTarget.onmouseleave = null
// }
const onMouseOut = () => {
document.removeEventListener('mousemove', onMouseMove)
window?.removeEventListener('mouseout', onMouseOut)
}
window?.addEventListener('mouseout', onMouseOut)
} }
dragTarget.ondragstart = function () { dragTarget.ondragstart = function () {

View file

@ -67,11 +67,28 @@ export default class UserTestManager {
}[], }[],
} }
constructor(private readonly app: App) { constructor(
private readonly app: App,
private readonly storageKey: string,
) {
this.userRecorder = new Recorder(app) this.userRecorder = new Recorder(app)
const taskIndex = this.app.sessionStorage.getItem('or_uxt_task_index') const sessionId = this.app.getSessionID()
const savedSessionId = this.app.localStorage.getItem('or_uxt_session_id')
if (sessionId !== savedSessionId) {
this.app.localStorage.removeItem(this.storageKey)
this.app.localStorage.removeItem('or_uxt_session_id')
this.app.localStorage.removeItem('or_uxt_test_id')
this.app.localStorage.removeItem('or_uxt_task_index')
this.app.localStorage.removeItem('or_uxt_test_start')
}
const taskIndex = this.app.localStorage.getItem('or_uxt_task_index')
if (taskIndex) { if (taskIndex) {
this.currentTaskIndex = parseInt(taskIndex, 10) this.currentTaskIndex = parseInt(taskIndex, 10)
this.durations.testStart = parseInt(
this.app.localStorage.getItem('or_uxt_test_start') as string,
10,
)
} }
} }
@ -99,9 +116,18 @@ export default class UserTestManager {
} }
signalTest = (status: 'begin' | 'done' | 'skipped') => { signalTest = (status: 'begin' | 'done' | 'skipped') => {
const ingest = this.app.options.ingestPoint
const timestamp = this.app.timestamp() const timestamp = this.app.timestamp()
const duration = timestamp - this.durations.testStart if (status === 'begin' && this.testId) {
this.app.localStorage.setItem(this.storageKey, this.testId.toString())
this.app.localStorage.setItem('or_uxt_test_start', timestamp.toString())
} else {
this.app.localStorage.removeItem(this.storageKey)
this.app.localStorage.removeItem('or_uxt_task_index')
this.app.localStorage.removeItem('or_uxt_test_start')
}
const ingest = this.app.options.ingestPoint
const start = this.durations.testStart || timestamp
const duration = timestamp - start
return fetch(`${ingest}/v1/web/uxt/signals/test`, { return fetch(`${ingest}/v1/web/uxt/signals/test`, {
method: 'POST', method: 'POST',
@ -118,7 +144,7 @@ export default class UserTestManager {
}) })
} }
getTest = (id: number, token: string) => { getTest = (id: number, token: string, inProgress?: boolean) => {
this.testId = id this.testId = id
this.token = token this.token = token
const ingest = this.app.options.ingestPoint const ingest = this.app.options.ingestPoint
@ -131,6 +157,13 @@ export default class UserTestManager {
.then(({ test }: { test: Test }) => { .then(({ test }: { test: Test }) => {
this.test = test this.test = test
this.createGreeting(test.title, test.reqMic, test.reqCamera) this.createGreeting(test.title, test.reqMic, test.reqCamera)
if (inProgress) {
if (test.reqMic || test.reqCamera) {
void this.userRecorder.startRecording(30, Quality.Standard, test.reqMic, test.reqCamera)
}
this.showWidget(test.description, test.tasks, true)
this.showTaskSection()
}
}) })
.catch((err) => { .catch((err) => {
console.log('OR: Error fetching test', err) console.log('OR: Error fetching test', err)
@ -140,6 +173,7 @@ export default class UserTestManager {
hideTaskSection = () => false hideTaskSection = () => false
showTaskSection = () => true showTaskSection = () => true
collapseWidget = () => false collapseWidget = () => false
removeGreeting = () => false
createGreeting(title: string, micRequired: boolean, cameraRequired: boolean) { createGreeting(title: string, micRequired: boolean, cameraRequired: boolean) {
const titleElement = createElement('div', 'title', styles.titleStyle, title) const titleElement = createElement('div', 'title', styles.titleStyle, title)
@ -164,18 +198,22 @@ export default class UserTestManager {
'Read guidelines to begin', 'Read guidelines to begin',
) )
buttonElement.onclick = () => { this.removeGreeting = () => {
this.container.innerHTML = '' this.container.innerHTML = ''
if (micRequired || cameraRequired) { if (micRequired || cameraRequired) {
void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired) void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired)
} }
this.durations.testStart = this.app.timestamp()
void this.signalTest('begin')
this.showWidget(this.test?.description || '', this.test?.tasks || [])
this.container.removeChild(buttonElement) this.container.removeChild(buttonElement)
this.container.removeChild(noticeElement) this.container.removeChild(noticeElement)
this.container.removeChild(descriptionElement) this.container.removeChild(descriptionElement)
this.container.removeChild(titleElement) this.container.removeChild(titleElement)
return false
}
buttonElement.onclick = () => {
this.removeGreeting()
this.durations.testStart = this.app.timestamp()
void this.signalTest('begin')
this.showWidget(this.test?.description || '', this.test?.tasks || [])
} }
this.container.append(titleElement, descriptionElement, noticeElement, buttonElement) this.container.append(titleElement, descriptionElement, noticeElement, buttonElement)
@ -191,6 +229,7 @@ export default class UserTestManager {
task_id: number task_id: number
allow_typing: boolean allow_typing: boolean
}[], }[],
inProgress?: boolean,
) { ) {
this.container.innerHTML = '' this.container.innerHTML = ''
Object.assign(this.bg.style, { Object.assign(this.bg.style, {
@ -222,7 +261,11 @@ export default class UserTestManager {
void this.signalTest('skipped') void this.signalTest('skipped')
document.body.removeChild(this.bg) document.body.removeChild(this.bg)
} }
this.hideTaskSection() if (!inProgress) {
this.hideTaskSection()
} else {
this.toggleDescriptionVisibility()
}
} }
createTitleSection() { createTitleSection() {
@ -280,18 +323,24 @@ export default class UserTestManager {
return title return title
} }
// eslint-disable-next-line @typescript-eslint/no-empty-function
toggleDescriptionVisibility = () => {}
createDescriptionSection(description: string) { createDescriptionSection(description: string) {
const section = createElement('div', 'description_section_or', styles.descriptionWidgetStyle) const section = createElement('div', 'description_section_or', styles.descriptionWidgetStyle)
const titleContainer = createElement('div', 'description_s_title_or', styles.sectionTitleStyle) const titleContainer = createElement('div', 'description_s_title_or', styles.sectionTitleStyle)
const title = createElement('div', 'title', {}, 'Introduction & Guidelines') const title = createElement('div', 'title', {}, 'Introduction & Guidelines')
const icon = createElement('div', 'icon', styles.symbolIcon, '-') const icon = createElement('div', 'icon', styles.symbolIcon, '-')
const content = createElement('div', 'content', styles.contentStyle) const content = createElement('div', 'content', styles.contentStyle)
const ul = document.createElement('ul') const descriptionC = createElement('div', 'text_description', {
ul.innerHTML = description maxHeight: '250px',
overflow: 'scroll',
})
descriptionC.innerHTML = description
const button = createElement('div', 'button_begin_or', styles.buttonWidgetStyle, 'Begin Test') const button = createElement('div', 'button_begin_or', styles.buttonWidgetStyle, 'Begin Test')
titleContainer.append(title, icon) titleContainer.append(title, icon)
content.append(ul, button) content.append(descriptionC, button)
section.append(titleContainer, content) section.append(titleContainer, content)
const toggleDescriptionVisibility = () => { const toggleDescriptionVisibility = () => {
@ -304,6 +353,15 @@ export default class UserTestManager {
} }
titleContainer.onclick = toggleDescriptionVisibility titleContainer.onclick = toggleDescriptionVisibility
this.toggleDescriptionVisibility = () => {
this.widgetGuidelinesVisible = false
icon.textContent = this.widgetGuidelinesVisible ? '-' : '+'
Object.assign(
content.style,
this.widgetGuidelinesVisible ? styles.contentStyle : { display: 'none' },
)
content.removeChild(button)
}
button.onclick = () => { button.onclick = () => {
toggleDescriptionVisibility() toggleDescriptionVisibility()
if (this.test) { if (this.test) {
@ -416,6 +474,19 @@ export default class UserTestManager {
return true return true
} }
const highlightActive = () => {
const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`)
if (activeTaskEl) {
Object.assign(activeTaskEl.style, styles.taskNumberActive)
}
for (let i = 0; i < this.currentTaskIndex; i++) {
const taskEl = document.getElementById(`or_task_${i}`)
if (taskEl) {
Object.assign(taskEl.style, styles.taskNumberDone)
}
}
}
titleContainer.onclick = toggleTasksVisibility titleContainer.onclick = toggleTasksVisibility
closePanelButton.onclick = this.collapseWidget closePanelButton.onclick = this.collapseWidget
@ -437,29 +508,20 @@ export default class UserTestManager {
}) })
} }
void this.signalTask(tasks[this.currentTaskIndex].task_id, 'begin') void this.signalTask(tasks[this.currentTaskIndex].task_id, 'begin')
const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`) highlightActive()
if (activeTaskEl) {
Object.assign(activeTaskEl.style, styles.taskNumberActive)
}
for (let i = 0; i < this.currentTaskIndex; i++) {
const taskEl = document.getElementById(`or_task_${i}`)
if (taskEl) {
Object.assign(taskEl.style, styles.taskNumberDone)
}
}
} else { } else {
this.showEndSection() this.showEndSection()
} }
this.app.sessionStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString()) this.app.localStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString())
} }
updateTaskContent()
setTimeout(() => { setTimeout(() => {
const firstTaskEl = document.getElementById('or_task_0') const firstTaskEl = document.getElementById('or_task_0')
console.log(firstTaskEl, styles.taskNumberActive)
if (firstTaskEl) { if (firstTaskEl) {
Object.assign(firstTaskEl.style, styles.taskNumberActive) Object.assign(firstTaskEl.style, styles.taskNumberActive)
} }
updateTaskContent()
highlightActive()
}, 1) }, 1)
return section return section
} }

View file

@ -8,6 +8,7 @@ export const bgStyle = {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
zIndex: 999999,
} }
export const containerStyle = { export const containerStyle = {