refactor(ui/player): create player context for webplayer components

This commit is contained in:
sylenien 2022-11-22 14:23:30 +01:00
parent c5209efd87
commit 1621f73c69
14 changed files with 271 additions and 441 deletions

View file

@ -0,0 +1,70 @@
import React from 'react';
import {
connectPlayer,
} from 'Player';
import PlayerBlock from '../Session_/PlayerBlock';
import styles from '../Session_/session.module.css';
import { countDaysFrom } from 'App/date';
import cn from 'classnames';
import RightBlock from './RightBlock';
function PlayerContent({ session, live, fullscreen, activeTab, setActiveTab, hasError }) {
const sessionDays = countDaysFrom(session.startedAt);
return (
<div className="relative">
{hasError ? (
<div
className="inset-0 flex items-center justify-center absolute"
style={{
// background: '#f6f6f6',
height: 'calc(100vh - 50px)',
zIndex: '999',
}}
>
<div className="flex flex-col items-center">
<div className="text-lg -mt-8">
{sessionDays > 2 ? 'Session not found.' : 'This session is still being processed.'}
</div>
<div className="text-sm">
{sessionDays > 2
? 'Please check your data retention policy.'
: 'Please check it again in a few minutes.'}
</div>
</div>
</div>
) : (
<div className={cn('flex', { 'pointer-events-none': hasError })}>
<div
className="w-full"
style={activeTab && !fullscreen ? { maxWidth: 'calc(100% - 270px)' } : undefined}
>
<div className={cn(styles.session, 'relative')} data-fullscreen={fullscreen}>
<PlayerBlock activeTab={activeTab} />
</div>
</div>
{activeTab !== '' && (
<RightMenu
activeTab={activeTab}
setActiveTab={setActiveTab}
fullscreen={fullscreen}
tabs={TABS}
live={live}
/>
)}
</div>
)}
</div>
);
}
function RightMenu({ live, tabs, activeTab, setActiveTab, fullscreen }) {
return (
!live &&
!fullscreen && <RightBlock tabs={tabs} setActiveTab={setActiveTab} activeTab={activeTab} />
);
}
export default connectPlayer((state) => ({
showEvents: !state.showEvents,
hasError: state.error,
}))(PlayerContent);

View file

