feature(ui): assist - wip

This commit is contained in:
Shekar Siri 2021-06-16 15:41:40 +05:30
parent 5d12bc6259
commit 372b9c4b2e
17 changed files with 196 additions and 86 deletions

View file

@ -1,5 +1,5 @@
import React from 'react'
import ChatWindow from './components/ChatWindow/ChatWindow'
import ChatWindow from './ChatWindow/ChatWindow'
import ScreenSharing from './ScreenSharing/ScreenSharing'
function Assist() {

View file

@ -0,0 +1,17 @@
import React from 'react'
import VideoContainer from '../components/VideoContainer/VideoContainer'
// import stl from './chatWindow.css';
function ChatWindow() {
return (
<div className="fixed border radius bg-white z-50 shadow-xl mt-16">
<div className="p-2">
<VideoContainer />
<div className="py-1" />
<VideoContainer />
</div>
</div>
)
}
export default ChatWindow

View file

@ -1,6 +1,6 @@
.wrapepr {
background-color: white;
border: solid thin $gray-medium
border: solid thin #CCC;
border-radius: 3px;
position: fixed;
height: 400px;

View file

@ -0,0 +1,3 @@
.btn {
}

View file

@ -0,0 +1,24 @@
import React from 'react'
import { Button, Icon } from 'UI'
import cn from 'classnames'
// import stl from './AassistActions.css'
interface Props {
isLive: false;
}
function AssistActions({ }: Props) {
return (
<div className="flex items-center">
<div className={cn('cursor-pointer p-2 mr-2')}>
<Icon name="telephone" size="20" />
</div>
<div className="flex items-center p-2 cursor-pointer">
<Icon name="controller" size="20" />
<span className="ml-2">Request Control</span>
</div>
</div>
)
}
export default AssistActions

View file

@ -0,0 +1 @@
export { default } from './AssistActions'

View file

@ -1,15 +0,0 @@
import React from 'react'
import VideoContainer from '../VideoContainer/VideoContainer'
// impdort stl from './chatWindow.css'
function ChatWindow() {
return (
<div className="fixed border radius bg-white z-50 shadow-md">
<div className="p-2">
<VideoContainer />
</div>
</div>
)
}
export default ChatWindow

View file

@ -1,50 +1,19 @@
import React, { useEffect } from 'react'
import { Button, Icon } from 'UI'
function VideoContainer() {
const constraints = {
'video': true,
'audio': true
}
async function playVideoFromCamera() {
try {
const constraints = {'video': true, 'audio': true};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const videoElement = document.querySelector('video#localVideo');
// videoElement.srcObject = stream;
} catch(error) {
console.error('Error opening video camera.', error);
}
}
function getConnectedDevices(type, callback) {
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const filtered = devices.filter(device => device.kind === type);
callback(filtered);
});
}
function VideoContainer() {
useEffect(() => {
getConnectedDevices('videoinput', cameras => console.log('Cameras found', cameras));
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('Got MediaStream:', stream);
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
// TODO check for video stream and display
}, [])
return (
<div className="relative h-20 bg-gray-light-shade border p-1" style={{ height: '160px', width: '200px' }}>
<div className="absolute left-0 right-0 bottom-0 flex justify-center border border-gray-300 p-1 bg-white radius">
<div className="absolute left-0 right-0 bottom-0 flex justify-center border border-gray-300 p-1 bg-white radius bg-opacity-25">
<Button plain size="small">
<Icon name="mic" size="20" />
<Icon name="mic" size="16" />
</Button>
<Button plain size="small">
<Icon name="camera-video" size="20" />
<Icon name="camera-video" size="16" />
</Button>
</div>
</div>

View file

@ -25,7 +25,7 @@ import { LAST_7_DAYS } from 'Types/app/period';
import { resetFunnel } from 'Duck/funnels';
import { resetFunnelFilters } from 'Duck/funnelFilters'
import NoSessionsMessage from '../shared/NoSessionsMessage';
import Assist from 'Components/Assist'
import LiveSessionList from './LiveSessionList'
const AUTOREFRESH_INTERVAL = 10 * 60 * 1000;
@ -135,16 +135,16 @@ export default class BugFinder extends React.PureComponent {
setActiveTab = tab => {
this.props.setActiveTab(tab);
}
render() {
const { activeFlow, activeTab } = this.props;
const { showRehydratePanel } = this.state;
console.log('activeTab', activeTab)
return (
<div className="page-margin container-90 flex relative">
<Assist />
<div className="flex-1 flex">
<div className="side-menu">
<SessionsMenu
@ -159,12 +159,10 @@ export default class BugFinder extends React.PureComponent {
className="mb-5"
>
<EventFilter />
</div>
{activeFlow && activeFlow.type === 'flows' ?
<FunnelList />
:
<SessionList onMenuItemClick={this.setActiveTab} />
}
</div>
{ activeFlow && activeFlow.type === 'flows' && <FunnelList /> }
{ activeTab.type !== 'live' && <SessionList onMenuItemClick={this.setActiveTab} /> }
{ activeTab.type === 'live' && <LiveSessionList /> }
</div>
</div>
<RehydrateSlidePanel

View file

@ -0,0 +1,29 @@
import React from 'react'
// import { fetchLiveList } from 'Duck/sessions'
import { connect } from 'react-redux';
import { NoContent } from 'UI';
import { List } from 'immutable';
interface Props {
loading: Boolean,
list?: List<any>
}
function LiveSessionList({ loading, list }: Props ) {
return (
<div>
<NoContent
title={"No live sessions!"}
subtext="Please try changing your search parameters."
icon="exclamation-circle"
show={ !loading && list && list.size === 0}
>
</NoContent>
</div>
)
}
export default connect(state => ({
}), { })(LiveSessionList)

View file

@ -0,0 +1 @@
export { default } from './LiveSessionList'

View file

@ -123,7 +123,7 @@ export default class SessionList extends React.PureComponent {
const { activeTab, allList, total } = this.props;
var filteredList;
if (activeTab.type !== ALL && activeTab.type !== 'bookmark') { // Watchdog sessions
if (activeTab.type !== ALL && activeTab.type !== 'bookmark' && activeTab.type !== 'live') { // Watchdog sessions
filteredList = allList.filter(session => activeTab.fits(session))
} else {
filteredList = allList

View file

@ -81,6 +81,16 @@ function SessionsMenu(props) {
onClick={() => onMenuItemClick({ name: 'Bookmarks', type: 'bookmark' })}
/>
</div>
<div className="my-3">
<SideMenuitem
title="Assist"
iconName="person"
active={activeTab.type === 'live'}
onClick={() => onMenuItemClick({ name: 'Assist', type: 'live' })}
/>
</div>
<div className={cn(stl.divider, 'mb-4')} />
<SavedSearchList />
</div>

View file

@ -0,0 +1,66 @@
import { useEffect } from 'react';
import { connect } from 'react-redux';
import { Loader } from 'UI';
import { toggleFullscreen, closeBottomBlock } from 'Duck/components/player';
import {
PlayerProvider,
connectPlayer,
init as initPlayer,
clean as cleanPlayer,
} from 'Player';
import { Controls as PlayerControls } from 'Player';
import Assist from 'Components/Assist'
import PlayerBlockHeader from '../Session_/PlayerBlockHeader';
import EventsBlock from '../Session_/EventsBlock';
import PlayerBlock from '../Session_/PlayerBlock';
import styles from '../Session_/session.css';
const EventsBlockConnected = connectPlayer(state => ({
currentTimeEventIndex: state.eventListNow.length > 0 ? state.eventListNow.length - 1 : 0,
playing: state.playing,
}))(EventsBlock)
const InitLoader = connectPlayer(state => ({
loading: !state.initialized
}))(Loader);
function WebPlayer ({ session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt }) {
useEffect(() => {
initPlayer(session, jwt);
return () => cleanPlayer()
}, [ session.sessionId ]);
// LAYOUT (TODO: local layout state - useContext or something..)
useEffect(() => () => {
toggleFullscreen(false);
closeBottomBlock();
}, [])
return (
<PlayerProvider>
<InitLoader className="flex-1">
<Assist />
<PlayerBlockHeader fullscreen={fullscreen}/>
<div className={ styles.session } data-fullscreen={fullscreen}>
<PlayerBlock />
</div>
</InitLoader>
</PlayerProvider>
);
}
export default connect(state => ({
session: state.getIn([ 'sessions', 'current' ]),
jwt: state.get('jwt'),
fullscreen: state.getIn([ 'components', 'player', 'fullscreen' ]),
}), {
toggleFullscreen,
closeBottomBlock,
})(WebPlayer)

View file

@ -6,6 +6,7 @@ import { fetchList as fetchSlackList } from 'Duck/integrations/slack';
import { Link, NoContent, Loader } from 'UI';
import { sessions as sessionsRoute } from 'App/routes';
import LivePlayer from './LivePlayer';
import WebPlayer from './WebPlayer';
import IOSPlayer from './IOSPlayer';
@ -48,7 +49,7 @@ function Session({
<Loader className="flex-1" loading={ loading || sessionId !== session.sessionId }>
{ session.isIOS
? <IOSPlayer session={session} />
: <WebPlayer />
: <LivePlayer />
}
</Loader>
</NoContent>

View file

@ -14,6 +14,7 @@ import { fetchList as fetchListIntegration } from 'Duck/integrations/actions';
import cls from './playerBlockHeader.css';
import Issues from './Issues/Issues';
import Autoplay from './Autoplay';
import AssistActions from '../Assist/components/AssistActions';
const SESSIONS_ROUTE = sessionsRoute();
@ -89,7 +90,7 @@ export default class PlayerBlockHeader extends React.PureComponent {
live,
disabled,
jiraConfig,
fullscreen
fullscreen,
} = this.props;
return (
@ -112,31 +113,36 @@ export default class PlayerBlockHeader extends React.PureComponent {
<HeaderInfo icon={ osIcon(userOs) } label={ userOs } />
<div className='ml-auto flex items-center'>
<Autoplay />
<div className={ cls.divider } />
<IconButton
className="mr-2"
tooltip="Bookmark"
onClick={ this.toggleFavorite }
loading={ loading }
icon={ favorite ? 'star-solid' : 'star' }
// label={ favorite ? 'Favourited' : 'Favourite' }
plain
/>
<SharePopup
entity="sessions"
id={ sessionId }
trigger={
{ !live && <AssistActions isLive /> }
{ live && (
<>
<Autoplay />
<div className={ cls.divider } />
<IconButton
className="mr-2"
tooltip="Share Session"
disabled={ disabled }
icon={ 'share-alt' }
//label="Share"
tooltip="Bookmark"
onClick={ this.toggleFavorite }
loading={ loading }
icon={ favorite ? 'star-solid' : 'star' }
// label={ favorite ? 'Favourited' : 'Favourite' }
plain
/>
}
/>
<SharePopup
entity="sessions"
id={ sessionId }
trigger={
<IconButton
className="mr-2"
tooltip="Share Session"
disabled={ disabled }
icon={ 'share-alt' }
//label="Share"
plain
/>
}
/>
</>
)}
{ !live && jiraConfig && jiraConfig.token && <Issues sessionId={ sessionId } /> }
</div>
</div>