Compare commits
4 commits
main
...
revert_upd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e8fa7614d | ||
|
|
269c931111 | ||
|
|
27a5c0716b | ||
|
|
cff96eba92 |
10 changed files with 30 additions and 133 deletions
|
|
@ -16,14 +16,12 @@ import { IFRAME } from 'App/constants/storageKeys';
|
||||||
import stl from './playerBlockHeader.module.css';
|
import stl from './playerBlockHeader.module.css';
|
||||||
import UserCard from './EventsBlock/UserCard';
|
import UserCard from './EventsBlock/UserCard';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Switch } from 'antd';
|
|
||||||
|
|
||||||
const SESSIONS_ROUTE = sessionsRoute();
|
const SESSIONS_ROUTE = sessionsRoute();
|
||||||
|
|
||||||
function PlayerBlockHeader(props: any) {
|
function PlayerBlockHeader(props: any) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hideBack, setHideBack] = React.useState(false);
|
const [hideBack, setHideBack] = React.useState(false);
|
||||||
const { uiPlayerStore } = useStore();
|
|
||||||
const { player, store } = React.useContext(PlayerContext);
|
const { player, store } = React.useContext(PlayerContext);
|
||||||
const { uxtestingStore, customFieldStore, projectsStore, sessionStore } =
|
const { uxtestingStore, customFieldStore, projectsStore, sessionStore } =
|
||||||
useStore();
|
useStore();
|
||||||
|
|
@ -125,25 +123,9 @@ function PlayerBlockHeader(props: any) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{uiPlayerStore.showSearchEventsSwitchButton ? (
|
|
||||||
<div className="px-2 relative flex items-center border-r border-r-gray-lighter">
|
|
||||||
<Switch
|
|
||||||
checked={uiPlayerStore.showOnlySearchEvents}
|
|
||||||
onChange={uiPlayerStore.setShowOnlySearchEvents}
|
|
||||||
style={{
|
|
||||||
background: uiPlayerStore.showOnlySearchEvents
|
|
||||||
? '#f0a930'
|
|
||||||
: 'rgba(0, 0, 0, 0.25)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="ml-2 whitespace-nowrap">
|
|
||||||
{t('Search Events Only')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="px-2 relative"
|
className="px-2 relative border-l border-l-gray-lighter"
|
||||||
style={{ minWidth: activeTab === 'EXPORT' ? '360px' : '270px' }}
|
style={{ minWidth: activeTab === 'EXPORT' ? '360px' : '270px' }}
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ const UXTTABS = {
|
||||||
|
|
||||||
let playerInst: IPlayerContext['player'] | undefined;
|
let playerInst: IPlayerContext['player'] | undefined;
|
||||||
|
|
||||||
const isDefaultEventsFilterSearch = (filters: FilterItem[]) => {
|
|
||||||
return filters.length === 1 && filters[0].key === 'location' && filters[0].value[0] === '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function WebPlayer(props: any) {
|
function WebPlayer(props: any) {
|
||||||
const {
|
const {
|
||||||
notesStore,
|
notesStore,
|
||||||
|
|
@ -42,7 +38,6 @@ function WebPlayer(props: any) {
|
||||||
uxtestingStore,
|
uxtestingStore,
|
||||||
uiPlayerStore,
|
uiPlayerStore,
|
||||||
integrationsStore,
|
integrationsStore,
|
||||||
searchStore,
|
|
||||||
} = useStore();
|
} = useStore();
|
||||||
const devTools = sessionStore.devTools
|
const devTools = sessionStore.devTools
|
||||||
const session = sessionStore.current;
|
const session = sessionStore.current;
|
||||||
|
|
@ -62,17 +57,6 @@ function WebPlayer(props: any) {
|
||||||
const [fullView, setFullView] = useState(false);
|
const [fullView, setFullView] = useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (searchStore.instance.filters?.length && !isDefaultEventsFilterSearch(searchStore.instance.filters)) {
|
|
||||||
uiPlayerStore.setSearchEventsSwitchButton(true);
|
|
||||||
uiPlayerStore.setShowOnlySearchEvents(true);
|
|
||||||
} else {
|
|
||||||
uiPlayerStore.setSearchEventsSwitchButton(false);
|
|
||||||
uiPlayerStore.setShowOnlySearchEvents(false);
|
|
||||||
}
|
|
||||||
}, [searchStore.instance.filters]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
openedAt.current = Date.now();
|
|
||||||
const handleActivation = () => {
|
const handleActivation = () => {
|
||||||
if (!document.hidden) {
|
if (!document.hidden) {
|
||||||
setWindowActive(true);
|
setWindowActive(true);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ function EventGroupWrapper(props) {
|
||||||
isLastInGroup,
|
isLastInGroup,
|
||||||
isSelected,
|
isSelected,
|
||||||
isCurrent,
|
isCurrent,
|
||||||
isSearched,
|
|
||||||
isEditing,
|
isEditing,
|
||||||
showSelection,
|
showSelection,
|
||||||
isFirst,
|
isFirst,
|
||||||
|
|
@ -100,7 +99,7 @@ function EventGroupWrapper(props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadowColor = isSearched ? '#F0A930' : props.isPrev
|
const shadowColor = props.isPrev
|
||||||
? '#A7BFFF'
|
? '#A7BFFF'
|
||||||
: props.isCurrent
|
: props.isCurrent
|
||||||
? '#394EFF'
|
? '#394EFF'
|
||||||
|
|
@ -128,7 +127,7 @@ function EventGroupWrapper(props) {
|
||||||
width: 10,
|
width: 10,
|
||||||
height: 10,
|
height: 10,
|
||||||
transform: 'rotate(45deg) translate(0, -50%)',
|
transform: 'rotate(45deg) translate(0, -50%)',
|
||||||
background: isSearched ? '#F0A930' : '#394EFF',
|
background: '#394EFF',
|
||||||
zIndex: 99,
|
zIndex: 99,
|
||||||
borderRadius: '.15rem',
|
borderRadius: '.15rem',
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { mergeEventLists, sortEvents } from 'Types/session';
|
||||||
import { TYPES } from 'Types/session/event';
|
import { TYPES } from 'Types/session/event';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { VList, VListHandle } from 'virtua';
|
import { VList, VListHandle } from 'virtua';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { PlayerContext } from 'App/components/Session/playerContext';
|
import { PlayerContext } from 'App/components/Session/playerContext';
|
||||||
|
|
@ -47,7 +47,6 @@ function EventsBlock(props: IProps) {
|
||||||
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
const zoomStartTs = uiPlayerStore.timelineZoom.startTs;
|
||||||
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
const zoomEndTs = uiPlayerStore.timelineZoom.endTs;
|
||||||
const { store, player } = React.useContext(PlayerContext);
|
const { store, player } = React.useContext(PlayerContext);
|
||||||
const [currentTimeEventIndex, setCurrentTimeEventIndex] = React.useState(0);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
time,
|
time,
|
||||||
|
|
@ -95,8 +94,8 @@ function EventsBlock(props: IProps) {
|
||||||
? 'time' in e
|
? 'time' in e
|
||||||
? e.time >= zoomStartTs && e.time <= zoomEndTs
|
? e.time >= zoomStartTs && e.time <= zoomEndTs
|
||||||
: false
|
: false
|
||||||
: true
|
: true,
|
||||||
).filter((e: any) => !e.noteId && e.type !== 'TABCHANGE' && uiPlayerStore.showOnlySearchEvents ? e.isHighlighted : true);
|
);
|
||||||
}, [
|
}, [
|
||||||
filteredLength,
|
filteredLength,
|
||||||
notesWithEvtsLength,
|
notesWithEvtsLength,
|
||||||
|
|
@ -104,7 +103,6 @@ function EventsBlock(props: IProps) {
|
||||||
zoomEnabled,
|
zoomEnabled,
|
||||||
zoomStartTs,
|
zoomStartTs,
|
||||||
zoomEndTs,
|
zoomEndTs,
|
||||||
uiPlayerStore.showOnlySearchEvents
|
|
||||||
]);
|
]);
|
||||||
const findLastFitting = React.useCallback(
|
const findLastFitting = React.useCallback(
|
||||||
(time: number) => {
|
(time: number) => {
|
||||||
|
|
@ -129,9 +127,7 @@ function EventsBlock(props: IProps) {
|
||||||
[usedEvents, time, endTime],
|
[usedEvents, time, endTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const currentTimeEventIndex = findLastFitting(time);
|
||||||
setCurrentTimeEventIndex(findLastFitting(time));
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const write = ({
|
const write = ({
|
||||||
target: { value },
|
target: { value },
|
||||||
|
|
@ -187,7 +183,6 @@ function EventsBlock(props: IProps) {
|
||||||
const isTabChange = 'type' in event && event.type === 'TABCHANGE';
|
const isTabChange = 'type' in event && event.type === 'TABCHANGE';
|
||||||
const isCurrent = index === currentTimeEventIndex;
|
const isCurrent = index === currentTimeEventIndex;
|
||||||
const isPrev = index < currentTimeEventIndex;
|
const isPrev = index < currentTimeEventIndex;
|
||||||
const isSearched = event.isHighlighted
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EventGroupWrapper
|
<EventGroupWrapper
|
||||||
|
|
@ -199,7 +194,6 @@ function EventsBlock(props: IProps) {
|
||||||
isLastEvent={isLastEvent}
|
isLastEvent={isLastEvent}
|
||||||
isLastInGroup={isLastInGroup}
|
isLastInGroup={isLastInGroup}
|
||||||
isCurrent={isCurrent}
|
isCurrent={isCurrent}
|
||||||
isSearched={isSearched}
|
|
||||||
showSelection={!playing}
|
showSelection={!playing}
|
||||||
isNote={isNote}
|
isNote={isNote}
|
||||||
isTabChange={isTabChange}
|
isTabChange={isTabChange}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
MobilePlayerContext,
|
MobilePlayerContext,
|
||||||
} from 'Components/Session/playerContext';
|
} from 'Components/Session/playerContext';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import stl from './timeline.module.css';
|
||||||
import { getTimelinePosition } from './getTimelinePosition';
|
import { getTimelinePosition } from './getTimelinePosition';
|
||||||
import { useStore } from '@/mstore';
|
import { useStore } from '@/mstore';
|
||||||
|
|
||||||
|
|
@ -15,14 +16,8 @@ function EventsList() {
|
||||||
const { tabStates } = store.get();
|
const { tabStates } = store.get();
|
||||||
const scale = 100 / endTime;
|
const scale = 100 / endTime;
|
||||||
const events = React.useMemo(
|
const events = React.useMemo(
|
||||||
() => Object.values(tabStates)[0]?.eventList.filter((e) => {
|
() => Object.values(tabStates)[0]?.eventList.filter((e) => e.time) || [],
|
||||||
if (uiPlayerStore.showOnlySearchEvents) {
|
[eventCount],
|
||||||
return e.time && (e as any).isHighlighted
|
|
||||||
} else {
|
|
||||||
return e.time
|
|
||||||
}
|
|
||||||
}) || [],
|
|
||||||
[eventCount, uiPlayerStore.showOnlySearchEvents],
|
|
||||||
);
|
);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const hasDuplicates = events.some(
|
const hasDuplicates = events.some(
|
||||||
|
|
@ -39,7 +34,7 @@ function EventsList() {
|
||||||
<div
|
<div
|
||||||
/* @ts-ignore TODO */
|
/* @ts-ignore TODO */
|
||||||
key={`${e.key}_${e.time}`}
|
key={`${e.key}_${e.time}`}
|
||||||
className={`absolute w-[2px] h-[10px] z-[3] pointer-events-none ${e.isHighlighted ? 'bg-[#f0a930]' : 'bg-[#394eff]'}`}
|
className={stl.event}
|
||||||
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
@ -59,7 +54,7 @@ function MobileEventsList() {
|
||||||
<div
|
<div
|
||||||
/* @ts-ignore TODO */
|
/* @ts-ignore TODO */
|
||||||
key={`${e.key}_${e.time}`}
|
key={`${e.key}_${e.time}`}
|
||||||
className={`absolute w-[2px] h-[10px] z-[3] pointer-events-none ${e.isHighlighted ? 'bg-[#f0a930]' : 'bg-[#394eff]'}`}
|
className={stl.event}
|
||||||
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
style={{ left: `${getTimelinePosition(e.time, scale)}%` }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
import FilterItem from '@/mstore/types/filterItem';
|
|
||||||
|
|
||||||
export const checkEventWithFilters = (event: Event, filters: FilterItem[]) => {
|
|
||||||
let result = false;
|
|
||||||
filters.forEach((filter) => {
|
|
||||||
if (filter.key.toUpperCase() === event.type.toUpperCase()) {
|
|
||||||
if (filter.operator) {
|
|
||||||
const operator = operators[filter.operator];
|
|
||||||
if (operator) {
|
|
||||||
result = !!operator(event.label, filter.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result
|
|
||||||
};
|
|
||||||
|
|
||||||
const operators = {
|
|
||||||
is: (val: string, target: string[]) => target.some((t) => val.includes(t)),
|
|
||||||
isAny: () => true,
|
|
||||||
isNot: (val: string, target: string[]) =>
|
|
||||||
!target.some((t) => val.includes(t)),
|
|
||||||
contains: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.includes(t)),
|
|
||||||
notContains: (val: string, target: string[]) =>
|
|
||||||
!target.some((t) => val.includes(t)),
|
|
||||||
startsWith: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.startsWith(t)),
|
|
||||||
endsWith: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.endsWith(t)),
|
|
||||||
greaterThan: (val: number, target: number) => val > target,
|
|
||||||
greaterOrEqual: (val: number, target: number) => val >= target,
|
|
||||||
lessOrEqual: (val: number, target: number) => val <= target,
|
|
||||||
lessThan: (val: number, target: number) => val < target,
|
|
||||||
on: (val: string, target: string[]) => target.some((t) => val.includes(t)),
|
|
||||||
notOn: (val: string, target: string[]) =>
|
|
||||||
!target.some((t) => val.includes(t)),
|
|
||||||
onAny: () => true,
|
|
||||||
selectorIs: (val: string, target: string[]) => target.some((t) => val.includes(t)),
|
|
||||||
selectorIsAny: () => true,
|
|
||||||
selectorIsNot: (val: string, target: string[]) =>
|
|
||||||
!target.some((t) => val.includes(t)),
|
|
||||||
selectorContains: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.includes(t)),
|
|
||||||
selectorNotContains: (val: string, target: string[]) =>
|
|
||||||
!target.some((t) => val.includes(t)),
|
|
||||||
selectorStartsWith: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.startsWith(t)),
|
|
||||||
selectorEndsWith: (val: string, target: string[]) =>
|
|
||||||
target.some((t) => val.endsWith(t)),
|
|
||||||
};
|
|
||||||
|
|
@ -49,6 +49,23 @@
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.event {
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 10px;
|
||||||
|
background: $main;
|
||||||
|
z-index: 3;
|
||||||
|
pointer-events: none;
|
||||||
|
/* top: 0; */
|
||||||
|
/* bottom: 0; */
|
||||||
|
/* &:hover {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-left: -6px;
|
||||||
|
z-index: 1;
|
||||||
|
};*/
|
||||||
|
}
|
||||||
|
|
||||||
/* .event.click, .event.input {
|
/* .event.click, .event.input {
|
||||||
background: $green;
|
background: $green;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import { LAST_7_DAYS } from 'Types/app/period';
|
||||||
import { filterMap } from 'App/mstore/searchStore';
|
import { filterMap } from 'App/mstore/searchStore';
|
||||||
import { getDateRangeFromValue } from 'App/dateRange';
|
import { getDateRangeFromValue } from 'App/dateRange';
|
||||||
import { searchStore, searchStoreLive } from './index';
|
import { searchStore, searchStoreLive } from './index';
|
||||||
import { checkEventWithFilters } from '@/components/Session_/Player/Controls/checkEventWithFilters';
|
|
||||||
const range = getDateRangeFromValue(LAST_7_DAYS);
|
const range = getDateRangeFromValue(LAST_7_DAYS);
|
||||||
|
|
||||||
const defaultDateFilters = {
|
const defaultDateFilters = {
|
||||||
|
|
@ -341,14 +340,7 @@ export default class SessionStore {
|
||||||
const eventsData: Record<string, any[]> = {};
|
const eventsData: Record<string, any[]> = {};
|
||||||
try {
|
try {
|
||||||
const evData = await sessionService.getSessionEvents(sessionId);
|
const evData = await sessionService.getSessionEvents(sessionId);
|
||||||
|
Object.assign(eventsData, evData);
|
||||||
Object.assign(eventsData, {
|
|
||||||
...evData,
|
|
||||||
events: evData.events.map((e) => ({
|
|
||||||
...e,
|
|
||||||
isHighlighted: checkEventWithFilters(e, searchStore.instance.filters)
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to fetch events', e);
|
console.error('Failed to fetch events', e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ export const blockValues = [
|
||||||
|
|
||||||
export default class UiPlayerStore {
|
export default class UiPlayerStore {
|
||||||
fullscreen = false;
|
fullscreen = false;
|
||||||
showOnlySearchEvents = false;
|
|
||||||
showSearchEventsSwitchButton = false;
|
|
||||||
|
|
||||||
bottomBlock = 0;
|
bottomBlock = 0;
|
||||||
|
|
||||||
|
|
@ -147,12 +145,4 @@ export default class UiPlayerStore {
|
||||||
setZoomTab = (tab: 'overview' | 'journey' | 'issues' | 'errors') => {
|
setZoomTab = (tab: 'overview' | 'journey' | 'issues' | 'errors') => {
|
||||||
this.zoomTab = tab;
|
this.zoomTab = tab;
|
||||||
};
|
};
|
||||||
|
|
||||||
setShowOnlySearchEvents = (show: boolean) => {
|
|
||||||
this.showOnlySearchEvents = show;
|
|
||||||
};
|
|
||||||
|
|
||||||
setSearchEventsSwitchButton = (show: boolean) => {
|
|
||||||
this.showSearchEventsSwitchButton = show;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ interface IEvent {
|
||||||
path: string;
|
path: string;
|
||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
isHighlighted?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConsoleEvent extends IEvent {
|
interface ConsoleEvent extends IEvent {
|
||||||
|
|
@ -119,8 +118,6 @@ class Event {
|
||||||
|
|
||||||
messageId: IEvent['messageId'];
|
messageId: IEvent['messageId'];
|
||||||
|
|
||||||
isHighlighted: IEvent['isHighlighted'];
|
|
||||||
|
|
||||||
constructor(event: IEvent) {
|
constructor(event: IEvent) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
time: event.time,
|
time: event.time,
|
||||||
|
|
@ -128,7 +125,6 @@ class Event {
|
||||||
key: event.key,
|
key: event.key,
|
||||||
tabId: event.tabId,
|
tabId: event.tabId,
|
||||||
messageId: event.messageId,
|
messageId: event.messageId,
|
||||||
isHighlighted: event.isHighlighted,
|
|
||||||
target: {
|
target: {
|
||||||
path: event.target?.path || event.targetPath,
|
path: event.target?.path || event.targetPath,
|
||||||
label: event.target?.label,
|
label: event.target?.label,
|
||||||
|
|
@ -190,7 +186,6 @@ export class Click extends Event {
|
||||||
this.count = evt.count;
|
this.count = evt.count;
|
||||||
this.hesitation = evt.hesitation;
|
this.hesitation = evt.hesitation;
|
||||||
this.selector = evt.selector;
|
this.selector = evt.selector;
|
||||||
this.isHighlighted = evt.isHighlighted;
|
|
||||||
if (isClickRage) {
|
if (isClickRage) {
|
||||||
this.type = CLICKRAGE;
|
this.type = CLICKRAGE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue