agora-edu-core
Version:
Core APIs for building an online classroom
465 lines (445 loc) • 17.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AgoraWidgetController = void 0;
var _agoraRteSdk = require("agora-rte-sdk");
var _difference = _interopRequireDefault(require("lodash/difference"));
var _get = _interopRequireDefault(require("lodash/get"));
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
var _mobx = require("mobx");
var _configs = require("../../../../configs");
var _helper = require("./helper");
var _type = require("./type");
var _batch = require("../../../../utils/batch");
var _dec, _dec2, _dec3, _class, _class2, _descriptor;
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _initializerDefineProperty(e, i, r, l) { r && Object.defineProperty(e, i, { enumerable: r.enumerable, configurable: r.configurable, writable: r.writable, value: r.initializer ? r.initializer.call(l) : void 0 }); }
function _applyDecoratedDescriptor(i, e, r, n, l) { var a = {}; return Object.keys(n).forEach(function (i) { a[i] = n[i]; }), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = !0), a = r.slice().reverse().reduce(function (r, n) { return n(i, e, r) || r; }, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a; }
function _initializerWarningHelper(r, e) { throw Error("Decorating class property failed. Please ensure that transform-class-properties is enabled and runs after the decorators transform."); }
let AgoraWidgetController = exports.AgoraWidgetController = (_dec = _agoraRteSdk.Log.attach({
proxyMethods: false
}), _dec2 = _mobx.action.bound, _dec3 = _mobx.action.bound, _dec(_class = (_class2 = class AgoraWidgetController {
constructor(_scene, _api, _stateListeners) {
this._scene = _scene;
this._api = _api;
this._stateListeners = _stateListeners;
this.logger = void 0;
this._eventBus = new _agoraRteSdk.AGEventEmitter();
this._zIndexController = new AgoraWidgetZIndexController();
_initializerDefineProperty(this, "_widgetIds", _descriptor, this);
this._cache = (0, _helper.createClosure)();
this._addEventListener();
}
/**
* widget消息总线
*/
get eventBus() {
return this._eventBus;
}
/**
* 当前存在的 Widget 实例ID列表
*/
get widgetIds() {
return this._widgetIds;
}
/**
* 层级控制器
*/
get zIndexController() {
return this._zIndexController;
}
/**
* 房间配置
*/
get classroomConfig() {
return _configs.EduClassroomConfig.shared;
}
/**
* 添加一个组件状态监听器
* @param listener
*/
/** @en
*
* @param listener
*/
addWidgetStateListener(listener) {
this._stateListeners.push(listener);
}
/**
* 移除一个组件状态监听器
* @param listener
*/
removeWidgetStateListener(listener) {
this._stateListeners = this._stateListeners.filter(ls => listener !== ls);
}
/**
* 向指定 Widget 发送消息
* @param toWidgetId
* @param messageType
* @param args
*/
sendMessage(toWidgetId, messageType, message) {
this._eventBus.emit(`widget-message-${toWidgetId}-${messageType}`, message);
}
/**
* 增加一个消息监听器
* @param listener
*/
addWidgetMessageListener(listener) {
this._eventBus.on(`widget-message-${listener.widgetId}-${listener.messageType}`, listener.onMessage);
}
/**
* 移除一个消息监听器
* @param listener
*/
removeWidgetMessageListener(listener) {
this._eventBus.off(`widget-message-${listener.widgetId}-${listener.messageType}`, listener.onMessage);
}
/**
* 广播消息
*/
broadcast(messageType, message) {
this._eventBus.emit(`broadcast:${messageType}`, message);
}
/**
* 监听广播消息
*/
addBroadcastListener(listener) {
this._eventBus.on(`broadcast:${listener.messageType}`, listener.onMessage);
}
/**
* 移除一个广播消息监听器
*/
removeBroadcastListener(listener) {
this._eventBus.off(`broadcast:${listener.messageType}`, listener.onMessage);
}
/**
* 设置 widget 为活跃
* @param widgetId
* @param extra
* @returns
*/
setWidegtActive(widgetId, props, ownerUserUuid) {
return this._api.updateWidgetProperties(this._scene.sceneId, widgetId, _objectSpread(_objectSpread({}, props), {}, {
ownerUserUuid,
state: _type.WidgetState.Active
}));
}
/**
* 设置 widget 为不活跃
* @param widgetId
* @returns
*/
setWidgetInactive(widgetId, props) {
return this._api.updateWidgetProperties(this._scene.sceneId, widgetId, _objectSpread(_objectSpread({}, props), {}, {
state: _type.WidgetState.Inactive
}));
}
/**
* 删除房间 Widget
* @param widgetId
* @param keys
* @returns
*/
deleteWidget(widgetId) {
return this._api.deleteWidget(this._scene.sceneId, widgetId);
}
/**
* 删除用户属性字段
* @param widgetId
* @param keys
* @returns
*/
removeWidgetUserProperties(widgetId, keys) {
const {
userUuid
} = _configs.EduClassroomConfig.shared.sessionInfo;
return this._api.removeWidgetUserProperties(this._scene.sceneId, widgetId, userUuid, {
properties: keys
});
}
/**
* 删除扩展信息字段
* @param widgetId
* @param keys
* @returns
*/
removeWidgetExtraProperties(widgetId, keys) {
return this._api.removeWidgetExtraProperties(this._scene.sceneId, widgetId, {
properties: keys
});
}
/**
* 更新 widget 属性
* @param widgetId
* @param props
* @returns
*/
updateWidgetProperties(widgetId, props) {
return this._api.updateWidgetProperties(this._scene.sceneId, widgetId, _objectSpread({}, props));
}
/**
* 更新 widget 用户属性
* @param widgetId
* @param props
*/
updateWidgetUserProperties(widgetId, props) {
const {
userUuid
} = _configs.EduClassroomConfig.shared.sessionInfo;
this._api.updateWidgetUserProperties(this._scene.sceneId, widgetId, userUuid, _objectSpread({}, props));
}
/**
* 销毁
*/
destroy() {
this._removeEventListener();
}
/**
* 获取 Widget 属性
* @param widgetId
* @returns
*/
getWidgetProperties(widgetId) {
const key = `properties-${widgetId}`;
const prev = this._cache.prev(key);
return prev;
}
/**
* 获取 Widget 用户属性
* @param widgetId
* @returns
*/
getWidgetUserProperties(widgetId) {
const key = `user-${widgetId}`;
const prev = this._cache.prev(key);
return prev;
}
/**
* 获取 Widget 轨迹同步信息
* @param widgetId
* @returns
*/
getWidgetTrack(widgetId) {
const key = `track-${widgetId}`;
const prev = this._cache.prev(key);
return prev;
}
/**
* 获取 Widget 状态
* @param widgetId
*/
getWidgetState(widgetId) {
const key = `state-${widgetId}`;
const prev = this._cache.prev(key);
return prev;
}
_addEventListener() {
this._scene.on(_agoraRteSdk.AgoraRteEventType.RoomPropertyUpdated, this._handleRoomPropertiesChange);
this._scene.on(_agoraRteSdk.AgoraRteEventType.UserPropertyUpdated, this._handleUserPropertiesChange);
this._scene.on(_agoraRteSdk.AgoraRteEventType.UserPropertyListUpdated, this._updateBatchUserProperties);
this._scene.on(_agoraRteSdk.AgoraRteEventType.UserAdded, this._handleUserAdded);
}
_removeEventListener() {
this._scene.off(_agoraRteSdk.AgoraRteEventType.RoomPropertyUpdated, this._handleRoomPropertiesChange);
this._scene.off(_agoraRteSdk.AgoraRteEventType.UserPropertyUpdated, this._handleUserPropertiesChange);
this._scene.off(_agoraRteSdk.AgoraRteEventType.UserPropertyListUpdated, this._updateBatchUserProperties);
this._scene.off(_agoraRteSdk.AgoraRteEventType.UserAdded, this._handleUserAdded);
}
_handleWidgetStateChange(widgetId, state) {
this._stateListeners.forEach(listener => {
if (state === _type.WidgetState.Active) {
listener.onActive(widgetId);
} else if (state === _type.WidgetState.Inactive) {
listener.onInactive(widgetId);
}
});
}
_handleWidgetPropertiesChange(widgetId, props) {
this._stateListeners.forEach(listener => {
listener.onPropertiesUpdate(widgetId, props);
});
}
_handleWidgetUserPropertiesChange(widgetId, props) {
this._stateListeners.forEach(listener => {
listener.onUserPropertiesUpdate(widgetId, props);
});
}
_handleWidgetTrackChange(widgetId, track) {
this._stateListeners.forEach(listener => {
listener.onTrackUpdate(widgetId, track);
});
}
_extractProps(props) {
var _others$extra;
// pick out state prop which cause additional change callback
const {
state,
size,
position
} = props,
others = _objectWithoutProperties(props, ["state", "size", "position"]);
const _ref = (_others$extra = others.extra) !== null && _others$extra !== void 0 ? _others$extra : {},
{
zIndex
} = _ref,
extra = _objectWithoutProperties(_ref, ["zIndex"]);
const rs = [state, _objectSpread(_objectSpread({}, others), {}, {
extra
})];
if (props.size !== undefined || props.position !== undefined) {
rs.push({
size: props.size,
position: props.position,
zIndex
});
}
return rs;
}
_compareToPrevious(widgetId, keyPath, next) {
const key = `${keyPath}-${widgetId}`;
const prev = this._cache.prev(key);
const rawPrev = JSON.stringify(prev);
const rawNext = JSON.stringify(next);
// prevent unnecessary re-rendering
if (rawPrev !== rawNext) {
this._cache.next(key, next);
return true;
}
return false;
}
_clearCache(widgetId) {
this._cache.next(`properties-${widgetId}`, undefined);
this._cache.next(`user-${widgetId}`, undefined);
this._cache.next(`track-${widgetId}`, undefined);
this._cache.next(`state-${widgetId}`, undefined);
}
_handleRoomPropertiesChange(changedRoomProperties, roomProperties, operator, cause) {
// 处理变更逻辑
changedRoomProperties.forEach((0, _mobx.action)(key => {
if (key === 'widgets') {
const widgetsMap = (0, _get.default)(roomProperties, `widgets`, {});
const widgetIds = Object.keys(widgetsMap).sort();
const removedWidgetIds = (0, _difference.default)(this._widgetIds, widgetIds);
// Widget被移除,反激活
removedWidgetIds.forEach(widgetId => {
this._handleWidgetStateChange(widgetId, _type.WidgetState.Inactive);
this._clearCache(widgetId);
});
// 阻止无意义的变更
if (!(0, _isEqual.default)(this._widgetIds, widgetIds)) {
this._widgetIds = widgetIds;
}
// 遍历当前widget列表并更新widget状态
for (const widgetId in widgetsMap) {
const widgetProperties = (0, _get.default)(roomProperties, `widgets.${widgetId}`, {});
const [state, props, trackProps] = this._extractProps(widgetProperties);
// 处理属性更新逻辑
const propertiesChanged = this._compareToPrevious(widgetId, 'properties', props);
if (propertiesChanged) {
this._handleWidgetPropertiesChange(widgetId, props);
}
const trackChanged = this._compareToPrevious(widgetId, 'track', trackProps);
// 处理轨迹同步更新逻辑
if (trackChanged && operator.userUuid !== _configs.EduClassroomConfig.shared.sessionInfo.userUuid) {
this._handleWidgetTrackChange(widgetId, trackProps);
}
const stateCacheKey = `state-${widgetId}`;
const prevState = this._cache.prev(stateCacheKey);
const becomeActive = prevState !== _type.WidgetState.Active && state === _type.WidgetState.Active;
const becomeInactive = prevState === _type.WidgetState.Active && state === _type.WidgetState.Inactive;
// 处理 widget 激活逻辑
if (becomeActive) {
this._handleWidgetStateChange(widgetId, _type.WidgetState.Active);
}
// 处理 widget 反激活逻辑
if (becomeInactive) {
this._handleWidgetStateChange(widgetId, _type.WidgetState.Inactive);
}
this._cache.next(stateCacheKey, state);
}
}
}));
}
_handleUserPropertiesChange(userUuid, userProperties, operator, cause) {
const {
userUuid: localUserUuid
} = _configs.EduClassroomConfig.shared.sessionInfo;
if (userUuid === localUserUuid) {
const widgetsMap = (0, _get.default)(userProperties, `widgets`, {});
// 遍历当前widget列表并更新widget状态
for (const widgetId in widgetsMap) {
const widgetProperties = (0, _get.default)(userProperties, `widgets.${widgetId}`, {});
// 处理属性变更逻辑
const changed = this._compareToPrevious(widgetId, 'user', widgetProperties);
if (changed) {
this._handleWidgetUserPropertiesChange(widgetId, widgetProperties);
}
}
}
}
_updateBatchUserProperties(users, batch, operator, cause) {
_batch.BatchRecord.getBatchRecord(batch.id).setCurrent(batch.index).setTotal(batch.total).setCallback(batchArray => {
const allUsers = batchArray.flat();
for (const user of allUsers) {
const {
userUuid,
userProperties
} = user;
this._handleUserPropertiesChange(userUuid, userProperties);
}
}).addChunk(users).execute();
}
_handleUserAdded(users) {
users.forEach(_ref2 => {
let {
userUuid,
userProperties
} = _ref2;
const {
userUuid: localUserUuid
} = _configs.EduClassroomConfig.shared.sessionInfo;
if (userUuid === localUserUuid) {
const widgetsMap = userProperties.get('widgets') || {};
// 遍历当前widget列表并更新widget状态
for (const widgetId in widgetsMap) {
const widgetProperties = (0, _get.default)(widgetsMap, widgetId, {});
// 处理属性变更逻辑
const changed = this._compareToPrevious(widgetId, 'user', widgetProperties);
if (changed) {
this._handleWidgetUserPropertiesChange(widgetId, widgetProperties);
}
}
}
});
}
}, _descriptor = _applyDecoratedDescriptor(_class2.prototype, "_widgetIds", [_mobx.observable], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return [];
}
}), _applyDecoratedDescriptor(_class2.prototype, "widgetIds", [_mobx.computed], Object.getOwnPropertyDescriptor(_class2.prototype, "widgetIds"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_handleRoomPropertiesChange", [_dec2], Object.getOwnPropertyDescriptor(_class2.prototype, "_handleRoomPropertiesChange"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_handleUserPropertiesChange", [_agoraRteSdk.bound], Object.getOwnPropertyDescriptor(_class2.prototype, "_handleUserPropertiesChange"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_updateBatchUserProperties", [_dec3], Object.getOwnPropertyDescriptor(_class2.prototype, "_updateBatchUserProperties"), _class2.prototype), _applyDecoratedDescriptor(_class2.prototype, "_handleUserAdded", [_agoraRteSdk.bound], Object.getOwnPropertyDescriptor(_class2.prototype, "_handleUserAdded"), _class2.prototype), _class2)) || _class);
class AgoraWidgetZIndexController {
constructor() {
this._latestZIndex = 0;
}
incrementZIndex() {
return this._latestZIndex += 1;
}
get latestZIndex() {
return this._latestZIndex;
}
setZIndex(zIndex) {
this._latestZIndex = Math.max(this._latestZIndex, zIndex);
}
}