@tencentcloud/roomkit-electron-vue3
Version:
<h1 align="center"> TUIRoomKit</h1> Conference (TUIRoomKit) is a product suitable for multi-person audio and video conversation scenarios such as business meetings, webinars, and online education. By integrating this product, you can add room management,
573 lines (526 loc) • 18.6 kB
text/typescript
import mitt from 'mitt';
import useGetRoomEngine from '../hooks/useRoomEngine';
import { EventType, IRoomService, RoomInitData, RoomParam } from './types';
import TUIRoomEngine, {
TRTCVideoFillMode,
TRTCVideoMirrorType,
TRTCVideoRotation,
TUIKickedOutOfRoomReason,
TUIRole,
TUIRoomEvents,
} from '@tencentcloud/tuiroom-engine-electron';
import { useBasicStore } from '../stores/basic';
import { UserInfo, useRoomStore } from '../stores/room';
import { useChatStore } from '../stores/chat';
import useDeviceManager from '../hooks/useDeviceManager';
import logger from '../utils/common/logger';
import { isMobile } from '../utils/environment';
import i18n from '../locales';
import { MESSAGE_DURATION } from '../constants/message';
import {
ComponentConfig,
ComponentManager,
ComponentName,
} from './manager/componentManager';
import { ConfigManager, LanguageOption, Theme } from './manager/configManager';
import { SelfInfoOptions, UserManager } from './manager/userManager';
import { LifeCycleManager } from './manager/lifeCycleManager';
import { MediaManager } from './manager/mediaManager';
import { TrackingManager } from './manager/trackingManager';
import {
JoinParams,
RoomActionManager,
StartParams,
RoomParamsInfo,
} from './manager/roomActionManager';
import { WaterMark } from './function/waterMark';
import { VirtualBackground } from './function/virtualBackground';
import { BasicBeauty } from './function/basicBeauty';
import { ScheduleConferenceManager } from './manager/scheduleConferenceManager';
import { ConferenceInvitationManager } from './manager/conferenceInvitationManager';
import { DataReportManager } from './manager/dataReportManager';
import { ErrorHandler } from './function/errorHandler';
import { ChatManager } from './manager/chatManager';
import { WidgetsManager } from './manager/widgetsManager';
import { TUILogin } from '@tencentcloud/tui-core';
import { AITask } from './function/aiTask';
import initWidgets from '../components/common/widgets';
const { t } = i18n.global;
const logPrefix = '[RoomService]';
export const roomEngine = useGetRoomEngine();
export class RoomService implements IRoomService {
static instance?: RoomService;
public roomEngine = roomEngine;
public t = t;
private emitter = mitt();
public componentManager = new ComponentManager(this);
public configManager = new ConfigManager(this);
public userManager = new UserManager(this);
public mediaManager = new MediaManager(this);
public lifeCycleManager: LifeCycleManager = new LifeCycleManager(this);
public roomActionManager: RoomActionManager = new RoomActionManager(this);
public waterMark = new WaterMark(this);
public virtualBackground = new VirtualBackground(this);
public basicBeauty = new BasicBeauty(this);
public scheduleConferenceManager: ScheduleConferenceManager =
new ScheduleConferenceManager(this);
public conferenceInvitationManager: ConferenceInvitationManager =
new ConferenceInvitationManager(this);
public dataReportManager: DataReportManager = new DataReportManager();
public errorHandler: ErrorHandler = new ErrorHandler(this);
public chatManager: ChatManager = new ChatManager(this);
public aiTask: AITask = new AITask(this);
public trackingManager: TrackingManager = new TrackingManager();
public widgetsManager: WidgetsManager = new WidgetsManager(this);
get basicStore() {
return useBasicStore();
}
get roomStore() {
return useRoomStore();
}
get chatStore() {
return useChatStore();
}
constructor() {
this.lifeCycleManager.start();
this.initEventCtx();
TUIRoomEngine.once('ready', () => {
this.bindRoomEngineEvents();
this.handleRoomEngineReady();
this.emit(EventType.SERVICE_READY);
});
}
private initEventCtx() {
this.onError = this.onError.bind(this);
this.onRoomDismissed = this.onRoomDismissed.bind(this);
this.onUserNetworkQualityChanged =
this.onUserNetworkQualityChanged.bind(this);
this.onKickedOutOfRoom = this.onKickedOutOfRoom.bind(this);
this.onSendMessageForUserDisableChanged =
this.onSendMessageForUserDisableChanged.bind(this);
this.onUserSigExpired = this.onUserSigExpired.bind(this);
this.onKickedOffLine = this.onKickedOffLine.bind(this);
this.onAllUserCameraDisableChanged =
this.onAllUserCameraDisableChanged.bind(this);
this.onAllUserMicrophoneDisableChanged =
this.onAllUserMicrophoneDisableChanged.bind(this);
this.onScreenShareForAllUserDisableChanged =
this.onScreenShareForAllUserDisableChanged.bind(this);
this.onUserInfoChanged = this.onUserInfoChanged.bind(this);
}
static getInstance(): RoomService {
if (!RoomService.instance) {
RoomService.instance = new RoomService();
}
return RoomService.instance;
}
static destroyInstance(): void {
if (!RoomService.instance) return;
RoomService.instance.lifeCycleManager.stop();
RoomService.instance.unBindRoomEngineEvents();
RoomService.instance.waterMark.dispose();
RoomService.instance.virtualBackground.dispose();
RoomService.instance.basicBeauty.dispose();
RoomService.instance.scheduleConferenceManager.dispose();
RoomService.instance.conferenceInvitationManager.dispose();
RoomService.instance.mediaManager.dispose();
RoomService.instance.chatManager.dispose();
RoomService.instance = undefined;
}
public useExtension(extension: any) {
// eslint-disable-next-line no-underscore-dangle
extension._bind(this);
}
public getRoomContainer(): HTMLElement | Element {
return (
document.fullscreenElement ||
document.getElementById('roomContainer') ||
document.body
);
}
public initMediaDeviceList() {
useDeviceManager({ listenForDeviceChange: true });
}
public bindRoomEngineEvents() {
roomEngine.instance?.on(TUIRoomEvents.onError, this.onError);
roomEngine.instance?.on(
TUIRoomEvents.onRoomDismissed,
this.onRoomDismissed
);
roomEngine.instance?.on(
TUIRoomEvents.onUserNetworkQualityChanged,
this.onUserNetworkQualityChanged
);
roomEngine.instance?.on(
TUIRoomEvents.onKickedOutOfRoom,
this.onKickedOutOfRoom
);
roomEngine.instance?.on(
TUIRoomEvents.onSendMessageForUserDisableChanged,
this.onSendMessageForUserDisableChanged
);
roomEngine.instance?.on(
TUIRoomEvents.onUserSigExpired,
this.onUserSigExpired
);
roomEngine.instance?.on(
TUIRoomEvents.onKickedOffLine,
this.onKickedOffLine
);
roomEngine.instance?.on(
TUIRoomEvents.onAllUserCameraDisableChanged,
this.onAllUserCameraDisableChanged
);
roomEngine.instance?.on(
TUIRoomEvents.onAllUserMicrophoneDisableChanged,
this.onAllUserMicrophoneDisableChanged
);
roomEngine.instance?.on(
TUIRoomEvents.onScreenShareForAllUserDisableChanged,
this.onScreenShareForAllUserDisableChanged
);
roomEngine.instance?.on(
TUIRoomEvents.onUserInfoChanged,
this.onUserInfoChanged
);
}
public unBindRoomEngineEvents() {
roomEngine.instance?.off(TUIRoomEvents.onError, this.onError);
roomEngine.instance?.off(
TUIRoomEvents.onRoomDismissed,
this.onRoomDismissed
);
roomEngine.instance?.off(
TUIRoomEvents.onUserNetworkQualityChanged,
this.onUserNetworkQualityChanged
);
roomEngine.instance?.off(
TUIRoomEvents.onKickedOutOfRoom,
this.onKickedOutOfRoom
);
roomEngine.instance?.off(
TUIRoomEvents.onSendMessageForUserDisableChanged,
this.onSendMessageForUserDisableChanged
);
roomEngine.instance?.off(
TUIRoomEvents.onUserSigExpired,
this.onUserSigExpired
);
roomEngine.instance?.off(
TUIRoomEvents.onKickedOffLine,
this.onKickedOffLine
);
roomEngine.instance?.off(
TUIRoomEvents.onAllUserCameraDisableChanged,
this.onAllUserCameraDisableChanged
);
roomEngine.instance?.off(
TUIRoomEvents.onAllUserMicrophoneDisableChanged,
this.onAllUserMicrophoneDisableChanged
);
roomEngine.instance?.off(
TUIRoomEvents.onScreenShareForAllUserDisableChanged,
this.onScreenShareForAllUserDisableChanged
);
roomEngine.instance?.off(
TUIRoomEvents.onUserInfoChanged,
this.onUserInfoChanged
);
}
private onError(error: any) {
logger.error('roomEngine.onError: ', error);
this.errorHandler.handleError(error, 'onError');
}
private onRoomDismissed(eventInfo: { roomId: string }) {
const { roomId } = eventInfo;
logger.log(`${logPrefix}onRoomDismissed:`, roomId);
this.emit(EventType.ROOM_NOTICE_MESSAGE_BOX, {
title: t('Note'),
message: t('The host closed the room.'),
confirmButtonText: t('Sure'),
duration: 5000,
callback: async () => {
this.emit(EventType.ROOM_DISMISS, {});
this.resetStore();
},
});
}
private onUserNetworkQualityChanged(eventInfo: { userNetworkList: [] }) {
this.basicStore.setLocalQuality(eventInfo.userNetworkList);
}
private async onKickedOutOfRoom(eventInfo: {
roomId: string;
reason: TUIKickedOutOfRoomReason;
message: string;
}) {
const { roomId, reason, message } = eventInfo;
try {
let notice = '';
switch (reason) {
case TUIKickedOutOfRoomReason.kKickedByAdmin:
notice = t('kicked out of the room by the host');
break;
case TUIKickedOutOfRoomReason.kKickedByLoggedOnOtherDevice:
notice = t('Kicked out of the room on other devices');
break;
case TUIKickedOutOfRoomReason.kKickedByServer:
notice = t('kicked out of the room by serve');
break;
case TUIKickedOutOfRoomReason.kNetworkDisconnected:
notice = t('Network disconnection timeout, exiting room');
break;
case TUIKickedOutOfRoomReason.kJoinRoomStatusInvalidDuringOffline:
notice = t(
'You have been kicked out of a room or the room has been disbanded while you were offline'
);
break;
case TUIKickedOutOfRoomReason.kCountOfJoinedRoomsExceedLimit:
notice = t('The number of joined rooms has exceeded the limit');
break;
default:
notice = t('kicked out of the room');
}
this.emit(EventType.ROOM_NOTICE_MESSAGE_BOX, {
title: t('Note'),
message: notice,
confirmButtonText: t('Sure'),
callback: async () => {
this.emit(EventType.KICKED_OUT, { roomId, reason, message });
this.resetStore();
},
});
} catch (error) {
logger.error(`${logPrefix}onKickedOutOfRoom error:`, error);
}
}
private onSendMessageForUserDisableChanged(data: {
userId: string;
isDisable: boolean;
}) {
const { userId, isDisable } = data;
if (userId === this.roomStore.localUser.userId) {
const tipMessage = isDisable
? t('You have been banned from text chat')
: t('You are allowed to text chat');
this.emit(EventType.ROOM_NOTICE_MESSAGE, {
code: -1,
type: 'warning',
message: tipMessage,
duration: MESSAGE_DURATION.NORMAL,
});
this.chatStore.setSendMessageDisableChanged(isDisable);
}
this.roomStore.setMuteUserChat(userId, isDisable);
}
private onUserSigExpired() {
this.emit(EventType.ROOM_NOTICE_MESSAGE_BOX, {
title: t('Note'),
message: t('userSig has expired'),
confirmButtonText: t('Sure'),
callback: () => {
this.emit(EventType.USER_SIG_EXPIRED, {});
this.resetStore();
},
});
}
private onKickedOffLine(eventInfo: { message: string }) {
const { message } = eventInfo;
this.emit(EventType.ROOM_NOTICE_MESSAGE_BOX, {
title: t('Note'),
message: t(
'The system has detected that your account has been kicked offline'
),
confirmButtonText: t('Sure'),
callback: async () => {
this.emit(EventType.KICKED_OFFLINE, { message });
},
});
}
private async onAllUserCameraDisableChanged(eventInfo: {
roomId: string;
isDisable: boolean;
}) {
const { isDisable } = eventInfo;
if (
isDisable !== this.roomStore.isCameraDisableForAllUser &&
this.roomStore.localUser.userRole === TUIRole.kGeneralUser
) {
this.roomStore.setCanControlSelfVideo(!isDisable);
}
this.handleVideoStateChange(isDisable);
this.roomStore.setDisableCameraForAllUserByAdmin(isDisable);
}
private async handleVideoStateChange(isDisableVideo: boolean) {
const tipMessage = isDisableVideo
? t('All videos disabled')
: t('All videos enabled');
this.emit(EventType.ROOM_NOTICE_MESSAGE, {
type: 'success',
message: tipMessage,
duration: MESSAGE_DURATION.NORMAL,
});
/**
* If the host lifts the full ban on video, users does not actively turn up the user camera,
* If the host open and does not actively turn up the user camera
*
**/
if (
isDisableVideo &&
this.roomStore.localUser.userRole === TUIRole.kGeneralUser
) {
await roomEngine.instance?.closeLocalCamera();
}
}
private async onUserInfoChanged(eventInfo: { userInfo: UserInfo }) {
const { userId, nameCard } = eventInfo.userInfo;
const oldNameCard = this.roomStore.userInfoObj[userId]?.nameCard;
if (oldNameCard === nameCard) return;
this.roomStore.updateUserInfo({ userId, nameCard });
}
private async onAllUserMicrophoneDisableChanged(eventInfo: {
roomId: string;
isDisable: boolean;
}) {
const { isDisable } = eventInfo;
if (
isDisable !== this.roomStore.isMicrophoneDisableForAllUser &&
this.roomStore.localUser.userRole === TUIRole.kGeneralUser
) {
this.roomStore.setCanControlSelfAudio(!isDisable);
}
this.handleAudioStateChange(isDisable);
this.roomStore.setDisableMicrophoneForAllUserByAdmin(isDisable);
}
private async handleAudioStateChange(isDisableAudio: boolean) {
const tipMessage = isDisableAudio
? t('All audios disabled')
: t('All audios enabled');
this.emit(EventType.ROOM_NOTICE_MESSAGE, {
type: 'success',
message: tipMessage,
duration: MESSAGE_DURATION.NORMAL,
});
/**
* If the moderator unmutes the entire staff, users does not actively bring up the user's microphone;
* if the moderator turns on the full staff mute, users actively turns off the user's microphone
*
**/
if (
isDisableAudio &&
this.roomStore.localUser.userRole === TUIRole.kGeneralUser
) {
await roomEngine.instance?.muteLocalAudio();
}
}
private async onScreenShareForAllUserDisableChanged(eventInfo: {
roomId: string;
isDisable: boolean;
}) {
const { isDisable } = eventInfo;
this.roomStore.setDisableScreenShareForAllUserByAdmin(isDisable);
}
public resetStore() {
this.basicStore.reset();
this.chatStore.reset();
this.roomStore.reset();
}
private storeInit(option: RoomInitData) {
this.basicStore.setBasicInfo(option);
this.userManager.setLocalUser(option);
}
public async initRoomKit(option: RoomInitData) {
initWidgets(this.widgetsManager);
this.storeInit(option);
const { sdkAppId, userId, userSig } = option;
TUILogin.login({
SDKAppID: sdkAppId,
userID: userId,
userSig,
useUploadPlugin: true,
});
const { chat } = TUILogin.getContext();
await TUIRoomEngine.login({ sdkAppId, userId, userSig, tim: chat });
this.emit(EventType.ROOM_LOGIN);
}
public async start(roomId: string, params?: StartParams) {
return this.roomActionManager.start(roomId, params);
}
public async join(roomId: string, params?: JoinParams) {
return this.roomActionManager.join(roomId, params);
}
public async createRoom(options: {
roomId: string;
roomName?: string;
roomMode: 'FreeToSpeak' | 'SpeakAfterTakingSeat';
roomParam?: RoomParam;
}) {
await this.roomActionManager.createRoom(options);
}
public async enterRoom(options: { roomId: string; roomParam?: RoomParam }) {
await this.roomActionManager.enterRoom(options);
}
public async leaveRoom() {
await this.roomActionManager.leaveRoom();
}
public async dismissRoom() {
await this.roomActionManager.dismissRoom();
}
public fetchRoomInfo(options?: RoomParamsInfo) {
return this.roomActionManager.fetchRoomInfo(options);
}
public async handleRoomEngineReady() {
const storageCurrentTheme = localStorage.getItem('tuiRoom-currentTheme');
storageCurrentTheme && this.basicStore.setDefaultTheme(storageCurrentTheme);
// Set the default rendering mode of local video
const trtcCloud = roomEngine.instance?.getTRTCCloud();
// eslint-disable-next-line no-nested-ternary
const mirrorType = isMobile
? TRTCVideoMirrorType.TRTCVideoMirrorType_Auto
: this.basicStore.isLocalStreamMirror
? TRTCVideoMirrorType.TRTCVideoMirrorType_Enable
: TRTCVideoMirrorType.TRTCVideoMirrorType_Disable;
await trtcCloud?.setLocalRenderParams({
mirrorType,
rotation: TRTCVideoRotation.TRTCVideoRotation0,
fillMode: TRTCVideoFillMode.TRTCVideoFillMode_Fill,
});
}
public logOut() {
this.emit(EventType.USER_LOGOUT);
this.resetStore();
}
on(eventType: EventType, callback: (data?: any) => void) {
this.emitter.on(eventType, callback);
}
off(eventType: EventType, callback: (data?: any) => void) {
this.emitter.off(eventType, callback);
}
emit(eventType: EventType, data?: any) {
this.emitter.emit(eventType, data);
}
// Component Manager
public getComponentConfig(name: ComponentName) {
return this.componentManager.getComponentConfig(name);
}
public setComponentConfig(options: Partial<ComponentConfig>) {
return this.componentManager.setComponentConfig(options);
}
// Config Manager
public setTheme(theme: Theme) {
return this.configManager.setTheme(theme);
}
public setLanguage(language: LanguageOption) {
return this.configManager.setLanguage(language);
}
// User Manager
setSelfInfo(options: SelfInfoOptions) {
return this.userManager.setSelfInfo(options);
}
getDisplayName(options: UserInfo) {
return this.userManager.getDisplayName(options);
}
getMediaManager() {
return this.mediaManager;
}
}
export const roomService = RoomService.getInstance();