feature(ui) - events toggle and duration counter
This commit is contained in:
parent
5c0f321b00
commit
eef908648b
13 changed files with 103 additions and 21 deletions
|
|
@ -21,8 +21,11 @@ function AssistActions({ toggleChatWindow, userId, calling }: Props) {
|
|||
const [ localStream, setLocalStream ] = useState<MediaStream | null>(null);
|
||||
const [ endCall, setEndCall ] = useState<()=>void>(()=>{});
|
||||
|
||||
function onClose(stream) {
|
||||
console.log("Closed")
|
||||
useEffect(() => {
|
||||
return endCall
|
||||
}, [])
|
||||
|
||||
function onClose(stream) {
|
||||
stream.getTracks().forEach(t=>t.stop());
|
||||
}
|
||||
function onReject() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
.wrapper {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-top-left-radius: 20px;
|
||||
border-bottom-left-radius: 20px;
|
||||
padding: 5px;
|
||||
box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react'
|
||||
import { Icon, Popup } from 'UI'
|
||||
import { connectPlayer, toggleEvents } from 'Player';
|
||||
import cn from 'classnames'
|
||||
import stl from './EventsToggleButton.css'
|
||||
|
||||
function EventsToggleButton({ showEvents, toggleEvents }) {
|
||||
return (
|
||||
<Popup
|
||||
trigger={
|
||||
<button
|
||||
className={cn("absolute right-0 z-50", stl.wrapper)}
|
||||
onClick={toggleEvents}
|
||||
>
|
||||
<Icon
|
||||
name={ showEvents ? 'chevron-double-right' : 'chevron-double-left' }
|
||||
size="12"
|
||||
/>
|
||||
</button>
|
||||
}
|
||||
content={ showEvents ? 'Hide Events' : 'Show Events' }
|
||||
size="tiny"
|
||||
inverted
|
||||
position="bottom right"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default connectPlayer(state => ({
|
||||
showEvents: !state.showEvents
|
||||
}), { toggleEvents })(EventsToggleButton)
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './EventsToggleButton'
|
||||
|
|
@ -8,13 +8,15 @@ import {
|
|||
init as initPlayer,
|
||||
clean as cleanPlayer,
|
||||
} from 'Player';
|
||||
import { Controls as PlayerControls } from 'Player';
|
||||
import { Controls as PlayerControls, toggleEvents } from 'Player';
|
||||
import cn from 'classnames'
|
||||
|
||||
|
||||
import PlayerBlockHeader from '../Session_/PlayerBlockHeader';
|
||||
import EventsBlock from '../Session_/EventsBlock';
|
||||
import PlayerBlock from '../Session_/PlayerBlock';
|
||||
import styles from '../Session_/session.css';
|
||||
import EventsToggleButton from './EventsToggleButton';
|
||||
|
||||
|
||||
|
||||
|
|
@ -28,6 +30,19 @@ const InitLoader = connectPlayer(state => ({
|
|||
loading: !state.initialized
|
||||
}))(Loader);
|
||||
|
||||
const PlayerContentConnected = connectPlayer(state => ({
|
||||
showEvents: !state.showEvents
|
||||
}), { toggleEvents })(PlayerContent);
|
||||
|
||||
|
||||
function PlayerContent({ live, fullscreen, showEvents, toggleEvents }) {
|
||||
return (
|
||||
<div className={ cn(styles.session, 'relative') } data-fullscreen={fullscreen}>
|
||||
<PlayerBlock />
|
||||
{ showEvents && !live && !fullscreen && <EventsBlockConnected player={PlayerControls}/> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function WebPlayer ({ session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt }) {
|
||||
useEffect(() => {
|
||||
|
|
@ -44,10 +59,7 @@ function WebPlayer ({ session, toggleFullscreen, closeBottomBlock, live, fullscr
|
|||
<PlayerProvider>
|
||||
<InitLoader className="flex-1">
|
||||
<PlayerBlockHeader fullscreen={fullscreen}/>
|
||||
<div className={ styles.session } data-fullscreen={fullscreen}>
|
||||
<PlayerBlock />
|
||||
{ !live && !fullscreen && <EventsBlockConnected player={PlayerControls}/> }
|
||||
</div>
|
||||
<PlayerContentConnected fullscreen={fullscreen} live={live} />
|
||||
</InitLoader>
|
||||
</PlayerProvider>
|
||||
);
|
||||
|
|
@ -61,5 +73,5 @@ export default connect(state => ({
|
|||
}), {
|
||||
toggleFullscreen,
|
||||
closeBottomBlock,
|
||||
})(WebPlayer)
|
||||
})(WebPlayer)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { attach as attachPlayer, Controls as PlayerControls, connectPlayer } fro
|
|||
import Controls from './Controls';
|
||||
import stl from './player.css';
|
||||
import AutoplayTimer from '../AutoplayTimer';
|
||||
import EventsToggleButton from '../../Session/EventsToggleButton';
|
||||
|
||||
|
||||
const ScreenWrapper = withOverlay()(React.memo(() => <div className={ stl.screenWrapper } />));
|
||||
|
|
@ -20,7 +21,8 @@ const ScreenWrapper = withOverlay()(React.memo(() => <div className={ stl.screen
|
|||
disabled: state.cssLoading || state.messagesLoading || state.inspectorMode,
|
||||
removeOverlay: !state.messagesLoading && state.inspectorMode || state.live,
|
||||
completed: state.completed,
|
||||
autoplay: state.autoplay
|
||||
autoplay: state.autoplay,
|
||||
live: state.live
|
||||
}))
|
||||
@connect(state => ({
|
||||
//session: state.getIn([ 'sessions', 'current' ]),
|
||||
|
|
@ -105,6 +107,7 @@ export default class Player extends React.PureComponent {
|
|||
completed,
|
||||
autoplay,
|
||||
nextId,
|
||||
live,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
|
@ -124,6 +127,7 @@ export default class Player extends React.PureComponent {
|
|||
// label="Esc"
|
||||
// />
|
||||
}
|
||||
{!live && !fullscreen && <EventsToggleButton /> }
|
||||
<div className="relative flex-1">
|
||||
{ !removeOverlay &&
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { Duration } from 'luxon';
|
||||
import { durationFormatted, formatTimeOrDate } from 'App/date';
|
||||
|
||||
interface Props {
|
||||
startTime: any
|
||||
}
|
||||
|
||||
function Counter({ startTime }: Props) {
|
||||
const [count, setCount] = useState(0)
|
||||
let intervalId;
|
||||
const [duration, setDuration] = useState(new Date().getTime() - startTime)
|
||||
|
||||
useEffect(() => {
|
||||
setInterval(function() {
|
||||
setCount(count + 1000)
|
||||
}, 1000)
|
||||
}, [])
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(() => {
|
||||
setDuration(duration + 1000)
|
||||
}, 1000)
|
||||
}
|
||||
return () => clearInterval(intervalId)
|
||||
}, [duration])
|
||||
|
||||
return (
|
||||
<div className="mx-2">
|
||||
{startTime && Duration.fromMillis(startTime + count).toFormat('m:ss')}
|
||||
{startTime && Duration.fromMillis(duration).toFormat('m:ss')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ export default class SessionItem extends React.PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center px-4" style={{ width: '150px'}}>
|
||||
<div className="text-xl">{ formattedDuration }</div>
|
||||
<div className="text-xl">
|
||||
{ live ? <Counter startTime={startedAt} /> : formattedDuration }
|
||||
</div>
|
||||
<Label label="Duration" />
|
||||
</div>
|
||||
|
||||
|
|
@ -109,8 +111,7 @@ export default class SessionItem extends React.PureComponent {
|
|||
<Label label="Errors" color={errorsCount > 0 ? '' : 'color-gray-medium'} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ live && <Counter startTime={startedAt} /> }
|
||||
|
||||
{ live && <LiveTag isLive={true} /> }
|
||||
|
||||
<div className={ cn(stl.iconDetails, 'px-4') }>
|
||||
|
|
|
|||
|
|
@ -27,11 +27,13 @@ const SPEED_STORAGE_KEY = "__$player-speed$__";
|
|||
const SKIP_STORAGE_KEY = "__$player-skip$__";
|
||||
const SKIP_TO_ISSUE_STORAGE_KEY = "__$player-skip-to-issue$__";
|
||||
const AUTOPLAY_STORAGE_KEY = "__$player-autoplay$__";
|
||||
const SHOW_EVENTS_STORAGE_KEY = "__$player-show-events$__";
|
||||
const storedSpeed: number = parseInt(localStorage.getItem(SPEED_STORAGE_KEY) || "") ;
|
||||
const initialSpeed = [1,2,4,8,16].includes(storedSpeed) ? storedSpeed : 1;
|
||||
const initialSkip = !!localStorage.getItem(SKIP_STORAGE_KEY);
|
||||
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 = {
|
||||
...SUPER_INITIAL_STATE,
|
||||
|
|
@ -50,6 +52,7 @@ export const INITIAL_NON_RESETABLE_STATE = {
|
|||
skipToIssue: initialSkipToIssue,
|
||||
autoplay: initialAutoplay,
|
||||
speed: initialSpeed,
|
||||
showEvents: initialShowEvents
|
||||
}
|
||||
|
||||
export default class Player extends MessageDistributor {
|
||||
|
|
@ -197,6 +200,12 @@ export default class Player extends MessageDistributor {
|
|||
localStorage.setItem(AUTOPLAY_STORAGE_KEY, `${autoplay}`);
|
||||
update({ autoplay });
|
||||
}
|
||||
|
||||
toggleEvents() {
|
||||
const showEvents = !getState().showEvents;
|
||||
localStorage.setItem(SHOW_EVENTS_STORAGE_KEY, `${showEvents}`);
|
||||
update({ showEvents });
|
||||
}
|
||||
|
||||
_updateSpeed(speed: number) {
|
||||
localStorage.setItem(SPEED_STORAGE_KEY, `${speed}`);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export const toggleSkip = initCheck((...args) => instance.toggleSkip(...args));
|
|||
export const toggleSkipToIssue = initCheck((...args) => instance.toggleSkipToIssue(...args));
|
||||
export const toggleAutoplay = initCheck((...args) => instance.toggleAutoplay(...args));
|
||||
export const toggleSpeed = initCheck((...args) => instance.toggleSpeed(...args));
|
||||
export const toggleEvents = initCheck((...args) => instance.toggleEvents(...args));
|
||||
export const speedUp = initCheck((...args) => instance.speedUp(...args));
|
||||
export const speedDown = initCheck((...args) => instance.speedDown(...args));
|
||||
export const attach = initCheck((...args) => instance.attach(...args));
|
||||
|
|
@ -76,6 +77,7 @@ export const Controls = {
|
|||
toggleSkip,
|
||||
toggleSkipToIssue,
|
||||
toggleAutoplay,
|
||||
toggleEvents,
|
||||
toggleSpeed,
|
||||
speedUp,
|
||||
speedDown,
|
||||
|
|
|
|||
4
frontend/app/svg/icons/chevron-double-left.svg
Normal file
4
frontend/app/svg/icons/chevron-double-left.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-double-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8.354 1.646a.5.5 0 0 1 0 .708L2.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/>
|
||||
<path fill-rule="evenodd" d="M12.354 1.646a.5.5 0 0 1 0 .708L6.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 447 B |
4
frontend/app/svg/icons/chevron-double-right.svg
Normal file
4
frontend/app/svg/icons/chevron-double-right.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-double-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M3.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L9.293 8 3.646 2.354a.5.5 0 0 1 0-.708z"/>
|
||||
<path fill-rule="evenodd" d="M7.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L13.293 8 7.646 2.354a.5.5 0 0 1 0-.708z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 450 B |
|
|
@ -5,13 +5,13 @@ require('dotenv').config()
|
|||
|
||||
const oss = {
|
||||
name: 'oss',
|
||||
PRODUCTION: false,
|
||||
PRODUCTION: true,
|
||||
SENTRY_ENABLED: false,
|
||||
SENTRY_URL: "",
|
||||
CAPTCHA_ENABLED: process.env.CAPTCHA_ENABLED === 'true',
|
||||
CAPTCHA_SITE_KEY: process.env.CAPTCHA_SITE_KEY,
|
||||
ORIGIN: () => 'window.location.origin',
|
||||
API_EDP: 'https://do.openreplay.com/api',
|
||||
API_EDP: () => 'window.location.origin + "/api"',
|
||||
ASSETS_HOST: () => 'window.location.origin + "/assets"',
|
||||
VERSION: '1.0.0',
|
||||
SOURCEMAP: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue