diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx index 6a300cce7..097a57694 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx @@ -68,9 +68,6 @@ function Player(props: IProps) { playerContext.player.attach(parentElement); setAttached(true) } - if (isAttached && isReady) { - playerContext.player.play(); - } }, [isReady]); React.useEffect(() => { diff --git a/frontend/app/components/Session/WebPlayer.tsx b/frontend/app/components/Session/WebPlayer.tsx index 383b70c63..659dbe540 100644 --- a/frontend/app/components/Session/WebPlayer.tsx +++ b/frontend/app/components/Session/WebPlayer.tsx @@ -70,7 +70,8 @@ function WebPlayer(props: any) { if (showNoteModal) { contextValue.player.pause() } - if (activeTab !== 'Click Map' && !showNoteModal && isPlayerReady) { + + if (activeTab === '' && !showNoteModal && isPlayerReady) { contextValue.player && contextValue.player.play() } }, [activeTab, isPlayerReady, showNoteModal]) diff --git a/frontend/app/components/Session_/Storage/Storage.tsx b/frontend/app/components/Session_/Storage/Storage.tsx index f955912e7..8f7f3e92e 100644 --- a/frontend/app/components/Session_/Storage/Storage.tsx +++ b/frontend/app/components/Session_/Storage/Storage.tsx @@ -12,6 +12,7 @@ import BottomBlock from '../BottomBlock/index'; import DiffRow from './DiffRow'; import cn from 'classnames'; import stl from './storage.module.css'; +import logger from "App/logger"; function getActionsName(type: string) { switch (type) { @@ -23,10 +24,19 @@ function getActionsName(type: string) { } } +const storageDecodeKeys = { + [STORAGE_TYPES.REDUX]: ['state', 'action'], + [STORAGE_TYPES.NGRX]: ['state', 'action'], + [STORAGE_TYPES.VUEX]: ['state', 'mutation'], + [STORAGE_TYPES.ZUSTAND]: ['state', 'mutation'], + [STORAGE_TYPES.MOBX]: ['payload'], + [STORAGE_TYPES.NONE]: ['state, action', 'payload', 'mutation'], +} interface Props { hideHint: (args: string) => void; hintIsHidden: boolean; } + function Storage(props: Props) { const lastBtnRef = React.useRef(); const [showDiffs, setShowDiffs] = React.useState(false); @@ -37,6 +47,30 @@ function Storage(props: Props) { const list = selectStorageList(state); const type = selectStorageType(state); + const decodeMessage = (msg: any) => { + const decoded = {}; + const pureMSG = { ...msg } + const keys = storageDecodeKeys[type]; + try { + keys.forEach(key => { + if (pureMSG[key]) { + // @ts-ignore TODO: types for decoder + decoded[key] = player.decodeMessage(pureMSG[key]); + } + }); + } catch (e) { + logger.error("Error on message decoding: ", e, pureMSG); + return null; + } + return { ...pureMSG, ...decoded }; + } + + const decodedList = React.useMemo(() => { + return listNow.map(msg => { + return decodeMessage(msg) + }) + }, [listNow.length]) + const focusNextButton = () => { if (lastBtnRef.current) { lastBtnRef.current.focus(); @@ -106,27 +140,30 @@ function Storage(props: Props) { player.jump(list[listNow.length].time); }; - const renderItem = (item: Record, i: number, prevItem: Record) => { + const renderItem = (item: Record, i: number, prevItem?: Record) => { let src; let name; + const itemD = item + const prevItemD = prevItem ? prevItem : undefined + switch (type) { case STORAGE_TYPES.REDUX: case STORAGE_TYPES.NGRX: - src = item.action; + src = itemD.action; name = src && src.type; break; case STORAGE_TYPES.VUEX: - src = item.mutation; + src = itemD.mutation; name = src && src.type; break; case STORAGE_TYPES.MOBX: - src = item.payload; + src = itemD.payload; name = `@${item.type} ${src && src.type}`; break; case STORAGE_TYPES.ZUSTAND: src = null; - name = item.mutation.join(''); + name = itemD.mutation.join(''); } if (src !== null && !showDiffs) { @@ -144,7 +181,7 @@ function Storage(props: Props) { ) : ( <> - {renderDiff(item, prevItem)} + {renderDiff(itemD, prevItemD)}
{typeof item.duration === 'number' && ( -
{formatMs(item.duration)}
+
{formatMs(itemD.duration)}
)}
{i + 1 < listNow.length && ( - )} @@ -281,14 +318,18 @@ function Storage(props: Props) { {'Empty state.'}
) : ( - + )}
)}
- {listNow.map((item: Record, i: number) => - renderItem(item, i, i > 0 ? listNow[i - 1] : undefined) + {decodedList.map((item: Record, i: number) => + renderItem(item, i, i > 0 ? decodedList[i - 1] : undefined) )}
@@ -306,3 +347,47 @@ export default connect( hideHint, } )(observer(Storage)); + + +/** + * TODO: compute diff and only decode the required parts + * WIP example + * function useStorageDecryptedList(list: Record[], type: string, player: IWebPlayer) { + * const [decryptedList, setDecryptedList] = React.useState(list); + * const [listLength, setLength] = React.useState(list.length) + * + * const decodeMessage = (msg: any, type: StorageType) => { + * const decoded = {}; + * const pureMSG = { ...msg } + * const keys = storageDecodeKeys[type]; + * try { + * keys.forEach(key => { + * if (pureMSG[key]) { + * // @ts-ignore TODO: types for decoder + * decoded[key] = player.decodeMessage(pureMSG[key]); + * } + * }); + * } catch (e) { + * logger.error("Error on message decoding: ", e, pureMSG); + * return null; + * } + * return { ...pureMSG, ...decoded }; + * } + * + * React.useEffect(() => { + * if (list.length !== listLength) { + * const last = list[list.length - 1]._index; + * let diff; + * if (last < decryptedList[decryptedList.length - 1]._index) { + * + * } + * diff = list.filter(item => !decryptedList.includes(i => i._index === item._index)) + * const decryptedDiff = diff.map(item => { + * return player.decodeMessage(item) + * }) + * const result = + * } + * }, [list.length]) + * } + * + * */ \ No newline at end of file diff --git a/frontend/app/components/shared/CodeSnippet/CodeSnippet.tsx b/frontend/app/components/shared/CodeSnippet/CodeSnippet.tsx index c9629bf7a..116392534 100644 --- a/frontend/app/components/shared/CodeSnippet/CodeSnippet.tsx +++ b/frontend/app/components/shared/CodeSnippet/CodeSnippet.tsx @@ -4,8 +4,8 @@ import Highlight from 'react-highlight'; const inputModeOptions = [ { label: 'Record all inputs', value: 'plain' }, - { label: 'Ignore all inputs', value: 'obscured' }, { label: 'Obscure all inputs', value: 'hidden' }, + { label: 'Ignore all inputs', value: 'obscured' }, ]; const inputModeOptionsMap: any = {} diff --git a/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js b/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js index b8bd87b6a..4b51fc963 100644 --- a/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js +++ b/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js @@ -9,8 +9,8 @@ import CodeSnippet from '../../CodeSnippet'; const inputModeOptions = [ { label: 'Record all inputs', value: 'plain' }, - { label: 'Ignore all inputs', value: 'obscured' }, { label: 'Obscure all inputs', value: 'hidden' }, + { label: 'Ignore all inputs', value: 'obscured' }, ]; const inputModeOptionsMap = {} diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts index d1a56f9fd..9ca769598 100644 --- a/frontend/app/player/web/WebPlayer.ts +++ b/frontend/app/player/web/WebPlayer.ts @@ -7,6 +7,7 @@ import MessageManager from './MessageManager' import InspectorController from './addons/InspectorController' import TargetMarker from './addons/TargetMarker' import Screen, { ScaleMode } from './Screen/Screen' +import { Message } from "Player/web/messages"; // export type State = typeof WebPlayer.INITIAL_STATE @@ -83,6 +84,11 @@ export default class WebPlayer extends Player { this.targetMarker.updateMarkedTargets() } + // delayed message decoding for state plugins + decodeMessage = (msg: Message) => { + return this.messageManager.decodeMessage(msg) + } + // Inspector & marker mark(e: Element) { this.inspectorController.marker?.mark(e) diff --git a/tracker/tracker/CHANGELOG.md b/tracker/tracker/CHANGELOG.md index e3a4c3d78..6a8e25690 100644 --- a/tracker/tracker/CHANGELOG.md +++ b/tracker/tracker/CHANGELOG.md @@ -3,6 +3,8 @@ - Default text input mode is now Obscured - Use `@medv/finder` instead of our own implementation of `getSelector` for better clickmaps experience +## 5.0.0 + - Added "tel" to supported input types - Added `{ withCurrentTime: true }` to `tracker.getSessionURL` method which will return sessionURL with current session's timestamp - Added Network module that captures fetch/xhr by default (with no plugin required) diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index 15acecaa9..e2e93bff7 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -89,7 +89,7 @@ export default function (app: App, opts: Partial): void { { obscureInputNumbers: true, obscureInputEmails: true, - defaultInputMode: InputMode.Plain, + defaultInputMode: InputMode.Obscured, obscureInputDates: false, }, opts,