fix(tracker): uxt fixes
This commit is contained in:
parent
bca4b25e64
commit
f394d08ec3
5 changed files with 103 additions and 49 deletions
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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 () {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue