Merge pull request #1042 from openreplay/main-dev

changes(ui) - commits from dev branch
This commit is contained in:
Shekar Siri 2023-03-15 12:13:08 +01:00 committed by GitHub
commit 6bc8796db9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 18 deletions

View file

@ -68,9 +68,6 @@ function Player(props: IProps) {
playerContext.player.attach(parentElement);
setAttached(true)
}
if (isAttached && isReady) {
playerContext.player.play();
}
}, [isReady]);
React.useEffect(() => {

View file

@ -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])

View file

@ -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<HTMLButtonElement>();
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<string, any>, i: number, prevItem: Record<string, any>) => {
const renderItem = (item: Record<string, any>, i: number, prevItem?: Record<string, any>) => {
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) {
</div>
) : (
<>
{renderDiff(item, prevItem)}
{renderDiff(itemD, prevItemD)}
<div style={{ flex: 2 }} className="flex pl-10 pt-2">
<JSONTree
name={ensureString(name)}
@ -160,11 +197,11 @@ function Storage(props: Props) {
className="flex-1 flex gap-2 pt-2 items-center justify-end self-start"
>
{typeof item.duration === 'number' && (
<div className="font-size-12 color-gray-medium">{formatMs(item.duration)}</div>
<div className="font-size-12 color-gray-medium">{formatMs(itemD.duration)}</div>
)}
<div className="w-12">
{i + 1 < listNow.length && (
<button className={stl.button} onClick={() => jump(item.time, item._index)}>
<button className={stl.button} onClick={() => player.jump(item.time)}>
{'JUMP'}
</button>
)}
@ -281,14 +318,18 @@ function Storage(props: Props) {
{'Empty state.'}
</div>
) : (
<JSONTree collapsed={2} src={listNow.length === 0 ? list[0].state : listNow[listNow.length - 1].state} />
<JSONTree collapsed={2} src={
listNow.length === 0
? decodeMessage(list[0]).state
: decodeMessage(listNow[listNow.length - 1]).state}
/>
)}
</div>
)}
<div className="flex" style={{ width: showStore ? '75%' : '100%' }}>
<Autoscroll className="ph-10">
{listNow.map((item: Record<string, any>, i: number) =>
renderItem(item, i, i > 0 ? listNow[i - 1] : undefined)
{decodedList.map((item: Record<string, any>, i: number) =>
renderItem(item, i, i > 0 ? decodedList[i - 1] : undefined)
)}
</Autoscroll>
</div>
@ -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<string, any>[], 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])
* }
*
* */

View file

@ -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 = {}

View file

@ -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 = {}

View file

@ -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)

View file

@ -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)

View file

@ -89,7 +89,7 @@ export default function (app: App, opts: Partial<Options>): void {
{
obscureInputNumbers: true,
obscureInputEmails: true,
defaultInputMode: InputMode.Plain,
defaultInputMode: InputMode.Obscured,
obscureInputDates: false,
},
opts,