plutoless / eEducation

e-education solutions(Agora Reference Design)
184 stars 124 forks source link

New Whiteboard class instance creation stuck #162

Closed iNishant closed 4 years ago

iNishant commented 4 years ago

We have a next.js project and we are using JS instead of TS. For some reason creating a new instance of the whiteboard class export const whiteboard = new Whiteboard() stops the code execution.

The code looks like this (after TS->JS conversion):

import { EventEmitter } from 'events'
import { videoPlugin } from '@netless/white-video-plugin'
import { audioPlugin } from '@netless/white-audio-plugin'
import {
  WhiteWebSdk,
  DeviceType,
  createPlugins,
  RoomPhase,
} from 'white-web-sdk'
import { Subject } from 'rxjs'
import { isEmpty, get } from 'lodash'
import GlobalStorage from '../utils/custom-storage'
import { roomStore } from './room'
import { globalStore } from './global'
import { t } from '../i18n'

const ENABLE_LOG = process.env.REACT_APP_AGORA_LOG === 'true'
const pathName = (path) => {
  const reg = /\/([^\/]*)\//g
  reg.exec(path)
  if (RegExp.$1 === 'aria') {
    return ''
  }
  return RegExp.$1
}
export const plugins = createPlugins({ video: videoPlugin, audio: audioPlugin })
plugins.setPluginContext('video', { identity: 'guest' })
plugins.setPluginContext('audio', { identity: 'guest' })

class Whiteboard extends EventEmitter {
  constructor() {
    super()
    this.defaultState = {
      joined: false,
      scenes: [],
      currentScenePath: '',
      currentHeight: 0,
      currentWidth: 0,
      dirs: [],
      zoomRadio: 0,
      scale: 0,
      recording: false,
      startTime: 0,
      endTime: 0,
      room: null,
      loading: true,
      ...GlobalStorage.read('mediaDirs'),
      totalPage: 0,
      currentPage: 0,
      type: 'static',
    }
    this.client = new WhiteWebSdk({
      deviceType: DeviceType.Surface,
      // handToolKey: " ",
      plugins,
      loggerOptions: {
        disableReportLog: !ENABLE_LOG,
        reportLevelMask: 'debug',
        printLevelMask: 'debug',
      },
    })
    this.operator = null
    this.subject = null
    this.state = this.defaultState
  }

  initialize() {
    this.subject = new Subject()
    this.state = { ...this.defaultState }
    this.subject.next(this.state)
  }

  subscribe(updateState) {
    this.initialize()
    this.subject && this.subject.subscribe(updateState)
  }

  unsubscribe() {
    this.subject && this.subject.unsubscribe()
    this.subject = null
  }

  commit(state) {
    this.subject && this.subject.next(state)
  }

  updateState(newState) {
    this.state = { ...this.state, ...newState }
    this.commit(this.state)
  }

  getNameByScenePath(scenePath) {
    const sceneMap = get(this.state.room, `state.globalState.sceneMap`, {})
    console.log('sceneMap', sceneMap)
    return get(sceneMap, scenePath, 'default name')
  }

  updateRoomState() {
    if (!this.state.room) return
    console.log('current room state', this.state.room.state)
    const roomState = this.state.room.state
    const path = roomState.sceneState.scenePath
    const { ppt } = roomState.sceneState.scenes[0]
    const type = isEmpty(ppt) ? 'static' : 'dynamic'
    const currentPage = roomState.sceneState.index
    const totalPage = roomState.sceneState.scenes.length
    if (type !== 'dynamic') {
      this.state = { ...this.state, currentHeight: 0, currentWidth: 0 }
    } else {
      this.state = {
        ...this.state,
        currentHeight: get(ppt, 'height', 0),
        currentWidth: get(ppt, 'width', 0),
      }
    }
    const entrieScenes = this.state.room ? this.state.room.entireScenes() : {}
    const paths = Object.keys(entrieScenes)
    const scenes = []
    for (const dirPath of paths) {
      const sceneInfo = {
        path: dirPath,
        file: {
          name: this.getNameByScenePath(dirPath),
          type: 'whiteboard',
        },
        type: 'static',
        rootPath: '',
      }
      if (entrieScenes[dirPath]) {
        sceneInfo.rootPath =
          ['/', '/init'].indexOf(dirPath) !== -1
            ? '/init'
            : `${dirPath}/${entrieScenes[dirPath][0].name}`
        sceneInfo.type = entrieScenes[dirPath][0].ppt ? 'dynamic' : 'static'
        if (sceneInfo.type === 'dynamic') {
          sceneInfo.file.type = 'ppt'
        }
      }
      scenes.push(sceneInfo)
    }
    const _dirPath = pathName(path)
    const currentScenePath = _dirPath === '' ? '/' : `/${_dirPath}`
    const _dirs = []
    scenes.forEach((it) => {
      _dirs.push({
        path: it.path,
        rootPath: it.rootPath,
        file: it.file,
      })
    })
    this.state = {
      ...this.state,
      scenes,
      currentScenePath,
      dirs: _dirs,
      totalPage,
      currentPage,
      type,
    }
    this.commit(this.state)
  }

