joinClass method

Future<void> joinClass()

Implementation

Future<void> joinClass() async {
  TCICLog.info("joinClass: joinClass start", actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
  // 复位上一次的失败诊断
  _lastJoinClassErrorCode = null;
  _lastJoinClassRequestId = null;
  _lastJoinClassFailReason = null;
  joinClassSuccess = false;
  _classSessionActive = false;
  _stateDataObs.updateIsJoinClassSuccess(false);
  final smallWindowMode = isInSmallWindowMode();
  if (smallWindowMode) {
    TCICLog.info("joinClass: smallWindowMode is true", actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
    joinClassSuccess = true;
    _eventbus.fire(MainEvent(type: EventTypeEnum.joinClassSuccess));
    EnterClassPerf.mark('joinClassSuccess.smallWindow');
    return;
  }
  if (isGroupRoom() && isStudent() && (_config.groupLiveCode == null || _config.groupLiveCode!.isEmpty)) {
    _eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
    throw StateError('groupLiveCode is required for student in group room');
  }
  var body = JoinClassBody(classId: _config.classId, role: getRole().index, groupLiveCode: isGroupRoom() ? _config.groupLiveCode : null);
  EnterClassPerf.mark('joinClass.http.start');
  var data = await networkService.joinClass(body);
  EnterClassPerf.mark('joinClass.http.end');
  if (data != null) {
    if (data.errorCode == 0) {
      joinClassTimeStamp = data.timestamp * 1000;
      _membersInfoObs.updateJoinClassTime(data.nanoTimestamp);
      _liveSteamInfoObs.updateLiveStreamInfo(data.streams);
      _membersInfoObs.updateOnlineStudentCount(data.studentOnlineNumber);
      final Map<String, Member> formatedMembers = {};
      for (var item in data.members) {
        formatedMembers[item.userId] = item;
      }

      _markClassSessionActive('joinClassHttpSuccess');
      _membersInfoObs.updateIsSelfOnline(true, "joinClassHttpSuccess");
      _ctrlBusinessLogic.initMembers(formatedMembers);
      startHeartBeat();

      userSig = data.userSig;
      // 等待 IM 初始化完成(_initTIM 由 schoolInfoSuccess 事件触发,与 joinClass HTTP 并行)。
      // 旧实现:盲等 1s(即便 IM 已就绪也强制 sleep;若未就绪则 1s 也不够)。
      // 新实现:直接 await 初始化 Future,已就绪即 0ms 通过。
      EnterClassPerf.mark('imInit.waitStart[ready=$imInitSuccess]');
      if (!imInitSuccess) {
        await (_imInitFuture ?? Future<void>.value());
      }
      EnterClassPerf.mark('imInit.waitEnd[ready=$imInitSuccess]');
      EnterClassPerf.mark('tim.login.start');
      var loginResult = await TIM.login(userId: _config.userId, userSig: data.userSig);
      EnterClassPerf.mark('tim.login.end[ok=$loginResult]');
      if (loginResult) {
        imLoginSuccess = true;
        _eventbus.fire(MainEvent(type: EventTypeEnum.imLoginSuccess));
        EnterClassPerf.mark('joinImGroups.start');
        var joinResult = await _joinImGroups(getImJoinTargets());
        EnterClassPerf.mark('joinImGroups.end');
        if (!joinResult) {
          _lastJoinClassFailReason = 'im_join_group_failed';
          await _rollbackJoinClassSession('im_join_group_failed');
          _eventbus.fire(MainEvent(type: EventTypeEnum.someThingErrorInIm));
          TCICLog.error('join im group failed', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
          return;
        }
        joinClassSuccess = true;
        _stateDataObs.updateIsJoinClassSuccess(true);
        _eventbus.fire(MainEvent(type: EventTypeEnum.joinClassSuccess));
        EnterClassPerf.mark('joinClassSuccess');

        // 加入课堂成功后,异步获取全量成员列表(不阻塞主流程)
        _fetchAllMembersInBackground();
      } else {
        _lastJoinClassFailReason = 'im_login_failed';
        await _rollbackJoinClassSession('im_login_failed');
        _eventbus.fire(MainEvent(type: EventTypeEnum.someThingErrorInIm));
        TCICLog.error('im login failed', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
      }
    } else {
      _lastJoinClassErrorCode = data.errorCode;
      _lastJoinClassRequestId = data.requestId;
      _lastJoinClassFailReason = 'backend_reject';
      TCICLog.error(
          'joinClass: backend rejected, errorCode=${data.errorCode} requestId=${data.requestId} classId=${_config.classId}',
          actionModule: ActionModule.tcicController.name,
          actionName: ActionName.joinClass.name);
      _eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
      fireMainBreakEvent(MainProcessBreakenEventEnum.joinClassFailed, data.errorCode, requestId: data.requestId);
    }
  } else {
    _lastJoinClassErrorCode = null;
    _lastJoinClassRequestId = null;
    _lastJoinClassFailReason = 'http_null_response';
    TCICLog.error(
        'joinClass: HTTP returned null (network/timeout/parse) classId=${_config.classId}',
        actionModule: ActionModule.tcicController.name,
        actionName: ActionName.joinClass.name);
    _eventbus.fire(MainEvent(type: EventTypeEnum.joinClassFailed));
    fireMainBreakEvent(MainProcessBreakenEventEnum.joinClassFailed, 0, requestId: "");
  }
  TCICLog.info('userId: ${_config.userId}', actionModule: ActionModule.tcicController.name, actionName: ActionName.joinClass.name);
}