@ -1,139 +0,0 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Loader, Modal } from 'UI';
import { toggleFullscreen, closeBottomBlock } from 'Duck/components/player';
import { fetchList } from 'Duck/integrations';
import { PlayerProvider, injectNotes, connectPlayer, init as initPlayer, clean as cleanPlayer, Controls } from 'Player';
import cn from 'classnames';
import RightBlock from './RightBlock';
import withLocationHandlers from 'HOCs/withLocationHandlers';
import { useStore } from 'App/mstore'
import PlayerBlockHeader from '../Session_/PlayerBlockHeader';
import PlayerBlock from '../Session_/PlayerBlock';
import styles from '../Session_/session.module.css';
import { countDaysFrom } from 'App/date';
import ReadNote from '../Session_/Player/Controls/components/ReadNote';
import { fetchList as fetchMembers } from 'Duck/member';
const TABS = {
EVENTS: 'User Steps',
HEATMAPS: 'Click Map',
};
const InitLoader = connectPlayer((state) => ({
loading: !state.initialized,
}))(Loader);
const PlayerContentConnected = connectPlayer((state) => ({
showEvents: !state.showEvents,
hasError: state.error,
}))(PlayerContent);
function PlayerContent({ session, live, fullscreen, activeTab, setActiveTab, hasError }) {
const sessionDays = countDaysFrom(session.startedAt);
return (
<div className="relative">
{hasError ? (
<div className="inset-0 flex items-center justify-center absolute" style={{
// background: '#f6f6f6',
height: 'calc(100vh - 50px)',
zIndex: '999',
}}>
<div className="flex flex-col items-center">
<div className="text-lg -mt-8">{sessionDays > 2 ? 'Session not found.' : 'This session is still being processed.'}</div>
<div className="text-sm">{sessionDays > 2 ? 'Please check your data retention policy.' : 'Please check it again in a few minutes.'}</div>
</div>
</div>
) : (
<div className={cn('flex', { 'pointer-events-none': hasError })}>
<div className="w-full" style={activeTab && !fullscreen ? { maxWidth: 'calc(100% - 270px)'} : undefined}>
<div className={cn(styles.session, 'relative')} data-fullscreen={fullscreen}>
<PlayerBlock activeTab={activeTab} />
</div>
</div>
{activeTab !== '' && <RightMenu activeTab={activeTab} setActiveTab={setActiveTab} fullscreen={fullscreen} tabs={TABS} live={live} />}
</div>
)}
</div>
);
}
function RightMenu({ live, tabs, activeTab, setActiveTab, fullscreen }) {
return !live && !fullscreen && <RightBlock tabs={tabs} setActiveTab={setActiveTab} activeTab={activeTab} />;
}
function WebPlayer(props) {
const { session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, fetchList } = props;
const { notesStore } = useStore()
const [activeTab, setActiveTab] = useState('');
const [showNoteModal, setShowNote] = useState(false)
const [noteItem, setNoteItem] = useState(null)
useEffect(() => {
fetchList('issues');
initPlayer(session, jwt);
props.fetchMembers()
notesStore.fetchSessionNotes(session.sessionId).then(r => {
injectNotes(r)
const note = props.query.get('note');
if (note) {
Controls.pause()
setNoteItem(notesStore.getNoteById(parseInt(note, 10), r))
setShowNote(true)
}
})
const jumptTime = props.query.get('jumpto');
if (jumptTime) {
Controls.jump(parseInt(jumptTime));
}
return () => cleanPlayer();
}, [session.sessionId]);
// LAYOUT (TODO: local layout state - useContext or something..)
useEffect(
() => () => {
toggleFullscreen(false);
closeBottomBlock();
},
[]
);
const onNoteClose = () => {setShowNote(false); Controls.togglePlay()}
return (
<PlayerProvider>
<InitLoader className="flex-1">
<PlayerBlockHeader activeTab={activeTab} setActiveTab={setActiveTab} tabs={TABS} fullscreen={fullscreen} />
<PlayerContentConnected activeTab={activeTab} fullscreen={fullscreen} live={live} setActiveTab={setActiveTab} session={session} />
<Modal open={showNoteModal} onClose={onNoteClose}>
{showNoteModal ? (
<ReadNote
userEmail={props.members.find(m => m.id === noteItem?.userId)?.email || ''}
note={noteItem}
onClose={onNoteClose}
notFound={!noteItem}
/>
) : null}
</Modal>
</InitLoader>
</PlayerProvider>
);
}
export default connect(
(state) => ({
session: state.getIn(['sessions', 'current']),
jwt: state.get('jwt'),
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
showEvents: state.get('showEvents'),
members: state.getIn(['members', 'list']),
}),
{
toggleFullscreen,
closeBottomBlock,
fetchList,
fetchMembers,
}
)(withLocationHandlers()(WebPlayer));

View file

