From bdf5dbba0a33a0cea712d0b0b713fc5e2a659ff0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 1 Aug 2023 16:58:42 +0200 Subject: [PATCH] change(ui) - handle iframe (#1431) * change(ui): iframe handle routes * change(ui): disable player actions on iframe --- frontend/app/Router.js | 411 ++++++++++-------- .../EventsBlock/UserCard/UserCard.js | 4 +- .../Player/ReplayPlayer/PlayerBlockHeader.tsx | 8 +- frontend/app/components/Session_/Subheader.js | 5 +- .../app/components/shared/NotFoundPage.tsx | 24 + frontend/app/constants/storageKeys.ts | 3 +- 6 files changed, 276 insertions(+), 179 deletions(-) create mode 100644 frontend/app/components/shared/NotFoundPage.tsx diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 574cf08a3..83ef198f4 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -15,11 +15,12 @@ import APIClient from './api_client'; import * as routes from './routes'; import { OB_DEFAULT_TAB, isRoute } from 'App/routes'; import Signup from 'Components/Signup'; -import { fetchTenants } from 'Duck/user'; +import { fetchTenants, setJwt } from 'Duck/user'; import { setSessionPath } from 'Duck/sessions'; import { ModalProvider } from './components/Modal'; -import { GLOBAL_DESTINATION_PATH, GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys'; +import { GLOBAL_DESTINATION_PATH, GLOBAL_HAS_NO_RECORDINGS, IFRAME } from 'App/constants/storageKeys'; import SupportCallout from 'Shared/SupportCallout'; +import NotFoundPage from 'Shared/NotFoundPage'; const Login = lazy(() => import('Components/Login/Login')); const ForgotPassword = lazy(() => import('Components/ForgotPassword/ForgotPassword')); @@ -48,7 +49,7 @@ const Onboarding = withSiteIdUpdater(OnboardingPure); const FunnelPage = withSiteIdUpdater(FunnelPagePure); const FunnelsDetails = withSiteIdUpdater(FunnelDetailsPure); const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails); -const Multiview = withSiteIdUpdater(MultiviewPure) +const Multiview = withSiteIdUpdater(MultiviewPure); const withSiteId = routes.withSiteId; const METRICS_PATH = routes.metrics(); @@ -68,7 +69,7 @@ const SESSIONS_PATH = routes.sessions(); const FFLAGS_PATH = routes.fflags(); const FFLAG_PATH = routes.fflag(); const FFLAG_CREATE_PATH = routes.newFFlag(); -const FFLAG_READ_PATH = routes.fflagRead() +const FFLAG_READ_PATH = routes.fflagRead(); const NOTES_PATH = routes.notes(); const BOOKMARKS_PATH = routes.bookmarks(); const ASSIST_PATH = routes.assist(); @@ -92,193 +93,257 @@ const MULTIVIEW_INDEX_PATH = routes.multiviewIndex(); @withStore @withRouter @connect( - (state) => { - const siteId = state.getIn(['site', 'siteId']); - const jwt = state.getIn(['user', 'jwt']); - const changePassword = state.getIn(['user', 'account', 'changePassword']); - const userInfoLoading = state.getIn(['user', 'fetchUserInfoRequest', 'loading']); - return { - jwt, - siteId, - changePassword, - sites: state.getIn(['site', 'list']), - isLoggedIn: jwt !== null && !changePassword, - loading: siteId === null || userInfoLoading, - email: state.getIn(['user', 'account', 'email']), - account: state.getIn(['user', 'account']), - organisation: state.getIn(['user', 'account', 'name']), - tenantId: state.getIn(['user', 'account', 'tenantId']), - tenants: state.getIn(['user', 'tenants']), - existingTenant: state.getIn(['user', 'authDetails', 'tenants']), - onboarding: state.getIn(['user', 'onboarding']), - isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee' || state.getIn(['user', 'authDetails', 'edition']) === 'ee', - }; - }, - { - fetchUserInfo, - fetchTenants, - setSessionPath, - fetchSiteList, - } + (state) => { + const siteId = state.getIn(['site', 'siteId']); + const jwt = state.getIn(['user', 'jwt']); + const changePassword = state.getIn(['user', 'account', 'changePassword']); + const userInfoLoading = state.getIn(['user', 'fetchUserInfoRequest', 'loading']); + return { + jwt, + siteId, + changePassword, + sites: state.getIn(['site', 'list']), + isLoggedIn: jwt !== null && !changePassword, + loading: siteId === null || userInfoLoading, + email: state.getIn(['user', 'account', 'email']), + account: state.getIn(['user', 'account']), + organisation: state.getIn(['user', 'account', 'name']), + tenantId: state.getIn(['user', 'account', 'tenantId']), + tenants: state.getIn(['user', 'tenants']), + existingTenant: state.getIn(['user', 'authDetails', 'tenants']), + onboarding: state.getIn(['user', 'onboarding']), + isEnterprise: state.getIn(['user', 'account', 'edition']) === 'ee' || state.getIn(['user', 'authDetails', 'edition']) === 'ee' + }; + }, + { + fetchUserInfo, + fetchTenants, + setSessionPath, + fetchSiteList, + setJwt + } ) class Router extends React.Component { - constructor(props) { - super(props); - if (props.isLoggedIn) { - this.fetchInitialData(); - } + constructor(props) { + super(props); + if (props.isLoggedIn) { + this.fetchInitialData(); } - fetchInitialData = async () => { - const siteIdFromPath = parseInt(window.location.pathname.split("/")[1]) - await this.props.fetchUserInfo() - await this.props.fetchSiteList(siteIdFromPath) - const { mstore } = this.props; - mstore.initClient(); + const urlJWT = new URLSearchParams(window.location.search).get('jwt'); + + if (urlJWT && !props.isLoggedIn) { + props.setJwt(urlJWT) + } + + this.state = { + isIframe: this.checkIframe(), }; + } - componentDidMount() { - const { isLoggedIn, location } = this.props; - const destinationPath = localStorage.getItem(GLOBAL_DESTINATION_PATH); - if (!isLoggedIn && !location.pathname.includes('login')) { - localStorage.setItem(GLOBAL_DESTINATION_PATH, location.pathname); - } else if (isLoggedIn && destinationPath && !location.pathname.includes(destinationPath)) { - this.props.history.push(destinationPath || '/'); - localStorage.removeItem(GLOBAL_DESTINATION_PATH); - } + checkIframe = () => { + const urlParams = new URLSearchParams(window.location.search); + const iframe = urlParams.get('iframe'); + + if (iframe && iframe === 'true') { + localStorage.setItem(IFRAME, true); + return true; + } else { + localStorage.removeItem(IFRAME); + return false; + } + }; + + fetchInitialData = async () => { + const siteIdFromPath = parseInt(window.location.pathname.split('/')[1]); + await this.props.fetchUserInfo(); + await this.props.fetchSiteList(siteIdFromPath); + const { mstore } = this.props; + mstore.initClient(); + }; + + componentDidMount() { + const { isLoggedIn, location } = this.props; + const destinationPath = localStorage.getItem(GLOBAL_DESTINATION_PATH); + + if (!isLoggedIn && !location.pathname.includes('login')) { + localStorage.setItem(GLOBAL_DESTINATION_PATH, location.pathname); + } else if (isLoggedIn && destinationPath && !location.pathname.includes(destinationPath)) { + this.props.history.push(destinationPath || '/'); + localStorage.removeItem(GLOBAL_DESTINATION_PATH); + } + } + + componentDidUpdate(prevProps, prevState) { + this.props.setSessionPath(prevProps.location); + const destinationPath = localStorage.getItem(GLOBAL_DESTINATION_PATH); + + if (prevProps.email !== this.props.email && !this.props.email) { + this.props.fetchTenants(); } - componentDidUpdate(prevProps, prevState) { - this.props.setSessionPath(prevProps.location); - const destinationPath = localStorage.getItem(GLOBAL_DESTINATION_PATH); - - if (prevProps.email !== this.props.email && !this.props.email) { - this.props.fetchTenants(); - } - - if ( - destinationPath && - !prevProps.isLoggedIn && - this.props.isLoggedIn && - destinationPath !== routes.login() && - destinationPath !== '/' - ) { - this.props.history.push(destinationPath); - } - - if (!prevProps.isLoggedIn && this.props.isLoggedIn) { - this.fetchInitialData(); - } + if ( + destinationPath && + !prevProps.isLoggedIn && + this.props.isLoggedIn && + destinationPath !== routes.login() && + destinationPath !== '/' + ) { + this.props.history.push(destinationPath); } - render() { - const { isLoggedIn, jwt, siteId, sites, loading, changePassword, location, existingTenant, onboarding, isEnterprise } = this.props; - const siteIdList = sites.map(({ id }) => id).toJS(); - const hideHeader = (location.pathname && location.pathname.includes('/session/')) - || location.pathname.includes('/assist/') - || location.pathname.includes('multiview'); - const isPlayer = isRoute(SESSION_PATH, location.pathname) - || isRoute(LIVE_SESSION_PATH, location.pathname) - || isRoute(MULTIVIEW_PATH, location.pathname) - || isRoute(MULTIVIEW_INDEX_PATH, location.pathname); + if (!prevProps.isLoggedIn && this.props.isLoggedIn) { + this.fetchInitialData(); + } + } - const redirectToOnboarding = !onboarding && localStorage.getItem(GLOBAL_HAS_NO_RECORDINGS) === 'true' - return isLoggedIn ? ( - - - - {!hideHeader &&
} - }> - - - - { - const client = new APIClient(jwt); - switch (location.pathname) { - case '/integrations/slack': - client.post('integrations/slack/add', { - code: location.search.split('=')[1], - state: tenantId, - }); - break; - case '/integrations/msteams': - client.post('integrations/msteams/add', { - code: location.search.split('=')[1], - state: tenantId, - }); - break; - } - return ; - }} - /> - {redirectToOnboarding && } + render() { + const { + isLoggedIn, + jwt, + siteId, + sites, + loading, + changePassword, + location, + existingTenant, + onboarding, + isEnterprise + } = this.props; + const siteIdList = sites.map(({ id }) => id).toJS(); + const hideHeader = (location.pathname && location.pathname.includes('/session/')) + || location.pathname.includes('/assist/') + || location.pathname.includes('multiview'); + const isPlayer = isRoute(SESSION_PATH, location.pathname) + || isRoute(LIVE_SESSION_PATH, location.pathname) + || isRoute(MULTIVIEW_PATH, location.pathname) + || isRoute(MULTIVIEW_INDEX_PATH, location.pathname); - {/* DASHBOARD and Metrics */} - - - - - - - - - - + const redirectToOnboarding = !onboarding && localStorage.getItem(GLOBAL_HAS_NO_RECORDINGS) === 'true'; + const { isIframe } = this.state; - - - - - {/**/} - {/**/} - - - - - - - } /> - {routes.redirects.map(([fr, to]) => ( - - ))} - - - - - {!isEnterprise && !isPlayer && } - - ) : ( - }> - - - - - + if (isIframe) { + if (isLoggedIn) { + return ( + + + }> + + + + } /> + + - {!isEnterprise && } - + + + ); + } else { + return ( + + ); + } } + + return isLoggedIn ? ( + + + + {!hideHeader &&
} + }> + + + + { + const client = new APIClient(jwt); + switch (location.pathname) { + case '/integrations/slack': + client.post('integrations/slack/add', { + code: location.search.split('=')[1], + state: tenantId + }); + break; + case '/integrations/msteams': + client.post('integrations/msteams/add', { + code: location.search.split('=')[1], + state: tenantId + }); + break; + } + return ; + }} + /> + {redirectToOnboarding && } + + {/* DASHBOARD and Metrics */} + + + + + + + + + + + + + + + + {/**/} + {/**/} + + + + + + + } /> + {routes.redirects.map(([fr, to]) => ( + + ))} + + + + + {!isEnterprise && !isPlayer && } + + ) : ( + }> + + + + + + + {!isEnterprise && } + + ); + } } export default () => ( - - - + + + ); diff --git a/frontend/app/components/Session/Player/ReplayPlayer/EventsBlock/UserCard/UserCard.js b/frontend/app/components/Session/Player/ReplayPlayer/EventsBlock/UserCard/UserCard.js index f2368fc1e..fdf0d5d9f 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/EventsBlock/UserCard/UserCard.js +++ b/frontend/app/components/Session/Player/ReplayPlayer/EventsBlock/UserCard/UserCard.js @@ -11,6 +11,7 @@ import { withRequest } from 'HOCs'; import SessionInfoItem from 'Components/Session_/SessionInfoItem'; import { useModal } from 'App/components/Modal'; import UserSessionsModal from 'Shared/UserSessionsModal'; +import { IFRAME } from 'App/constants/storageKeys'; function UserCard({ className, request, session, width, height, similarSessions, loading }) { const { settingsStore } = useStore(); @@ -125,9 +126,10 @@ export default withRequest({ // inner component function UserName({ name, userId, hash }) { + const hasIframe = localStorage.getItem(IFRAME) === 'true'; const { showModal } = useModal(); const onClick = () => { showModal(, { right: true, width: 700 }); }; - return
{}}>{name}
; + return
{}}>{name}
; } diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx index fd6c8e3e8..b4b11b2f1 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx @@ -1,3 +1,4 @@ + import React from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; @@ -16,6 +17,7 @@ import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import stl from './playerBlockHeader.module.css'; import { fetchListActive as fetchMetadata } from 'Duck/customField'; +import { IFRAME } from 'App/constants/storageKeys'; const SESSIONS_ROUTE = sessionsRoute(); @@ -42,8 +44,8 @@ function PlayerBlockHeader(props: any) { } = props; React.useEffect(() => { - const queryParams = new URLSearchParams(location.search); - setHideBack(queryParams.has('iframe') && queryParams.get('iframe') === 'true'); + const iframe = localStorage.getItem(IFRAME) || false; + setHideBack(!!iframe && iframe === 'true'); if (metaList.size === 0) fetchMetadata(); }, []); @@ -90,7 +92,7 @@ function PlayerBlockHeader(props: any) {
- {live && ( + {live && !hideBack && ( <>
diff --git a/frontend/app/components/Session_/Subheader.js b/frontend/app/components/Session_/Subheader.js index 2b08cc5e7..a6187b4e5 100644 --- a/frontend/app/components/Session_/Subheader.js +++ b/frontend/app/components/Session_/Subheader.js @@ -13,6 +13,8 @@ import { observer } from 'mobx-react-lite'; import AutoplayToggle from 'Shared/AutoplayToggle'; import { connect } from 'react-redux'; import SessionTabs from 'Components/Session/Player/SharedComponents/SessionTabs' +import { IFRAME } from 'App/constants/storageKeys'; +import cn from 'classnames'; const localhostWarn = (project) => project + '_localhost_warn'; @@ -22,6 +24,7 @@ function SubHeader(props) { const [showWarningModal, setWarning] = React.useState(defaultLocalhostWarn); const { player, store } = React.useContext(PlayerContext); const { width, height, endTime, location: currentLocation = 'loading...', } = store.get(); + const hasIframe = localStorage.getItem(IFRAME) === "true"; const enabledIntegration = useMemo(() => { const { integrations } = props; @@ -103,7 +106,7 @@ function SubHeader(props) { ) : null}