插件扩展
插件扩展通常不包含具体的界面,但可以在界面初始化及关键事件触发时得到通知并执行代码。例如可以通过监听用户发送消息,并在消息发送之前修改消息的内容。
每一个插件扩展需要提供一个入口模块文件,在 package.json 文件中通过 main 属性指定。如果不指定此文件则默认使用扩展包目录的 index.js 文件作为主入口模块文件。扩展主入口模块文件为一个 JavaScript 模块,当喧喧加载完毕时会逐个加载各个扩展的主入口模块。在扩展主入口模块中可以访问全局扩展对象 global.Xext 。扩展主入口模块应该返回一个对象,该对象可以包含如下生命周期函数:
扩展主入口模块
| 函数 | 说明 | 参数 |
onAttach(ext) | 当扩展被加载后调用,此时可以对扩展进行初始化 |
|
onReady(ext) | 当界面加载完毕时调用,此时扩展可以处理与界面相关操作。 |
|
onDetach(ext) | 当扩展被卸载时调用,此时应该将扩展使用的资源进行释放,例如销毁定时器等 |
|
onUserLogin(user, error) | 当用户登录完成时调用; |
|
onUserLoginout(user) | 当当前登录的退出登录时调用 |
|
onUserStatusChange(status, oldStatus, user) | 当用户状态发生变化时调用 |
用户状态代码含义:
|
onSendChatMessages(messages, chat, user) | 当用户发送聊天消息时调用 |
|
onReceiveChatMessages(messages, user) | 当用户接收到聊天消息时调用 |
|
onRenderChatMessageContent(content) | 当在界面上需要转化 markdown 格式的消息文本为 html 时会调用此回调方法 |
|
MainView | 当作为内嵌应用时的 React 实现的界面主组件 |
|
replaceViews | 用于配置替换系统内置界面组件 |
|
commands | 扩展支持的命令 |
|
contextMenuCreators | 为消息增加操作菜单 |
|
urlInspectors | 网址解释器,可以将消息中的网址渲染成卡片形式 |
|
下面为一个简单等插件扩展主入口模块示例:
// 从全局扩展对象中引入模块
const {
app,
components,
utils
} = global.Xext;
// 用于存储计时器标志
let timerTask = null;
module.exports = {
onAttach: (ext) => {
// 扩展加载完毕了, 此时设置一个计时器,在加载完成 10 秒中之后在界面上显示一个消息
timerTask = setTimeout(() => {
alert('扩展加载完成已经 10 秒钟了,刚刚加载等扩展名称是:' + ext.displayName);
});
},
onDetach: (ext) => {
// 扩展将被卸载,此时应该清理计时器
clearTimeout(timerTask);
timerTask = null;
},
onUserLogin: (user, error) => {
// 当用户登录时在此处可以进行相关操作,下面以显示当前登录等结果和用户名为例
if (user && !error) { // 表示登录成功
components.Modal.alert('用户登录成功了,用户名称是:' + user.displayName);
} else {
components.Modal.alert('用户登录失败了。');
}
},
}应用的插件机制
当一个扩展类型为 app (应用)时,同样可以在 package.json 文件中使用 main 属性指定一个主入口模块文件,从而使得一个应用扩展具备插件扩展的机制。同理,也可以将此方式理解为一个包含应用界面的插件。
replaceViews :界面替换机制
在主入口模块中可以使用 replaceViews 字段指定一个对象来替换喧喧默认的界面组件,这些组件在 /app/views 目录下。replaceViews 对象的键名为要替换的组件路径,键值为要用来替换的 React 组件类或组件函数。通过界面替换机制,可以使用插件的形式来定制喧喧的界面,例如将官方的登录界面替换为自己的实现。
下面的例子将展示使用自定义的 React 组件来替换官方的用户头像组件。这样可以将官方的圆形用户头像替换为方形的头像。更加详细的代码参考官方例子 replace-user-avatar-example。
// 主入口文件 index.js
const UserAvatar = require('./user-avatar');
module.exports = {
replaceViews: {
'common/user-avatar': UserAvatar,
}
};// user-avatar.js 文件
// 从全局扩展对象中引入模块
const {
views,
components,
utils,
nodeModules,
} = global.Xext;
const {React} = nodeModules;
const {PropTypes, Component} = React;
const {StatusDot} = views.common;
const {Avatar, Emojione} = components;
const {HtmlHelper} = utils;
let todayTime = new Date();
todayTime.setHours(0, 0, 0, 0);
todayTime = todayTime.getTime();
class UserAvatar extends Component {
render() {
const user = this.props.user;
const className = this.props.className;
const showStatusDot = this.props.showStatusDot;
// 使用 react 形式返回新的用户头像
}
}
UserAvatar.propTypes = {
user: PropTypes.object,
className: PropTypes.string,
showStatusDot: PropTypes.bool,
};
UserAvatar.defaultProps = {
className: null,
showStatusDot: null,
user: null,
};
module.exports = UserAvatar;