@ -0,0 +1,127 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Modal } from 'UI';
import { toggleFullscreen, closeBottomBlock } from 'Duck/components/player';
import { fetchList } from 'Duck/integrations';
import {
PlayerProvider,
createWebPlayer,
} from 'Player';
import withLocationHandlers from 'HOCs/withLocationHandlers';
import { useStore } from 'App/mstore';
import PlayerBlockHeader from '../Session_/PlayerBlockHeader';
import ReadNote from '../Session_/Player/Controls/components/ReadNote';
import { fetchList as fetchMembers } from 'Duck/member';
import PlayerContent from './PlayerContent'
import {
IPlayerContext,
PlayerContext,
defaultContextValue
} from './playerContext'
const TABS = {
EVENTS: 'User Steps',
HEATMAPS: 'Click Map',
};
function WebPlayer(props: any) {
const { session, toggleFullscreen, closeBottomBlock, live, fullscreen, jwt, fetchList } = props;
const { notesStore } = useStore();
const [activeTab, setActiveTab] = useState('');
const [showNoteModal, setShowNote] = useState(false);
const [noteItem, setNoteItem] = useState(null);
const [contextValue, setContextValue] = useState<IPlayerContext>(defaultContextValue)
useEffect(() => {
fetchList('issues');
const [WebPlayerInst, PlayerStore] = createWebPlayer(session, jwt);
setContextValue({ player: WebPlayerInst, store: PlayerStore })
// initPlayer(session, jwt); TODOPlayer
props.fetchMembers();
notesStore.fetchSessionNotes(session.sessionId).then((r) => {
// WebPlayerInst.injectNotes(r);
// PlayerStore.update({ notes: r })
const note = props.query.get('note');
if (note) {
WebPlayerInst.pause();
setNoteItem(notesStore.getNoteById(parseInt(note, 10), r));
setShowNote(true);
}
});
const jumptTime = props.query.get('jumpto');
if (jumptTime) {
WebPlayerInst.jump(parseInt(jumptTime));
}
return () => WebPlayerInst.clean();
}, [session.sessionId]);
// LAYOUT (TODO: local layout state - useContext or something..)
useEffect(
() => () => {
toggleFullscreen(false);
closeBottomBlock();
},
[]
);
const onNoteClose = () => {
setShowNote(false);
contextValue.player.togglePlay();
};
if (!contextValue.player) return null;
return (
<PlayerContext.Provider value={contextValue}>
<PlayerProvider>
<>
<PlayerBlockHeader
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={TABS}
fullscreen={fullscreen}
/>
{/* @ts-ignore */}
<PlayerContent
activeTab={activeTab}
fullscreen={fullscreen}
live={live}
setActiveTab={setActiveTab}
session={session}
/>
<Modal open={showNoteModal} onClose={onNoteClose}>
{showNoteModal ? (
<ReadNote
userEmail={props.members.find((m: Record<string, any>) => m.id === noteItem?.userId)?.email || ''}
note={noteItem}
onClose={onNoteClose}
notFound={!noteItem}
/>
) : null}
</Modal>
</>
</PlayerProvider>
</PlayerContext.Provider>
);
}
export default connect(
(state: any) => ({
session: state.getIn(['sessions', 'current']),
jwt: state.get('jwt'),
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
showEvents: state.get('showEvents'),
members: state.getIn(['members', 'list']),
}),
{
toggleFullscreen,
closeBottomBlock,
fetchList,
fetchMembers,
}
)(withLocationHandlers()(WebPlayer));

View file

@ -0,0 +1,12 @@
import { createContext } from 'react';
import {
IWebPlayer,
IStore
} from 'Player'
export interface IPlayerContext {
player: IWebPlayer
store: IStore,
}
export const defaultContextValue: IPlayerContext = { player: undefined, store: undefined}
export const PlayerContext = createContext<IPlayerContext>(defaultContextValue);

View file

@ -1,18 +0,0 @@
import React from 'react'
import { connectPlayer, jump } from 'Player';
import ConsoleContent from './ConsoleContent';
@connectPlayer(state => ({
logs: state.logList,
// time: state.time,
livePlay: state.livePlay,
listNow: state.logListNow,
}))
export default class Console extends React.PureComponent {
render() {
const { logs, time, listNow } = this.props;
return (
<ConsoleContent jump={!this.props.livePlay && jump} logs={logs} lastIndex={listNow.length - 1} logsNow={listNow} />
);
}
}

View file

