diff --git a/frontend/app/Router.js b/frontend/app/Router.js
index e7f05190e..7e5511231 100644
--- a/frontend/app/Router.js
+++ b/frontend/app/Router.js
@@ -24,6 +24,7 @@ import * as routes from './routes';
import { OB_DEFAULT_TAB } from 'App/routes';
import Signup from './components/Signup/Signup';
import { fetchTenants } from 'Duck/user';
+import { setSessionPath } from 'Duck/sessions';
const BugFinder = withSiteIdUpdater(BugFinderPure);
const Dashboard = withSiteIdUpdater(DashboardPure);
@@ -73,9 +74,12 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB);
onboarding: state.getIn([ 'user', 'onboarding' ])
};
}, {
- fetchUserInfo, fetchTenants
+ fetchUserInfo, fetchTenants, setSessionPath
})
class Router extends React.Component {
+ state = {
+ destinationPath: null
+ }
constructor(props) {
super(props);
if (props.isLoggedIn) {
@@ -85,10 +89,23 @@ class Router extends React.Component {
props.fetchTenants();
}
- componentDidUpdate(prevProps) {
+ componentDidMount() {
+ const { isLoggedIn, location } = this.props;
+ if (!isLoggedIn) {
+ this.setState({ destinationPath: location.pathname });
+ }
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ this.props.setSessionPath(prevProps.location.pathname)
if (prevProps.email !== this.props.email && !this.props.email) {
this.props.fetchTenants();
}
+
+ if (!prevProps.isLoggedIn && this.props.isLoggedIn && this.state.destinationPath !== routes.login()) {
+ this.props.history.push(this.state.destinationPath);
+ this.setState({ destinationPath: null });
+ }
}
render() {
diff --git a/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx
new file mode 100644
index 000000000..ff62a899a
--- /dev/null
+++ b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx
@@ -0,0 +1,38 @@
+import React, { useEffect, useState } from 'react';
+import { SlideModal, Icon } from 'UI';
+import SessionList from '../SessionList';
+import stl from './assistTabs.css'
+
+interface Props {
+ userId: any,
+}
+
+const AssistTabs = (props: Props) => {
+ const [showMenu, setShowMenu] = useState(false)
+
+ return (
+
diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js
index 70b0953fb..cb0ffd55a 100644
--- a/frontend/app/components/Errors/List/List.js
+++ b/frontend/app/components/Errors/List/List.js
@@ -2,7 +2,7 @@ import cn from 'classnames';
import { connect } from 'react-redux';
import { Set, List as ImmutableList } from "immutable";
import { NoContent, Loader, Checkbox, LoadMoreButton, IconButton, Input, DropdownPlain } from 'UI';
-import { merge, resolve,unresolve,ignore } from "Duck/errors";
+import { merge, resolve, unresolve, ignore, updateCurrentPage } from "Duck/errors";
import { applyFilter } from 'Duck/filters';
import { IGNORED, RESOLVED, UNRESOLVED } from 'Types/errorInfo';
import SortDropdown from 'Components/BugFinder/Filters/SortDropdown';
@@ -30,18 +30,19 @@ const sortOptions = Object.entries(sortOptionsMap)
state.getIn(["errors", "unresolve", "loading"]),
ignoreLoading: state.getIn([ "errors", "ignore", "loading" ]),
mergeLoading: state.getIn([ "errors", "merge", "loading" ]),
+ currentPage: state.getIn(["errors", "currentPage"]),
}), {
merge,
resolve,
unresolve,
ignore,
- applyFilter
+ applyFilter,
+ updateCurrentPage,
})
export default class List extends React.PureComponent {
state = {
checkedAll: false,
checkedIds: Set(),
- showPages: 1,
sort: {}
}
@@ -106,7 +107,7 @@ export default class List extends React.PureComponent {
this.applyToAllChecked(this.props.ignore);
}
- addPage = () => this.setState({ showPages: this.state.showPages + 1 })
+ addPage = () => this.props.updateCurrentPage(this.props.currentPage + 1)
writeOption = (e, { name, value }) => {
const [ sort, order ] = value.split('-');
@@ -123,16 +124,16 @@ export default class List extends React.PureComponent {
resolveToggleLoading,
mergeLoading,
onFilterChange,
+ currentPage,
} = this.props;
const {
checkedAll,
checkedIds,
- showPages,
sort
} = this.state;
const someLoading = loading || ignoreLoading || resolveToggleLoading || mergeLoading;
const currentCheckedIds = this.currentCheckedIds();
- const displayedCount = Math.min(showPages * PER_PAGE, list.size);
+ const displayedCount = Math.min(currentPage * PER_PAGE, list.size);
let _list = sort.sort ? list.sortBy(i => i[sort.sort]) : list;
_list = sort.order === 'desc' ? _list.reverse() : _list;
diff --git a/frontend/app/components/Session/LivePlayer.js b/frontend/app/components/Session/LivePlayer.js
index cbc3410cd..878ddf10d 100644
--- a/frontend/app/components/Session/LivePlayer.js
+++ b/frontend/app/components/Session/LivePlayer.js
@@ -31,17 +31,19 @@ const InitLoader = connectPlayer(state => ({
}))(Loader);
-function WebPlayer ({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request }) {
+function WebPlayer ({ showAssist, session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, loadingCredentials, assistCredendials, request, isEnterprise, hasSessionsPath }) {
useEffect(() => {
if (!loadingCredentials) {
- initPlayer(session, jwt, assistCredendials);
+ initPlayer(session, jwt, assistCredendials, !hasSessionsPath && session.live);
}
return () => cleanPlayer()
}, [ session.sessionId, loadingCredentials, assistCredendials ]);
// LAYOUT (TODO: local layout state - useContext or something..)
useEffect(() => {
- request();
+ if (isEnterprise) {
+ request();
+ }
return () => {
toggleFullscreen(false);
closeBottomBlock();
@@ -60,7 +62,7 @@ function WebPlayer ({ showAssist, session, toggleFullscreen, closeBottomBlock, l
);
-}
+};
export default withRequest({
initialData: null,
@@ -69,11 +71,17 @@ export default withRequest({
dataName: 'assistCredendials',
loadingName: 'loadingCredentials',
})(withPermissions(['SESSION_REPLAY', 'ASSIST_LIVE'], '', true)(connect(
- state => ({
- session: state.getIn([ 'sessions', 'current' ]),
- showAssist: state.getIn([ 'sessions', 'showChatWindow' ]),
- jwt: state.get('jwt'),
- fullscreen: state.getIn([ 'components', 'player', 'fullscreen' ]),
- }),
+ state => {
+ const isAssist = state.getIn(['sessions', 'activeTab']).type === 'live';
+ const hasSessioPath = state.getIn([ 'sessions', 'sessionPath' ]).includes('/sessions');
+ return {
+ session: state.getIn([ 'sessions', 'current' ]),
+ showAssist: state.getIn([ 'sessions', 'showChatWindow' ]),
+ jwt: state.get('jwt'),
+ fullscreen: state.getIn([ 'components', 'player', 'fullscreen' ]),
+ hasSessionsPath: hasSessioPath && !isAssist,
+ isEnterprise: state.getIn([ 'user', 'client', 'edition' ]) === 'ee',
+ }
+ },
{ toggleFullscreen, closeBottomBlock },
-)(WebPlayer)));
\ No newline at end of file
+)(WebPlayer)));
diff --git a/frontend/app/components/Session/Session.js b/frontend/app/components/Session/Session.js
index 6b90ff4c1..0138e7e50 100644
--- a/frontend/app/components/Session/Session.js
+++ b/frontend/app/components/Session/Session.js
@@ -20,6 +20,7 @@ function Session({
session,
fetchSession,
fetchSlackList,
+ hasSessionsPath
}) {
usePageTitle("OpenReplay Session Player");
useEffect(() => {
@@ -34,7 +35,7 @@ function Session({
return () => {
if (!session.exists()) return;
}
- },[ sessionId ]);
+ },[ sessionId, hasSessionsPath ]);
return (
{ session.isIOS
?
- : (session.live ? : )
+ : (session.live && !hasSessionsPath ? : )
}
@@ -59,11 +60,14 @@ function Session({
export default withPermissions(['SESSION_REPLAY'], '', true)(connect((state, props) => {
const { match: { params: { sessionId } } } = props;
+ const isAssist = state.getIn(['sessions', 'activeTab']).type === 'live';
+ const hasSessiosPath = state.getIn([ 'sessions', 'sessionPath' ]).includes('/sessions');
return {
sessionId,
loading: state.getIn([ 'sessions', 'loading' ]),
hasErrors: !!state.getIn([ 'sessions', 'errors' ]),
session: state.getIn([ 'sessions', 'current' ]),
+ hasSessionsPath: hasSessiosPath && !isAssist,
};
}, {
fetchSession,
diff --git a/frontend/app/components/Session_/Fetch/Fetch.js b/frontend/app/components/Session_/Fetch/Fetch.js
index 71e7a2fb9..8bc4f2117 100644
--- a/frontend/app/components/Session_/Fetch/Fetch.js
+++ b/frontend/app/components/Session_/Fetch/Fetch.js
@@ -1,56 +1,89 @@
-//import cn from 'classnames';
import { getRE } from 'App/utils';
import { Label, NoContent, Input, SlideModal, CloseButton } from 'UI';
-import { connectPlayer, pause } from 'Player';
-import Autoscroll from '../Autoscroll';
+import { connectPlayer, pause, jump } from 'Player';
+// import Autoscroll from '../Autoscroll';
import BottomBlock from '../BottomBlock';
import TimeTable from '../TimeTable';
import FetchDetails from './FetchDetails';
import { renderName, renderDuration } from '../Network';
+import { connect } from 'react-redux';
+import { setTimelinePointer } from 'Duck/sessions';
@connectPlayer(state => ({
list: state.fetchList,
}))
+@connect(state => ({
+ timelinePointer: state.getIn(['sessions', 'timelinePointer']),
+}), { setTimelinePointer })
export default class Fetch extends React.PureComponent {
state = {
filter: "",
+ filteredList: this.props.list,
current: null,
+ currentIndex: 0,
+ showFetchDetails: false,
+ hasNextError: false,
+ hasPreviousError: false,
}
- onFilterChange = (e, { value }) => this.setState({ filter: value })
+
+ onFilterChange = (e, { value }) => {
+ const { list } = this.props;
+ const filterRE = getRE(value, 'i');
+ const filtered = list
+ .filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status));
+ this.setState({ filter: value, filteredList: value ? filtered : list, currentIndex: 0 });
+ }
setCurrent = (item, index) => {
pause()
+ jump(item.time)
this.setState({ current: item, currentIndex: index });
}
- closeModal = () => this.setState({ current: null})
+ onRowClick = (item, index) => {
+ pause()
+ this.setState({ current: item, currentIndex: index, showFetchDetails: true });
+ this.props.setTimelinePointer(null);
+ }
+
+ closeModal = () => this.setState({ current: null, showFetchDetails: false });
nextClickHander = () => {
- const { list } = this.props;
- const { currentIndex } = this.state;
+ // const { list } = this.props;
+ const { currentIndex, filteredList } = this.state;
- if (currentIndex === list.length - 1) return;
+ if (currentIndex === filteredList.length - 1) return;
const newIndex = currentIndex + 1;
- this.setCurrent(list[newIndex], newIndex);
+ this.setCurrent(filteredList[newIndex], newIndex);
+ this.setState({ showFetchDetails: true });
}
prevClickHander = () => {
- const { list } = this.props;
- const { currentIndex } = this.state;
+ // const { list } = this.props;
+ const { currentIndex, filteredList } = this.state;
if (currentIndex === 0) return;
const newIndex = currentIndex - 1;
- this.setCurrent(list[newIndex], newIndex);
+ this.setCurrent(filteredList[newIndex], newIndex);
+ this.setState({ showFetchDetails: true });
+ }
+
+ static getDerivedStateFromProps(nextProps, prevState) {
+ const { filteredList } = prevState;
+ if (nextProps.timelinePointer) {
+ let activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time);
+ activeItem = activeItem || filteredList[filteredList.length - 1];
+ return {
+ current: activeItem,
+ currentIndex: filteredList.indexOf(activeItem),
+ };
+ }
}
render() {
- const { list } = this.props;
- const { filter, current, currentIndex } = this.state;
- const filterRE = getRE(filter, 'i');
- const filtered = list
- .filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status));
-
+ // const { list } = this.props;
+ const { current, currentIndex, showFetchDetails, filteredList } = this.state;
return (
}
- isDisplayed={ current != null }
- content={ current &&
+ isDisplayed={ current != null && showFetchDetails }
+ content={ current && showFetchDetails &&
}
onClose={ this.closeModal }
@@ -88,25 +121,31 @@ export default class Fetch extends React.PureComponent {
Fetch
-
+
{[
@@ -120,7 +159,7 @@ export default class Fetch extends React.PureComponent {
width: 60,
}, {
label: "Name",
- width: 130,
+ width: 180,
render: renderName,
},
{
diff --git a/frontend/app/components/Session_/Network/Network.js b/frontend/app/components/Session_/Network/Network.js
index 878266bda..150cadc59 100644
--- a/frontend/app/components/Session_/Network/Network.js
+++ b/frontend/app/components/Session_/Network/Network.js
@@ -3,14 +3,10 @@ import { connectPlayer, jump, pause } from 'Player';
import { QuestionMarkHint, Popup, Tabs, Input } from 'UI';
import { getRE } from 'App/utils';
import { TYPES } from 'Types/session/resource';
-import { formatBytes } from 'App/utils';
-import { formatMs } from 'App/date';
-
-import TimeTable from '../TimeTable';
-import BottomBlock from '../BottomBlock';
-import InfoLine from '../BottomBlock/InfoLine';
import stl from './network.css';
import NetworkContent from './NetworkContent';
+import { connect } from 'react-redux';
+import { setTimelinePointer } from 'Duck/sessions';
const ALL = 'ALL';
const XHR = 'xhr';
@@ -28,73 +24,24 @@ const TAB_TO_TYPE_MAP = {
[ MEDIA ]: TYPES.MEDIA,
[ OTHER ]: TYPES.OTHER
}
-const TABS = [ ALL, XHR, JS, CSS, IMG, MEDIA, OTHER ].map(tab => ({
- text: tab,
- key: tab,
-}));
-
-const DOM_LOADED_TIME_COLOR = "teal";
-const LOAD_TIME_COLOR = "red";
export function renderName(r) {
return (
- { r.name } }
- content={