fix litjs support, fix autocomplete modal options reset, fix dashboard chart density (#3382)
* Litjs fixes2 (#3381) * ui: fixes for litjs capture * ui: introduce vmode for lwc light dom * ui: fixup the mode toggle and remover * ui: fix filter options reset, fix dashboard chart density
This commit is contained in:
parent
447fc26a2a
commit
58da1d3f64
19 changed files with 178 additions and 67 deletions
|
|
@ -6,6 +6,7 @@ import DefaultPlaying from 'Shared/SessionSettings/components/DefaultPlaying';
|
||||||
import DefaultTimezone from 'Shared/SessionSettings/components/DefaultTimezone';
|
import DefaultTimezone from 'Shared/SessionSettings/components/DefaultTimezone';
|
||||||
import ListingVisibility from 'Shared/SessionSettings/components/ListingVisibility';
|
import ListingVisibility from 'Shared/SessionSettings/components/ListingVisibility';
|
||||||
import MouseTrailSettings from 'Shared/SessionSettings/components/MouseTrailSettings';
|
import MouseTrailSettings from 'Shared/SessionSettings/components/MouseTrailSettings';
|
||||||
|
import VirtualModeSettings from '../shared/SessionSettings/components/VirtualMode';
|
||||||
import DebugLog from './DebugLog';
|
import DebugLog from './DebugLog';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ function SessionsListingSettings() {
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<MouseTrailSettings />
|
<MouseTrailSettings />
|
||||||
<DebugLog />
|
<DebugLog />
|
||||||
|
<VirtualModeSettings />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -181,9 +181,10 @@ function WidgetChart(props: Props) {
|
||||||
}
|
}
|
||||||
prevMetricRef.current = _metric;
|
prevMetricRef.current = _metric;
|
||||||
const timestmaps = drillDownPeriod.toTimestamps();
|
const timestmaps = drillDownPeriod.toTimestamps();
|
||||||
|
const density = props.isPreview ? metric.density : dashboardStore.selectedDensity
|
||||||
const payload = isSaved
|
const payload = isSaved
|
||||||
? { ...metricParams }
|
? { ...metricParams, density }
|
||||||
: { ...params, ...timestmaps, ..._metric.toJson() };
|
: { ...params, ...timestmaps, ..._metric.toJson(), density };
|
||||||
debounceRequest(
|
debounceRequest(
|
||||||
_metric,
|
_metric,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ function RangeGranularity({
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAST_24_HR_MS = 24 * 60 * 60 * 1000;
|
const PAST_24_HR_MS = 24 * 60 * 60 * 1000;
|
||||||
function calculateGranularities(periodDurationMs: number) {
|
export function calculateGranularities(periodDurationMs: number) {
|
||||||
const granularities = [
|
const granularities = [
|
||||||
{ label: 'Hourly', durationMs: 60 * 60 * 1000 },
|
{ label: 'Hourly', durationMs: 60 * 60 * 1000 },
|
||||||
{ label: 'Daily', durationMs: 24 * 60 * 60 * 1000 },
|
{ label: 'Daily', durationMs: 24 * 60 * 60 * 1000 },
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ function SubHeader(props) {
|
||||||
projectsStore,
|
projectsStore,
|
||||||
userStore,
|
userStore,
|
||||||
issueReportingStore,
|
issueReportingStore,
|
||||||
|
settingsStore
|
||||||
} = useStore();
|
} = useStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { favorite } = sessionStore.current;
|
const { favorite } = sessionStore.current;
|
||||||
|
|
@ -45,7 +46,7 @@ function SubHeader(props) {
|
||||||
const currentSession = sessionStore.current;
|
const currentSession = sessionStore.current;
|
||||||
const projectId = projectsStore.siteId;
|
const projectId = projectsStore.siteId;
|
||||||
const integrations = integrationsStore.issues.list;
|
const integrations = integrationsStore.issues.list;
|
||||||
const { store } = React.useContext(PlayerContext);
|
const { player, store } = React.useContext(PlayerContext);
|
||||||
const { location: currentLocation = 'loading...' } = store.get();
|
const { location: currentLocation = 'loading...' } = store.get();
|
||||||
const hasIframe = localStorage.getItem(IFRAME) === 'true';
|
const hasIframe = localStorage.getItem(IFRAME) === 'true';
|
||||||
const [hideTools, setHideTools] = React.useState(false);
|
const [hideTools, setHideTools] = React.useState(false);
|
||||||
|
|
@ -127,6 +128,13 @@ function SubHeader(props) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showVModeBadge = store.get().vModeBadge;
|
||||||
|
const onVMode = () => {
|
||||||
|
settingsStore.sessionSettings.updateKey('virtualMode', true);
|
||||||
|
player.enableVMode?.();
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
|
|
@ -143,6 +151,8 @@ function SubHeader(props) {
|
||||||
siteId={projectId!}
|
siteId={projectId!}
|
||||||
currentLocation={currentLocation}
|
currentLocation={currentLocation}
|
||||||
version={currentSession?.trackerVersion ?? ''}
|
version={currentSession?.trackerVersion ?? ''}
|
||||||
|
virtualElsFailed={showVModeBadge}
|
||||||
|
onVMode={onVMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SessionTabs />
|
<SessionTabs />
|
||||||
|
|
|
||||||
|
|
@ -34,38 +34,46 @@ const WarnBadge = React.memo(
|
||||||
currentLocation,
|
currentLocation,
|
||||||
version,
|
version,
|
||||||
siteId,
|
siteId,
|
||||||
|
virtualElsFailed,
|
||||||
|
onVMode,
|
||||||
}: {
|
}: {
|
||||||
currentLocation: string;
|
currentLocation: string;
|
||||||
version: string;
|
version: string;
|
||||||
siteId: string;
|
siteId: string;
|
||||||
|
virtualElsFailed: boolean;
|
||||||
|
onVMode: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const localhostWarnSiteKey = localhostWarn(siteId);
|
const localhostWarnSiteKey = localhostWarn(siteId);
|
||||||
const defaultLocalhostWarn =
|
const defaultLocalhostWarn =
|
||||||
localStorage.getItem(localhostWarnSiteKey) !== '1';
|
localStorage.getItem(localhostWarnSiteKey) !== '1';
|
||||||
const localhostWarnActive =
|
const localhostWarnActive = Boolean(
|
||||||
currentLocation &&
|
currentLocation &&
|
||||||
defaultLocalhostWarn &&
|
defaultLocalhostWarn &&
|
||||||
/(localhost)|(127.0.0.1)|(0.0.0.0)/.test(currentLocation);
|
/(localhost)|(127.0.0.1)|(0.0.0.0)/.test(currentLocation)
|
||||||
|
)
|
||||||
const trackerVersion = window.env.TRACKER_VERSION ?? undefined;
|
const trackerVersion = window.env.TRACKER_VERSION ?? undefined;
|
||||||
const trackerVerDiff = compareVersions(version, trackerVersion);
|
const trackerVerDiff = compareVersions(version, trackerVersion);
|
||||||
const trackerWarnActive = trackerVerDiff !== VersionComparison.Same;
|
const trackerWarnActive = trackerVerDiff !== VersionComparison.Same;
|
||||||
|
|
||||||
const [showLocalhostWarn, setLocalhostWarn] =
|
const [warnings, setWarnings] = React.useState<[localhostWarn: boolean, trackerWarn: boolean, virtualElsFailWarn: boolean]>([localhostWarnActive, trackerWarnActive, virtualElsFailed])
|
||||||
React.useState(localhostWarnActive);
|
|
||||||
const [showTrackerWarn, setTrackerWarn] = React.useState(trackerWarnActive);
|
|
||||||
|
|
||||||
const closeWarning = (type: 1 | 2) => {
|
React.useEffect(() => {
|
||||||
|
setWarnings([localhostWarnActive, trackerWarnActive, virtualElsFailed])
|
||||||
|
}, [localhostWarnActive, trackerWarnActive, virtualElsFailed])
|
||||||
|
|
||||||
|
const closeWarning = (type: 0 | 1 | 2) => {
|
||||||
if (type === 1) {
|
if (type === 1) {
|
||||||
localStorage.setItem(localhostWarnSiteKey, '1');
|
localStorage.setItem(localhostWarnSiteKey, '1');
|
||||||
setLocalhostWarn(false);
|
|
||||||
}
|
|
||||||
if (type === 2) {
|
|
||||||
setTrackerWarn(false);
|
|
||||||
}
|
}
|
||||||
|
setWarnings((prev) => {
|
||||||
|
const newWarnings = [...prev];
|
||||||
|
newWarnings[type] = false;
|
||||||
|
return newWarnings;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!showLocalhostWarn && !showTrackerWarn) return null;
|
if (!warnings.some(el => el === true)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -79,7 +87,7 @@ const WarnBadge = React.memo(
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showLocalhostWarn ? (
|
{warnings[0] ? (
|
||||||
<div className="px-3 py-1 border border-gray-lighter drop-shadow-md rounded bg-active-blue flex items-center justify-between">
|
<div className="px-3 py-1 border border-gray-lighter drop-shadow-md rounded bg-active-blue flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<span>{t('Some assets may load incorrectly on localhost.')}</span>
|
<span>{t('Some assets may load incorrectly on localhost.')}</span>
|
||||||
|
|
@ -101,7 +109,7 @@ const WarnBadge = React.memo(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{showTrackerWarn ? (
|
{warnings[1] ? (
|
||||||
<div className="px-3 py-1 border border-gray-lighter drop-shadow-md rounded bg-active-blue flex items-center justify-between">
|
<div className="px-3 py-1 border border-gray-lighter drop-shadow-md rounded bg-active-blue flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -125,6 +133,21 @@ const WarnBadge = React.memo(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="py-1 ml-3 cursor-pointer"
|
||||||
|
onClick={() => closeWarning(2)}
|
||||||
|
>
|
||||||
|
<Icon name="close" size={16} color="black" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{warnings[2] ? (
|
||||||
|
<div className="px-3 py-1 border border-gray-lighter drop-shadow-md rounded bg-active-blue flex items-center justify-between">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div>{t('If you have issues displaying custom HTML elements (i.e when using LWC), consider turning on Virtual Mode.')}</div>
|
||||||
|
<div className='link' onClick={onVMode}>{t('Enable')}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="py-1 ml-3 cursor-pointer"
|
className="py-1 ml-3 cursor-pointer"
|
||||||
onClick={() => closeWarning(2)}
|
onClick={() => closeWarning(2)}
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ export function AutocompleteModal({
|
||||||
if (index === blocksAmount - 1 && blocksAmount > 1) {
|
if (index === blocksAmount - 1 && blocksAmount > 1) {
|
||||||
str += ' and ';
|
str += ' and ';
|
||||||
}
|
}
|
||||||
str += `"${block.trim()}"`;
|
str += block.trim();
|
||||||
if (index < blocksAmount - 2) {
|
if (index < blocksAmount - 2) {
|
||||||
str += ', ';
|
str += ', ';
|
||||||
}
|
}
|
||||||
|
|
@ -188,10 +188,10 @@ export function AutocompleteModal({
|
||||||
{query.length ? (
|
{query.length ? (
|
||||||
<div className="border-y border-y-gray-light py-2">
|
<div className="border-y border-y-gray-light py-2">
|
||||||
<div
|
<div
|
||||||
className="whitespace-normal rounded cursor-pointer text-teal hover:bg-active-blue px-2 py-1"
|
className="whitespace-nowrap truncate w-full rounded cursor-pointer text-teal hover:bg-active-blue px-2 py-1"
|
||||||
onClick={applyQuery}
|
onClick={applyQuery}
|
||||||
>
|
>
|
||||||
{t('Apply')} {queryStr}
|
{t('Apply')} <span className='font-semibold'>{queryStr}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,10 @@ const FilterAutoComplete = observer(
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
|
if (!initialFocus) {
|
||||||
|
setOptions(topValues.map((i) => ({ value: i.value, label: i.value })));
|
||||||
|
}
|
||||||
setInitialFocus(true);
|
setInitialFocus(true);
|
||||||
setOptions(topValues.map((i) => ({ value: i.value, label: i.value })));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import ListingVisibility from './components/ListingVisibility';
|
||||||
import DefaultPlaying from './components/DefaultPlaying';
|
import DefaultPlaying from './components/DefaultPlaying';
|
||||||
import DefaultTimezone from './components/DefaultTimezone';
|
import DefaultTimezone from './components/DefaultTimezone';
|
||||||
import CaptureRate from './components/CaptureRate';
|
import CaptureRate from './components/CaptureRate';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function SessionSettings() {
|
function SessionSettings() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useStore } from 'App/mstore';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { Switch } from 'UI';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
function VirtualModeSettings() {
|
||||||
|
const { settingsStore } = useStore();
|
||||||
|
const { sessionSettings } = settingsStore;
|
||||||
|
const { virtualMode } = sessionSettings;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const updateSettings = (checked: boolean) => {
|
||||||
|
settingsStore.sessionSettings.updateKey('virtualMode', !virtualMode);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg">{t('Virtual Mode')}</h3>
|
||||||
|
<div className="my-1">
|
||||||
|
{t('Change this setting if you have issues with recordings containing Lightning Web Components (or similar custom HTML Element libraries).')}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<Switch onChange={updateSettings} checked={virtualMode} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default observer(VirtualModeSettings);
|
||||||
|
|
@ -9,6 +9,7 @@ export const GLOBAL_HAS_NO_RECORDINGS = '__$global-hasNoRecordings$__';
|
||||||
export const SITE_ID_STORAGE_KEY = '__$user-siteId$__';
|
export const SITE_ID_STORAGE_KEY = '__$user-siteId$__';
|
||||||
export const GETTING_STARTED = '__$user-gettingStarted$__';
|
export const GETTING_STARTED = '__$user-gettingStarted$__';
|
||||||
export const MOUSE_TRAIL = '__$session-mouseTrail$__';
|
export const MOUSE_TRAIL = '__$session-mouseTrail$__';
|
||||||
|
export const VIRTUAL_MODE_KEY = '__$session-virtualMode$__'
|
||||||
export const IFRAME = '__$session-iframe$__';
|
export const IFRAME = '__$session-iframe$__';
|
||||||
export const JWT_PARAM = '__$session-jwt-param$__';
|
export const JWT_PARAM = '__$session-jwt-param$__';
|
||||||
export const MENU_COLLAPSED = '__$global-menuCollapsed$__';
|
export const MENU_COLLAPSED = '__$global-menuCollapsed$__';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { makeAutoObservable, runInAction } from 'mobx';
|
import { makeAutoObservable, runInAction, reaction } from 'mobx';
|
||||||
import { dashboardService, metricService } from 'App/services';
|
import { dashboardService, metricService } from 'App/services';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import Period, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period';
|
import Period, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period';
|
||||||
|
|
@ -6,6 +6,7 @@ import { getRE } from 'App/utils';
|
||||||
import Filter from './types/filter';
|
import Filter from './types/filter';
|
||||||
import Widget from './types/widget';
|
import Widget from './types/widget';
|
||||||
import Dashboard from './types/dashboard';
|
import Dashboard from './types/dashboard';
|
||||||
|
import { calculateGranularities } from '@/components/Dashboard/components/WidgetDateRange/RangeGranularity';
|
||||||
|
|
||||||
interface DashboardFilter {
|
interface DashboardFilter {
|
||||||
query?: string;
|
query?: string;
|
||||||
|
|
@ -36,7 +37,7 @@ export default class DashboardStore {
|
||||||
|
|
||||||
drillDownPeriod: Record<string, any> = Period({ rangeName: LAST_24_HOURS });
|
drillDownPeriod: Record<string, any> = Period({ rangeName: LAST_24_HOURS });
|
||||||
|
|
||||||
selectedDensity: number = 7; // depends on default drilldown, 7 points here!!!;
|
selectedDensity: number = 7;
|
||||||
|
|
||||||
comparisonPeriods: Record<string, any> = {};
|
comparisonPeriods: Record<string, any> = {};
|
||||||
|
|
||||||
|
|
@ -83,10 +84,25 @@ export default class DashboardStore {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
|
|
||||||
this.resetDrillDownFilter();
|
this.resetDrillDownFilter();
|
||||||
|
|
||||||
|
this.createDensity(this.period.getDuration());
|
||||||
|
reaction(
|
||||||
|
() => this.period,
|
||||||
|
(period) => {
|
||||||
|
this.createDensity(period.getDuration());
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
setDensity = (density: any) => {
|
createDensity = (duration: number) => {
|
||||||
this.selectedDensity = parseInt(density, 10);
|
const densityOpts = calculateGranularities(duration);
|
||||||
|
const defaultOption = densityOpts[densityOpts.length - 2];
|
||||||
|
|
||||||
|
this.setDensity(defaultOption.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
setDensity = (density: number) => {
|
||||||
|
this.selectedDensity = density;
|
||||||
};
|
};
|
||||||
|
|
||||||
get sortedDashboards() {
|
get sortedDashboards() {
|
||||||
|
|
@ -529,7 +545,7 @@ export default class DashboardStore {
|
||||||
const data = await metricService.getMetricChartData(
|
const data = await metricService.getMetricChartData(
|
||||||
metric,
|
metric,
|
||||||
params,
|
params,
|
||||||
isSaved,
|
isSaved
|
||||||
);
|
);
|
||||||
resolve(metric.setData(data, period, isComparison, density));
|
resolve(metric.setData(data, period, isComparison, density));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
SHOWN_TIMEZONE,
|
SHOWN_TIMEZONE,
|
||||||
DURATION_FILTER,
|
DURATION_FILTER,
|
||||||
MOUSE_TRAIL,
|
MOUSE_TRAIL,
|
||||||
|
VIRTUAL_MODE_KEY,
|
||||||
} from 'App/constants/storageKeys';
|
} from 'App/constants/storageKeys';
|
||||||
import { DateTime, Settings } from 'luxon';
|
import { DateTime, Settings } from 'luxon';
|
||||||
|
|
||||||
|
|
@ -71,27 +72,19 @@ export const generateGMTZones = (): Timezone[] => {
|
||||||
|
|
||||||
export default class SessionSettings {
|
export default class SessionSettings {
|
||||||
defaultTimezones = [...generateGMTZones()];
|
defaultTimezones = [...generateGMTZones()];
|
||||||
|
|
||||||
skipToIssue: boolean = localStorage.getItem(SKIP_TO_ISSUE) === 'true';
|
skipToIssue: boolean = localStorage.getItem(SKIP_TO_ISSUE) === 'true';
|
||||||
|
|
||||||
timezone: Timezone;
|
timezone: Timezone;
|
||||||
|
|
||||||
durationFilter: any = JSON.parse(
|
durationFilter: any = JSON.parse(
|
||||||
localStorage.getItem(DURATION_FILTER) ||
|
localStorage.getItem(DURATION_FILTER) ||
|
||||||
JSON.stringify(defaultDurationFilter),
|
JSON.stringify(defaultDurationFilter),
|
||||||
);
|
);
|
||||||
|
|
||||||
captureRate: string = '0';
|
captureRate: string = '0';
|
||||||
|
|
||||||
conditionalCapture: boolean = false;
|
conditionalCapture: boolean = false;
|
||||||
|
|
||||||
captureConditions: { name: string; captureRate: number; filters: any[] }[] =
|
captureConditions: { name: string; captureRate: number; filters: any[] }[] =
|
||||||
[];
|
[];
|
||||||
|
|
||||||
mouseTrail: boolean = localStorage.getItem(MOUSE_TRAIL) !== 'false';
|
mouseTrail: boolean = localStorage.getItem(MOUSE_TRAIL) !== 'false';
|
||||||
|
|
||||||
shownTimezone: 'user' | 'local';
|
shownTimezone: 'user' | 'local';
|
||||||
|
virtualMode: boolean = localStorage.getItem(VIRTUAL_MODE_KEY) === 'true';
|
||||||
usingLocal: boolean = false;
|
usingLocal: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ export default class Widget {
|
||||||
fromJson(json: any, period?: any) {
|
fromJson(json: any, period?: any) {
|
||||||
json.config = json.config || {};
|
json.config = json.config || {};
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
this.dashboardId = json.dashboardId;
|
||||||
this.metricId = json.metricId;
|
this.metricId = json.metricId;
|
||||||
this.widgetId = json.widgetId;
|
this.widgetId = json.widgetId;
|
||||||
this.metricValue = this.metricValueFromArray(
|
this.metricValue = this.metricValueFromArray(
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ export default class MessageLoader {
|
||||||
|
|
||||||
let artificialStartTime = Infinity;
|
let artificialStartTime = Infinity;
|
||||||
let startTimeSet = false;
|
let startTimeSet = false;
|
||||||
|
|
||||||
msgs.forEach((msg, i) => {
|
msgs.forEach((msg, i) => {
|
||||||
if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) {
|
if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) {
|
||||||
if ('actionTime' in msg && msg.actionTime) {
|
if ('actionTime' in msg && msg.actionTime) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Decoder } from 'syncod';
|
import { Decoder } from 'syncod';
|
||||||
import logger from 'App/logger';
|
import logger from 'App/logger';
|
||||||
|
import { VIRTUAL_MODE_KEY } from '@/constants/storageKeys';
|
||||||
import type { Store, ILog, SessionFilesInfo } from 'Player';
|
import type { Store, ILog, SessionFilesInfo } from 'Player';
|
||||||
import TabSessionManager, { TabState } from 'Player/web/TabManager';
|
import TabSessionManager, { TabState } from 'Player/web/TabManager';
|
||||||
import ActiveTabManager from 'Player/web/managers/ActiveTabManager';
|
import ActiveTabManager from 'Player/web/managers/ActiveTabManager';
|
||||||
|
|
@ -69,6 +69,7 @@ export interface State extends ScreenState {
|
||||||
tabChangeEvents: TabChangeEvent[];
|
tabChangeEvents: TabChangeEvent[];
|
||||||
closedTabs: string[];
|
closedTabs: string[];
|
||||||
sessionStart: number;
|
sessionStart: number;
|
||||||
|
vModeBadge: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const visualChanges = [
|
export const visualChanges = [
|
||||||
|
|
@ -99,6 +100,7 @@ export default class MessageManager {
|
||||||
closedTabs: [],
|
closedTabs: [],
|
||||||
sessionStart: 0,
|
sessionStart: 0,
|
||||||
tabNames: {},
|
tabNames: {},
|
||||||
|
vModeBadge: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
private clickManager: ListWalker<MouseClick> = new ListWalker();
|
private clickManager: ListWalker<MouseClick> = new ListWalker();
|
||||||
|
|
@ -126,7 +128,6 @@ export default class MessageManager {
|
||||||
private tabsAmount = 0;
|
private tabsAmount = 0;
|
||||||
|
|
||||||
private tabChangeEvents: TabChangeEvent[] = [];
|
private tabChangeEvents: TabChangeEvent[] = [];
|
||||||
|
|
||||||
private activeTab = '';
|
private activeTab = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -142,8 +143,19 @@ export default class MessageManager {
|
||||||
this.activityManager = new ActivityManager(
|
this.activityManager = new ActivityManager(
|
||||||
this.session.duration.milliseconds,
|
this.session.duration.milliseconds,
|
||||||
); // only if not-live
|
); // only if not-live
|
||||||
|
|
||||||
|
const vMode = localStorage.getItem(VIRTUAL_MODE_KEY);
|
||||||
|
if (vMode === 'true') {
|
||||||
|
this.setVirtualMode(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private virtualMode = false;
|
||||||
|
public setVirtualMode = (virtualMode: boolean) => {
|
||||||
|
this.virtualMode = virtualMode;
|
||||||
|
Object.values(this.tabs).forEach((tab) => tab.setVirtualMode(virtualMode));
|
||||||
|
};
|
||||||
|
|
||||||
public getListsFullState = () => {
|
public getListsFullState = () => {
|
||||||
const fullState: Record<string, any> = {};
|
const fullState: Record<string, any> = {};
|
||||||
for (const tab in Object.keys(this.tabs)) {
|
for (const tab in Object.keys(this.tabs)) {
|
||||||
|
|
@ -394,6 +406,9 @@ export default class MessageManager {
|
||||||
this.sessionStart,
|
this.sessionStart,
|
||||||
this.initialLists,
|
this.initialLists,
|
||||||
);
|
);
|
||||||
|
if (this.virtualMode) {
|
||||||
|
this.tabs[msg.tabId].setVirtualMode(this.virtualMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastMessageTime = Math.max(msg.time, this.lastMessageTime);
|
const lastMessageTime = Math.max(msg.time, this.lastMessageTime);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ export default class TabSessionManager {
|
||||||
tabStates: { [tabId: string]: TabState };
|
tabStates: { [tabId: string]: TabState };
|
||||||
tabNames: { [tabId: string]: string };
|
tabNames: { [tabId: string]: string };
|
||||||
location?: string;
|
location?: string;
|
||||||
|
vModeBadge?: boolean;
|
||||||
}>,
|
}>,
|
||||||
private readonly screen: Screen,
|
private readonly screen: Screen,
|
||||||
private readonly id: string,
|
private readonly id: string,
|
||||||
|
|
@ -116,6 +117,14 @@ export default class TabSessionManager {
|
||||||
screen,
|
screen,
|
||||||
this.session.isMobile,
|
this.session.isMobile,
|
||||||
this.setCSSLoading,
|
this.setCSSLoading,
|
||||||
|
() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.state.update({
|
||||||
|
vModeBadge: true,
|
||||||
|
})
|
||||||
|
// easier to spot the warning appearing plus more time to go over all messages
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
this.lists = new Lists(initialLists);
|
this.lists = new Lists(initialLists);
|
||||||
initialLists?.event?.forEach((e: Record<string, string>) => {
|
initialLists?.event?.forEach((e: Record<string, string>) => {
|
||||||
|
|
@ -126,6 +135,10 @@ export default class TabSessionManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setVirtualMode = (virtualMode: boolean) => {
|
||||||
|
this.pagesManager.setVirtualMode(virtualMode);
|
||||||
|
};
|
||||||
|
|
||||||
setSession = (session: any) => {
|
setSession = (session: any) => {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,10 @@ export default class WebPlayer extends Player {
|
||||||
inspectorMode: false,
|
inspectorMode: false,
|
||||||
mobsFetched: false,
|
mobsFetched: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
private inspectorController: InspectorController;
|
private inspectorController: InspectorController;
|
||||||
|
|
||||||
protected screen: Screen;
|
protected screen: Screen;
|
||||||
|
|
||||||
protected readonly messageManager: MessageManager;
|
protected readonly messageManager: MessageManager;
|
||||||
|
|
||||||
protected readonly messageLoader: MessageLoader;
|
protected readonly messageLoader: MessageLoader;
|
||||||
|
|
||||||
private targetMarker: TargetMarker;
|
private targetMarker: TargetMarker;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -104,6 +99,10 @@ export default class WebPlayer extends Player {
|
||||||
window.__OPENREPLAY_DEV_TOOLS__.player = this;
|
window.__OPENREPLAY_DEV_TOOLS__.player = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableVMode = () => {
|
||||||
|
this.messageManager.setVirtualMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
preloadFirstFile(data: Uint8Array, fileKey?: string) {
|
preloadFirstFile(data: Uint8Array, fileKey?: string) {
|
||||||
void this.messageLoader.preloadFirstFile(data, fileKey);
|
void this.messageLoader.preloadFirstFile(data, fileKey);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,47 +44,33 @@ const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/;
|
||||||
|
|
||||||
export default class DOMManager extends ListWalker<Message> {
|
export default class DOMManager extends ListWalker<Message> {
|
||||||
private readonly vTexts: Map<number, VText> = new Map(); // map vs object here?
|
private readonly vTexts: Map<number, VText> = new Map(); // map vs object here?
|
||||||
|
|
||||||
private readonly vElements: Map<number, VElement> = new Map();
|
private readonly vElements: Map<number, VElement> = new Map();
|
||||||
|
|
||||||
private readonly olVRoots: Map<number, OnloadVRoot> = new Map();
|
private readonly olVRoots: Map<number, OnloadVRoot> = new Map();
|
||||||
|
|
||||||
/** required to keep track of iframes, frameId : vnodeId */
|
/** required to keep track of iframes, frameId : vnodeId */
|
||||||
private readonly iframeRoots: Record<number, number> = {};
|
private readonly iframeRoots: Record<number, number> = {};
|
||||||
|
|
||||||
private shadowRootParentMap: Map<number, number> = new Map();
|
private shadowRootParentMap: Map<number, number> = new Map();
|
||||||
|
|
||||||
/** Constructed StyleSheets https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
|
/** Constructed StyleSheets https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
|
||||||
* as well as <style> tag owned StyleSheets
|
* as well as <style> tag owned StyleSheets
|
||||||
*/
|
*/
|
||||||
private olStyleSheets: Map<number, OnloadStyleSheet> = new Map();
|
private olStyleSheets: Map<number, OnloadStyleSheet> = new Map();
|
||||||
|
|
||||||
/** @depreacted since tracker 4.0.2 Mapping by nodeID */
|
/** @depreacted since tracker 4.0.2 Mapping by nodeID */
|
||||||
private olStyleSheetsDeprecated: Map<number, OnloadStyleSheet> = new Map();
|
private olStyleSheetsDeprecated: Map<number, OnloadStyleSheet> = new Map();
|
||||||
|
|
||||||
private upperBodyId: number = -1;
|
private upperBodyId: number = -1;
|
||||||
|
|
||||||
private nodeScrollManagers: Map<number, ListWalker<SetNodeScroll>> =
|
private nodeScrollManagers: Map<number, ListWalker<SetNodeScroll>> =
|
||||||
new Map();
|
new Map();
|
||||||
|
|
||||||
private stylesManager: StylesManager;
|
private stylesManager: StylesManager;
|
||||||
|
|
||||||
private focusManager: FocusManager = new FocusManager(this.vElements);
|
private focusManager: FocusManager = new FocusManager(this.vElements);
|
||||||
|
|
||||||
private selectionManager: SelectionManager;
|
private selectionManager: SelectionManager;
|
||||||
|
|
||||||
private readonly screen: Screen;
|
private readonly screen: Screen;
|
||||||
|
|
||||||
private readonly isMobile: boolean;
|
private readonly isMobile: boolean;
|
||||||
|
|
||||||
private readonly stringDict: Record<number, string>;
|
private readonly stringDict: Record<number, string>;
|
||||||
|
|
||||||
private readonly globalDict: {
|
private readonly globalDict: {
|
||||||
get: (key: string) => string | undefined;
|
get: (key: string) => string | undefined;
|
||||||
all: () => Record<string, string>;
|
all: () => Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
public readonly time: number;
|
public readonly time: number;
|
||||||
|
private virtualMode: boolean = false;
|
||||||
|
private showVModeBadge?: () => void;
|
||||||
|
|
||||||
constructor(params: {
|
constructor(params: {
|
||||||
screen: Screen;
|
screen: Screen;
|
||||||
|
|
@ -96,6 +82,8 @@ export default class DOMManager extends ListWalker<Message> {
|
||||||
get: (key: string) => string | undefined;
|
get: (key: string) => string | undefined;
|
||||||
all: () => Record<string, string>;
|
all: () => Record<string, string>;
|
||||||
};
|
};
|
||||||
|
virtualMode?: boolean;
|
||||||
|
showVModeBadge?: () => void;
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
this.screen = params.screen;
|
this.screen = params.screen;
|
||||||
|
|
@ -105,6 +93,8 @@ export default class DOMManager extends ListWalker<Message> {
|
||||||
this.globalDict = params.globalDict;
|
this.globalDict = params.globalDict;
|
||||||
this.selectionManager = new SelectionManager(this.vElements, params.screen);
|
this.selectionManager = new SelectionManager(this.vElements, params.screen);
|
||||||
this.stylesManager = new StylesManager(params.screen, params.setCssLoading);
|
this.stylesManager = new StylesManager(params.screen, params.setCssLoading);
|
||||||
|
this.virtualMode = params.virtualMode || false;
|
||||||
|
this.showVModeBadge = params.showVModeBadge;
|
||||||
setupWindowLogging(this.vTexts, this.vElements, this.olVRoots);
|
setupWindowLogging(this.vTexts, this.vElements, this.olVRoots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,14 +440,18 @@ export default class DOMManager extends ListWalker<Message> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// shadow DOM for a custom element + SALESFORCE (<slot>)
|
// shadow DOM for a custom element + SALESFORCE (<slot>)
|
||||||
const isCustomElement = vElem.tagName.includes('-') || vElem.tagName === 'SLOT';
|
const isCustomElement =
|
||||||
const isNotActualIframe = !["IFRAME", "FRAME"].includes(vElem.tagName.toUpperCase());
|
vElem.tagName.includes('-') || vElem.tagName === 'SLOT';
|
||||||
const isLikelyShadowRoot = isCustomElement && isNotActualIframe;
|
const hasSlots = vElem.tagName === 'SLOT';
|
||||||
|
|
||||||
if (isLikelyShadowRoot) {
|
if (isCustomElement) {
|
||||||
// Store the mapping but don't create the actual shadow root
|
if (this.virtualMode) {
|
||||||
this.shadowRootParentMap.set(msg.id, msg.frameID);
|
// Store the mapping but don't create the actual shadow root
|
||||||
return;
|
this.shadowRootParentMap.set(msg.id, msg.frameID);
|
||||||
|
return;
|
||||||
|
} else if (hasSlots) {
|
||||||
|
this.showVModeBadge?.();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real iframes
|
// Real iframes
|
||||||
|
|
@ -473,7 +467,11 @@ export default class DOMManager extends ListWalker<Message> {
|
||||||
case MType.AdoptedSsInsertRule: {
|
case MType.AdoptedSsInsertRule: {
|
||||||
const styleSheet = this.olStyleSheets.get(msg.sheetID);
|
const styleSheet = this.olStyleSheets.get(msg.sheetID);
|
||||||
if (!styleSheet) {
|
if (!styleSheet) {
|
||||||
logger.warn('No stylesheet was created for ', msg, this.olStyleSheets);
|
logger.warn(
|
||||||
|
'No stylesheet was created for ',
|
||||||
|
msg,
|
||||||
|
this.olStyleSheets,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
insertRule(styleSheet, msg);
|
insertRule(styleSheet, msg);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export default class PagesManager extends ListWalker<DOMManager> {
|
||||||
private screen: Screen,
|
private screen: Screen,
|
||||||
private isMobile: boolean,
|
private isMobile: boolean,
|
||||||
private setCssLoading: (flag: boolean) => void,
|
private setCssLoading: (flag: boolean) => void,
|
||||||
|
private showVModeBadge: () => void,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +31,10 @@ export default class PagesManager extends ListWalker<DOMManager> {
|
||||||
Assumed that messages added in a correct time sequence.
|
Assumed that messages added in a correct time sequence.
|
||||||
*/
|
*/
|
||||||
falseOrder = false;
|
falseOrder = false;
|
||||||
|
virtualMode = false;
|
||||||
|
setVirtualMode = (virtualMode: boolean) => {
|
||||||
|
this.virtualMode = virtualMode;
|
||||||
|
};
|
||||||
|
|
||||||
appendMessage(m: Message): void {
|
appendMessage(m: Message): void {
|
||||||
if ([MType.StringDict, MType.StringDictGlobal].includes(m.tp)) {
|
if ([MType.StringDict, MType.StringDictGlobal].includes(m.tp)) {
|
||||||
|
|
@ -62,6 +67,8 @@ export default class PagesManager extends ListWalker<DOMManager> {
|
||||||
get: (key: string) => this.globalDictionary.get(key),
|
get: (key: string) => this.globalDictionary.get(key),
|
||||||
all: () => Object.fromEntries(this.globalDictionary),
|
all: () => Object.fromEntries(this.globalDictionary),
|
||||||
},
|
},
|
||||||
|
virtualMode: this.virtualMode,
|
||||||
|
showVModeBadge: this.showVModeBadge,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
this.falseOrder = false;
|
this.falseOrder = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue