feat(frontend): use ResourceTiming from file instead of database
This commit is contained in:
parent
58a155885a
commit
ea8cf98beb
16 changed files with 165 additions and 395 deletions
|
|
@ -1,148 +0,0 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { connectPlayer, } from 'Player';
|
||||
import { Tooltip, TextEllipsis } from 'UI';
|
||||
import { getRE } from 'App/utils';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import stl from './network.module.css';
|
||||
import NetworkContent from './NetworkContent';
|
||||
import { connect } from 'react-redux';
|
||||
import { setTimelinePointer } from 'Duck/sessions';
|
||||
|
||||
const ALL = 'ALL';
|
||||
const XHR = 'xhr';
|
||||
const JS = 'js';
|
||||
const CSS = 'css';
|
||||
const IMG = 'img';
|
||||
const MEDIA = 'media';
|
||||
const OTHER = 'other';
|
||||
|
||||
const TAB_TO_TYPE_MAP = {
|
||||
[XHR]: TYPES.XHR,
|
||||
[JS]: TYPES.JS,
|
||||
[CSS]: TYPES.CSS,
|
||||
[IMG]: TYPES.IMG,
|
||||
[MEDIA]: TYPES.MEDIA,
|
||||
[OTHER]: TYPES.OTHER,
|
||||
};
|
||||
|
||||
export function renderName(r) {
|
||||
return (
|
||||
<div className="flex justify-between items-center grow-0 w-full">
|
||||
<Tooltip
|
||||
style={{ maxWidth: '75%' }}
|
||||
title={<div className={stl.popupNameContent}>{r.url}</div>}
|
||||
>
|
||||
<TextEllipsis>{r.name}</TextEllipsis>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderDuration(r) {
|
||||
if (!r.success) return 'x';
|
||||
|
||||
const text = `${Math.round(r.duration)}ms`;
|
||||
if (!r.isRed && !r.isYellow) return text;
|
||||
|
||||
let tooltipText;
|
||||
let className = 'w-full h-full flex items-center ';
|
||||
if (r.isYellow) {
|
||||
tooltipText = 'Slower than average';
|
||||
className += 'warn color-orange';
|
||||
} else {
|
||||
tooltipText = 'Much slower than average';
|
||||
className += 'error color-red';
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltipText}>
|
||||
<div className={cn(className, stl.duration)}> {text} </div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@connectPlayer((state) => ({
|
||||
location: state.location,
|
||||
resources: state.resourceList,
|
||||
domContentLoadedTime: state.domContentLoadedTime,
|
||||
loadTime: state.loadTime,
|
||||
// time: state.time,
|
||||
playing: state.playing,
|
||||
domBuildingTime: state.domBuildingTime,
|
||||
fetchPresented: state.fetchList.length > 0,
|
||||
listNow: state.resourceListNow,
|
||||
}))
|
||||
@connect(
|
||||
(state) => ({
|
||||
timelinePointer: state.getIn(['sessions', 'timelinePointer']),
|
||||
}),
|
||||
{ setTimelinePointer }
|
||||
)
|
||||
export default class Network extends React.PureComponent {
|
||||
state = {
|
||||
filter: '',
|
||||
filteredList: this.props.resources,
|
||||
activeTab: ALL,
|
||||
currentIndex: 0,
|
||||
};
|
||||
|
||||
onRowClick = (e, index) => {
|
||||
// no action for direct click on network requests (so far), there is a jump button, and we don't have more information for than is already displayed in the table
|
||||
};
|
||||
|
||||
onTabClick = (activeTab) => this.setState({ activeTab });
|
||||
|
||||
onFilterChange = (e, { value }) => {
|
||||
const { resources } = this.props;
|
||||
const filterRE = getRE(value, 'i');
|
||||
const filtered = resources.filter(
|
||||
({ type, name }) =>
|
||||
filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[activeTab])
|
||||
);
|
||||
|
||||
this.setState({ filter: value, filteredList: value ? filtered : resources, currentIndex: 0 });
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
const { filteredList } = prevState;
|
||||
if (nextProps.timelinePointer) {
|
||||
const activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time);
|
||||
return {
|
||||
currentIndex: activeItem ? filteredList.indexOf(activeItem) : filteredList.length - 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { location, domContentLoadedTime, loadTime, domBuildingTime, fetchPresented, listNow } =
|
||||
this.props;
|
||||
const { filteredList } = this.state;
|
||||
const resourcesSize = filteredList.reduce(
|
||||
(sum, { decodedBodySize }) => sum + (decodedBodySize || 0),
|
||||
0
|
||||
);
|
||||
const transferredSize = filteredList.reduce(
|
||||
(sum, { headerSize, encodedBodySize }) => sum + (headerSize || 0) + (encodedBodySize || 0),
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<NetworkContent
|
||||
// time = { time }
|
||||
location={location}
|
||||
resources={filteredList}
|
||||
domContentLoadedTime={domContentLoadedTime}
|
||||
loadTime={loadTime}
|
||||
domBuildingTime={domBuildingTime}
|
||||
fetchPresented={fetchPresented}
|
||||
resourcesSize={resourcesSize}
|
||||
transferredSize={transferredSize}
|
||||
onRowClick={this.onRowClick}
|
||||
currentIndex={listNow.length - 1}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import cn from 'classnames';
|
||||
import { QuestionMarkHint, Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI';
|
||||
import { getRE } from 'App/utils';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import { ResourceType } from 'Player';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import { formatMs } from 'App/date';
|
||||
|
||||
|
|
@ -21,12 +21,12 @@ const MEDIA = 'media';
|
|||
const OTHER = 'other';
|
||||
|
||||
const TAB_TO_TYPE_MAP = {
|
||||
[XHR]: TYPES.XHR,
|
||||
[JS]: TYPES.JS,
|
||||
[CSS]: TYPES.CSS,
|
||||
[IMG]: TYPES.IMG,
|
||||
[MEDIA]: TYPES.MEDIA,
|
||||
[OTHER]: TYPES.OTHER,
|
||||
[XHR]: ResourceType.XHR,
|
||||
[JS]: ResourceType.SCRIPT,
|
||||
[CSS]: ResourceType.CSS,
|
||||
[IMG]: ResourceType.IMG,
|
||||
[MEDIA]: ResourceType.MEDIA,
|
||||
[OTHER]: ResourceType.OTHER,
|
||||
};
|
||||
const TABS = [ALL, XHR, JS, CSS, IMG, MEDIA, OTHER].map((tab) => ({
|
||||
text: tab,
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from './Network';
|
||||
export * from './Network';
|
||||
|
|
@ -3,7 +3,7 @@ import { observer } from 'mobx-react-lite';
|
|||
import { Duration } from 'luxon';
|
||||
|
||||
import { Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import { ResourceType } from 'Player';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import { formatMs } from 'App/date';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
|
|
@ -28,13 +28,13 @@ const MEDIA = 'media';
|
|||
const OTHER = 'other';
|
||||
|
||||
const TYPE_TO_TAB = {
|
||||
[TYPES.XHR]: XHR,
|
||||
[TYPES.FETCH]: XHR,
|
||||
[TYPES.JS]: JS,
|
||||
[TYPES.CSS]: CSS,
|
||||
[TYPES.IMG]: IMG,
|
||||
[TYPES.MEDIA]: MEDIA,
|
||||
[TYPES.OTHER]: OTHER,
|
||||
[ResourceType.XHR]: XHR,
|
||||
[ResourceType.FETCH]: XHR,
|
||||
[ResourceType.SCRIPT]: JS,
|
||||
[ResourceType.CSS]: CSS,
|
||||
[ResourceType.IMG]: IMG,
|
||||
[ResourceType.MEDIA]: MEDIA,
|
||||
[ResourceType.OTHER]: OTHER,
|
||||
}
|
||||
|
||||
const TAP_KEYS = [ALL, XHR, JS, CSS, IMG, MEDIA, OTHER] as const;
|
||||
|
|
@ -154,7 +154,7 @@ function NetworkPanel({ startedAt }: { startedAt: number }) {
|
|||
const activeIndex = devTools[INDEX_KEY].index;
|
||||
|
||||
const list = useMemo(() =>
|
||||
// TODO: better merge (with body size info)
|
||||
// TODO: better merge (with body size info) - do it in player
|
||||
resourceList.filter(res => !fetchList.some(ft => {
|
||||
// res.url !== ft.url doesn't work on relative URLs appearing within fetchList (to-fix in player)
|
||||
if (res.name !== ft.name) { return false }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import FetchBasicDetails from './components/FetchBasicDetails';
|
||||
import { Button } from 'UI';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import { ResourceType } from 'Player';
|
||||
import FetchTabs from './components/FetchTabs/FetchTabs';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { DateTime } from 'luxon';
|
||||
|
|
@ -17,7 +17,7 @@ function FetchDetailsModal(props: Props) {
|
|||
const [resource, setResource] = useState(props.resource);
|
||||
const [first, setFirst] = useState(false);
|
||||
const [last, setLast] = useState(false);
|
||||
const isXHR = resource.type === TYPES.XHR || resource.type === TYPES.FETCH
|
||||
const isXHR = resource.type === ResourceType.XHR || resource.type === ResourceType.FETCH
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
settingsStore: { sessionSettings: { timezone }},
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
import { Decoder } from "syncod";
|
||||
import logger from 'App/logger';
|
||||
|
||||
import Resource, { TYPES as RES_TYPES } from 'Types/session/resource';
|
||||
import { TYPES as EVENT_TYPES } from 'Types/session/event';
|
||||
import { Log } from './types';
|
||||
import { Log } from './types/log';
|
||||
import { Resource, ResourceType, getResourceFromResourceTiming, getResourceFromNetworkRequest } from './types/resource'
|
||||
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
|
|
@ -395,19 +395,13 @@ export default class MessageManager {
|
|||
Log(msg)
|
||||
)
|
||||
break;
|
||||
case MType.ResourceTiming:
|
||||
// TODO: merge `resource` and `fetch` lists into one here instead of UI
|
||||
this.lists.lists.resource.insert(getResourceFromResourceTiming(msg, this.sessionStart))
|
||||
break;
|
||||
case MType.Fetch:
|
||||
case MType.NetworkRequest:
|
||||
this.lists.lists.fetch.insert(new Resource({
|
||||
method: msg.method,
|
||||
url: msg.url,
|
||||
request: msg.request,
|
||||
response: msg.response,
|
||||
status: msg.status,
|
||||
duration: msg.duration,
|
||||
type: msg.type === "xhr" ? RES_TYPES.XHR : RES_TYPES.FETCH,
|
||||
time: Math.max(msg.timestamp - this.sessionStart, 0), // !!! doesn't look good. TODO: find solution to show negative timings
|
||||
index,
|
||||
}) as Timed)
|
||||
this.lists.lists.fetch.insert(getResourceFromNetworkRequest(msg, this.sessionStart))
|
||||
break;
|
||||
case MType.Redux:
|
||||
decoded = this.decodeStateMessage(msg, ["state", "action"]);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Log, LogLevel } from './types'
|
||||
import { Log, LogLevel } from './types/log'
|
||||
|
||||
import type { Store } from 'App/player'
|
||||
import Player from '../player/Player'
|
||||
|
|
@ -30,7 +30,6 @@ export default class WebPlayer extends Player {
|
|||
let initialLists = live ? {} : {
|
||||
event: session.events || [],
|
||||
stack: session.stackEvents || [],
|
||||
resource: session.resources || [], // MBTODO: put ResourceTiming in file
|
||||
exceptions: session.errors?.map(({ name, ...rest }: any) =>
|
||||
Log({
|
||||
level: LogLevel.ERROR,
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
export enum LogLevel {
|
||||
INFO = 'info',
|
||||
LOG = 'log',
|
||||
//ASSERT = 'assert', //?
|
||||
WARN = 'warn',
|
||||
ERROR = 'error',
|
||||
EXCEPTION = 'exception',
|
||||
}
|
||||
|
||||
export interface ILog {
|
||||
level: LogLevel
|
||||
value: string
|
||||
time: number
|
||||
index?: number
|
||||
errorId?: string
|
||||
}
|
||||
|
||||
export const Log = (log: ILog) => ({
|
||||
isRed: log.level === LogLevel.EXCEPTION || log.level === LogLevel.ERROR,
|
||||
isYellow: log.level === LogLevel.WARN,
|
||||
...log
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
// func getResourceType(initiator string, URL string) string {
|
||||
// switch initiator {
|
||||
// case "xmlhttprequest", "fetch":
|
||||
// return "fetch"
|
||||
// case "img":
|
||||
// return "img"
|
||||
// default:
|
||||
// switch getURLExtention(URL) {
|
||||
// case "css":
|
||||
// return "stylesheet"
|
||||
// case "js":
|
||||
// return "script"
|
||||
// case "png", "gif", "jpg", "jpeg", "svg":
|
||||
// return "img"
|
||||
// case "mp4", "mkv", "ogg", "webm", "avi", "mp3":
|
||||
// return "media"
|
||||
// default:
|
||||
// return "other"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
2
frontend/app/player/web/types/index.ts
Normal file
2
frontend/app/player/web/types/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './log'
|
||||
export * from './resource'
|
||||
23
frontend/app/player/web/types/log.ts
Normal file
23
frontend/app/player/web/types/log.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export const enum LogLevel {
|
||||
INFO = 'info',
|
||||
LOG = 'log',
|
||||
//ASSERT = 'assert', //?
|
||||
WARN = 'warn',
|
||||
ERROR = 'error',
|
||||
EXCEPTION = 'exception',
|
||||
}
|
||||
|
||||
export interface ILog {
|
||||
level: LogLevel
|
||||
value: string
|
||||
time: number
|
||||
index?: number
|
||||
errorId?: string
|
||||
}
|
||||
|
||||
export const Log = (log: ILog) => ({
|
||||
isRed: log.level === LogLevel.EXCEPTION || log.level === LogLevel.ERROR,
|
||||
isYellow: log.level === LogLevel.WARN,
|
||||
...log
|
||||
})
|
||||
|
||||
114
frontend/app/player/web/types/resource.ts
Normal file
114
frontend/app/player/web/types/resource.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import type { ResourceTiming, NetworkRequest, Fetch } from '../messages'
|
||||
|
||||
export const enum ResourceType {
|
||||
XHR = 'xhr',
|
||||
FETCH = 'fetch',
|
||||
SCRIPT = 'script',
|
||||
CSS = 'css',
|
||||
IMG = 'img',
|
||||
MEDIA = 'media',
|
||||
OTHER = 'other',
|
||||
}
|
||||
|
||||
function getURLExtention(url: string): string {
|
||||
const pts = url.split(".")
|
||||
return pts[pts.length-1] || ""
|
||||
}
|
||||
|
||||
// maybe move this thing to the tracker
|
||||
function getResourceType(initiator: string, url: string): ResourceType {
|
||||
switch (initiator) {
|
||||
case "xmlhttprequest":
|
||||
case "fetch":
|
||||
return ResourceType.FETCH
|
||||
case "img":
|
||||
return ResourceType.IMG
|
||||
default:
|
||||
switch (getURLExtention(url)) {
|
||||
case "css":
|
||||
return ResourceType.CSS
|
||||
case "js":
|
||||
return ResourceType.SCRIPT
|
||||
case "png":
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "svg":
|
||||
return ResourceType.IMG
|
||||
case "mp4":
|
||||
case "mkv":
|
||||
case "ogg":
|
||||
case "webm":
|
||||
case "avi":
|
||||
case "mp3":
|
||||
return ResourceType.MEDIA
|
||||
default:
|
||||
return ResourceType.OTHER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getResourceName(url: string) {
|
||||
return url
|
||||
.split('/')
|
||||
.filter((s) => s !== '')
|
||||
.pop();
|
||||
}
|
||||
|
||||
|
||||
const YELLOW_BOUND = 10;
|
||||
const RED_BOUND = 80;
|
||||
|
||||
|
||||
interface IResource {
|
||||
//index: number,
|
||||
time: number,
|
||||
type: ResourceType,
|
||||
url: string,
|
||||
status: string,
|
||||
method: string,
|
||||
duration: number,
|
||||
success: boolean,
|
||||
ttfb?: number,
|
||||
request?: string,
|
||||
response?: string,
|
||||
headerSize?: number,
|
||||
encodedBodySize?: number,
|
||||
decodedBodySize?: number,
|
||||
responseBodySize?: number,
|
||||
}
|
||||
|
||||
|
||||
export const Resource = (resource: IResource) => ({
|
||||
...resource,
|
||||
name: getResourceName(resource.url),
|
||||
isRed: !resource.success, //|| resource.score >= RED_BOUND,
|
||||
isYellow: false, // resource.score < RED_BOUND && resource.score >= YELLOW_BOUND,
|
||||
})
|
||||
|
||||
|
||||
export function getResourceFromResourceTiming(msg: ResourceTiming, sessStart: number) {
|
||||
const success = msg.duration > 0 // might be duration=0 when cached
|
||||
const type = getResourceType(msg.initiator, msg.url)
|
||||
return Resource({
|
||||
...msg,
|
||||
type,
|
||||
method: type === ResourceType.FETCH ? ".." : "GET", // should be GET for all non-XHR/Fetch resources, right?
|
||||
success,
|
||||
status: success ? '2xx-3xx' : '4xx-5xx',
|
||||
time: Math.max(0, msg.timestamp - sessStart)
|
||||
})
|
||||
}
|
||||
|
||||
export function getResourceFromNetworkRequest(msg: NetworkRequest | Fetch, sessStart: number) {
|
||||
return Resource({
|
||||
...msg,
|
||||
// @ts-ignore
|
||||
type: msg?.type === "xhr" ? ResourceType.XHR : ResourceType.FETCH,
|
||||
success: msg.status < 400,
|
||||
status: String(msg.status),
|
||||
time: Math.max(0, msg.timestamp - sessStart),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
import Record from 'Types/Record';
|
||||
import { getResourceName } from 'App/utils';
|
||||
|
||||
const XHR = 'xhr' as const;
|
||||
const FETCH = 'fetch' as const;
|
||||
const JS = 'script' as const;
|
||||
const CSS = 'css' as const;
|
||||
const IMG = 'img' as const;
|
||||
const MEDIA = 'media' as const;
|
||||
const OTHER = 'other' as const;
|
||||
|
||||
function getResourceStatus(status: number, success: boolean) {
|
||||
if (status !== undefined) return String(status);
|
||||
if (typeof success === 'boolean' || typeof success === 'number') {
|
||||
return !!success
|
||||
? '2xx-3xx'
|
||||
: '4xx-5xx';
|
||||
}
|
||||
return '2xx-3xx';
|
||||
}
|
||||
|
||||
export const TYPES = {
|
||||
XHR,
|
||||
FETCH,
|
||||
JS,
|
||||
CSS,
|
||||
IMG,
|
||||
MEDIA,
|
||||
OTHER,
|
||||
"stylesheet": CSS,
|
||||
}
|
||||
|
||||
const YELLOW_BOUND = 10;
|
||||
const RED_BOUND = 80;
|
||||
|
||||
export function isRed(r: IResource) {
|
||||
return !r.success || r.score >= RED_BOUND;
|
||||
}
|
||||
|
||||
interface IResource {
|
||||
type: keyof typeof TYPES,
|
||||
url: string,
|
||||
name: string,
|
||||
status: number,
|
||||
duration: number,
|
||||
index: number,
|
||||
time: number,
|
||||
ttfb: number,
|
||||
timewidth: number,
|
||||
success: boolean,
|
||||
score: number,
|
||||
method: string,
|
||||
request: string,
|
||||
response: string,
|
||||
headerSize: number,
|
||||
encodedBodySize: number,
|
||||
decodedBodySize: number,
|
||||
responseBodySize: number,
|
||||
timings: Record<string, any>
|
||||
datetime: number
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export default class Resource {
|
||||
name = 'Resource'
|
||||
type: IResource["type"]
|
||||
status: string
|
||||
success: IResource["success"]
|
||||
time: IResource["time"]
|
||||
ttfb: IResource["ttfb"]
|
||||
url: IResource["url"]
|
||||
duration: IResource["duration"]
|
||||
index: IResource["index"]
|
||||
timewidth: IResource["timewidth"]
|
||||
score: IResource["score"]
|
||||
method: IResource["method"]
|
||||
request: IResource["request"]
|
||||
response: IResource["response"]
|
||||
headerSize: IResource["headerSize"]
|
||||
encodedBodySize: IResource["encodedBodySize"]
|
||||
decodedBodySize: IResource["decodedBodySize"]
|
||||
responseBodySize: IResource["responseBodySize"]
|
||||
timings: IResource["timings"]
|
||||
|
||||
constructor({ status, success, time, datetime, timestamp, timings, ...resource }: IResource) {
|
||||
|
||||
// adjusting for 201, 202 etc
|
||||
const reqSuccess = 300 > status || success
|
||||
Object.assign(this, {
|
||||
...resource,
|
||||
name: getResourceName(resource.url),
|
||||
status: getResourceStatus(status, success),
|
||||
success: reqSuccess,
|
||||
time: typeof time === 'number' ? time : datetime || timestamp,
|
||||
ttfb: timings && timings.ttfb,
|
||||
timewidth: timings && timings.timewidth,
|
||||
timings,
|
||||
isRed: !reqSuccess || resource.score >= RED_BOUND,
|
||||
isYellow: resource.score < RED_BOUND && resource.score >= YELLOW_BOUND,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { Duration } from 'luxon';
|
||||
import SessionEvent, { TYPES, EventData, InjectedEvent } from './event';
|
||||
import StackEvent from './stackEvent';
|
||||
import Resource from './resource';
|
||||
import SessionError, { IError } from './error';
|
||||
import Issue, { IIssue } from './issue';
|
||||
import { Note } from 'App/services/NotesService'
|
||||
|
|
@ -31,8 +30,6 @@ export interface ISession {
|
|||
duration: number,
|
||||
events: InjectedEvent[],
|
||||
stackEvents: StackEvent[],
|
||||
resources: Resource[],
|
||||
missedResources: Resource[],
|
||||
metadata: [],
|
||||
favorite: boolean,
|
||||
filterId?: string,
|
||||
|
|
@ -119,7 +116,6 @@ export default class Session {
|
|||
duration: ISession["duration"]
|
||||
events: ISession["events"]
|
||||
stackEvents: ISession["stackEvents"]
|
||||
resources: ISession["resources"]
|
||||
metadata: ISession["metadata"]
|
||||
favorite: ISession["favorite"]
|
||||
filterId?: ISession["filterId"]
|
||||
|
|
@ -181,7 +177,6 @@ export default class Session {
|
|||
devtoolsURL = [],
|
||||
mobsUrl = [],
|
||||
notes = [],
|
||||
resources = [],
|
||||
...session
|
||||
} = sessionData
|
||||
const duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration);
|
||||
|
|
@ -208,13 +203,6 @@ export default class Session {
|
|||
})
|
||||
}
|
||||
|
||||
let resourcesList = resources.map((r) => new Resource(r as any));
|
||||
resourcesList.forEach((r: Resource) => {
|
||||
r.time = Math.max(0, r.time - startedAt)
|
||||
})
|
||||
resourcesList = resourcesList.sort((r1, r2) => r1.time - r2.time);
|
||||
const missedResources = resourcesList.filter(({ success }) => !success);
|
||||
|
||||
const stackEventsList: StackEvent[] = []
|
||||
if (stackEvents?.length || session.userEvents?.length) {
|
||||
const mergedArrays = [...stackEvents, ...session.userEvents]
|
||||
|
|
@ -245,8 +233,6 @@ export default class Session {
|
|||
siteId: projectId,
|
||||
events,
|
||||
stackEvents: stackEventsList,
|
||||
resources: resourcesList,
|
||||
missedResources,
|
||||
userDevice,
|
||||
userDeviceType,
|
||||
isMobile,
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { Record } from 'immutable';
|
||||
|
||||
const DomBuildingTime = Record({
|
||||
avg: undefined,
|
||||
chart: [],
|
||||
});
|
||||
|
||||
|
||||
function fromJS(data = {}) {
|
||||
if (data instanceof DomBuildingTime) return data;
|
||||
return new DomBuildingTime(data);
|
||||
}
|
||||
|
||||
export default fromJS;
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import { getChartFormatter } from './helper';
|
||||
import DomBuildingTime from './domBuildingTime';
|
||||
|
||||
export const WIDGET_LIST = [
|
||||
{
|
||||
key: "resourcesLoadingTime",
|
||||
name: "Resource Fetch Time",
|
||||
description: 'List of resources that are slowing down your website, sorted by the number of impacted sessions.',
|
||||
thumb: 'na.png',
|
||||
type: 'resources',
|
||||
dataWrapper: (list, period) => DomBuildingTime(list)
|
||||
.update("chart", getChartFormatter(period))
|
||||
},
|
||||
];
|
||||
|
||||
export const WIDGET_KEYS = WIDGET_LIST.map(({ key }) => key);
|
||||
|
||||
const WIDGET_MAP = {};
|
||||
WIDGET_LIST.forEach(w => { WIDGET_MAP[ w.key ] = w; });
|
||||
|
||||
const OVERVIEW_WIDGET_MAP = {};
|
||||
WIDGET_LIST.filter(w => w.type === 'overview').forEach(w => { OVERVIEW_WIDGET_MAP[ w.key ] = w; });
|
||||
|
||||
export {
|
||||
WIDGET_MAP,
|
||||
OVERVIEW_WIDGET_MAP
|
||||
};
|
||||
|
|
@ -17,13 +17,6 @@ export function debounce(callback, wait, context = this) {
|
|||
};
|
||||
}
|
||||
|
||||
export function getResourceName(url: string) {
|
||||
return url
|
||||
.split('/')
|
||||
.filter((s) => s !== '')
|
||||
.pop();
|
||||
}
|
||||
|
||||
/* eslint-disable no-mixed-operators */
|
||||
export function randomInt(a, b) {
|
||||
const min = (b ? a : 0) - 0.5;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue