diff --git a/frontend/app/components/Session/Player/ClickMapRenderer/Renderer.tsx b/frontend/app/components/Session/Player/ClickMapRenderer/Renderer.tsx index a365dc163..8a51717e5 100644 --- a/frontend/app/components/Session/Player/ClickMapRenderer/Renderer.tsx +++ b/frontend/app/components/Session/Player/ClickMapRenderer/Renderer.tsx @@ -7,21 +7,15 @@ import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite' function Player() { - const [wrapperHeight, setWrapperHeight] = React.useState(0); const playerContext = React.useContext(PlayerContext); const screenWrapper = React.useRef(null); - const portHeight = playerContext.store.get().portHeight React.useEffect(() => { - const parentElement = findDOMNode(screenWrapper.current) as HTMLDivElement | null; //TODO: good architecture + const parentElement = findDOMNode(screenWrapper.current) as HTMLDivElement | null; if (parentElement) { playerContext.player.attach(parentElement); - playerContext.player.play(); } }, []); - React.useEffect(() => { - setWrapperHeight(portHeight) - }, [portHeight]); if (!playerContext.player) return null; @@ -31,7 +25,7 @@ function Player() { >
-
+
); diff --git a/frontend/app/components/Session/Player/ClickMapRenderer/ThinPlayer.tsx b/frontend/app/components/Session/Player/ClickMapRenderer/ThinPlayer.tsx index 58ad80535..886bb848c 100644 --- a/frontend/app/components/Session/Player/ClickMapRenderer/ThinPlayer.tsx +++ b/frontend/app/components/Session/Player/ClickMapRenderer/ThinPlayer.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; -import { createWebPlayer } from 'Player'; +import { createClickMapPlayer } from 'Player'; import { makeAutoObservable } from 'mobx'; import withLocationHandlers from 'HOCs/withLocationHandlers'; import PlayerContent from './ThinPlayerContent'; @@ -20,11 +20,10 @@ function WebPlayer(props: any) { const [contextValue, setContextValue] = useState(defaultContextValue); useEffect(() => { - const [WebPlayerInst, PlayerStore] = createWebPlayer(customSession, (state) => + const [WebPlayerInst, PlayerStore] = createClickMapPlayer(customSession, (state) => makeAutoObservable(state) ); setContextValue({ player: WebPlayerInst, store: PlayerStore }); - WebPlayerInst.setMarkerClick(onMarkerClick) return () => WebPlayerInst.clean(); }, [session.sessionId]); @@ -35,10 +34,10 @@ function WebPlayer(props: any) { contextValue.player && contextValue.player.play() if (isPlayerReady && insights.size > 0) { setTimeout(() => { - contextValue.player.jump(jumpTimestamp) contextValue.player.pause() - contextValue.player.scaleFullPage() - setTimeout(() => { contextValue.player.showClickmap(insights) }, 250) + contextValue.player.jump(jumpTimestamp) + contextValue.player.scale() + setTimeout(() => { contextValue.player.showClickmap(insights, onMarkerClick) }, 250) }, 500) } return () => { diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx index 27756f170..3aa3f1f8e 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx @@ -13,7 +13,6 @@ interface IProps { activeTab: string; jiraConfig: Record fullView?: boolean - isClickmap?: boolean } function PlayerBlock(props: IProps) { @@ -24,14 +23,12 @@ function PlayerBlock(props: IProps) { activeTab, jiraConfig, fullView = false, - isClickmap } = props; - const shouldShowSubHeader = !fullscreen && !fullView && !isClickmap + const shouldShowSubHeader = !fullscreen && !fullView return (
{shouldShowSubHeader ? ( @@ -39,7 +36,6 @@ function PlayerBlock(props: IProps) {
); diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx index 8e46b1163..7a4f3c132 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx @@ -17,11 +17,10 @@ interface IProps { fullscreen: boolean; activeTab: string; setActiveTab: (tab: string) => void; - isClickmap: boolean; session: Session } -function PlayerContent({ session, fullscreen, activeTab, setActiveTab, isClickmap }: IProps) { +function PlayerContent({ session, fullscreen, activeTab, setActiveTab }: IProps) { const { store } = React.useContext(PlayerContext) const { @@ -60,7 +59,7 @@ function PlayerContent({ session, fullscreen, activeTab, setActiveTab, isClickma style={activeTab && !fullscreen ? { maxWidth: 'calc(100% - 270px)' } : undefined} >
- +
{activeTab !== '' && ( diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx index e07ffd70e..0e41640f6 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerInst.tsx @@ -43,7 +43,6 @@ interface IProps { nextId: string; sessionId: string; activeTab: string; - isClickmap?: boolean; updateLastPlayedSession: (id: string) => void } diff --git a/frontend/app/components/Session/WebPlayer.tsx b/frontend/app/components/Session/WebPlayer.tsx index 0a9e3c48c..eb4fbf8db 100644 --- a/frontend/app/components/Session/WebPlayer.tsx +++ b/frontend/app/components/Session/WebPlayer.tsx @@ -9,7 +9,6 @@ import withLocationHandlers from 'HOCs/withLocationHandlers'; import { useStore } from 'App/mstore'; import PlayerBlockHeader from './Player/ReplayPlayer/PlayerBlockHeader'; import ReadNote from '../Session_/Player/Controls/components/ReadNote'; -import { fetchList as fetchMembers } from 'Duck/member'; import PlayerContent from './Player/ReplayPlayer/PlayerContent'; import { IPlayerContext, PlayerContext, defaultContextValue } from './playerContext'; import { observer } from 'mobx-react-lite'; @@ -28,7 +27,6 @@ function WebPlayer(props: any) { fullscreen, fetchList, customSession, - isClickmap, insights, jumpTimestamp, onMarkerClick, @@ -41,28 +39,21 @@ function WebPlayer(props: any) { const [contextValue, setContextValue] = useState(defaultContextValue); useEffect(() => { - if (!isClickmap) { - fetchList('issues'); - } - const usedSession = isClickmap && customSession ? customSession : session; + fetchList('issues'); - const [WebPlayerInst, PlayerStore] = createWebPlayer(usedSession, (state) => + const [WebPlayerInst, PlayerStore] = createWebPlayer(session, (state) => makeAutoObservable(state) ); setContextValue({ player: WebPlayerInst, store: PlayerStore }); - if (!isClickmap) { - notesStore.fetchSessionNotes(session.sessionId).then((r) => { - const note = props.query.get('note'); - if (note) { - WebPlayerInst.pause(); - setNoteItem(notesStore.getNoteById(parseInt(note, 10), r)); - setShowNote(true); - } - }); - } else { - WebPlayerInst.setMarkerClick(onMarkerClick) - } + notesStore.fetchSessionNotes(session.sessionId).then((r) => { + const note = props.query.get('note'); + if (note) { + WebPlayerInst.pause(); + setNoteItem(notesStore.getNoteById(parseInt(note, 10), r)); + setShowNote(true); + } + }) const jumpToTime = props.query.get('jumpto'); const freeze = props.query.get('freeze') @@ -100,34 +91,29 @@ function WebPlayer(props: any) { return ( - <> - {!isClickmap ? ( - + {/* @ts-ignore */} + + + {showNoteModal ? ( + ) : null} - {/* @ts-ignore */} - - - {showNoteModal ? ( - - ) : null} - - + ); } @@ -146,6 +132,5 @@ export default connect( toggleFullscreen, closeBottomBlock, fetchList, - fetchMembers, } )(withLocationHandlers()(observer(WebPlayer))); diff --git a/frontend/app/player/create.ts b/frontend/app/player/create.ts index a07f3910d..016b8eb52 100644 --- a/frontend/app/player/create.ts +++ b/frontend/app/player/create.ts @@ -27,6 +27,18 @@ export function createWebPlayer(session: Record, wrapStore?: (s:IWe } +export function createClickMapPlayer(session: Record, wrapStore?: (s:IWebPlayerStore) => IWebPlayerStore): [IWebPlayer, IWebPlayerStore] { + let store: WebPlayerStore = new SimpleStore({ + ...WebPlayer.INITIAL_STATE, + }) + if (wrapStore) { + store = wrapStore(store) + } + + const player = new WebPlayer(store, session, false, true) + return [player, store] +} + export function createLiveWebPlayer(session: Record, config: RTCIceServer[], wrapStore?: (s:IWebLivePlayerStore) => IWebLivePlayerStore): [IWebLivePlayer, IWebLivePlayerStore] { let store: WebLivePlayerStore = new SimpleStore({ ...WebLivePlayer.INITIAL_STATE, diff --git a/frontend/app/player/player/Animator.ts b/frontend/app/player/player/Animator.ts index ab759bd50..e60b28d78 100644 --- a/frontend/app/player/player/Animator.ts +++ b/frontend/app/player/player/Animator.ts @@ -153,15 +153,18 @@ export default class Animator { } freeze() { - if (this.store.get().ready) { - // making sure that replay is displayed completely - setTimeout(() => { - this.store.update({ freeze: true }) - this.pause() - }, 250) - } else { - setTimeout(() => this.freeze(), 500) - } + return new Promise(res => { + if (this.store.get().ready) { + // making sure that replay is displayed completely + setTimeout(() => { + this.store.update({ freeze: true }) + this.pause() + res() + }, 250) + } else { + setTimeout(() => res(this.freeze()), 500) + } + }) } togglePlay = () => { diff --git a/frontend/app/player/web/Screen/Screen.ts b/frontend/app/player/web/Screen/Screen.ts index 9283c4e4b..b095385b1 100644 --- a/frontend/app/player/web/Screen/Screen.ts +++ b/frontend/app/player/web/Screen/Screen.ts @@ -12,6 +12,13 @@ export const INITIAL_STATE: State = { } +export enum ScaleMode { + Embed, + //AdjustParentWidth + AdjustParentHeight, +} + + function getElementsFromInternalPoint(doc: Document, { x, y }: Point): Element[] { // @ts-ignore (IE, Edge) if (typeof doc.msElementsFromRect === 'function') { @@ -55,9 +62,9 @@ export default class Screen { private readonly iframe: HTMLIFrameElement; private readonly screen: HTMLDivElement; - private parentElement: HTMLElement | null = null; + private parentElement: HTMLElement | null = null - constructor(isMobile: boolean) { + constructor(isMobile: boolean, private scaleMode: ScaleMode = ScaleMode.Embed) { const iframe = document.createElement('iframe'); iframe.className = styles.iframe; this.iframe = iframe; @@ -79,11 +86,10 @@ export default class Screen { attach(parentElement: HTMLElement) { if (this.parentElement) { this.parentElement = null - console.error("BaseScreen: Trying to attach an attached screen."); + console.warn("BaseScreen: reattaching the screen."); } parentElement.appendChild(this.screen); - this.parentElement = parentElement; /* == For the Inspecting Document content == */ @@ -124,6 +130,7 @@ export default class Screen { private boundingRect: DOMRect | null = null; private getBoundingClientRect(): DOMRect { if (this.boundingRect === null) { + // TODO: use this.screen instead in order to separate overlay functionality return this.boundingRect = this.overlay.getBoundingClientRect() // expensive operation? } return this.boundingRect @@ -200,48 +207,41 @@ export default class Screen { if (!this.parentElement) return; const { offsetWidth, offsetHeight } = this.parentElement; - this.scaleRatio = Math.min(offsetWidth / width, offsetHeight / height); - if (this.scaleRatio > 1) { - this.scaleRatio = 1; - } else { - this.scaleRatio = Math.round(this.scaleRatio * 1e3) / 1e3; + let translate = "" + let posStyles = {} + switch (this.scaleMode) { + case ScaleMode.Embed: + this.scaleRatio = Math.min(offsetWidth / width, offsetHeight / height) + translate = "translate(-50%, -50%)" + break; + case ScaleMode.AdjustParentHeight: + this.scaleRatio = offsetWidth / width + translate = "translate(-50%, 0)" + posStyles = { top: 0 } + break; } - this.screen.style.transform = `scale(${ this.scaleRatio }) translate(-50%, -50%)`; - this.screen.style.width = width + 'px'; - this.screen.style.height = height + 'px'; - this.iframe.style.width = width + 'px'; - this.iframe.style.height = height + 'px'; - this.boundingRect = this.overlay.getBoundingClientRect(); - } - - scaleFullPage() { - if (!this.parentElement || !this.document) return; - - const { width: boxWidth } = this.parentElement.getBoundingClientRect(); - const { height, width } = this.document.body.getBoundingClientRect(); - this.overlay.remove() - - this.scaleRatio = boxWidth/width; if (this.scaleRatio > 1) { this.scaleRatio = 1; } else { this.scaleRatio = Math.round(this.scaleRatio * 1e3) / 1e3; } - Object.assign(this.screen.style, { - top: '0', - left: '50%', + if (this.scaleMode === ScaleMode.AdjustParentHeight) { + this.parentElement.style.height = this.scaleRatio * height + 'px' + } + + Object.assign(this.screen.style, posStyles, { height: height + 'px', width: width + 'px', - transform: `scale(${this.scaleRatio}) translate(-50%, 0)`, + transform: `scale(${this.scaleRatio}) ${translate}`, }) Object.assign(this.iframe.style, { width: width + 'px', height: height + 'px', }) - return height + this.boundingRect = this.overlay.getBoundingClientRect(); } } diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts index fb4386676..2c7f33f45 100644 --- a/frontend/app/player/web/WebPlayer.ts +++ b/frontend/app/player/web/WebPlayer.ts @@ -6,7 +6,8 @@ import Player from '../player/Player' import MessageManager from './MessageManager' import InspectorController from './addons/InspectorController' import TargetMarker from './addons/TargetMarker' -import Screen from './Screen/Screen' +import Screen, { ScaleMode } from './Screen/Screen' + // export type State = typeof WebPlayer.INITIAL_STATE @@ -17,7 +18,6 @@ export default class WebPlayer extends Player { ...MessageManager.INITIAL_STATE, inspectorMode: false, - portHeight: 0, } private readonly inspectorController: InspectorController @@ -26,7 +26,7 @@ export default class WebPlayer extends Player { private targetMarker: TargetMarker - constructor(protected wpState: Store, session: any, live: boolean) { + constructor(protected wpState: Store, session: any, live: boolean, isClickMap = false) { let initialLists = live ? {} : { event: session.events || [], stack: session.stackEvents || [], @@ -40,7 +40,7 @@ export default class WebPlayer extends Player { ) || [], } - const screen = new Screen(session.isMobile) + const screen = new Screen(session.isMobile, isClickMap ? ScaleMode.AdjustParentHeight : ScaleMode.Embed) const messageManager = new MessageManager(session, wpState, screen, initialLists) super(wpState, messageManager) this.screen = screen @@ -71,20 +71,11 @@ export default class WebPlayer extends Player { } scale = () => { - console.log('called scale') const { width, height } = this.wpState.get() this.screen.scale({ width, height }) this.inspectorController.scale({ width, height }) - // this.updateMarketTargets() ?? - } - - scaleFullPage = () => { - window.removeEventListener('resize', this.scale) - window.addEventListener('resize', this.scaleFullPage) - - const portHeight = this.screen.scaleFullPage() - return this.wpState.update({ portHeight }) + this.targetMarker.updateMarkedTargets() } // Inspector & marker @@ -119,16 +110,11 @@ export default class WebPlayer extends Player { } showClickmap = (...args: Parameters) => { - this.freeze() - if (this.wpState.get().portHeight !== 0) { + this.screen.overlay.remove() // hack. TODO: 1.split Screen functionalities (overlay, mounter) 2. separate ClickMapPlayer class that does not create overlay + this.targetMarker.injectTargets(...args) + this.freeze().then(() => { this.targetMarker.injectTargets(...args) - } else { - setTimeout(() => this.showClickmap(...args), 500) - } - } - - setMarkerClick = (...args: Parameters) => { - this.targetMarker.setOnMarkerClick(...args) + }) } toggleUserName = (name?: string) => { diff --git a/frontend/app/player/web/addons/TargetMarker.ts b/frontend/app/player/web/addons/TargetMarker.ts index bac17db8a..1c8236d7d 100644 --- a/frontend/app/player/web/addons/TargetMarker.ts +++ b/frontend/app/player/web/addons/TargetMarker.ts @@ -39,7 +39,6 @@ export default class TargetMarker { private clickMapOverlay: HTMLDivElement | null = null private clickContainers: HTMLDivElement[] = [] private smallClicks: HTMLDivElement[] = [] - private onMarkerClick: (selector: string, innerText: string) => void static INITIAL_STATE: State = { markedTargets: null, activeTargetIndex: 0 @@ -50,7 +49,7 @@ export default class TargetMarker { private readonly store: Store, ) {} - updateMarketTargets() { + updateMarkedTargets() { const { markedTargets } = this.store.get() if (markedTargets) { this.store.update({ @@ -137,7 +136,10 @@ export default class TargetMarker { } - injectTargets(selections: { selector: string, count: number, clickRage?: boolean }[] | null) { + injectTargets( + selections: { selector: string, count: number, clickRage?: boolean }[] | null, + onMarkerClick?: (selector: string, innerText: string) => void, + ) { if (selections) { const totalCount = selections.reduce((a, b) => { return a + b.count @@ -183,7 +185,7 @@ export default class TargetMarker { border.onclick = (e) => { e.stopPropagation() const innerText = el.innerText.length > 25 ? `${el.innerText.slice(0, 20)}...` : el.innerText - this.onMarkerClick?.(s.selector, innerText) + onMarkerClick?.(s.selector, innerText) this.clickContainers.forEach(container => { if (container.id === containerId) { container.style.visibility = "visible" @@ -202,7 +204,7 @@ export default class TargetMarker { overlay.onclick = (e) => { e.stopPropagation() - this.onMarkerClick('', '') + onMarkerClick?.('', '') this.clickContainers.forEach(container => { container.style.visibility = "hidden" }) @@ -228,8 +230,4 @@ export default class TargetMarker { } } - setOnMarkerClick(cb: (selector: string) => void) { - this.onMarkerClick = cb - } - }