@ -1,123 +0,0 @@
import React from 'react';
import cn from 'classnames';
import { getRE } from 'App/utils';
import { Icon, NoContent, Tabs, Input } from 'UI';
import { jump } from 'Player';
import { LEVEL } from 'Types/session/log';
import Autoscroll from '../Autoscroll';
import BottomBlock from '../BottomBlock';
import stl from './console.module.css';
import ConsoleRow from './ConsoleRow';
// import { Duration } from 'luxon';
const ALL = 'ALL';
const INFO = 'INFO';
const WARNINGS = 'WARNINGS';
const ERRORS = 'ERRORS';
const LEVEL_TAB = {
[LEVEL.INFO]: INFO,
[LEVEL.LOG]: INFO,
[LEVEL.WARNING]: WARNINGS,
[LEVEL.ERROR]: ERRORS,
[LEVEL.EXCEPTION]: ERRORS,
};
const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({ text: tab, key: tab }));
// eslint-disable-next-line complexity
const getIconProps = (level) => {
switch (level) {
case LEVEL.INFO:
case LEVEL.LOG:
return {
name: 'console/info',
color: 'blue2',
};
case LEVEL.WARN:
case LEVEL.WARNING:
return {
name: 'console/warning',
color: 'red2',
};
case LEVEL.ERROR:
return {
name: 'console/error',
color: 'red',
};
}
return null;
};
function renderWithNL(s = '') {
if (typeof s !== 'string') return '';
return s.split('\n').map((line, i) => <div className={cn({ 'ml-20': i !== 0 })}>{line}</div>);
}
export default class ConsoleContent extends React.PureComponent {
state = {
filter: '',
activeTab: ALL,
};
onTabClick = (activeTab) => this.setState({ activeTab });
onFilterChange = ({ target: { value } }) => this.setState({ filter: value });
render() {
const { logs, isResult, additionalHeight, logsNow } = this.props;
const time = logsNow.length > 0 ? logsNow[logsNow.length - 1].time : undefined;
const { filter, activeTab, currentError } = this.state;
const filterRE = getRE(filter, 'i');
const filtered = logs.filter(({ level, value }) =>
activeTab === ALL
? filterRE.test(value)
: filterRE.test(value) && LEVEL_TAB[level] === activeTab
);
const lastIndex = filtered.filter((item) => item.time <= time).length - 1;
return (
<>
<BottomBlock style={{ height: 300 + additionalHeight + 'px' }}>
<BottomBlock.Header showClose={!isResult}>
<div className="flex items-center">
<span className="font-semibold color-gray-medium mr-4">Console</span>
<Tabs tabs={TABS} active={activeTab} onClick={this.onTabClick} border={false} />
</div>
<Input
className="input-small h-8"
placeholder="Filter by keyword"
icon="search"
iconPosition="left"
name="filter"
height={28}
onChange={this.onFilterChange}
/>
</BottomBlock.Header>
<BottomBlock.Content>
<NoContent
title={
<div className="capitalize flex items-center mt-16">
<Icon name="info-circle" className="mr-2" size="18" />
No Data
</div>
}
size="small"
show={filtered.length === 0}
>
<Autoscroll autoScrollTo={Math.max(lastIndex, 0)}>
{filtered.map((l) => (
<ConsoleRow
log={l}
jump={jump}
iconProps={getIconProps(l.level)}
renderWithNL={renderWithNL}
/>
))}
</Autoscroll>
</NoContent>
</BottomBlock.Content>
</BottomBlock>
</>
);
}
}

View file

@ -1,48 +0,0 @@
import React, { useState } from 'react';
import cn from 'classnames';
import stl from '../console.module.css';
import { Icon } from 'UI';
import JumpButton from 'Shared/DevTools/JumpButton';
interface Props {
log: any;
iconProps: any;
jump?: any;
renderWithNL?: any;
}
function ConsoleRow(props: Props) {
const { log, iconProps, jump, renderWithNL } = props;
const [expanded, setExpanded] = useState(false);
const lines = log.value.split('\n').filter((l: any) => !!l);
const canExpand = lines.length > 1;
return (
<div
className={cn(stl.line, 'flex py-2 px-4 overflow-hidden group relative select-none', {
info: !log.isYellow() && !log.isRed(),
warn: log.isYellow(),
error: log.isRed(),
'cursor-pointer': canExpand,
})}
onClick={() => setExpanded(!expanded)}
>
<div className={cn(stl.timestamp)}>
<Icon size="14" className={stl.icon} {...iconProps} />
</div>
{/* <div className={cn(stl.timestamp, {})}>
{Duration.fromMillis(log.time).toFormat('mm:ss.SSS')}
</div> */}
<div key={log.key} className={cn('')} data-scroll-item={log.isRed()}>
<div className={cn(stl.message, 'flex items-center')}>
{canExpand && (
<Icon name={expanded ? 'caret-down-fill' : 'caret-right-fill'} className="mr-2" />
)}
<span>{renderWithNL(lines.pop())}</span>
</div>
{canExpand && expanded && lines.map((l: any) => <div className="ml-4 mb-1">{l}</div>)}
</div>
<JumpButton onClick={() => jump(log.time)} />
</div>
);
}
export default ConsoleRow;

View file

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

View file

@ -1,38 +0,0 @@
.message {
overflow-x: auto;
margin-left: 10px;
font-size: 13px;
overflow-x: auto;
&::-webkit-scrollbar {
height: 2px;
}
}
.line {
font-family: 'Menlo', 'monaco', 'consolas', monospace;
/* margin-top: -1px; ??? */
display: flex;
align-items: flex-start;
border-bottom: solid thin $gray-light-shade;
&:hover {
background-coor: $active-blue !important;
}
}
.timestamp {
}
.activeRow {
background-color: $teal-light !important;
}
.icon {
padding-top: 4px;
margin-right: 7px;
}
.inactiveRow {
opacity: 0.5;
}

View file

