Merge pull request #1042 from openreplay/main-dev
changes(ui) - commits from dev branch
This commit is contained in:
commit
6bc8796db9
8 changed files with 109 additions and 18 deletions
|
|
@ -68,9 +68,6 @@ function Player(props: IProps) {
|
|||
playerContext.player.attach(parentElement);
|
||||
setAttached(true)
|
||||
}
|
||||
if (isAttached && isReady) {
|
||||
playerContext.player.play();
|
||||
}
|
||||
}, [isReady]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
* }
|
||||
*
|
||||
* */
|
||||
|
|
@ -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 = {}
|
||||
|
|
|
|||
|
|
@ -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 = {}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue