插件扩展
插件扩展通常不包含具体的界面,但可以在界面初始化及关键事件触发时得到通知并执行代码。例如可以通过监听用户发送消息,并在消息发送之前修改消息的内容。
每一个插件扩展需要提供一个入口模块文件,在 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;