From 7ffaeb319f4e833b58f83c7927c5aec7ea6d9352 Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Mon, 12 May 2025 15:00:18 +0200 Subject: [PATCH] ui: introduce vmode for lwc light dom --- .../Client/SessionsListingSettings.tsx | 2 + .../app/components/Session_/Subheader.tsx | 12 ++++- .../app/components/Session_/WarnBadge.tsx | 49 ++++++++++++++----- .../SessionSettings/SessionSettings.tsx | 1 + .../components/VirtualMode.tsx | 30 ++++++++++++ frontend/app/constants/storageKeys.ts | 1 + frontend/app/mstore/types/sessionSettings.ts | 11 +---- frontend/app/player/web/MessageLoader.ts | 19 ------- frontend/app/player/web/MessageManager.ts | 9 +++- frontend/app/player/web/TabManager.ts | 6 +++ frontend/app/player/web/WebPlayer.ts | 9 ++-- .../app/player/web/managers/DOM/DOMManager.ts | 41 +++++++--------- .../app/player/web/managers/PagesManager.ts | 2 + 13 files changed, 124 insertions(+), 68 deletions(-) create mode 100644 frontend/app/components/shared/SessionSettings/components/VirtualMode.tsx diff --git a/frontend/app/components/Client/SessionsListingSettings.tsx b/frontend/app/components/Client/SessionsListingSettings.tsx index 02875ed0c..8fc159911 100644 --- a/frontend/app/components/Client/SessionsListingSettings.tsx +++ b/frontend/app/components/Client/SessionsListingSettings.tsx @@ -6,6 +6,7 @@ import DefaultPlaying from 'Shared/SessionSettings/components/DefaultPlaying'; import DefaultTimezone from 'Shared/SessionSettings/components/DefaultTimezone'; import ListingVisibility from 'Shared/SessionSettings/components/ListingVisibility'; import MouseTrailSettings from 'Shared/SessionSettings/components/MouseTrailSettings'; +import VirtualModeSettings from '../shared/SessionSettings/components/VirtualMode'; import DebugLog from './DebugLog'; import { useTranslation } from 'react-i18next'; @@ -35,6 +36,7 @@ function SessionsListingSettings() {
+
diff --git a/frontend/app/components/Session_/Subheader.tsx b/frontend/app/components/Session_/Subheader.tsx index addea8840..77de34cc1 100644 --- a/frontend/app/components/Session_/Subheader.tsx +++ b/frontend/app/components/Session_/Subheader.tsx @@ -38,6 +38,7 @@ function SubHeader(props) { projectsStore, userStore, issueReportingStore, + settingsStore } = useStore(); const { t } = useTranslation(); const { favorite } = sessionStore.current; @@ -45,7 +46,7 @@ function SubHeader(props) { const currentSession = sessionStore.current; const projectId = projectsStore.siteId; const integrations = integrationsStore.issues.list; - const { store } = React.useContext(PlayerContext); + const { player, store } = React.useContext(PlayerContext); const { location: currentLocation = 'loading...' } = store.get(); const hasIframe = localStorage.getItem(IFRAME) === 'true'; 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 ( <>
diff --git a/frontend/app/components/Session_/WarnBadge.tsx b/frontend/app/components/Session_/WarnBadge.tsx index 1f4bc275b..a75459485 100644 --- a/frontend/app/components/Session_/WarnBadge.tsx +++ b/frontend/app/components/Session_/WarnBadge.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Alert } from 'antd'; import { Icon } from 'UI'; import { useTranslation } from 'react-i18next'; +import { VIRTUAL_MODE_KEY } from '@/player/web/MessageManager'; const localhostWarn = (project: string) => `${project}_localhost_warn`; @@ -34,10 +35,14 @@ const WarnBadge = React.memo( currentLocation, version, siteId, + virtualElsFailed, + onVMode, }: { currentLocation: string; version: string; siteId: string; + virtualElsFailed: boolean; + onVMode: () => void; }) => { const { t } = useTranslation(); const localhostWarnSiteKey = localhostWarn(siteId); @@ -51,21 +56,28 @@ const WarnBadge = React.memo( const trackerVerDiff = compareVersions(version, trackerVersion); const trackerWarnActive = trackerVerDiff !== VersionComparison.Same; - const [showLocalhostWarn, setLocalhostWarn] = - React.useState(localhostWarnActive); - const [showTrackerWarn, setTrackerWarn] = React.useState(trackerWarnActive); + const [warnings, setWarnings] = React.useState([localhostWarnActive, trackerWarnActive, virtualElsFailed]) - const closeWarning = (type: 1 | 2) => { + React.useEffect(() => { + setWarnings([localhostWarnActive, trackerWarnActive, virtualElsFailed]) + }, [localhostWarnActive, trackerWarnActive, virtualElsFailed]) + const closeWarning = (type: 0 | 1 | 2) => { if (type === 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; + const switchVirtualMode = () => { + + } + + console.log(warnings) + if (!warnings.some(el => el === true)) return null; return (
- {showLocalhostWarn ? ( + {warnings[0] ? (
{t('Some assets may load incorrectly on localhost.')} @@ -101,7 +113,7 @@ const WarnBadge = React.memo(
) : null} - {showTrackerWarn ? ( + {warnings[1] ? (
@@ -133,6 +145,21 @@ const WarnBadge = React.memo(
) : null} + {warnings[2] ? ( +
+
+
{t('If you have issues displaying custom HTML elements (i.e when using LWC), consider turning on Virtual Mode.')}
+
{t('Enable')}
+
+ +
+ +
+
+ ) : null}
); }, 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/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/player/web/MessageLoader.ts b/frontend/app/player/web/MessageLoader.ts index 6cdafdc52..f56947cf0 100644 --- a/frontend/app/player/web/MessageLoader.ts +++ b/frontend/app/player/web/MessageLoader.ts @@ -80,26 +80,7 @@ export default class MessageLoader { let artificialStartTime = Infinity; let startTimeSet = false; - const customElIds = new Set(); - const connectedCustomRoots = new Set(); msgs.forEach((msg, i) => { - if (msg.tp === MType.CreateElementNode) { - if (msg.tag.includes('-')) { - customElIds.add(msg.id); - } - } - if (msg.tp === MType.CreateIFrameDocument) { - if (customElIds.has(msg.frameID)) { - connectedCustomRoots.add(msg.id); - } - } - if (msg.tp === MType.AdoptedSsAddOwner) { - if (connectedCustomRoots.has(msg.id)) { - // custom els with styles connected to elements - // @ts-ignore - // this.messageManager.setVirtualMode(true) - } - } if (msg.tp === MType.Redux || msg.tp === MType.ReduxDeprecated) { if ('actionTime' in msg && msg.actionTime) { msg.time = msg.actionTime - this.session.startedAt; diff --git a/frontend/app/player/web/MessageManager.ts b/frontend/app/player/web/MessageManager.ts index aed9ea204..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(); @@ -141,6 +143,11 @@ 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; diff --git a/frontend/app/player/web/TabManager.ts b/frontend/app/player/web/TabManager.ts index 191391699..d30f954de 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,11 @@ export default class TabSessionManager { screen, this.session.isMobile, this.setCSSLoading, + () => { + this.state.update({ + vModeBadge: true, + }) + } ); this.lists = new Lists(initialLists); initialLists?.event?.forEach((e: Record) => { diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts index ca1c29d60..50b77ee39 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( @@ -124,6 +119,10 @@ export default class WebPlayer extends Player { }) } + 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 fdebd7cb8..67c3415d0 100644 --- a/frontend/app/player/web/managers/DOM/DOMManager.ts +++ b/frontend/app/player/web/managers/DOM/DOMManager.ts @@ -44,48 +44,34 @@ 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