UNPKG

5.82 kBJavaScriptView Raw
1/* eslint-disable max-len */
2
3/**
4 * 插件接口
5 *
6 * <p>
7 * 插件是由一个或多个扩展点组成的字典。SDK 的扩展点可以分为两类:
8 * <p>
9 * 第一类扩展点是类实例化之后的回调,包括 <code>Realtime</code>、<code>IMClient</code> 与 <code>Conversation</code>。这些扩展点可以通过一个同步的 Decorator 进行扩展。Decorator 接受一个对应的实例并对其进行一些操作。
10 * 特别的,由于注册自定义消息类这个需求特别的常用,额外定义一个 messageClasses 扩展点来做这件事情。
11 * <p>
12 * 第二类扩展点是在某些事件处理前、后可以注入逻辑的点。
13 * 其中 <code>beforeMessageParse</code>,<code>afterMessageParse</code> 可以通过一个异步的 Middleware 进行扩展。Middleware 接受一个对象,返回一个同类型对象或同类型对象的 Promise。
14 * <code>beforeMessageDispatch</code> 可以通过返回一个 boolean 类型的 shouldDispatch 值来控制是否要继续派发收到的消息。
15 * <p>
16 * 如果使用了多个插件,这些 hook 会按照插件数组的顺序依次执行。前一个 Middleware 的返回值会作为参数传给后一个 Middleware。
17 *
18 * @interface Plugin
19 * @since 3.1.0
20 */
21
22/* eslint-enable max-len */
23
24/**
25 * 插件名称,用于在日志中显示异常的插件
26 *
27 * @name Plugin.name
28 * @type string
29 */
30
31/**
32 * 插件注册的消息类型
33 *
34 * @name Plugin.messageClasses
35 * @type AVMessage[]
36 */
37
38/**
39 * 在 Realtime 实例化后对其进行修饰。
40 * <p>
41 * 接受一个参数为 Realtime 实例。
42 *
43 * @name Plugin.onRealtimeCreate
44 * @type Function
45 */
46
47/**
48 * 在 IMClient 实例化后对其进行修饰。
49 * <p>
50 * 接受一个参数为 IMClient 实例。
51 *
52 * @name Plugin.onIMClientCreate
53 * @type Function
54 */
55
56/**
57 * 在 Conversation 实例化后对其进行修饰。
58 * <p>
59 * 接受一个参数为 Conversation 实例。
60 * 需要注意的是,该扩展点并不是在 <code>{@link IMClient#createConversation}</code> 方法创建成功后调用的 hook,
61 * 而是所有的 Conversation 实例化的时候(包括但不限于 query 时)调用的 hook。
62 *
63 * @name Plugin.onConversationCreate
64 * @type Function
65 */
66
67/**
68 * 在对消息进行 parse 之前,对原始消息进行修改。
69 * <p>
70 * 接受一个参数为原始消息,是某个消息的内容,一般是一个 JSON 对象。
71 * 该方法需要返回一个 JSON 对象。如果这个结果是异步得到的,也可以返回一个 Promise(fulfilled with a JSON)。
72 *
73 * @name Plugin.beforeMessageParse
74 * @type Function
75 */
76
77/**
78 * 在对消息进行 parse 之后,对消息实例进行修改。
79 * <p>
80 * 接受一个参数为消息实例,一般是一个已注册的 Message 类或其子类的实例。
81 * 该方法需要返回一个同类型的消息实例。如果这个结果是异步得到的,也可以返回一个 Promise。
82 *
83 * @name Plugin.afterMessageParse
84 * @type Function
85 */
86
87/**
88 * 在收到消息之后,派发消息之前,控制是否派发这条消息。
89 * <p>
90 * 接受参数为 message 与 conversation。
91 * 该方法需要返回 boolean 类型的值,如果返回 false 则 SDK 不再派发这条消息,后续的 beforeMessageDispatch 也不会执行。
92 * 如果这个结果是异步得到的,也可以返回一个 Promise。
93 *
94 * @name Plugin.beforeMessageDispatch
95 * @type Function
96 * @since 3.4.0
97 */
98
99import { ensureArray, tap } from './utils';
100
101const checkType = middleware => param => {
102 const { constructor } = param;
103 return Promise.resolve(param)
104 .then(middleware)
105 .then(
106 tap(result => {
107 if (result === undefined || result === null) {
108 // eslint-disable-next-line max-len
109 return console.warn(
110 `Middleware[${middleware._pluginName ||
111 'anonymous plugin'}:${middleware.name ||
112 'anonymous middleware'}] param/return types not match. It returns ${result} while a ${
113 param.constructor.name
114 } expected.`
115 );
116 }
117 if (!(result instanceof constructor)) {
118 // eslint-disable-next-line max-len
119 return console.warn(
120 `Middleware[${middleware._pluginName ||
121 'anonymous plugin'}:${middleware.name ||
122 'anonymous middleware'}] param/return types not match. It returns a ${
123 result.constructor.name
124 } while a ${param.constructor.name} expected.`
125 );
126 }
127 return 0;
128 })
129 );
130};
131
132export const applyDecorators = (decorators, target) => {
133 if (decorators) {
134 decorators.forEach(decorator => {
135 try {
136 decorator(target);
137 } catch (error) {
138 if (decorator._pluginName) {
139 error.message += `[${decorator._pluginName}]`;
140 }
141 throw error;
142 }
143 });
144 }
145};
146
147export const applyMiddlewares = middlewares => target =>
148 ensureArray(middlewares).reduce(
149 (previousPromise, middleware) =>
150 previousPromise.then(checkType(middleware)).catch(error => {
151 if (middleware._pluginName) {
152 // eslint-disable-next-line no-param-reassign
153 error.message += `[${middleware._pluginName}]`;
154 }
155 throw error;
156 }),
157 Promise.resolve(target)
158 );
159
160export const applyDispatcher = (dispatchers, payload) =>
161 ensureArray(dispatchers).reduce(
162 (resultPromise, dispatcher) =>
163 resultPromise
164 .then(
165 shouldDispatch =>
166 shouldDispatch === false ? false : dispatcher(...payload)
167 )
168 .catch(error => {
169 if (dispatcher._pluginName) {
170 // eslint-disable-next-line no-param-reassign
171 error.message += `[${dispatcher._pluginName}]`;
172 }
173 throw error;
174 }),
175 Promise.resolve(true)
176 );