closeWarning(2)}
diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/AutocompleteModal.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/AutocompleteModal.tsx
index e8d86beb4..88042887c 100644
--- a/frontend/app/components/shared/Filters/FilterAutoComplete/AutocompleteModal.tsx
+++ b/frontend/app/components/shared/Filters/FilterAutoComplete/AutocompleteModal.tsx
@@ -125,7 +125,7 @@ export function AutocompleteModal({
if (index === blocksAmount - 1 && blocksAmount > 1) {
str += ' and ';
}
- str += `"${block.trim()}"`;
+ str += block.trim();
if (index < blocksAmount - 2) {
str += ', ';
}
@@ -188,10 +188,10 @@ export function AutocompleteModal({
{query.length ? (
- {t('Apply')} {queryStr}
+ {t('Apply')} {queryStr}
) : null}
diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx
index 2b6fa260a..41fc685b1 100644
--- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx
+++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx
@@ -128,8 +128,10 @@ const FilterAutoComplete = observer(
};
const handleFocus = () => {
+ if (!initialFocus) {
+ setOptions(topValues.map((i) => ({ value: i.value, label: i.value })));
+ }
setInitialFocus(true);
- setOptions(topValues.map((i) => ({ value: i.value, label: i.value })));
};
return (
diff --git a/frontend/app/components/shared/SessionSettings/SessionSettings.tsx b/frontend/app/components/shared/SessionSettings/SessionSettings.tsx
index 4556a0cb8..60da59598 100644
--- a/frontend/app/components/shared/SessionSettings/SessionSettings.tsx
+++ b/frontend/app/components/shared/SessionSettings/SessionSettings.tsx
@@ -5,6 +5,7 @@ import ListingVisibility from './components/ListingVisibility';
import DefaultPlaying from './components/DefaultPlaying';
import DefaultTimezone from './components/DefaultTimezone';
import CaptureRate from './components/CaptureRate';
+
import { useTranslation } from 'react-i18next';
function SessionSettings() {
diff --git a/frontend/app/components/shared/SessionSettings/components/VirtualMode.tsx b/frontend/app/components/shared/SessionSettings/components/VirtualMode.tsx
new file mode 100644
index 000000000..690d64ac2
--- /dev/null
+++ b/frontend/app/components/shared/SessionSettings/components/VirtualMode.tsx
@@ -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 (
+
+
{t('Virtual Mode')}
+
+ {t('Change this setting if you have issues with recordings containing Lightning Web Components (or similar custom HTML Element libraries).')}
+
+
+
+
+
+ );
+}
+
+export default observer(VirtualModeSettings);
diff --git a/frontend/app/constants/storageKeys.ts b/frontend/app/constants/storageKeys.ts
index 948f47bf7..02eab3e48 100644
--- a/frontend/app/constants/storageKeys.ts
+++ b/frontend/app/constants/storageKeys.ts
@@ -9,6 +9,7 @@ export const GLOBAL_HAS_NO_RECORDINGS = '__$global-hasNoRecordings$__';
export const SITE_ID_STORAGE_KEY = '__$user-siteId$__';
export const GETTING_STARTED = '__$user-gettingStarted$__';
export const MOUSE_TRAIL = '__$session-mouseTrail$__';
+export const VIRTUAL_MODE_KEY = '__$session-virtualMode$__'
export const IFRAME = '__$session-iframe$__';
export const JWT_PARAM = '__$session-jwt-param$__';
export const MENU_COLLAPSED = '__$global-menuCollapsed$__';
diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts
index 723b97c7d..f985b97ca 100644
--- a/frontend/app/mstore/dashboardStore.ts
+++ b/frontend/app/mstore/dashboardStore.ts
@@ -1,4 +1,4 @@
-import { makeAutoObservable, runInAction } from 'mobx';
+import { makeAutoObservable, runInAction, reaction } from 'mobx';
import { dashboardService, metricService } from 'App/services';
import { toast } from 'react-toastify';
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 Widget from './types/widget';
import Dashboard from './types/dashboard';
+import { calculateGranularities } from '@/components/Dashboard/components/WidgetDateRange/RangeGranularity';
interface DashboardFilter {
query?: string;
@@ -36,7 +37,7 @@ export default class DashboardStore {
drillDownPeriod: Record
= Period({ rangeName: LAST_24_HOURS });
- selectedDensity: number = 7; // depends on default drilldown, 7 points here!!!;
+ selectedDensity: number = 7;
comparisonPeriods: Record = {};
@@ -83,10 +84,25 @@ export default class DashboardStore {
makeAutoObservable(this);
this.resetDrillDownFilter();
+
+ this.createDensity(this.period.getDuration());
+ reaction(
+ () => this.period,
+ (period) => {
+ this.createDensity(period.getDuration());
+ }
+ )
}
- setDensity = (density: any) => {
- this.selectedDensity = parseInt(density, 10);
+ createDensity = (duration: number) => {
+ const densityOpts = calculateGranularities(duration);
+ const defaultOption = densityOpts[densityOpts.length - 2];
+
+ this.setDensity(defaultOption.key)
+ }
+
+ setDensity = (density: number) => {
+ this.selectedDensity = density;
};
get sortedDashboards() {
@@ -529,7 +545,7 @@ export default class DashboardStore {
const data = await metricService.getMetricChartData(
metric,
params,
- isSaved,
+ isSaved
);
resolve(metric.setData(data, period, isComparison, density));
} catch (error) {
diff --git a/frontend/app/mstore/types/sessionSettings.ts b/frontend/app/mstore/types/sessionSettings.ts
index c128c44ec..827dc767f 100644
--- a/frontend/app/mstore/types/sessionSettings.ts
+++ b/frontend/app/mstore/types/sessionSettings.ts
@@ -6,6 +6,7 @@ import {
SHOWN_TIMEZONE,
DURATION_FILTER,
MOUSE_TRAIL,
+ VIRTUAL_MODE_KEY,
} from 'App/constants/storageKeys';
import { DateTime, Settings } from 'luxon';
@@ -71,27 +72,19 @@ export const generateGMTZones = (): Timezone[] => {
export default class SessionSettings {
defaultTimezones = [...generateGMTZones()];
-
skipToIssue: boolean = localStorage.getItem(SKIP_TO_ISSUE) === 'true';
-
timezone: Timezone;
-
durationFilter: any = JSON.parse(
localStorage.getItem(DURATION_FILTER) ||
JSON.stringify(defaultDurationFilter),
);
-
captureRate: string = '0';
-
conditionalCapture: boolean = false;
-
captureConditions: { name: string; captureRate: number; filters: any[] }[] =
[];
-
mouseTrail: boolean = localStorage.getItem(MOUSE_TRAIL) !== 'false';
-
shownTimezone: 'user' | 'local';
-
+ virtualMode: boolean = localStorage.getItem(VIRTUAL_MODE_KEY) === 'true';
usingLocal: boolean = false;
constructor() {
diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts
index 2809ef63f..5ce757500 100644
--- a/frontend/app/mstore/types/widget.ts
+++ b/frontend/app/mstore/types/widget.ts
@@ -163,6 +163,7 @@ export default class Widget {
fromJson(json: any, period?: any) {
json.config = json.config || {};
runInAction(() => {
+ this.dashboardId = json.dashboardId;
this.metricId = json.metricId;
this.widgetId = json.widgetId;
this.metricValue = this.metricValueFromArray(
diff --git a/frontend/app/player/web/MessageLoader.ts b/frontend/app/player/web/MessageLoader.ts
index e77f82822..c43355363 100644
--- a/frontend/app/player/web/MessageLoader.ts
+++ b/frontend/app/player/web/MessageLoader.ts
@@ -80,7 +80,6 @@ export default class MessageLoader {
let artificialStartTime = Infinity;
let startTimeSet = false;
-
msgs.forEach((msg, i) => {
if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) {
if ('actionTime' in msg && msg.actionTime) {
diff --git a/frontend/app/player/web/MessageManager.ts b/frontend/app/player/web/MessageManager.ts
index 785b1d21e..ab3e0ae78 100644
--- a/frontend/app/player/web/MessageManager.ts
+++ b/frontend/app/player/web/MessageManager.ts
@@ -1,7 +1,7 @@
// @ts-ignore
import { Decoder } from 'syncod';
import logger from 'App/logger';
-
+import { VIRTUAL_MODE_KEY } from '@/constants/storageKeys';
import type { Store, ILog, SessionFilesInfo } from 'Player';
import TabSessionManager, { TabState } from 'Player/web/TabManager';
import ActiveTabManager from 'Player/web/managers/ActiveTabManager';
@@ -69,6 +69,7 @@ export interface State extends ScreenState {
tabChangeEvents: TabChangeEvent[];
closedTabs: string[];
sessionStart: number;
+ vModeBadge: boolean;
}
export const visualChanges = [
@@ -99,6 +100,7 @@ export default class MessageManager {
closedTabs: [],
sessionStart: 0,
tabNames: {},
+ vModeBadge: false,
};
private clickManager: ListWalker = new ListWalker();
@@ -126,7 +128,6 @@ export default class MessageManager {
private tabsAmount = 0;
private tabChangeEvents: TabChangeEvent[] = [];
-
private activeTab = '';
constructor(
@@ -142,8 +143,19 @@ export default class MessageManager {
this.activityManager = new ActivityManager(
this.session.duration.milliseconds,
); // 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 = () => {
const fullState: Record = {};
for (const tab in Object.keys(this.tabs)) {
@@ -394,6 +406,9 @@ export default class MessageManager {
this.sessionStart,
this.initialLists,
);
+ if (this.virtualMode) {
+ this.tabs[msg.tabId].setVirtualMode(this.virtualMode);
+ }
}
const lastMessageTime = Math.max(msg.time, this.lastMessageTime);
diff --git a/frontend/app/player/web/TabManager.ts b/frontend/app/player/web/TabManager.ts
index 574f7490e..71dd9ed53 100644
--- a/frontend/app/player/web/TabManager.ts
+++ b/frontend/app/player/web/TabManager.ts
@@ -99,6 +99,7 @@ export default class TabSessionManager {
tabStates: { [tabId: string]: TabState };
tabNames: { [tabId: string]: string };
location?: string;
+ vModeBadge?: boolean;
}>,
private readonly screen: Screen,
private readonly id: string,
@@ -116,6 +117,14 @@ export default class TabSessionManager {
screen,
this.session.isMobile,
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);
initialLists?.event?.forEach((e: Record) => {
@@ -126,6 +135,10 @@ export default class TabSessionManager {
});
}
+ public setVirtualMode = (virtualMode: boolean) => {
+ this.pagesManager.setVirtualMode(virtualMode);
+ };
+
setSession = (session: any) => {
this.session = session;
};
diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts
index ee92c7891..119ef56f9 100644
--- a/frontend/app/player/web/WebPlayer.ts
+++ b/frontend/app/player/web/WebPlayer.ts
@@ -21,15 +21,10 @@ export default class WebPlayer extends Player {
inspectorMode: false,
mobsFetched: false,
};
-
private inspectorController: InspectorController;
-
protected screen: Screen;
-
protected readonly messageManager: MessageManager;
-
protected readonly messageLoader: MessageLoader;
-
private targetMarker: TargetMarker;
constructor(
@@ -104,6 +99,10 @@ export default class WebPlayer extends Player {
window.__OPENREPLAY_DEV_TOOLS__.player = this;
}
+ enableVMode = () => {
+ this.messageManager.setVirtualMode(true);
+ }
+
preloadFirstFile(data: Uint8Array, fileKey?: string) {
void this.messageLoader.preloadFirstFile(data, fileKey);
}
diff --git a/frontend/app/player/web/managers/DOM/DOMManager.ts b/frontend/app/player/web/managers/DOM/DOMManager.ts
index d6c666548..b518395b0 100644
--- a/frontend/app/player/web/managers/DOM/DOMManager.ts
+++ b/frontend/app/player/web/managers/DOM/DOMManager.ts
@@ -44,47 +44,33 @@ const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/;
export default class DOMManager extends ListWalker {
private readonly vTexts: Map = new Map(); // map vs object here?
-
private readonly vElements: Map = new Map();
-
private readonly olVRoots: Map = new Map();
-
/** required to keep track of iframes, frameId : vnodeId */
private readonly iframeRoots: Record = {};
-
private shadowRootParentMap: Map = new Map();
-
/** Constructed StyleSheets https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
* as well as