feat (frontend-player): heatmaps player markup + overlay refactor
This commit is contained in:
parent
0e3fa5fd8e
commit
ebcf5ad655
22 changed files with 297 additions and 199 deletions
|
|
@ -1 +0,0 @@
|
|||
export { default } from './AutoplayTimer'
|
||||
|
|
@ -73,7 +73,7 @@ function getStorageName(type) {
|
|||
skip: state.skip,
|
||||
skipToIssue: state.skipToIssue,
|
||||
speed: state.speed,
|
||||
disabled: state.cssLoading || state.messagesLoading || state.inspectorMode,
|
||||
disabled: state.cssLoading || state.messagesLoading || state.inspectorMode || state.markedTargets,
|
||||
inspectorMode: state.inspectorMode,
|
||||
fullscreenDisabled: state.messagesLoading,
|
||||
logCount: state.logListNow.length,
|
||||
|
|
|
|||
77
frontend/app/components/Session_/Player/Overlay.tsx
Normal file
77
frontend/app/components/Session_/Player/Overlay.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import { connectPlayer, markTargets } from 'Player';
|
||||
import { getStatusText } from 'Player/MessageDistributor/managers/AssistManager';
|
||||
import type { MarkedTarget } from 'Player/MessageDistributor/StatedScreen/StatedScreen';
|
||||
|
||||
import AutoplayTimer from './Overlay/AutoplayTimer';
|
||||
import PlayIconLayer from './Overlay/PlayIconLayer';
|
||||
import LiveStatusText from './Overlay/LiveStatusText';
|
||||
import Loader from './Overlay/Loader';
|
||||
import ElementsMarker from './Overlay/ElementsMarker';
|
||||
|
||||
interface Props {
|
||||
playing: boolean,
|
||||
completed: boolean,
|
||||
inspectorMode: boolean,
|
||||
messagesLoading: boolean,
|
||||
loading: boolean,
|
||||
live: boolean,
|
||||
liveStatusText: string,
|
||||
autoplay: boolean,
|
||||
markedTargets: MarkedTarget[] | null,
|
||||
|
||||
nextId: string,
|
||||
togglePlay: () => void,
|
||||
}
|
||||
|
||||
function Overlay({
|
||||
playing,
|
||||
completed,
|
||||
inspectorMode,
|
||||
messagesLoading,
|
||||
loading,
|
||||
live,
|
||||
liveStatusText,
|
||||
autoplay,
|
||||
markedTargets,
|
||||
|
||||
nextId,
|
||||
togglePlay,
|
||||
}: Props) {
|
||||
|
||||
useEffect(() =>{
|
||||
setTimeout(() => markTargets([{ selector: 'div', count:6}]), 5000)
|
||||
setTimeout(() => markTargets(null), 8000)
|
||||
},[])
|
||||
|
||||
const showAutoplayTimer = !live && completed && autoplay && nextId
|
||||
const showPlayIconLayer = !live && !markedTargets && !inspectorMode && !loading && !showAutoplayTimer;
|
||||
const showLiveStatusText = live && liveStatusText && !loading;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ showAutoplayTimer && <AutoplayTimer /> }
|
||||
{ showLiveStatusText &&
|
||||
<LiveStatusText text={liveStatusText} />
|
||||
}
|
||||
{ messagesLoading && <Loader/> }
|
||||
{ showPlayIconLayer &&
|
||||
<PlayIconLayer playing={playing} togglePlay={togglePlay} />
|
||||
}
|
||||
{ markedTargets && <ElementsMarker targets={ markedTargets } />
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default connectPlayer(state => ({
|
||||
playing: state.playing,
|
||||
messagesLoading: state.messagesLoading,
|
||||
loading: state.messagesLoading || state.cssLoading,
|
||||
completed: state.completed,
|
||||
autoplay: state.autoplay,
|
||||
live: state.live,
|
||||
liveStatusText: getStatusText(state.peerConnectionStatus),
|
||||
markedTargets: state.markedTargets
|
||||
}))(Overlay);
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.overlayBg {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import cn from 'classnames';
|
||||
import { connect } from 'react-redux'
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { Button, Link } from 'UI'
|
||||
import { session as sessionRoute, withSiteId } from 'App/routes'
|
||||
import stl from './AutoplayTimer.css'
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import stl from './AutoplayTimer.css';
|
||||
import clsOv from './overlay.css';
|
||||
|
||||
function AutoplayTimer({ nextId, siteId, history }) {
|
||||
let timer
|
||||
|
|
@ -33,7 +35,7 @@ function AutoplayTimer({ nextId, siteId, history }) {
|
|||
return ''
|
||||
|
||||
return (
|
||||
<div className={stl.overlay}>
|
||||
<div className={ cn(clsOv.overlay, stl.overlayBg) } >
|
||||
<div className="border p-6 shadow-lg bg-white rounded">
|
||||
<div className="py-4">Next recording will be played in {counter}s</div>
|
||||
<div className="flex items-center">
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
import Marker from './ElementsMarker/Marker';
|
||||
|
||||
export default function ElementsMarker({ targets }) {
|
||||
return targets.map(t => <Marker target={t} />)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.marker {
|
||||
position: absolute;
|
||||
border: 2px dashed;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { Popup } from 'UI';
|
||||
import type { MarkedTarget } from 'Player/MessageDistributor/StatedScreen/StatedScreen';
|
||||
|
||||
import stl from './Marker.css';
|
||||
|
||||
interface Props {
|
||||
target: MarkedTarget;
|
||||
}
|
||||
|
||||
export default function Marker({ target }: Props) {
|
||||
const style = {
|
||||
top: `${ target.boundingRect.top }px`,
|
||||
left: `${ target.boundingRect.left }px`,
|
||||
width: `${ target.boundingRect.width }px`,
|
||||
height: `${ target.boundingRect.height }px`,
|
||||
}
|
||||
|
||||
return <div className={ stl.marker } style={ style } />
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.text {
|
||||
color: $gray-light;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import stl from './LiveStatusText.css';
|
||||
import ovStl from './overlay.css';
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export default function LiveStatusText({ text }: Props) {
|
||||
return <div className={ovStl.overlay}><span className={stl.text}>{text}</span></div>
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Loader } from 'UI';
|
||||
import ovStl from './overlay.css';
|
||||
|
||||
export default function OverlayLoader() {
|
||||
return <div className={ovStl.overlay}><Loader loading /></div>
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
.iconWrapper {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: all .2s; /* Animation */
|
||||
}
|
||||
|
||||
.zoomIcon {
|
||||
opacity: 1;
|
||||
transform: scale(1.8);
|
||||
transition: all .8s;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Icon } from 'UI';
|
||||
|
||||
import cls from './PlayIconLayer.css';
|
||||
import clsOv from './overlay.css';
|
||||
|
||||
interface Props {
|
||||
togglePlay: () => void,
|
||||
playing: boolean,
|
||||
}
|
||||
|
||||
export default function PlayIconLayer({ playing, togglePlay }: Props) {
|
||||
const [ showPlayOverlayIcon, setShowPlayOverlayIcon ] = useState(false);
|
||||
const togglePlayAnimated = useCallback(() => {
|
||||
setShowPlayOverlayIcon(true);
|
||||
togglePlay();
|
||||
setTimeout(
|
||||
() => setShowPlayOverlayIcon(false),
|
||||
800,
|
||||
);
|
||||
}, []);
|
||||
return (
|
||||
<div className={ clsOv.overlay } onClick={ togglePlayAnimated }>
|
||||
<div
|
||||
className={ cn(cls.iconWrapper, {
|
||||
[ cls.zoomIcon ]: showPlayOverlayIcon
|
||||
}) }
|
||||
>
|
||||
<Icon
|
||||
name={ playing ? "play" : "pause" }
|
||||
color="gray-medium"
|
||||
size={30}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
.wrapper {
|
||||
width: 30%;
|
||||
height: 30%;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -13,5 +8,4 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,29 +2,17 @@ import { connect } from 'react-redux';
|
|||
import { findDOMNode } from 'react-dom';
|
||||
import cn from 'classnames';
|
||||
import { Loader, IconButton, EscapeButton } from 'UI';
|
||||
import { hide as hideTargetDefiner, toggleInspectorMode } from 'Duck/components/targetDefiner';
|
||||
import { hide as hideTargetDefiner } from 'Duck/components/targetDefiner';
|
||||
import { fullscreenOff } from 'Duck/components/player';
|
||||
import withOverlay from 'Components/hocs/withOverlay';
|
||||
import { attach as attachPlayer, Controls as PlayerControls, connectPlayer } from 'Player';
|
||||
import Controls from './Controls';
|
||||
import Overlay from './Overlay';
|
||||
import stl from './player.css';
|
||||
import AutoplayTimer from '../AutoplayTimer';
|
||||
import EventsToggleButton from '../../Session/EventsToggleButton';
|
||||
import { getStatusText } from 'Player/MessageDistributor/managers/AssistManager';
|
||||
|
||||
|
||||
const ScreenWrapper = withOverlay()(React.memo(() => <div className={ stl.screenWrapper } />));
|
||||
|
||||
@connectPlayer(state => ({
|
||||
playing: state.playing,
|
||||
loading: state.messagesLoading,
|
||||
disconnected: state.disconnected,
|
||||
disabled: state.cssLoading || state.messagesLoading || state.inspectorMode,
|
||||
removeOverlay: !state.messagesLoading && state.inspectorMode || state.live,
|
||||
completed: state.completed,
|
||||
autoplay: state.autoplay,
|
||||
live: state.live,
|
||||
liveStatusText: getStatusText(state.peerConnectionStatus),
|
||||
}))
|
||||
@connect(state => ({
|
||||
//session: state.getIn([ 'sessions', 'current' ]),
|
||||
|
|
@ -32,15 +20,9 @@ const ScreenWrapper = withOverlay()(React.memo(() => <div className={ stl.screen
|
|||
nextId: state.getIn([ 'sessions', 'nextId' ]),
|
||||
}), {
|
||||
hideTargetDefiner,
|
||||
toggleInspectorMode: () => toggleInspectorMode(false),
|
||||
fullscreenOff,
|
||||
})
|
||||
export default class Player extends React.PureComponent {
|
||||
state = {
|
||||
showPlayOverlayIcon: false,
|
||||
|
||||
startedToPlayAt: Date.now(),
|
||||
};
|
||||
screenWrapper = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -48,69 +30,14 @@ export default class Player extends React.PureComponent {
|
|||
attachPlayer(parentElement);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.targetSelector !== this.props.targetSelector) {
|
||||
PlayerControls.mark(this.props.targetSelector);
|
||||
}
|
||||
if (prevProps.playing !== this.props.playing) {
|
||||
if (this.props.playing) {
|
||||
this.setState({ startedToPlayAt: Date.now() });
|
||||
} else {
|
||||
this.updateWatchingTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.playing) {
|
||||
this.updateWatchingTime();
|
||||
}
|
||||
}
|
||||
|
||||
updateWatchingTime() {
|
||||
const diff = Date.now() - this.state.startedToPlayAt;
|
||||
}
|
||||
|
||||
|
||||
// onTargetClick = (targetPath) => {
|
||||
// const { targetCustomList, location } = this.props;
|
||||
// const targetCustomFromList = targetCustomList !== this.props.targetSelector
|
||||
// .find(({ path }) => path === targetPath);
|
||||
// const target = targetCustomFromList
|
||||
// ? targetCustomFromList.set('location', location)
|
||||
// : { path: targetPath, isCustom: true, location };
|
||||
// this.props.showTargetDefiner(target);
|
||||
// }
|
||||
|
||||
togglePlay = () => {
|
||||
this.setState({ showPlayOverlayIcon: true });
|
||||
PlayerControls.togglePlay();
|
||||
|
||||
setTimeout(
|
||||
() => this.setState({ showPlayOverlayIcon: false }),
|
||||
800,
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
showPlayOverlayIcon,
|
||||
} = this.state;
|
||||
const {
|
||||
className,
|
||||
playing,
|
||||
disabled,
|
||||
removeOverlay,
|
||||
bottomBlockIsActive,
|
||||
loading,
|
||||
disconnected,
|
||||
fullscreen,
|
||||
fullscreenOff,
|
||||
completed,
|
||||
autoplay,
|
||||
nextId,
|
||||
live,
|
||||
liveStatusText,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
|
@ -120,40 +47,12 @@ export default class Player extends React.PureComponent {
|
|||
>
|
||||
{ fullscreen &&
|
||||
<EscapeButton onClose={ fullscreenOff } />
|
||||
// <IconButton
|
||||
// size="18"
|
||||
// className="ml-auto mb-5"
|
||||
// style={{ marginTop: '-5px' }}
|
||||
// onClick={ fullscreenOff }
|
||||
// size="small"
|
||||
// icon="close"
|
||||
// label="Esc"
|
||||
// />
|
||||
}
|
||||
{!live && !fullscreen && <EventsToggleButton /> }
|
||||
<div className="relative flex-1">
|
||||
{ (!removeOverlay || live && liveStatusText) &&
|
||||
<div
|
||||
className={ stl.overlay }
|
||||
onClick={ disabled ? null : this.togglePlay }
|
||||
>
|
||||
{ live && liveStatusText
|
||||
? <span className={stl.liveStatusText}>{liveStatusText}</span>
|
||||
: <Loader loading={ loading } />
|
||||
}
|
||||
{ !live &&
|
||||
<div
|
||||
className={ cn(stl.iconWrapper, {
|
||||
[ stl.zoomIcon ]: showPlayOverlayIcon
|
||||
}) }
|
||||
>
|
||||
<div className={ playing ? stl.playIcon : stl.pauseIcon } />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{ completed && autoplay && nextId && <AutoplayTimer /> }
|
||||
<ScreenWrapper
|
||||
<div className="relative flex-1 overflow-hidden">
|
||||
<Overlay nextId={nextId} togglePlay={PlayerControls.togglePlay} />
|
||||
<div
|
||||
className={ stl.screenWrapper }
|
||||
ref={ this.screenWrapper }
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
@import 'icons.css';
|
||||
|
||||
.playerBody {
|
||||
background: $white;
|
||||
/* border-radius: 3px; */
|
||||
|
|
@ -25,61 +23,8 @@
|
|||
font-weight: 200;
|
||||
color: $gray-medium;
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
/* &[data-protect] {
|
||||
pointer-events: none;
|
||||
background: $white;
|
||||
opacity: 0.3;
|
||||
}
|
||||
*/
|
||||
& .iconWrapper {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: all .2s; /* Animation */
|
||||
}
|
||||
|
||||
& .zoomIcon {
|
||||
opacity: 1;
|
||||
transform: scale(1.8);
|
||||
transition: all .8s;
|
||||
}
|
||||
|
||||
& .playIcon {
|
||||
@mixin icon play, $gray-medium, 30px;
|
||||
}
|
||||
|
||||
& .pauseIcon {
|
||||
@mixin icon pause, $gray-medium, 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.playerView {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.inspectorMode {
|
||||
z-index: 99991 !important;
|
||||
}
|
||||
|
||||
.liveStatusText {
|
||||
color: $gray-light;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import SVG from 'UI/SVG';
|
||||
import styles from './icon.css';
|
||||
|
|
|
|||
|
|
@ -306,9 +306,7 @@ export default class MessageDistributor extends StatedScreen {
|
|||
this.pagesManager.moveReady(t).then(() => {
|
||||
|
||||
const lastScroll = this.scrollManager.moveToLast(t, index);
|
||||
// @ts-ignore ??can't see double inheritance
|
||||
if (!!lastScroll && this.window) {
|
||||
// @ts-ignore
|
||||
this.window.scrollTo(lastScroll.x, lastScroll.y);
|
||||
}
|
||||
// Moving mouse and setting :hover classes on ready view
|
||||
|
|
@ -479,7 +477,6 @@ export default class MessageDistributor extends StatedScreen {
|
|||
|
||||
// TODO: clean managers?
|
||||
clean() {
|
||||
// @ts-ignore
|
||||
super.clean();
|
||||
//if (this._socket) this._socket.close();
|
||||
update(INITIAL_STATE);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const INITIAL_STATE: State = {
|
|||
export default abstract class BaseScreen {
|
||||
public readonly overlay: HTMLDivElement;
|
||||
private readonly iframe: HTMLIFrameElement;
|
||||
private readonly _screen: HTMLDivElement;
|
||||
protected readonly screen: HTMLDivElement;
|
||||
protected parentElement: HTMLElement | null = null;
|
||||
constructor() {
|
||||
const iframe = document.createElement('iframe');
|
||||
|
|
@ -44,7 +44,7 @@ export default abstract class BaseScreen {
|
|||
screen.className = styles.screen;
|
||||
screen.appendChild(iframe);
|
||||
screen.appendChild(overlay);
|
||||
this._screen = screen;
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
attach(parentElement: HTMLElement) {
|
||||
|
|
@ -52,7 +52,7 @@ export default abstract class BaseScreen {
|
|||
throw new Error("BaseScreen: Trying to attach an attached screen.");
|
||||
}
|
||||
|
||||
parentElement.appendChild(this._screen);
|
||||
parentElement.appendChild(this.screen);
|
||||
|
||||
this.parentElement = parentElement;
|
||||
// parentElement.onresize = this.scale;
|
||||
|
|
@ -115,29 +115,37 @@ export default abstract class BaseScreen {
|
|||
return this.getElementsFromInternalPoint(this.getInternalCoordinates(point));
|
||||
}
|
||||
|
||||
getElementBySelector(selector: string): Element | null {
|
||||
return this.document?.querySelector(selector) || null;
|
||||
}
|
||||
|
||||
display(flag: boolean = true) {
|
||||
this._screen.style.display = flag ? '' : 'none';
|
||||
this.screen.style.display = flag ? '' : 'none';
|
||||
}
|
||||
|
||||
displayFrame(flag: boolean = true) {
|
||||
this.iframe.style.display = flag ? '' : 'none';
|
||||
}
|
||||
|
||||
private s: number = 1;
|
||||
getScale() {
|
||||
return this.s;
|
||||
}
|
||||
|
||||
_scale() {
|
||||
if (!this.parentElement) return;
|
||||
let s = 1;
|
||||
const { height, width } = getState();
|
||||
const { offsetWidth, offsetHeight } = this.parentElement;
|
||||
|
||||
s = Math.min(offsetWidth / width, offsetHeight / height);
|
||||
if (s > 1) {
|
||||
s = 1;
|
||||
this.s = Math.min(offsetWidth / width, offsetHeight / height);
|
||||
if (this.s > 1) {
|
||||
this.s = 1;
|
||||
} else {
|
||||
s = Math.round(s * 1e3) / 1e3;
|
||||
this.s = Math.round(this.s * 1e3) / 1e3;
|
||||
}
|
||||
this._screen.style.transform = `scale(${ s }) translate(-50%, -50%)`;
|
||||
this._screen.style.width = width + 'px';
|
||||
this._screen.style.height = height + 'px';
|
||||
this.screen.style.transform = `scale(${ this.s }) 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';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,29 @@
|
|||
import Screen, { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './Screen';
|
||||
import Screen, { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './Screen/Screen';
|
||||
import { update, getState } from '../../store';
|
||||
|
||||
|
||||
//export interface targetPosition
|
||||
|
||||
interface BoundingRect {
|
||||
top: number,
|
||||
left: number,
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
export interface MarkedTarget {
|
||||
boundingRect: BoundingRect,
|
||||
el: Element,
|
||||
selector: string,
|
||||
count: number,
|
||||
}
|
||||
|
||||
export interface State extends SuperState {
|
||||
messagesLoading: boolean,
|
||||
cssLoading: boolean,
|
||||
disconnected: boolean,
|
||||
userPageLoading: boolean,
|
||||
markedTargets: MarkedTarget[] | null
|
||||
}
|
||||
|
||||
export const INITIAL_STATE: State = {
|
||||
|
|
@ -15,40 +32,78 @@ export const INITIAL_STATE: State = {
|
|||
cssLoading: false,
|
||||
disconnected: false,
|
||||
userPageLoading: false,
|
||||
}
|
||||
markedTargets: [],
|
||||
};
|
||||
|
||||
export default class StatedScreen extends Screen {
|
||||
constructor() { super(); }
|
||||
|
||||
setMessagesLoading(messagesLoading: boolean) {
|
||||
// @ts-ignore
|
||||
this.display(!messagesLoading);
|
||||
update({ messagesLoading });
|
||||
}
|
||||
|
||||
setCSSLoading(cssLoading: boolean) {
|
||||
// @ts-ignore
|
||||
|
||||
this.displayFrame(!cssLoading);
|
||||
update({ cssLoading });
|
||||
}
|
||||
|
||||
setDisconnected(disconnected: boolean) {
|
||||
if (!getState().live) return; //?
|
||||
// @ts-ignore
|
||||
this.display(!disconnected);
|
||||
update({ disconnected });
|
||||
}
|
||||
|
||||
setUserPageLoading(userPageLoading: boolean) {
|
||||
// @ts-ignore
|
||||
this.display(!userPageLoading);
|
||||
update({ userPageLoading });
|
||||
}
|
||||
|
||||
setSize({ height, width }: { height: number, width: number }) {
|
||||
update({ width, height });
|
||||
// @ts-ignore
|
||||
this.scale();
|
||||
|
||||
const { markedTargets } = getState();
|
||||
if (markedTargets) {
|
||||
update({
|
||||
markedTargets: markedTargets.map(mt => ({
|
||||
...mt,
|
||||
boundingRect: this.calculateRelativeBoundingRect(mt.el),
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private calculateRelativeBoundingRect(el: Element): BoundingRect {
|
||||
if (!this.parentElement) return {top:0, left:0, width:0,height:0} //TODO
|
||||
const { top, left, width, height } = el.getBoundingClientRect();
|
||||
const s = this.getScale();
|
||||
const scrinRect = this.screen.getBoundingClientRect();
|
||||
const parentRect = this.parentElement.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
top: top*s + scrinRect.top - parentRect.top,
|
||||
left: left*s + scrinRect.left - parentRect.left,
|
||||
width: width*s,
|
||||
height: height*s,
|
||||
}
|
||||
}
|
||||
|
||||
setMarkedTargets(selections: { selector: string, count: number }[] | null) {
|
||||
if (selections) {
|
||||
const targets: MarkedTarget[] = [];
|
||||
selections.forEach(s => {
|
||||
const el = this.getElementBySelector(s.selector);
|
||||
if (!el) return;
|
||||
targets.push({
|
||||
...s,
|
||||
el,
|
||||
boundingRect: this.calculateRelativeBoundingRect(el),
|
||||
})
|
||||
});
|
||||
update({ markedTargets: targets });
|
||||
} else {
|
||||
update({ markedTargets: null });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { goTo as listsGoTo } from './lists';
|
||||
import { update, getState } from './store';
|
||||
import MessageDistributor, { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './MessageDistributor';
|
||||
import MessageDistributor, { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './MessageDistributor/MessageDistributor';
|
||||
|
||||
const fps = 60;
|
||||
const performance = window.performance || { now: Date.now.bind(Date) };
|
||||
|
|
@ -35,7 +35,7 @@ const initialSkipToIssue = !!localStorage.getItem(SKIP_TO_ISSUE_STORAGE_KEY);
|
|||
const initialAutoplay = !!localStorage.getItem(AUTOPLAY_STORAGE_KEY);
|
||||
const initialShowEvents = !!localStorage.getItem(SHOW_EVENTS_STORAGE_KEY);
|
||||
|
||||
export const INITIAL_STATE: SuperState = {
|
||||
export const INITIAL_STATE = {
|
||||
...SUPER_INITIAL_STATE,
|
||||
time: 0,
|
||||
playing: false,
|
||||
|
|
@ -191,6 +191,11 @@ export default class Player extends MessageDistributor {
|
|||
update({ inspectorMode: false });
|
||||
}
|
||||
}
|
||||
|
||||
markTargets(targets: { selector: string, count: number }[] | null) {
|
||||
this.pause();
|
||||
this.setMarkedTargets(targets);
|
||||
}
|
||||
|
||||
toggleSkipToIssue() {
|
||||
const skipToIssue = !getState().skipToIssue;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export const markElement = initCheck((...args) => instance.marker && instance.ma
|
|||
export const scale = initCheck(() => instance.scale());
|
||||
export const toggleInspectorMode = initCheck((...args) => instance.toggleInspectorMode(...args));
|
||||
export const callPeer = initCheck((...args) => instance.assistManager.call(...args))
|
||||
export const markTargets = initCheck((...args) => instance.markTargets(...args))
|
||||
|
||||
export const Controls = {
|
||||
jump,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue