Merge remote-tracking branch 'origin/redux-toolkit-move' into rtm-temp

This commit is contained in:
Shekar Siri 2024-09-20 11:25:33 +05:30
commit 70293cd8de
11 changed files with 84 additions and 190 deletions

View file

@ -1,8 +1,7 @@
import React, { useMemo, useContext, useState, useRef } from 'react';
import { connect } from 'react-redux';
import { useStore } from 'App/mstore';
import TimeTracker from 'Components/Session_/Player/Controls/TimeTracker';
import stl from 'Components/Session_/Player/Controls/timeline.module.css';
import { setTimelinePointer, setTimelineHoverTime } from 'Duck/sessions';
import DraggableCircle from 'Components/Session_/Player/Controls/components/DraggableCircle';
import CustomDragLayer, { OnDragCallback } from 'Components/Session_/Player/Controls/components/CustomDragLayer';
import { debounce } from 'App/utils';
@ -11,13 +10,11 @@ import { PlayerContext, ILivePlayerContext } from 'App/components/Session/player
import { observer } from 'mobx-react-lite';
import { Duration } from 'luxon';
interface IProps {
setTimelineHoverTime: (t: number) => void
startedAt: number
tooltipVisible: boolean
}
function Timeline(props: IProps) {
function Timeline() {
const { sessionStore } = useStore();
const startedAt = sessionStore.current.startedAt ?? 0;
const tooltipVisible = sessionStore.timeLineTooltip.isVisible;
const setTimelineHoverTime = sessionStore.setTimelineTooltip;
// @ts-ignore
const { player, store } = useContext<ILivePlayerContext>(PlayerContext)
const [wasPlaying, setWasPlaying] = useState(false)
@ -35,7 +32,7 @@ function Timeline(props: IProps) {
const scale = 100 / endTime;
const debouncedJump = useMemo(() => debounce(player.jump, 500), [])
const debouncedTooltipChange = useMemo(() => debounce(props.setTimelineHoverTime, 50), [])
const debouncedTooltipChange = useMemo(() => debounce(setTimelineHoverTime, 50), [])
const onDragEnd = () => {
if (!liveTimeTravel) return;
@ -59,7 +56,7 @@ function Timeline(props: IProps) {
};
const getLiveTime = (e: React.MouseEvent) => {
const duration = new Date().getTime() - props.startedAt;
const duration = new Date().getTime() - startedAt;
// @ts-ignore type mismatch from react?
const p = e.nativeEvent.offsetX / e.target.offsetWidth;
const time = Math.max(Math.round(p * duration), 0);
@ -69,7 +66,7 @@ function Timeline(props: IProps) {
const showTimeTooltip = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target !== progressRef.current && e.target !== timelineRef.current) {
return props.tooltipVisible && hideTimeTooltip();
return tooltipVisible && hideTimeTooltip();
}
const [time, duration] = getLiveTime(e);
@ -136,7 +133,7 @@ function Timeline(props: IProps) {
onMouseEnter={showTimeTooltip}
onMouseLeave={hideTimeTooltip}
>
<TooltipContainer live />
<TooltipContainer />
<DraggableCircle
left={time * scale}
onDrop={onDragEnd}
@ -156,10 +153,4 @@ function Timeline(props: IProps) {
)
}
export default connect(
(state: any) => ({
startedAt: state.getIn(['sessions', 'current']).startedAt || 0,
tooltipVisible: state.getIn(['sessions', 'timeLineTooltip', 'isVisible']),
}),
{ setTimelinePointer, setTimelineHoverTime }
)(observer(Timeline))
export default observer(Timeline)

View file

@ -1,9 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { sessions as sessionsRoute, liveSession as liveSessionRoute, withSiteId } from 'App/routes';
import { BackLink, Link } from 'UI';
import { toggleFavorite, setSessionPath } from 'Duck/sessions';
import { sessions as sessionsRoute, withSiteId } from 'App/routes';
import { BackLink } from 'UI';
import cn from 'classnames';
import SessionMetaList from 'Shared/SessionItem/SessionMetaList';
import UserCard from '../ReplayPlayer/EventsBlock/UserCard';
@ -23,10 +22,10 @@ function PlayerBlockHeader(props: any) {
const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false };
const { width = 0, height = 0, showEvents = false } = playerState;
const { customFieldStore, projectsStore } = useStore();
const { customFieldStore, projectsStore, sessionStore } = useStore();
const session = sessionStore.current;
const siteId = projectsStore.siteId!;
const {
session,
fullscreen,
metaList,
setActiveTab,
@ -100,18 +99,12 @@ function PlayerBlockHeader(props: any) {
const PlayerHeaderCont = connect(
(state: any) => {
const session = state.getIn(['sessions', 'current']);
return {
session,
sessionPath: state.getIn(['sessions', 'sessionPath']),
funnelRef: state.getIn(['funnels', 'navRef']),
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
};
},
{
toggleFavorite,
setSessionPath,
}
)(observer(PlayerBlockHeader));

View file

@ -1,5 +1,4 @@
import React from 'react';
import { connect } from 'react-redux';
import { findDOMNode } from 'react-dom';
import cn from 'classnames';
import { EscapeButton } from 'UI';
@ -18,7 +17,6 @@ import { MobileExceptions } from 'Components/Session_/Exceptions/Exceptions';
import MobileControls from './MobileControls';
import Overlay from './MobileOverlay'
import stl from 'Components/Session_/Player/player.module.css';
import { updateLastPlayedSession } from 'Duck/sessions';
import { MobileOverviewPanel } from 'Components/Session_/OverviewPanel';
import MobileConsolePanel from 'Shared/DevTools/ConsolePanel/MobileConsolePanel';
import { MobilePlayerContext } from 'App/components/Session/playerContext';
@ -32,32 +30,28 @@ import { useStore } from 'App/mstore';
interface IProps {
fullView: boolean;
isMultiview?: boolean;
nextId: string;
sessionId: string;
activeTab: string;
updateLastPlayedSession: (id: string) => void
videoURL: string[];
setActiveTab: (tab: string) => void;
userDevice: string;
screenWidth: number;
screenHeight: number;
platform: string;
bottomBlock: any;
fullscreen?: boolean;
}
function Player(props: IProps) {
const defaultHeight = getDefaultPanelHeight()
const [panelHeight, setPanelHeight] = React.useState(defaultHeight);
const {
nextId,
activeTab,
fullView,
videoURL,
userDevice,
screenWidth,
screenHeight,
platform,
} = props;
const { uiPlayerStore } = useStore();
const { uiPlayerStore, sessionStore } = useStore();
const nextId = sessionStore.nextId;
const sessionId = sessionStore.current.sessionId;
const userDevice = sessionStore.current.userDevice;
const videoURL = sessionStore.current.videoURL;
const platform = sessionStore.current.platform;
const screenWidth = sessionStore.current.screenWidth!;
const screenHeight = sessionStore.current.screenHeight!;
const updateLastPlayedSession = sessionStore.updateLastPlayedSession;
const fullscreenOff = uiPlayerStore.fullscreenOff;
const fullscreen = uiPlayerStore.fullscreen;
const bottomBlock = uiPlayerStore.bottomBlock;
@ -68,7 +62,7 @@ function Player(props: IProps) {
const [isAttached, setAttached] = React.useState(false);
React.useEffect(() => {
props.updateLastPlayedSession(props.sessionId);
updateLastPlayedSession(sessionId);
const parentElement = findDOMNode(screenWrapper.current) as HTMLDivElement | null; //TODO: good architecture
if (parentElement && !isAttached) {
playerContext.player.attach(parentElement);
@ -166,17 +160,4 @@ function Player(props: IProps) {
);
}
export default connect(
(state: any) => ({
nextId: state.getIn(['sessions', 'nextId']),
sessionId: state.getIn(['sessions', 'current']).sessionId,
userDevice: state.getIn(['sessions', 'current']).userDevice,
videoURL: state.getIn(['sessions', 'current']).videoURL,
platform: state.getIn(['sessions', 'current']).platform,
screenWidth: state.getIn(['sessions', 'current']).screenWidth,
screenHeight: state.getIn(['sessions', 'current']).screenHeight,
}),
{
updateLastPlayedSession,
}
)(observer(Player));
export default observer(Player);

View file

@ -8,7 +8,6 @@ import {
withSiteId,
} from 'App/routes';
import { BackLink, Link } from 'UI';
import { toggleFavorite, setSessionPath } from 'Duck/sessions';
import cn from 'classnames';
import SessionMetaList from 'Shared/SessionItem/SessionMetaList';
import UserCard from './EventsBlock/UserCard';
@ -20,24 +19,23 @@ import { IFRAME } from 'App/constants/storageKeys';
const SESSIONS_ROUTE = sessionsRoute();
// TODO props
function PlayerBlockHeader(props: any) {
const [hideBack, setHideBack] = React.useState(false);
const { player, store } = React.useContext(PlayerContext);
const { uxtestingStore, customFieldStore, projectsStore } = useStore()
const { uxtestingStore, customFieldStore, projectsStore, sessionStore } = useStore()
const session = sessionStore.current;
const sessionPath = sessionStore.sessionPath;
const siteId = projectsStore.siteId!;
const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false }
const { width = 0, height = 0, showEvents = false } = playerState
const {
session,
fullscreen,
metaList,
closedLive = false,
setActiveTab,
activeTab,
history,
sessionPath,
} = props;
React.useEffect(() => {
@ -130,19 +128,12 @@ function PlayerBlockHeader(props: any) {
const PlayerHeaderCont = connect(
(state: any) => {
const session = state.getIn(['sessions', 'current']);
return {
session,
sessionPath: state.getIn(['sessions', 'sessionPath']),
funnelRef: state.getIn(['funnels', 'navRef']),
metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key),
};
},
{
toggleFavorite,
setSessionPath,
}
)(observer(PlayerBlockHeader));
export default withRouter(PlayerHeaderCont);

View file

@ -10,7 +10,6 @@ import { VList, VListHandle } from 'virtua';
import { PlayerContext } from 'App/components/Session/playerContext';
import { RootStore } from 'App/duck';
import { useStore } from 'App/mstore';
import { filterOutNote, setEventFilter } from 'Duck/sessions';
import { Icon } from 'UI';
import EventGroupWrapper from './EventGroupWrapper';
@ -18,19 +17,19 @@ import EventSearch from './EventSearch/EventSearch';
import styles from './eventsBlock.module.css';
interface IProps {
setEventFilter: (filter: { query: string }) => void;
filteredEvents: InjectedEvent[];
setActiveTab: (tab?: string) => void;
query: string;
events: Session['events'];
notesWithEvents: Session['notesWithEvents'];
filterOutNote: (id: string) => void;
eventsIndex: number[];
uxtVideo: string;
}
function EventsBlock(props: IProps) {
const { notesStore, uxtestingStore, uiPlayerStore } = useStore();
const { notesStore, uxtestingStore, uiPlayerStore, sessionStore } = useStore();
const session = sessionStore.current;
const notesWithEvents = session.notesWithEvents;
const uxtVideo = session.uxtVideo;
const filteredEvents = sessionStore.filteredEvents;
const query = sessionStore.eventsQuery;
const eventsIndex = sessionStore.eventsIndex;
const setEventFilter = sessionStore.setEventQuery;
const filterOutNote = sessionStore.filterOutNote;
const [mouseOver, setMouseOver] = React.useState(false);
const scroller = React.useRef<VListHandle>(null);
const zoomEnabled = uiPlayerStore.timelineZoom.enabled;
@ -47,12 +46,7 @@ function EventsBlock(props: IProps) {
} = store.get();
const {
filteredEvents,
eventsIndex,
filterOutNote,
query,
setActiveTab,
notesWithEvents = [],
} = props;
const notes = notesStore.sessionNotes;
@ -129,7 +123,7 @@ function EventsBlock(props: IProps) {
const write = ({
target: { value },
}: React.ChangeEvent<HTMLInputElement>) => {
props.setEventFilter({ query: value });
setEventFilter({ query: value });
setTimeout(() => {
if (!scroller.current) return;
@ -139,7 +133,7 @@ function EventsBlock(props: IProps) {
};
const clearSearch = () => {
props.setEventFilter({ query: '' });
setEventFilter({ query: '' });
setTimeout(() => {
if (!scroller.current) return;
@ -163,7 +157,7 @@ function EventsBlock(props: IProps) {
const onEventClick = (_: React.MouseEvent, event: { time: number }) => {
player.jump(event.time);
props.setEventFilter({ query: '' });
setEventFilter({ query: '' });
};
const onMouseOver = () => setMouseOver(true);
const onMouseLeave = () => setMouseOver(false);
@ -213,7 +207,7 @@ function EventsBlock(props: IProps) {
muted
autoPlay
controls
src={props.uxtVideo}
src={uxtVideo}
width={240}
/>
<div
@ -265,18 +259,4 @@ function EventsBlock(props: IProps) {
);
}
export default connect(
(state: RootStore) => ({
session: state.getIn(['sessions', 'current']),
notesWithEvents: state.getIn(['sessions', 'current']).notesWithEvents,
events: state.getIn(['sessions', 'current']).events,
uxtVideo: state.getIn(['sessions', 'current']).uxtVideo,
filteredEvents: state.getIn(['sessions', 'filteredEvents']),
query: state.getIn(['sessions', 'eventsQuery']),
eventsIndex: state.getIn(['sessions', 'eventsIndex']),
}),
{
setEventFilter,
filterOutNote,
}
)(observer(EventsBlock));
export default observer(EventsBlock);

View file

@ -1,8 +1,6 @@
import DraggableMarkers from 'Components/Session_/Player/Controls/components/ZoomDragLayer';
import React, { useEffect, useMemo, useContext, useState, useRef } from 'react';
import { connect } from 'react-redux';
import stl from './timeline.module.css';
import { setTimelinePointer, setTimelineHoverTime } from 'Duck/sessions';
import CustomDragLayer, { OnDragCallback } from './components/CustomDragLayer';
import { debounce } from 'App/utils';
import TooltipContainer from './components/TooltipContainer';
@ -10,18 +8,12 @@ import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import { DateTime, Duration } from 'luxon';
import Issue from 'Types/session/issue';
import { WebEventsList, MobEventsList } from './EventsList';
import NotesList from './NotesList';
import SkipIntervalsList from './SkipIntervalsList';
import TimelineTracker from 'Components/Session_/Player/Controls/TimelineTracker';
interface IProps {
issues: Issue[];
setTimelineHoverTime: (t: number) => void;
startedAt: number;
tooltipVisible: boolean;
timezone?: string;
isMobile?: boolean;
}
@ -29,10 +21,14 @@ function Timeline(props: IProps) {
const { player, store } = useContext(PlayerContext);
const [wasPlaying, setWasPlaying] = useState(false);
const [maxWidth, setMaxWidth] = useState(0);
const { settingsStore, uiPlayerStore } = useStore();
const { settingsStore, uiPlayerStore, sessionStore } = useStore();
const startedAt = sessionStore.current.startedAt ?? 0;
const tooltipVisible = sessionStore.timeLineTooltip.isVisible;
const setTimelineHoverTime = sessionStore.setTimelineTooltip;
const timezone = sessionStore.current.timezone;
const issues = sessionStore.current.issues;
const timelineZoomEnabled = uiPlayerStore.timelineZoom.enabled;
const { playing, skipToIssue, ready, endTime, devtoolsLoading, domLoading } = store.get();
const { issues, timezone } = props;
const progressRef = useRef<HTMLDivElement>(null);
const timelineRef = useRef<HTMLDivElement>(null);
@ -51,7 +47,7 @@ function Timeline(props: IProps) {
}, []);
const debouncedJump = useMemo(() => debounce(player.jump, 500), []);
const debouncedTooltipChange = useMemo(() => debounce(props.setTimelineHoverTime, 50), []);
const debouncedTooltipChange = useMemo(() => debounce(setTimelineHoverTime, 50), []);
const onDragEnd = () => {
if (wasPlaying) {
@ -78,17 +74,17 @@ function Timeline(props: IProps) {
// @ts-ignore black magic
!progressRef.current.contains(e.target)
) {
return props.tooltipVisible && hideTimeTooltip();
return tooltipVisible && hideTimeTooltip();
}
const time = getTime(e);
if (!time) return;
const tz = settingsStore.sessionSettings.timezone.value;
const timeStr = DateTime.fromMillis(props.startedAt + time)
const timeStr = DateTime.fromMillis(startedAt + time)
.setZone(tz)
.toFormat(`hh:mm:ss a`);
const userTimeStr = timezone
? DateTime.fromMillis(props.startedAt + time)
? DateTime.fromMillis(startedAt + time)
.setZone(timezone)
.toFormat(`hh:mm:ss a`)
: undefined;
@ -177,12 +173,4 @@ function Timeline(props: IProps) {
);
}
export default connect(
(state: any) => ({
issues: state.getIn(['sessions', 'current']).issues || [],
startedAt: state.getIn(['sessions', 'current']).startedAt || 0,
timezone: state.getIn(['sessions', 'current']).timezone,
tooltipVisible: state.getIn(['sessions', 'timeLineTooltip', 'isVisible']),
}),
{ setTimelinePointer, setTimelineHoverTime }
)(observer(Timeline));
export default observer(Timeline);

View file

@ -12,16 +12,13 @@ import {
iTag,
tagProps,
} from 'App/services/NotesService';
import { addNote, updateNote } from 'Duck/sessions';
import { Button, Checkbox, Icon } from 'UI';
import Select from 'Shared/Select';
interface Props {
time: number;
addNote: (note: Note) => void;
updateNote: (note: Note) => void;
sessionId: string;
isEdit?: boolean;
editNote?: WriteNote;
hideModal: () => void;
@ -29,13 +26,13 @@ interface Props {
function CreateNote({
time,
sessionId,
isEdit,
editNote,
updateNote,
hideModal,
}: Props) {
const { notesStore, integrationsStore } = useStore();
const { notesStore, integrationsStore, sessionStore } = useStore();
const sessionId = sessionStore.current.sessionId;
const updateNote = sessionStore.updateNote;
const slackChannels = integrationsStore.slack.list;
const fetchSlack = integrationsStore.slack.fetchIntegrations;
const teamsChannels = integrationsStore.msteams.list;
@ -323,10 +320,4 @@ function CreateNote({
);
}
export default connect(
(state: any) => {
const sessionId = state.getIn(['sessions', 'current']).sessionId;
return { sessionId };
},
{ addNote, updateNote,}
)(observer(CreateNote));
export default observer(CreateNote);

View file

@ -1,6 +1,5 @@
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { setAutoplayValues } from 'Duck/sessions';
import { withSiteId, session as sessionRoute } from 'App/routes';
import AutoplayToggle from 'Shared/AutoplayToggle/AutoplayToggle';
import { withRouter, RouteComponentProps } from 'react-router-dom';
@ -12,24 +11,21 @@ import { useStore } from 'App/mstore';
const PER_PAGE = 10;
interface Props extends RouteComponentProps {
previousId: string;
nextId: string;
defaultList: any;
currentPage: number;
total: number;
setAutoplayValues: () => void;
latestRequestTime: any;
sessionIds: any;
}
function QueueControls(props: Props) {
const { projectsStore } = useStore();
const { projectsStore, sessionStore, searchStore } = useStore();
const previousId = sessionStore.previousId;
const nextId = sessionStore.nextId;
const total = sessionStore.total;
const sessionIds = sessionStore.sessionIds ?? [];
const setAutoplayValues = sessionStore.setAutoplayValues;
const {
previousId,
nextId,
currentPage,
total,
sessionIds,
latestRequestTime,
match: {
// @ts-ignore
@ -37,19 +33,15 @@ function QueueControls(props: Props) {
}
} = props;
const { searchStore } = useStore();
const disabled = sessionIds.length === 0;
useEffect(() => {
if (latestRequestTime) {
props.setAutoplayValues();
setAutoplayValues();
const totalPages = Math.ceil(total / PER_PAGE);
const index = sessionIds.indexOf(sessionId);
// check for the last page and load the next
if (currentPage !== totalPages && index === sessionIds.length - 1) {
searchStore.fetchAutoplaySessions(currentPage + 1).then(props.setAutoplayValues);
searchStore.fetchAutoplaySessions(currentPage + 1).then(setAutoplayValues);
}
}
}, []);
@ -107,12 +99,7 @@ function QueueControls(props: Props) {
export default connect(
(state: any) => ({
previousId: state.getIn(['sessions', 'previousId']),
nextId: state.getIn(['sessions', 'nextId']),
currentPage: state.getIn(['search', 'currentPage']) || 1,
total: state.getIn(['sessions', 'total']) || 0,
sessionIds: state.getIn(['sessions', 'sessionIds']) || [],
latestRequestTime: state.getIn(['search', 'latestRequestTime'])
}),
{ setAutoplayValues }
)(withRouter(QueueControls));

View file

@ -3,20 +3,21 @@ import { BookmarkCheck, Bookmark as BookmarkIcn, Vault } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { toast } from 'react-toastify';
import { toggleFavorite } from 'Duck/sessions';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
interface Props {
toggleFavorite: (sessionId: string) => Promise<void>;
favorite: boolean;
sessionId: any;
isEnterprise: boolean;
noMargin?: boolean;
}
function Bookmark(props: Props) {
const { sessionId, favorite, isEnterprise, noMargin } = props;
const { sessionStore } = useStore();
const favorite = sessionStore.current.favorite;
const onToggleFavorite = sessionStore.toggleFavorite;
const { sessionId, isEnterprise } = props;
const [isFavorite, setIsFavorite] = useState(favorite);
const ADDED_MESSAGE = isEnterprise
? 'Session added to vault'
: 'Session added to your bookmarks';
@ -33,7 +34,7 @@ function Bookmark(props: Props) {
}, [favorite]);
const toggleFavorite = async () => {
props.toggleFavorite(sessionId).then(() => {
onToggleFavorite(sessionId).then(() => {
toast.success(isFavorite ? REMOVED_MESSAGE : ADDED_MESSAGE);
setIsFavorite(!isFavorite);
});
@ -65,8 +66,6 @@ function Bookmark(props: Props) {
export default connect(
(state: any) => ({
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
favorite: state.getIn(['sessions', 'current']).favorite,
}),
{ toggleFavorite }
)(Bookmark);
isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee',
}),
)(observer(Bookmark));

View file

@ -70,7 +70,7 @@ export default class SessionStore {
visitedEvents: Location[] = [];
insights: any[] = [];
host = '';
sessionPath = {};
sessionPath: Record<string, any> = {};
lastPlayedSessionId: string = '';
timeLineTooltip = {
time: 0,
@ -421,16 +421,6 @@ export default class SessionStore {
});
}
// Add Note
addNote(note: Note) {
this.current.notesWithEvents.push(note);
this.current.notesWithEvents.sort((a, b) => {
const aTs = a.time || a.timestamp;
const bTs = b.time || b.timestamp;
return aTs - bTs;
});
}
// Update Note
updateNote(note: Note) {
const noteIndex = this.current.notesWithEvents.findIndex((item) => {
@ -450,7 +440,6 @@ export default class SessionStore {
this.sessionPath = path;
}
// Update Last Played Session
updateLastPlayedSession(sessionId: string) {
const sIndex = this.list.findIndex((s) => s.sessionId === sessionId);
if (sIndex !== -1) {

View file

@ -174,6 +174,7 @@ export default class Session {
duration: Duration;
durationMs: ISession['durationMs'];
events: ISession['events'];
uxtVideo?: any;
stackEvents: ISession['stackEvents'];
metadata: ISession['metadata'];
favorite: ISession['favorite'];
@ -228,6 +229,9 @@ export default class Session {
fileKey: ISession['fileKey'];
durationSeconds: number;
liveOnly: boolean;
videoURL: string[]
screenWidth?: number
screenHeight?: number
constructor(plainSession?: ISession) {
const sessionData = plainSession || (emptyValues as unknown as ISession);