import { actionChannel , call , put , select , take , takeLatest } from 'redux-saga/effects' ; import { ActionTypes } from "./actionTypes" ; import { ActionTypes as PlayerActionTypes } from "../player/actionTypes" ; import { AppStore } from "../store" ; import { RecorderStatus } from "./store" ; import { Bundle } from "../linker" ; import { ReplayContext } from "../player/sagas" ; import { App } from "../index" ; import { PlayerInstant , playerReset } from "../player" ; export interface AutoRecordingParams { sliceName : string ; actionNames : string [ ] ; actions : any ; reducers : any ; initialState ? : any ; onResetDisabled ? : boolean ; } export function addAutoRecordingBehaviour ( { recordApi , replayApi } , { sliceName , actions , actionNames , reducers , initialState , onResetDisabled } ) { for ( let actionName of actionNames ) { const action = actions [ actionName ] ; recordApi . on ( action . type , function * ( addEvent , { payload } ) { yield call ( addEvent , action . type , payload ) ; } ) ; if ( initialState ) { replayApi . on ( 'start' , function * ( ) { yield put ( { type : PlayerActionTypes . PlayerReset , payload : { sliceName , state : initialState } } ) ; } ) ; } replayApi . on ( action . type , function * ( replayContext , event ) { const payload = event [ 2 ] ; yield put ( { type : action . type , payload } ) ; } ) ; } if ( ! onResetDisabled ) { replayApi . onReset ( function * ( instant ) { const sliceState = instant . state [ sliceName ] ; if ( sliceState ) { yield put ( playerReset ( { sliceName , state : sliceState } ) ) ; } } ) ; } } export interface RecordApi { onStart : Function ; start : any ; on : ( actionType : string , handler ) => void ; } export default function ( bundle ) { const startSagas = [ ] ; const actionHandlers = new Map ( ) ; const recordApi = { onStart : function ( saga ) { startSagas . push ( saga ) ; } , start : function * ( ) { const init = { } ; for ( let saga of startSagas ) { yield call ( saga , init ) ; } const event = [ 0 , 'start' , init ] ; yield put ( { type : ActionTypes . RecorderAddEvent , event } ) ; } , on : function ( actionType , handler ) { if ( actionHandlers . has ( actionType ) ) { throw new Error ( `multiple record handlers for ${ actionType }` ) ; } actionHandlers . set ( actionType , handler ) ; } } ; bundle . defineValue ( 'recordApi' , recordApi ) ; bundle . defineAction ( ActionTypes . RecorderAddEvent ) ; bundle . addReducer ( ActionTypes . RecorderAddEvent , ( state , action ) => { const { event } = action ; state . recorder . events . push ( event ) ; } ) ; bundle . defineAction ( ActionTypes . RecorderTruncate ) ; bundle . addReducer ( ActionTypes . RecorderTruncate , ( state , { payload : { position , audioTime } } ) => { state . recorder . events = state . recorder . events . slice ( 0 , position ) ; state . recorder . junkTime = ( state . recorder . suspendedAt - audioTime ) ; } ) ; bundle . defineAction ( ActionTypes . RecorderPopEvent ) ; bundle . addReducer ( ActionTypes . RecorderPopEvent , ( state ) => { state . recorder . events . pop ( ) ; } ) ; bundle . addSaga ( function * recordEvents ( ) { yield takeLatest ( ActionTypes . RecorderReady , function * ( action ) { const pattern = Array . from ( actionHandlers . keys ( ) ) ; yield take ( ActionTypes . RecorderStarted ) ; const channel = yield actionChannel ( pattern ) ; let done = false ; while ( ! done ) { const recordAction = yield take ( channel ) ; if ( recordAction . payload && false === recordAction . payload . record ) { console . log ( 'skip record' ) ; continue ; } const state = yield select ( ) ; const recorder = state . recorder ; const status = recorder . status ; if ( status !== RecorderStatus . Recording ) { continue ; } const audioTime = Math . round ( action . payload . recorderContext . audioContext . currentTime * 1000 ) - recorder . junkTime ; function * addEvent ( name , ... args ) { const event = [ audioTime , name , ... args ] ; yield put ( { type : ActionTypes . RecorderAddEvent , event } ) ; if ( name === 'end' ) { done = true ; } } yield call ( actionHandlers . get ( recordAction . type ) , addEvent , recordAction ) ; } channel . close ( ) ; } ) ; } ) ; }