change(ui) - offline vs live session

This commit is contained in:
Shekar Siri 2021-12-27 20:07:08 +05:30
parent 7c4b410a35
commit 9cc5542fc7
13 changed files with 99 additions and 48 deletions

View file

@ -1,18 +1,29 @@
import React, { useEffect, useState } from 'react';
import { SlideModal } from 'UI';
import { SlideModal, Icon } from 'UI';
import SessionList from '../SessionList';
import stl from './assistTabs.css'
interface Props {
userId: any,
}
const AssistTabs = React.memo((props: Props) => {
const AssistTabs = (props: Props) => {
const [showMenu, setShowMenu] = useState(false)
return (
<div className="relative mr-4">
<div className="p-2 cursor-pointer" onClick={() => setShowMenu(!showMenu)}>
Live Sessions
<div className="flex items-center">
<div
className={stl.btnLink}
onClick={() => setShowMenu(!showMenu)}
>
More Live Sessions
</div>
<span className="mx-3 color-gray-medium">by</span>
<div className="flex items-center">
<Icon name="user-alt" color="gray-darkest" />
<div className="ml-2">{props.userId}</div>
</div>
</div>
<SlideModal
title={ <div>Live Sessions by {props.userId}</div> }
@ -22,6 +33,6 @@ const AssistTabs = React.memo((props: Props) => {
/>
</div>
);
});
};
export default AssistTabs;

View file

@ -0,0 +1,5 @@
.btnLink {
cursor: pointer;
color: $green;
text-decoration: underline;
}

View file

@ -31,10 +31,10 @@ const InitLoader = connectPlayer(state => ({
}))(Loader);
const WebPlayer = React.memo(({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request }) => {
function WebPlayer({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request, hasSessionsPath }) {
useEffect(() => {
if (!loadingCredentials) {
initPlayer(session, jwt, assistCredendials);
initPlayer(session, jwt, assistCredendials, !hasSessionsPath && session.live);
}
return () => cleanPlayer()
}, [ session.sessionId, loadingCredentials, assistCredendials ]);
@ -60,7 +60,7 @@ const WebPlayer = React.memo(({ showAssist, session, toggleFullscreen, closeBott
</InitLoader>
</PlayerProvider>
);
});
};
export default withRequest({
initialData: null,
@ -74,6 +74,7 @@ export default withRequest({
showAssist: state.getIn([ 'sessions', 'showChatWindow' ]),
jwt: state.get('jwt'),
fullscreen: state.getIn([ 'components', 'player', 'fullscreen' ]),
hasSessionsPath: state.getIn([ 'sessions', 'sessionPath' ]).includes('/sessions'),
}),
{ toggleFullscreen, closeBottomBlock },
)(WebPlayer)));

View file

@ -6,7 +6,6 @@ import { fetchList as fetchSlackList } from 'Duck/integrations/slack';
import { Link, NoContent, Loader } from 'UI';
import { sessions as sessionsRoute } from 'App/routes';
import withPermissions from 'HOCs/withPermissions'
import { fetchLiveList } from 'Duck/sessions';
import LivePlayer from './LivePlayer';
import WebPlayer from './WebPlayer';
@ -21,8 +20,7 @@ function Session({
session,
fetchSession,
fetchSlackList,
fetchLiveList,
filters
hasSessionsPath
}) {
usePageTitle("OpenReplay Session Player");
useEffect(() => {
@ -37,13 +35,7 @@ function Session({
return () => {
if (!session.exists()) return;
}
},[ sessionId ]);
// useEffect(() => {
// if (session && session.live) {
// fetchLiveList(filters.toJS())
// }
// }, [session])
},[ sessionId, hasSessionsPath ]);
return (
<NoContent
@ -59,7 +51,7 @@ function Session({
<Loader className="flex-1" loading={ loading || sessionId !== session.sessionId }>
{ session.isIOS
? <IOSPlayer session={session} />
: (session.live ? <LivePlayer /> : <WebPlayer />)
: (session.live && !hasSessionsPath ? <LivePlayer /> : <WebPlayer />)
}
</Loader>
</NoContent>
@ -73,10 +65,9 @@ export default withPermissions(['SESSION_REPLAY'], '', true)(connect((state, pro
loading: state.getIn([ 'sessions', 'loading' ]),
hasErrors: !!state.getIn([ 'sessions', 'errors' ]),
session: state.getIn([ 'sessions', 'current' ]),
filters: state.getIn([ 'filters', 'appliedFilter' ]),
hasSessionsPath: state.getIn([ 'sessions', 'sessionPath' ]).includes('/sessions'),
};
}, {
fetchSession,
fetchSlackList,
fetchLiveList,
})(Session));

View file

@ -4,14 +4,14 @@ import { browserIcon, osIcon, deviceTypeIcon } from 'App/iconNames';
import { formatTimeOrDate } from 'App/date';
import { sessions as sessionsRoute, funnel as funnelRoute, funnelIssue as funnelIssueRoute, withSiteId } from 'App/routes';
import { Icon, CountryFlag, IconButton, BackLink } from 'UI';
import { toggleFavorite } from 'Duck/sessions';
import { toggleFavorite, setSessionPath } from 'Duck/sessions';
import cn from 'classnames';
import { connectPlayer } from 'Player';
import HeaderInfo from './HeaderInfo';
import SharePopup from '../shared/SharePopup/SharePopup';
import { fetchList as fetchListIntegration } from 'Duck/integrations/actions';
import cls from './playerBlockHeader.css';
import stl from './playerBlockHeader.css';
import Issues from './Issues/Issues';
import Autoplay from './Autoplay';
import AssistActions from '../Assist/components/AssistActions';
@ -38,8 +38,9 @@ function capitalise(str) {
funnelRef: state.getIn(['funnels', 'navRef']),
siteId: state.getIn([ 'user', 'siteId' ]),
funnelPage: state.getIn(['sessions', 'funnelPage']),
hasSessionsPath: state.getIn([ 'sessions', 'sessionPath' ]).includes('/sessions'),
}), {
toggleFavorite, fetchListIntegration
toggleFavorite, fetchListIntegration, setSessionPath
})
@withRouter
export default class PlayerBlockHeader extends React.PureComponent {
@ -87,21 +88,24 @@ export default class PlayerBlockHeader extends React.PureComponent {
userDevice,
userBrowserVersion,
userDeviceType,
live,
},
loading,
live,
// live,
disabled,
jiraConfig,
fullscreen,
hasSessionsPath
} = this.props;
const { history, siteId } = this.props;
// const { history, siteId } = this.props;
const _live = live && !hasSessionsPath;
return (
<div className={ cn(cls.header, "flex justify-between", { "hidden" : fullscreen}) }>
<div className={ cn(stl.header, "flex justify-between", { "hidden" : fullscreen}) }>
<div className="flex w-full">
<BackLink onClick={this.backHandler} label="Back" />
<div className={ cls.divider } />
<div className={ stl.divider } />
<div className="mx-4 flex items-center">
<CountryFlag country={ userCountry } />
@ -116,12 +120,17 @@ export default class PlayerBlockHeader extends React.PureComponent {
<HeaderInfo icon={ osIcon(userOs) } label={ userOs } />
<div className='ml-auto flex items-center'>
{ live && <AssistTabs userId={userId} />}
{ live && <AssistActions isLive userId={userId} /> }
{ !live && (
{ live && hasSessionsPath && (
<div className={stl.liveSwitchButton} onClick={() => this.props.setSessionPath('')}>
This Session is Now Continuing Live
</div>
)}
{ _live && <AssistTabs userId={userId} />}
{ _live && <AssistActions isLive userId={userId} /> }
{ !_live && (
<>
<Autoplay />
<div className={ cls.divider } />
<div className={ stl.divider } />
<IconButton
className="mr-2"
tooltip="Bookmark"
@ -145,7 +154,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
/>
</>
)}
{ !live && jiraConfig && jiraConfig.token && <Issues sessionId={ sessionId } /> }
{ !_live && jiraConfig && jiraConfig.token && <Issues sessionId={ sessionId } /> }
</div>
</div>
</div>

View file

@ -12,3 +12,15 @@
background-color: $gray-light;
}
.liveSwitchButton {
cursor: pointer;
padding: 3px 8px;
border: solid thin $green;
color: $green;
border-radius: 3px;
margin-right: 10px;
&:hover {
background-color: $green;
color: white;
}
}

View file

@ -10,21 +10,33 @@ import {
TextEllipsis
} from 'UI';
import { deviceTypeIcon } from 'App/iconNames';
import { toggleFavorite } from 'Duck/sessions';
import { session as sessionRoute } from 'App/routes';
import { toggleFavorite, setSessionPath } from 'Duck/sessions';
import { session as sessionRoute, withSiteId } from 'App/routes';
import { durationFormatted, formatTimeOrDate } from 'App/date';
import stl from './sessionItem.css';
import LiveTag from 'Shared/LiveTag';
import Bookmark from 'Shared/Bookmark';
import Counter from './Counter'
import { withRouter } from 'react-router-dom';
const Label = ({ label = '', color = 'color-gray-medium'}) => (
<div className={ cn('font-light text-sm', color)}>{label}</div>
)
@connect(state => ({
timezone: state.getIn(['sessions', 'timezone'])
}), { toggleFavorite })
timezone: state.getIn(['sessions', 'timezone']),
isAssist: state.getIn(['sessions', 'activeTab']).type === 'live',
siteId: state.getIn([ 'user', 'siteId' ]),
}), { toggleFavorite, setSessionPath })
@withRouter
export default class SessionItem extends React.PureComponent {
replaySession = () => {
const { history, session: { sessionId }, siteId, isAssist } = this.props;
if (!isAssist) {
this.props.setSessionPath(history.location.pathname)
}
history.push(withSiteId(sessionRoute(sessionId), siteId))
}
// eslint-disable-next-line complexity
render() {
const {
@ -110,9 +122,9 @@ export default class SessionItem extends React.PureComponent {
</div>
<div className={ stl.playLink } id="play-button" data-viewed={ viewed }>
<Link to={ sessionRoute(sessionId) }>
<div onClick={this.replaySession}>
<Icon name={ viewed ? 'play-fill' : 'play-circle-light' } size="30" color="teal" />
</Link>
</div>
</div>
</div>
</div>

View file

@ -92,7 +92,7 @@
display: flex;
align-items: center;
transition: all 0.2s;
/* opacity: 0; */
cursor: pointer;
&[data-viewed=true] {
opacity: 1;
}

View file

@ -119,7 +119,6 @@ const reducer = (state = initialState, action = {}) => {
let stages = [];
if (action.isRefresh) {
const activeStages = state.get('activeStages');
console.log('test', activeStages);
const oldInsights = state.get('insights');
const lastStage = action.data.stages[action.data.stages.length - 1]
const lastStageIndex = activeStages.toJS()[1];

View file

@ -8,7 +8,6 @@ import { getRE } from 'App/utils';
import { LAST_7_DAYS } from 'Types/app/period';
import { getDateRangeFromValue } from 'App/dateRange';
const INIT = 'sessions/INIT';
const FETCH_LIST = new RequestTypes('sessions/FETCH_LIST');
@ -26,6 +25,7 @@ const SET_AUTOPLAY_VALUES = 'sessions/SET_AUTOPLAY_VALUES';
const TOGGLE_CHAT_WINDOW = 'sessions/TOGGLE_CHAT_WINDOW';
const SET_FUNNEL_PAGE_FLAG = 'sessions/SET_FUNNEL_PAGE_FLAG';
const SET_TIMELINE_POINTER = 'sessions/SET_TIMELINE_POINTER';
const SET_SESSION_PATH = 'sessions/SET_SESSION_PATH';
const SET_ACTIVE_TAB = 'sessions/SET_ACTIVE_TAB';
@ -59,6 +59,7 @@ const initialState = Map({
host: '',
funnelPage: Map(),
timelinePointer: null,
sessionPath: '',
});
const reducer = (state = initialState, action = {}) => {
@ -246,6 +247,8 @@ const reducer = (state = initialState, action = {}) => {
return state.set('funnelPage', action.funnelPage ? Map(action.funnelPage) : false);
case SET_TIMELINE_POINTER:
return state.set('timelinePointer', action.pointer);
case SET_SESSION_PATH:
return state.set('sessionPath', action.path);
default:
return state;
}
@ -386,4 +389,11 @@ export function setTimelinePointer(pointer) {
type: SET_TIMELINE_POINTER,
pointer
}
}
export function setSessionPath(path) {
return {
type: SET_SESSION_PATH,
path
}
}

View file

@ -118,7 +118,7 @@ export default class MessageDistributor extends StatedScreen {
private navigationStartOffset: number = 0;
private lastMessageTime: number = 0;
constructor(private readonly session: any /*Session*/, jwt: string, config) {
constructor(private readonly session: any /*Session*/, jwt: string, config, live: boolean) {
super();
this.pagesManager = new PagesManager(this, this.session.isMobile)
this.mouseManager = new MouseManager(this);
@ -126,7 +126,7 @@ export default class MessageDistributor extends StatedScreen {
this.sessionStart = this.session.startedAt;
if (this.session.live) {
if (live) {
// const sockUrl = `wss://live.openreplay.com/1/${ this.session.siteId }/${ this.session.sessionId }/${ jwt }`;
// this.subscribeOnMessages(sockUrl);
initListsDepr({})

View file

@ -28,11 +28,11 @@ document.addEventListener("visibilitychange", function() {
}
});
export function init(session, jwt, config) {
const live = session.live;
export function init(session, jwt, config, live = false) {
// const live = session.live;
const endTime = !live && session.duration.valueOf();
instance = new Player(session, jwt, config);
instance = new Player(session, jwt, config, live);
update({
initialized: true,
live,

View file

@ -88,6 +88,7 @@ export default Record({
...session
}) => {
const duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration);
const durationSeconds = duration.valueOf();
const startedAt = +startTs;
const userDevice = session.userDevice || session.userDeviceType || 'Other';
@ -96,7 +97,7 @@ export default Record({
const events = List(session.events)
.map(e => SessionEvent({ ...e, time: e.timestamp - startedAt }))
.filter(({ type }) => type !== TYPES.CONSOLE);
.filter(({ type, time }) => type !== TYPES.CONSOLE && time <= durationSeconds);
let resources = List(session.resources)
.map(Resource);