diff --git a/frontend/app/player/MessageDistributor/Lists.ts b/frontend/app/player/MessageDistributor/Lists.ts index cb7e4d192..bae8d46de 100644 --- a/frontend/app/player/MessageDistributor/Lists.ts +++ b/frontend/app/player/MessageDistributor/Lists.ts @@ -1,23 +1,76 @@ -import type { Message } from './messages' import ListWalker from './managers/ListWalker'; +import ListWalkerWithMarks from './managers/ListWalkerWithMarks'; -export const LIST_NAMES = ["redux", "mobx", "vuex", "zustand", "ngrx", "graphql", "exceptions", "profiles"] as const; +import type { Message } from './messages' -export const INITIAL_STATE = {} -LIST_NAMES.forEach(name => { - INITIAL_STATE[`${name}ListNow`] = [] - INITIAL_STATE[`${name}List`] = [] -}) +const SIMPLE_LIST_NAMES = [ "event", "redux", "mobx", "vuex", "zustand", "ngrx", "graphql", "exceptions", "profiles"] as const; +const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack" ] as const; +//const entityNamesSimple = [ "event", "profile" ]; +const LIST_NAMES = [...SIMPLE_LIST_NAMES, ...MARKED_LIST_NAMES ]; -type ListsObject = { - [key in typeof LIST_NAMES[number]]: ListWalker -} +// TODO: provide correct types -export function initLists(): ListsObject { - const lists: Partial = {}; - for (var i = 0; i < LIST_NAMES.length; i++) { - lists[LIST_NAMES[i]] = new ListWalker(); +export const INITIAL_STATE = LIST_NAMES.reduce((state, name) => { + state[`${name}List`] = [] + state[`${name}ListNow`] = [] + if (MARKED_LIST_NAMES.includes(name)) { + state[`${name}MarkedCountNow`] = 0 + state[`${name}MarkedCount`] = 0 } - return lists as ListsObject; + return state +}, {}) + + +type SimpleListsObject = { + [key in typeof SIMPLE_LIST_NAMES[number]]: ListWalker } +type MarkedListsObject = { + [key in typeof MARKED_LIST_NAMES[number]]: ListWalkerWithMarks +} +type ListsObject = SimpleListsObject & MarkedListsObject + +type InitialLists = { + [key in typeof LIST_NAMES[number]]: any[] +} + +export default class Lists { + lists: ListsObject + constructor(initialLists: Partial = {}) { + const lists: Partial = {} + for (const name of SIMPLE_LIST_NAMES) { + lists[name] = new ListWalker(initialLists[name]) + } + for (const name of MARKED_LIST_NAMES) { + // TODO: provide types + lists[name] = new ListWalkerWithMarks((el) => el.isRed(), initialLists[name]) + } + this.lists = lists as ListsObject + } + + getFullListsState() { + return LIST_NAMES.reduce((state, name) => { + state[`${name}List`] = this.lists[name].list + return state + }, MARKED_LIST_NAMES.reduce((state, name) => { + state[`${name}MarkedCount`] = this.lists[name].markedCount + return state + }, {}) + ) + } + + moveGetState(t: number)/* : Partial */ { + return LIST_NAMES.reduce((state, name) => { + const lastMsg = this.lists[name].moveGetLast(t) // index: name === 'exceptions' ? undefined : index); + if (lastMsg != null) { + state[`${name}ListNow`] = this.lists[name].listNow + } + return state + }, MARKED_LIST_NAMES.reduce((state, name) => { + state[`${name}MarkedCountNow`] = this.lists[name].markedCountNow + return state + }, {}) + ); + } + +} \ No newline at end of file diff --git a/frontend/app/player/MessageDistributor/MessageDistributor.ts b/frontend/app/player/MessageDistributor/MessageDistributor.ts index 85b01e6a3..a80676a61 100644 --- a/frontend/app/player/MessageDistributor/MessageDistributor.ts +++ b/frontend/app/player/MessageDistributor/MessageDistributor.ts @@ -9,12 +9,6 @@ import Log from 'Types/session/log'; import { update } from '../store'; import { toast } from 'react-toastify'; -import { - init as initListsDepr, - append as listAppend, - setStartTime as setListsStartTime -} from '../lists'; - import StatedScreen from './StatedScreen/StatedScreen'; import ListWalker from './managers/ListWalker'; @@ -32,7 +26,7 @@ import { decryptSessionBytes } from './network/crypto'; import { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './StatedScreen/StatedScreen'; import { INITIAL_STATE as ASSIST_INITIAL_STATE, State as AssistState } from './managers/AssistManager'; -import { INITIAL_STATE as LISTS_INITIAL_STATE , LIST_NAMES, initLists } from './Lists'; +import Lists, { INITIAL_STATE as LISTS_INITIAL_STATE } from './Lists'; import type { PerformanceChartPoint } from './managers/PerformanceTrackManager'; import type { SkipInterval } from './managers/ActivityManager'; @@ -100,7 +94,7 @@ export default class MessageDistributor extends StatedScreen { private scrollManager: ListWalker = new ListWalker(); private readonly decoder = new Decoder(); - private readonly lists = initLists(); + private readonly lists: Lists; private activityManager: ActivityManager | null = null; @@ -118,28 +112,27 @@ export default class MessageDistributor extends StatedScreen { this.sessionStart = this.session.startedAt; if (live) { - initListsDepr({}) + this.lists = new Lists() this.assistManager.connect(this.session.agentToken); } else { this.activityManager = new ActivityManager(this.session.duration.milliseconds); /* == REFACTOR_ME == */ - const eventList = this.session.events.toJSON(); - - initListsDepr({ - event: eventList, - stack: this.session.stackEvents.toJSON(), - resource: this.session.resources.toJSON(), - }); - + const eventList = session.events.toJSON(); // TODO: fix types for events, remove immutable js eventList.forEach((e: Record) => { if (e.type === EVENT_TYPES.LOCATION) { //TODO type system this.locationEventManager.append(e); } - }); - this.session.errors.forEach((e: Record) => { - this.lists.exceptions.append(e); - }); + }) + + this.lists = new Lists({ + event: eventList, + stack: session.stackEvents.toJSON(), + resource: session.resources.toJSON(), + exceptions: session.errors.toJSON(), + }) + + /* === */ this.loadMessages(); } @@ -187,13 +180,11 @@ export default class MessageDistributor extends StatedScreen { private waitingForFiles: boolean = false private onFileReadSuccess = () => { - const stateToUpdate: {[key:string]: any} = { + const stateToUpdate = { performanceChartData: this.performanceTrackManager.chartData, performanceAvaliability: this.performanceTrackManager.avaliability, + ...this.lists.getFullListsState() } - LIST_NAMES.forEach(key => { - stateToUpdate[ `${ key }List` ] = this.lists[ key ].list - }) if (this.activityManager) { this.activityManager.end() stateToUpdate.skipIntervals = this.activityManager.list @@ -304,7 +295,6 @@ export default class MessageDistributor extends StatedScreen { /* == REFACTOR_ME == */ const lastLoadedLocationMsg = this.loadedLocationManager.moveGetLast(t, index); if (!!lastLoadedLocationMsg) { - setListsStartTime(lastLoadedLocationMsg.time) this.navigationStartOffset = lastLoadedLocationMsg.navigationStart - this.sessionStart; } const llEvent = this.locationEventManager.moveGetLast(t, index); @@ -340,14 +330,7 @@ export default class MessageDistributor extends StatedScreen { stateToUpdate.performanceChartTime = lastPerformanceTrackMessage.time; } - LIST_NAMES.forEach(key => { - const lastMsg = this.lists[key].moveGetLast(t, key === 'exceptions' ? undefined : index); - if (lastMsg != null) { - // @ts-ignore TODO: fix types - stateToUpdate[`${key}ListNow`] = this.lists[key].listNow; - } - }); - + Object.assign(stateToUpdate, this.lists.moveGetState(t)) Object.keys(stateToUpdate).length > 0 && update(stateToUpdate); /* Sequence of the managers is important here */ @@ -414,15 +397,15 @@ export default class MessageDistributor extends StatedScreen { /* Lists: */ case "console_log": if (msg.level === 'debug') break; - listAppend("log", Log({ + this.lists.lists.log.append(Log({ level: msg.level, value: msg.value, time, index, - })); + })) break; case "fetch": - listAppend("fetch", Resource({ + this.lists.lists.fetch.append(Resource({ method: msg.method, url: msg.url, payload: msg.request, @@ -469,42 +452,42 @@ export default class MessageDistributor extends StatedScreen { decoded = this.decodeStateMessage(msg, ["state", "action"]); logger.log('redux', decoded) if (decoded != null) { - this.lists.redux.append(decoded); + this.lists.lists.redux.append(decoded); } break; case "ng_rx": decoded = this.decodeStateMessage(msg, ["state", "action"]); logger.log('ngrx', decoded) if (decoded != null) { - this.lists.ngrx.append(decoded); + this.lists.lists.ngrx.append(decoded); } break; case "vuex": decoded = this.decodeStateMessage(msg, ["state", "mutation"]); logger.log('vuex', decoded) if (decoded != null) { - this.lists.vuex.append(decoded); + this.lists.lists.vuex.append(decoded); } break; case "zustand": decoded = this.decodeStateMessage(msg, ["state", "mutation"]) logger.log('zustand', decoded) if (decoded != null) { - this.lists.zustand.append(decoded) + this.lists.lists.zustand.append(decoded) } case "mob_x": decoded = this.decodeStateMessage(msg, ["payload"]); logger.log('mobx', decoded) if (decoded != null) { - this.lists.mobx.append(decoded); + this.lists.lists.mobx.append(decoded); } break; case "graph_ql": - this.lists.graphql.append(msg); + this.lists.lists.graphql.append(msg); break; case "profiler": - this.lists.profiles.append(msg); + this.lists.lists.profiles.append(msg); break; default: switch (msg.tp) { diff --git a/frontend/app/player/MessageDistributor/managers/ListWalker.ts b/frontend/app/player/MessageDistributor/managers/ListWalker.ts index e04c5bb83..c0d59c354 100644 --- a/frontend/app/player/MessageDistributor/managers/ListWalker.ts +++ b/frontend/app/player/MessageDistributor/managers/ListWalker.ts @@ -79,6 +79,23 @@ export default class ListWalker { return this.p; } + private hasNext() { + return this.p < this.length + } + private hasPrev() { + return this.p > 0 + } + protected moveNext(): T | null { + return this.hasNext() + ? this.list[ this.p++ ] + : null + } + protected movePrev(): T | null { + return this.hasPrev() + ? this.list[ --this.p ] + : null + } + /* Returns last message with the time <= t. Assumed that the current message is already handled so @@ -94,11 +111,11 @@ export default class ListWalker { let changed = false; while (this.p < this.length && this.list[this.p][key] <= val) { - this.p++; + this.moveNext() changed = true; } while (this.p > 0 && this.list[ this.p - 1 ][key] > val) { - this.p--; + this.movePrev() changed = true; } return changed ? this.list[ this.p - 1 ] : null; @@ -112,10 +129,10 @@ export default class ListWalker { const list = this.list while (list[this.p] && list[this.p].time <= t) { - fn(list[ this.p++ ]); + fn(this.moveNext()) } while (fnBack && this.p > 0 && list[ this.p - 1 ].time > t) { - fnBack(list[ --this.p ]); + fnBack(this.movePrev()); } } diff --git a/frontend/app/player/MessageDistributor/managers/ListWalkerWithMarks.ts b/frontend/app/player/MessageDistributor/managers/ListWalkerWithMarks.ts new file mode 100644 index 000000000..d2f2ccee3 --- /dev/null +++ b/frontend/app/player/MessageDistributor/managers/ListWalkerWithMarks.ts @@ -0,0 +1,42 @@ +import type { Timed } from '../messages/timed'; +import ListWalker from './ListWalker' + + +type CheckFn = (t: T) => boolean + + +export default class ListWalkerWithMarks extends ListWalker { + private _markCountNow: number = 0 + private _markCount: number = 0 + constructor(private isMarked: CheckFn, initialList: T[] = []) { + super(initialList) + this._markCount = initialList.reduce((n, item) => isMarked(item) ? n+1 : n, 0) + } + + append(item: T) { + if (this.isMarked(item)) { this._markCount++ } + super.append(item) + } + + protected moveNext() { + const val = super.moveNext() + if (val && this.isMarked(val)) { + this._markCountNow++ + } + return val + } + protected movePrev() { + const val = super.movePrev() + if (val && this.isMarked(val)) { + this._markCountNow-- + } + return val + } + get markedCountNow(): number { + return this._markCountNow + } + get markedCount(): number { + return this._markCount + } + +} \ No newline at end of file