  setCurrentScene(dirPath) {
    const currentDirIndex = this.state.dirs.findIndex(
      (it) => it.path === dirPath
    )
    this.state = { ...this.state, currentScenePath: dirPath }
    this.commit(this.state)
  }

  updateSceneState(sceneState) {
    const path = sceneState.scenePath
    const { ppt } = sceneState.scenes[0]
    const type = isEmpty(ppt) ? 'static' : 'dynamic'
    const currentPage = sceneState.index
    const totalPage = sceneState.scenes.length
    this.state = { ...this.state, currentPage, totalPage, type }
    this.commit(this.state)
  }

  updateScale(scale) {
    this.state = { ...this.state, scale }
    this.commit(this.state)
  }

  updateLoading(value) {
    this.state = { ...this.state, loading: value }
    this.commit(this.state)
  }

  async join({ rid, uuid, roomToken, location, userPayload }) {
    await this.leave()
    const identity = userPayload.identity === 'host' ? 'host' : 'guest'
    plugins.setPluginContext('video', { identity })
    plugins.setPluginContext('audio', { identity })
    const disableDeviceInputs = !!(
      location.match(/big-class/) && identity !== 'host'
    )
    const disableOperations = !!(
      location.match(/big-class/) && identity !== 'host'
    )
    const isWritable = !(location.match(/big-class/) && identity !== 'host')
    console.log(
      `[White] isWritable, ${isWritable}, disableDeviceInputs, ${disableDeviceInputs}, disableOperations, ${disableOperations}, location: ${location}`
    )
    const roomParams = {
      uuid,
      roomToken,
      disableBezier: true,
      disableDeviceInputs,
      disableOperations,
      isWritable,
    }
    const room = await this.client.joinRoom(roomParams, {
      onPhaseChanged: (phase) => {
        if (phase === RoomPhase.Connected) {
          this.updateLoading(false)
        } else {
          this.updateLoading(true)
        }
        console.log('[White] onPhaseChanged phase : ', phase)
      },
      onRoomStateChanged: (state) => {
        console.log('onRoomStateChanged', state)
        if (state.zoomScale) {
          whiteboard.updateScale(state.zoomScale)
        }
        if (state.sceneState || state.globalState) {
          whiteboard.updateRoomState()
        }
      },
      onDisconnectWithError: (error) => {},
      onKickedWithReason: (reason) => {},
      onKeyDown: (event) => {},
      onKeyUp: (event) => {},
      onHandToolActive: (active) => {},
      onPPTLoadProgress: (uuid, progress) => {},
    })
    this.state = { ...this.state, room }
    this.commit(this.state)
  }

  cleanRoom() {
    this.state = { ...this.state, room: null }
    this.commit(this.state)
  }

  async leave() {
    if (!this.state.room) return
    try {
      await this.state.room.disconnect()
    } catch (err) {
      console.warn('disconnect whiteboard failed', err)
    } finally {
      this.cleanRoom()
      console.log('cleanRoom')
    }
    this.updateLoading(true)
  }

  async destroy() {
    await this.leave()
    this.state = { ...this.defaultState }
    this.commit(this.state)
    this.removeAllListeners()
  }

  async lock() {
    const lockBoardStatus = Boolean(roomStore.state.course.lockBoard)
    const lockBoard = lockBoardStatus ? 0 : 1
    await roomStore.updateCourse({
      lockBoard,
    })
    if (lockBoard) {
      globalStore.showToast({
        type: 'notice-board',
        message: t('toast.whiteboard_lock'),
      })
    } else {
      globalStore.showToast({
        type: 'notice-board',
        message: t('toast.whiteboard_unlock'),
      })
    }
  }
}

export const whiteboard = new Whiteboard()
// TODO: Please remove it before release in production
// 备注:请在正式发布时删除操作的window属性
// @ts-ignore
window.netlessStore = whiteboard

Edit: After more investigation, this code is what is stopping the execution, any clue why this could be happening?

this.client = new WhiteWebSdk({
      deviceType: DeviceType.Surface,
      // handToolKey: " ",
      plugins,
      loggerOptions: {
        disableReportLog: !ENABLE_LOG,
        reportLevelMask: 'debug',
        printLevelMask: 'debug',
      },
    })
iNishant commented 4 years ago

Turns out it was an issue with white-web-sdk version number