@ -53,10 +53,10 @@ export default function Inspector () {
if (!doc) return null;
return (
<BottomBlock>
<BottomBlock>
<BottomBlock.Content>
<div onMouseLeave={ () => markElement(null) } className={stl.wrapper}>
<ElementView
<ElementView
element={ doc.documentElement }
level={0}
context={doc.defaultView}
@ -69,4 +69,4 @@ export default function Inspector () {
</BottomBlock.Content>
</BottomBlock>
);
}
}

View file

@ -40,12 +40,12 @@ export default class GraphQL extends React.PureComponent {
const { filter, current } = this.state;
const filterRE = getRE(filter, 'i');
const filtered = list
.filter(({ containerType, context, containerName = "", containerId = "", containerSrc="" }) =>
filterRE.test(containerName) ||
.filter(({ containerType, context, containerName = "", containerId = "", containerSrc="" }) =>
filterRE.test(containerName) ||
filterRE.test(containerId) ||
filterRE.test(containerSrc) ||
filterRE.test(CONTEXTS[ context ]) ||
filterRE.test(CONTAINER_TYPES[ containerType ]));
filterRE.test(CONTAINER_TYPES[ containerType ]));
const lastIndex = filtered.filter(item => item.time <= time).length - 1;
return (
<BottomBlock>
@ -64,7 +64,7 @@ export default class GraphQL extends React.PureComponent {
<QuestionMarkHint
content={
<>
<a className="color-teal underline mr-2" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Long_Tasks_API">Learn more </a>
<a className="color-teal underline mr-2" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Long_Tasks_API">Learn more </a>
about Long Tasks API
</>
}

View file

@ -20,21 +20,13 @@ import {
OVERVIEW,
} from 'Duck/components/player';
import NetworkPanel from 'Shared/DevTools/NetworkPanel';
import Console from '../Console/Console';
import StackEvents from '../StackEvents/StackEvents';
import Storage from '../Storage';
import Profiler from '../Profiler';
import { ConnectedPerformance } from '../Performance';
import GraphQL from '../GraphQL';
import Exceptions from '../Exceptions/Exceptions';
import LongTasks from '../LongTasks';
import Inspector from '../Inspector';
import {
attach as attachPlayer,
Controls as PlayerControls,
scale as scalePlayerScreen,
connectPlayer,
} from 'Player';
import Controls from './Controls';
import Overlay from './Overlay';
import stl from './player.module.css';
@ -42,70 +34,47 @@ import { updateLastPlayedSession } from 'Duck/sessions';
import OverviewPanel from '../OverviewPanel';
import ConsolePanel from 'Shared/DevTools/ConsolePanel';
import ProfilerPanel from 'Shared/DevTools/ProfilerPanel';
import { PlayerContext } from 'App/components/Session/playerContext';
@connectPlayer((state) => ({
live: state.live,
}))
@connect(
(state) => {
const isAssist = window.location.pathname.includes('/assist/');
return {
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
nextId: state.getIn(['sessions', 'nextId']),
sessionId: state.getIn(['sessions', 'current', 'sessionId']),
closedLive:
!!state.getIn(['sessions', 'errors']) ||
(isAssist && !state.getIn(['sessions', 'current', 'live'])),
};
},
{
hideTargetDefiner,
function Player(props) {
const {
className,
bottomBlockIsActive,
fullscreen,
fullscreenOff,
updateLastPlayedSession,
}
)
export default class Player extends React.PureComponent {
screenWrapper = React.createRef();
nextId,
closedLive,
bottomBlock,
activeTab,
} = props;
const playerContext = React.useContext(PlayerContext)
const screenWrapper = React.useRef();
componentDidUpdate(prevProps) {
if (
[prevProps.bottomBlock, this.props.bottomBlock].includes(NONE) ||
prevProps.fullscreen !== this.props.fullscreen
) {
scalePlayerScreen();
React.useEffect(() => {
props.updateLastPlayedSession(props.sessionId);
if (!props.closedLive) {
const parentElement = findDOMNode(screenWrapper.current); //TODO: good architecture
playerContext.player.attach(parentElement);
}
}
componentDidMount() {
this.props.updateLastPlayedSession(this.props.sessionId);
if (this.props.closedLive) return;
}, [])
const parentElement = findDOMNode(this.screenWrapper.current); //TODO: good architecture
attachPlayer(parentElement);
}
React.useEffect(() => {
playerContext.player.scale();
}, [props.bottomBlock, props.fullscreen, playerContext.player])
render() {
const {
className,
bottomBlockIsActive,
fullscreen,
fullscreenOff,
nextId,
closedLive,
bottomBlock,
activeTab,
} = this.props;
if (!playerContext.player) return null;
const maxWidth = activeTab ? 'calc(100vw - 270px)' : '100vw';
return (
<div
const maxWidth = activeTab ? 'calc(100vw - 270px)' : '100vw';
return (
<div
className={cn(className, stl.playerBody, 'flex flex-col relative', fullscreen && 'pb-2')}
data-bottom-block={bottomBlockIsActive}
>
{fullscreen && <EscapeButton onClose={fullscreenOff} />}
<div className="relative flex-1 overflow-hidden">
<Overlay nextId={nextId} togglePlay={PlayerControls.togglePlay} closedLive={closedLive} />
<div className={stl.screenWrapper} ref={this.screenWrapper} />
<Overlay nextId={nextId} togglePlay={playerContext.player.togglePlay} closedLive={closedLive} />
<div className={stl.screenWrapper} ref={screenWrapper} />
</div>
{!fullscreen && !!bottomBlock && (
<div style={{ maxWidth, width: '100%' }}>
@ -125,8 +94,25 @@ export default class Player extends React.PureComponent {
{bottomBlock === INSPECTOR && <Inspector />}
</div>
)}
<Controls {...PlayerControls} />
<Controls {...playerContext.player} />
</div>
);
}
)
}
export default connect((state) => {
const isAssist = window.location.pathname.includes('/assist/');
return {
fullscreen: state.getIn(['components', 'player', 'fullscreen']),
nextId: state.getIn(['sessions', 'nextId']),
sessionId: state.getIn(['sessions', 'current', 'sessionId']),
closedLive:
!!state.getIn(['sessions', 'errors']) ||
(isAssist && !state.getIn(['sessions', 'current', 'live'])),
};
},
{
hideTargetDefiner,
fullscreenOff,
updateLastPlayedSession,
}
)(Player)

View file

@ -33,7 +33,7 @@ export default class Profiler extends React.PureComponent {
return (
<React.Fragment>
<SlideModal
<SlideModal
title={ modalProfile && modalProfile.name }
isDisplayed={ modalProfile !== null }
content={ modalProfile && <ProfileInfo profile={ modalProfile } />}
@ -55,7 +55,7 @@ export default class Profiler extends React.PureComponent {
/>
</BottomBlock.Header>
<BottomBlock.Content>
<TimeTable
<TimeTable
rows={ filteredProfiles }
onRowClick={ this.onProfileClick }
hoverable

View file

@ -6,8 +6,10 @@ import Player, { State as PState } from './player/Player'
import WebPlayer from './_web/WebPlayer'
type WebPlayerStore = Store<PState & MMState>
export type IWebPlayer = WebPlayer
export type IWebPlayerStore = Store<PState & MMState>
export function createWebPlayer(session, wrapStore?: (s:WebPlayerStore) => WebPlayerStore): [WebPlayer, WebPlayerStore] {
export function createWebPlayer(session: Record<string, any>, wrapStore?: (s:IWebPlayerStore) => IWebPlayerStore): [IWebPlayer, IWebPlayerStore] {
let store: WebPlayerStore = new SimpleStore<PState & MMState>({
...Player.INITIAL_STATE,
...MM_INITIAL_STATE,
@ -20,7 +22,7 @@ export function createWebPlayer(session, wrapStore?: (s:WebPlayerStore) => WebPl
}
export function createLiveWebPlayer(session, config: RTCIceServer[], wrapStore?: (s:WebPlayerStore) => WebPlayerStore): [WebPlayer, WebPlayerStore] {
export function createLiveWebPlayer(session: Record<string, any>, config: RTCIceServer[], wrapStore?: (s:IWebPlayerStore) => IWebPlayerStore): [IWebPlayer, IWebPlayerStore] {
let store: WebPlayerStore = new SimpleStore<PState & MMState>({
...Player.INITIAL_STATE,
...MM_INITIAL_STATE,
@ -30,4 +32,4 @@ export function createLiveWebPlayer(session, config: RTCIceServer[], wrapStore?:
}
const player = new WebPlayer(store, session, config, true)
return [player, store]
}
}