@twilio/conversations 2.0.1 → 2.1.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/NOTICE.txt +679 -0
- package/builds/browser.js +889 -619
- package/builds/browser.js.map +1 -1
- package/builds/lib.d.ts +384 -129
- package/builds/lib.js +889 -619
- package/builds/lib.js.map +1 -1
- package/builds/twilio-conversations.js +1065 -939
- package/builds/twilio-conversations.min.js +2 -14
- package/dist/aggregated-delivery-receipt.js +6 -1
- package/dist/aggregated-delivery-receipt.js.map +1 -1
- package/dist/client.js +165 -142
- package/dist/client.js.map +1 -1
- package/dist/command-executor.js +16 -14
- package/dist/command-executor.js.map +1 -1
- package/dist/configuration.js +14 -10
- package/dist/configuration.js.map +1 -1
- package/dist/conversation.js +232 -159
- package/dist/conversation.js.map +1 -1
- package/dist/data/conversations.js +82 -78
- package/dist/data/conversations.js.map +1 -1
- package/dist/data/messages.js +43 -39
- package/dist/data/messages.js.map +1 -1
- package/dist/data/participants.js +100 -78
- package/dist/data/participants.js.map +1 -1
- package/dist/data/users.js +24 -22
- package/dist/data/users.js.map +1 -1
- package/dist/detailed-delivery-receipt.js +1 -1
- package/dist/detailed-delivery-receipt.js.map +1 -1
- package/dist/interfaces/attributes.js +147 -0
- package/dist/interfaces/attributes.js.map +1 -0
- package/dist/interfaces/notification-types.js +5 -5
- package/dist/interfaces/notification-types.js.map +1 -1
- package/dist/logger.js +36 -15
- package/dist/logger.js.map +1 -1
- package/dist/media.js +21 -9
- package/dist/media.js.map +1 -1
- package/dist/message-builder.js +56 -3
- package/dist/message-builder.js.map +1 -1
- package/dist/message.js +157 -78
- package/dist/message.js.map +1 -1
- package/dist/packages/conversations/package.json.js +1 -1
- package/dist/participant.js +101 -50
- package/dist/participant.js.map +1 -1
- package/dist/push-notification.js.map +1 -1
- package/dist/rest-paginator.js +16 -6
- package/dist/rest-paginator.js.map +1 -1
- package/dist/services/network.js +18 -14
- package/dist/services/network.js.map +1 -1
- package/dist/services/typing-indicator.js +20 -17
- package/dist/services/typing-indicator.js.map +1 -1
- package/dist/unsent-message.js.map +1 -1
- package/dist/user.js +87 -60
- package/dist/user.js.map +1 -1
- package/dist/util/deferred.js +3 -1
- package/dist/util/deferred.js.map +1 -1
- package/dist/util/index.js +6 -6
- package/dist/util/index.js.map +1 -1
- package/docs/assets/js/search.js +1 -1
- package/docs/classes/AggregatedDeliveryReceipt.html +0 -102
- package/docs/classes/Client.html +24 -132
- package/docs/classes/Conversation.html +37 -132
- package/docs/classes/DetailedDeliveryReceipt.html +1 -103
- package/docs/classes/Media.html +0 -102
- package/docs/classes/Message.html +73 -109
- package/docs/classes/MessageBuilder.html +78 -104
- package/docs/classes/Participant.html +37 -110
- package/docs/classes/PushNotification.html +0 -102
- package/docs/classes/RestPaginator.html +0 -102
- package/docs/classes/UnsentMessage.html +0 -102
- package/docs/classes/User.html +7 -109
- package/docs/index.html +93 -3
- package/docs/interfaces/ClientOptions.html +0 -102
- package/docs/interfaces/ConversationBindings.html +3001 -0
- package/docs/interfaces/ConversationEmailBinding.html +3001 -0
- package/docs/interfaces/ConversationState.html +0 -102
- package/docs/interfaces/CreateConversationOptions.html +1 -103
- package/docs/interfaces/LastMessage.html +0 -102
- package/docs/interfaces/Paginator.html +0 -102
- package/docs/interfaces/ParticipantBindings.html +3001 -0
- package/docs/interfaces/ParticipantEmailBinding.html +3001 -0
- package/docs/interfaces/PushNotificationData.html +0 -102
- package/docs/interfaces/SendEmailOptions.html +0 -102
- package/docs/interfaces/SendMediaOptions.html +0 -102
- package/docs/modules.html +93 -3
- package/package.json +23 -17
package/dist/services/network.js
CHANGED
@@ -141,10 +141,10 @@ class Network {
|
|
141
141
|
this.cleanupCache();
|
142
142
|
}
|
143
143
|
isExpired(timestamp) {
|
144
|
-
return !this.cacheLifetime ||
|
144
|
+
return !this.cacheLifetime || Date.now() - timestamp > this.cacheLifetime;
|
145
145
|
}
|
146
146
|
cleanupCache() {
|
147
|
-
for (
|
147
|
+
for (const [k, v] of this.cache) {
|
148
148
|
if (this.isExpired(v.timestamp)) {
|
149
149
|
this.cache.delete(k);
|
150
150
|
}
|
@@ -154,23 +154,25 @@ class Network {
|
|
154
154
|
}
|
155
155
|
}
|
156
156
|
pokeTimer() {
|
157
|
-
this.timer =
|
157
|
+
this.timer =
|
158
|
+
this.timer ||
|
159
|
+
setInterval(() => this.cleanupCache(), this.cacheLifetime * 2);
|
158
160
|
}
|
159
161
|
executeWithRetry(request, retryWhenThrottled = false) {
|
160
162
|
return new Promise((resolve, reject) => {
|
161
|
-
|
163
|
+
const codesToRetryOn = [502, 503, 504];
|
162
164
|
if (retryWhenThrottled) {
|
163
165
|
codesToRetryOn.push(429);
|
164
166
|
}
|
165
|
-
|
166
|
-
retrier.on(
|
167
|
+
const retrier = new operationRetrier.Retrier(this.configuration.backoffConfiguration);
|
168
|
+
retrier.on("attempt", () => {
|
167
169
|
request()
|
168
|
-
.then(result => retrier.succeeded(result))
|
169
|
-
.catch(err => {
|
170
|
+
.then((result) => retrier.succeeded(result))
|
171
|
+
.catch((err) => {
|
170
172
|
if (codesToRetryOn.indexOf(err.status) > -1) {
|
171
173
|
retrier.failed(err);
|
172
174
|
}
|
173
|
-
else if (err.message ===
|
175
|
+
else if (err.message === "Twilsock disconnected") {
|
174
176
|
// Ugly hack. We must make a proper exceptions for twilsock
|
175
177
|
retrier.failed(err);
|
176
178
|
}
|
@@ -182,19 +184,21 @@ class Network {
|
|
182
184
|
}
|
183
185
|
});
|
184
186
|
});
|
185
|
-
retrier.on(
|
186
|
-
|
187
|
-
|
187
|
+
retrier.on("succeeded", (result) => {
|
188
|
+
resolve(result);
|
189
|
+
});
|
190
|
+
retrier.on("cancelled", (err) => reject(err));
|
191
|
+
retrier.on("failed", (err) => reject(err));
|
188
192
|
retrier.start();
|
189
193
|
});
|
190
194
|
}
|
191
195
|
async get(url) {
|
192
|
-
|
196
|
+
const cacheEntry = this.cache.get(url);
|
193
197
|
if (cacheEntry && !this.isExpired(cacheEntry.timestamp)) {
|
194
198
|
return cacheEntry.response;
|
195
199
|
}
|
196
200
|
const headers = {};
|
197
|
-
|
201
|
+
const response = await this.executeWithRetry(() => this.services.transport.get(url, headers, this.configuration.productId), this.configuration.retryWhenThrottled);
|
198
202
|
this.cache.set(url, { response, timestamp: Date.now() });
|
199
203
|
this.pokeTimer();
|
200
204
|
return response;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"network.js","sources":["../../src/services/network.ts"],"sourcesContent":["import { Retrier } from
|
1
|
+
{"version":3,"file":"network.js","sources":["../../src/services/network.ts"],"sourcesContent":["import { Retrier } from \"@twilio/operation-retrier\";\nimport { Transport, TransportResult } from \"twilsock\";\nimport { Configuration } from \"../configuration\";\n\nimport Timeout = NodeJS.Timeout;\n\ninterface CacheEntry {\n response: TransportResult<unknown>;\n timestamp: number;\n}\n\nexport interface NetworkServices {\n transport: Transport;\n}\n\nclass Network {\n private readonly configuration: Configuration;\n private readonly services: NetworkServices;\n private cacheLifetime: number;\n\n private readonly cache: Map<string, CacheEntry>;\n private timer!: number | NodeJS.Timeout;\n\n constructor(configuration, services) {\n this.configuration = configuration;\n this.services = services;\n this.cache = new Map<string, CacheEntry>();\n this.cacheLifetime = this.configuration.httpCacheInterval * 100;\n this.cleanupCache();\n }\n\n private isExpired(timestamp: number): boolean {\n return !this.cacheLifetime || Date.now() - timestamp > this.cacheLifetime;\n }\n\n private cleanupCache() {\n for (const [k, v] of this.cache) {\n if (this.isExpired(v.timestamp)) {\n this.cache.delete(k);\n }\n }\n\n if (this.cache.size === 0) {\n clearInterval(this.timer as Timeout);\n }\n }\n\n pokeTimer() {\n this.timer =\n this.timer ||\n setInterval(() => this.cleanupCache(), this.cacheLifetime * 2);\n }\n\n private executeWithRetry<T>(\n request,\n retryWhenThrottled = false\n ): Promise<TransportResult<T>> {\n return new Promise((resolve, reject) => {\n const codesToRetryOn = [502, 503, 504];\n if (retryWhenThrottled) {\n codesToRetryOn.push(429);\n }\n\n const retrier = new Retrier(this.configuration.backoffConfiguration);\n retrier.on(\"attempt\", () => {\n request()\n .then((result) => retrier.succeeded(result))\n .catch((err) => {\n if (codesToRetryOn.indexOf(err.status) > -1) {\n retrier.failed(err);\n } else if (err.message === \"Twilsock disconnected\") {\n // Ugly hack. We must make a proper exceptions for twilsock\n retrier.failed(err);\n } else {\n // Fatal error\n retrier.removeAllListeners();\n retrier.cancel();\n reject(err);\n }\n });\n });\n\n retrier.on(\"succeeded\", (result) => {\n resolve(result);\n });\n retrier.on(\"cancelled\", (err) => reject(err));\n retrier.on(\"failed\", (err) => reject(err));\n\n retrier.start();\n });\n }\n\n async get<T>(url: string): Promise<TransportResult<T>> {\n const cacheEntry = this.cache.get(url);\n if (cacheEntry && !this.isExpired(cacheEntry.timestamp)) {\n return cacheEntry.response as TransportResult<T>;\n }\n\n const headers = {};\n const response = await this.executeWithRetry<T>(\n () =>\n this.services.transport.get<T>(\n url,\n headers,\n this.configuration.productId\n ),\n this.configuration.retryWhenThrottled\n );\n this.cache.set(url, { response, timestamp: Date.now() });\n this.pokeTimer();\n return response;\n }\n}\n\nexport { Network };\n"],"names":["Retrier"],"mappings":"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"}
|
@@ -133,7 +133,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
133
133
|
var logger = require('../logger.js');
|
134
134
|
var notificationTypes = require('../interfaces/notification-types.js');
|
135
135
|
|
136
|
-
const log = logger.Logger.scope(
|
136
|
+
const log = logger.Logger.scope("TypingIndicator");
|
137
137
|
/**
|
138
138
|
* An important note in regards to typing timeout timers. There are two places that the SDK can get the "typing_timeout" attribute from. The first
|
139
139
|
* place that the attribute appears in is the response received from POST -> /v1/typing REST call. In the body of that response, the value of the
|
@@ -159,9 +159,9 @@ class TypingIndicator {
|
|
159
159
|
this.sentUpdates = new Map();
|
160
160
|
}
|
161
161
|
get typingTimeout() {
|
162
|
-
return this.configuration.typingIndicatorTimeoutOverride
|
163
|
-
|
164
|
-
|
162
|
+
return (this.configuration.typingIndicatorTimeoutOverride ||
|
163
|
+
this.serviceTypingTimeout ||
|
164
|
+
this.configuration.typingIndicatorTimeoutDefault);
|
165
165
|
}
|
166
166
|
/**
|
167
167
|
* Initialize TypingIndicator controller
|
@@ -170,7 +170,7 @@ class TypingIndicator {
|
|
170
170
|
*/
|
171
171
|
initialize() {
|
172
172
|
// this.services.notificationClient.subscribe(NotificationTypes.TYPING_INDICATOR, 'twilsock');
|
173
|
-
this.services.notificationClient.on(
|
173
|
+
this.services.notificationClient.on("message", async (type, message) => {
|
174
174
|
if (type === notificationTypes.NotificationTypes.TYPING_INDICATOR) {
|
175
175
|
await this._handleRemoteTyping(message);
|
176
176
|
}
|
@@ -180,21 +180,23 @@ class TypingIndicator {
|
|
180
180
|
* Remote participants typing events handler
|
181
181
|
*/
|
182
182
|
async _handleRemoteTyping(message) {
|
183
|
-
log.trace(
|
183
|
+
log.trace("Got new typing indicator ", message);
|
184
184
|
this.getConversation(message.channel_sid)
|
185
|
-
.then(conversation => {
|
185
|
+
.then((conversation) => {
|
186
186
|
if (!conversation) {
|
187
187
|
return;
|
188
188
|
}
|
189
|
-
conversation.participants.forEach(participant => {
|
189
|
+
conversation.participants.forEach((participant) => {
|
190
190
|
if (participant.identity !== message.identity) {
|
191
191
|
return;
|
192
192
|
}
|
193
|
-
const timeout = this.configuration.typingIndicatorTimeoutOverride
|
193
|
+
const timeout = this.configuration.typingIndicatorTimeoutOverride
|
194
|
+
? this.configuration.typingIndicatorTimeoutOverride + 1000
|
195
|
+
: message.typing_timeout * 1000;
|
194
196
|
participant._startTyping(timeout);
|
195
197
|
});
|
196
198
|
})
|
197
|
-
.catch(err => {
|
199
|
+
.catch((err) => {
|
198
200
|
log.error(err);
|
199
201
|
throw err;
|
200
202
|
});
|
@@ -205,29 +207,30 @@ class TypingIndicator {
|
|
205
207
|
*/
|
206
208
|
send(conversationSid) {
|
207
209
|
const lastUpdate = this.sentUpdates.get(conversationSid);
|
208
|
-
if (lastUpdate && lastUpdate >
|
210
|
+
if (lastUpdate && lastUpdate > Date.now() - this.typingTimeout) {
|
209
211
|
return Promise.resolve();
|
210
212
|
}
|
211
213
|
this.sentUpdates.set(conversationSid, Date.now());
|
212
214
|
return this._send(conversationSid);
|
213
215
|
}
|
214
216
|
_send(conversationSid) {
|
215
|
-
log.trace(
|
217
|
+
log.trace("Sending typing indicator");
|
216
218
|
const url = this.configuration.links.typing;
|
217
219
|
const headers = {
|
218
|
-
|
220
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
219
221
|
};
|
220
222
|
const body = `ChannelSid=${conversationSid}`;
|
221
|
-
return
|
223
|
+
return this.services.twilsockClient
|
224
|
+
.post(url, headers, body, this.configuration.productId)
|
222
225
|
.then((response) => {
|
223
|
-
if (response.body.hasOwnProperty(
|
226
|
+
if (response.body.hasOwnProperty("typing_timeout")) {
|
224
227
|
this.serviceTypingTimeout = response.body.typing_timeout * 1000;
|
225
228
|
}
|
226
229
|
})
|
227
230
|
.catch((err) => {
|
228
|
-
log.error(
|
231
|
+
log.error("Failed to send typing indicator:", err);
|
229
232
|
throw err;
|
230
|
-
})
|
233
|
+
});
|
231
234
|
}
|
232
235
|
}
|
233
236
|
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"typing-indicator.js","sources":["../../src/services/typing-indicator.ts"],"sourcesContent":["import { Logger } from
|
1
|
+
{"version":3,"file":"typing-indicator.js","sources":["../../src/services/typing-indicator.ts"],"sourcesContent":["import { Logger } from \"../logger\";\n\nimport { Notifications } from \"@twilio/notifications\";\n\nimport { NotificationTypes } from \"../interfaces/notification-types\";\nimport { TwilsockClient } from \"twilsock\";\nimport { Configuration } from \"../configuration\";\n\nconst log = Logger.scope(\"TypingIndicator\");\n\nexport interface TypingIndicatorServices {\n twilsockClient: TwilsockClient;\n notificationClient: Notifications;\n}\n\n/**\n * An important note in regards to typing timeout timers. There are two places that the SDK can get the \"typing_timeout\" attribute from. The first\n * place that the attribute appears in is the response received from POST -> /v1/typing REST call. In the body of that response, the value of the\n * \"typing_timeout\" attribute will be exactly the same as defined in the console. The second place that the attribute appears in is from a\n * notification of type \"twilio.ipmsg.typing_indicator\". In this case, the \"typing_timeout\" value will be +1 of that in the console. This\n * intentional. The timeout returned from the POST -> /v1/typing call should be used to disable further calls for that period of time. On contrary,\n * the timeout returned from the notification should be used as the timeout for the \"typingEnded\" event, +1 is to account for latency.\n *\n * @private\n */\n\n/**\n * @class TypingIndicator\n *\n * @constructor\n * @private\n */\nclass TypingIndicator {\n private readonly services: TypingIndicatorServices;\n private readonly configuration: Configuration;\n\n private sentUpdates: Map<string, number>;\n private getConversation;\n private serviceTypingTimeout;\n\n constructor(\n getConversation,\n config: Configuration,\n services: TypingIndicatorServices\n ) {\n this.configuration = config;\n this.services = services;\n this.getConversation = getConversation;\n\n this.serviceTypingTimeout = null;\n this.sentUpdates = new Map();\n }\n\n public get typingTimeout(): number {\n return (\n this.configuration.typingIndicatorTimeoutOverride ||\n this.serviceTypingTimeout ||\n this.configuration.typingIndicatorTimeoutDefault\n );\n }\n\n /**\n * Initialize TypingIndicator controller\n * Registers for needed message types and sets listeners\n * @private\n */\n initialize(): void {\n // this.services.notificationClient.subscribe(NotificationTypes.TYPING_INDICATOR, 'twilsock');\n this.services.notificationClient.on(\"message\", async (type, message) => {\n if (type === NotificationTypes.TYPING_INDICATOR) {\n await this._handleRemoteTyping(message);\n }\n });\n }\n\n /**\n * Remote participants typing events handler\n */\n private async _handleRemoteTyping(message) {\n log.trace(\"Got new typing indicator \", message);\n\n this.getConversation(message.channel_sid)\n .then((conversation) => {\n if (!conversation) {\n return;\n }\n\n conversation.participants.forEach((participant) => {\n if (participant.identity !== message.identity) {\n return;\n }\n\n const timeout = this.configuration.typingIndicatorTimeoutOverride\n ? this.configuration.typingIndicatorTimeoutOverride + 1000\n : message.typing_timeout * 1000;\n participant._startTyping(timeout);\n });\n })\n .catch((err) => {\n log.error(err);\n throw err;\n });\n }\n\n /**\n * Send typing event for the given conversation sid\n * @param {String} conversationSid\n */\n send(conversationSid: string) {\n const lastUpdate = this.sentUpdates.get(conversationSid);\n if (lastUpdate && lastUpdate > Date.now() - this.typingTimeout) {\n return Promise.resolve();\n }\n\n this.sentUpdates.set(conversationSid, Date.now());\n return this._send(conversationSid);\n }\n\n private _send(conversationSid: string) {\n log.trace(\"Sending typing indicator\");\n\n const url = this.configuration.links.typing;\n const headers = {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n const body = `ChannelSid=${conversationSid}`;\n\n return this.services.twilsockClient\n .post<{ typing_timeout: number }>(\n url,\n headers,\n body,\n this.configuration.productId\n )\n .then((response) => {\n if (response.body.hasOwnProperty(\"typing_timeout\")) {\n this.serviceTypingTimeout = response.body.typing_timeout * 1000;\n }\n })\n .catch((err) => {\n log.error(\"Failed to send typing indicator:\", err);\n throw err;\n });\n }\n}\n\nexport { TypingIndicator };\n"],"names":["Logger","NotificationTypes"],"mappings":"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"}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"unsent-message.js","sources":["../src/unsent-message.ts"],"sourcesContent":["import { MediaCategory } from
|
1
|
+
{"version":3,"file":"unsent-message.js","sources":["../src/unsent-message.ts"],"sourcesContent":["import { MediaCategory } from \"@twilio/mcs-client\";\nimport { parseToNumber } from \"./util\";\nimport { SendEmailOptions, SendMediaOptions } from \"./conversation\";\nimport { JSONValue } from \"./types\";\n\n/**\n * An unsent message. Returned from {@link MessageBuilder.build}.\n */\nclass UnsentMessage {\n public text?: string;\n public attributes: JSONValue = {};\n public mediaContent: [MediaCategory, FormData | SendMediaOptions][] = [];\n public emailOptions: SendEmailOptions = {};\n\n /**\n * @internal\n */\n constructor(private messagesEntity) {}\n\n /**\n * Send the prepared message to the conversation.\n * @returns Index of the new message in the conversation.\n */\n async send(): Promise<number | null> {\n const response = await this.messagesEntity.sendV2(this);\n return parseToNumber(response.index);\n }\n}\n\nexport { UnsentMessage };\n"],"names":["parseToNumber"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA;;;AAGA,MAAM,aAAa;;;;IASjB,YAAoB,cAAc;QAAd,mBAAc,GAAd,cAAc,CAAA;QAP3B,eAAU,GAAc,EAAE,CAAC;QAC3B,iBAAY,GAAmD,EAAE,CAAC;QAClE,iBAAY,GAAqB,EAAE,CAAC;KAKL;;;;;IAMtC,MAAM,IAAI;QACR,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxD,OAAOA,mBAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;KACtC;;;;;"}
|
package/dist/user.js
CHANGED
@@ -134,6 +134,7 @@ var tslib_es6 = require('./node_modules/tslib/tslib.es6.js');
|
|
134
134
|
var logger = require('./logger.js');
|
135
135
|
var index = require('./util/index.js');
|
136
136
|
var declarativeTypeValidator = require('@twilio/declarative-type-validator');
|
137
|
+
var attributes = require('./interfaces/attributes.js');
|
137
138
|
var replayEventEmitter = require('@twilio/replay-event-emitter');
|
138
139
|
var isEqual = require('lodash.isequal');
|
139
140
|
|
@@ -141,7 +142,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
141
142
|
|
142
143
|
var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual);
|
143
144
|
|
144
|
-
const log = logger.Logger.scope(
|
145
|
+
const log = logger.Logger.scope("User");
|
145
146
|
/**
|
146
147
|
* Extended user information.
|
147
148
|
* Note that `isOnline` and `isNotifiable` properties are eligible
|
@@ -164,7 +165,7 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
164
165
|
* * {@link UserUpdateReason}[] `updateReasons` - array of reasons for the update
|
165
166
|
* @event
|
166
167
|
*/
|
167
|
-
this.updated =
|
168
|
+
this.updated = "updated";
|
168
169
|
/**
|
169
170
|
* Fired when the client has subscribed to the user.
|
170
171
|
*
|
@@ -172,7 +173,7 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
172
173
|
* 1. {@link User} `user` - the user in question
|
173
174
|
* @event
|
174
175
|
*/
|
175
|
-
this.userSubscribed =
|
176
|
+
this.userSubscribed = "userSubscribed";
|
176
177
|
/**
|
177
178
|
* Fired when the client has unsubscribed from the user.
|
178
179
|
*
|
@@ -180,9 +181,9 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
180
181
|
* 1. {@link User} `user` - the user in question
|
181
182
|
* @event
|
182
183
|
*/
|
183
|
-
this.userUnsubscribed =
|
184
|
+
this.userUnsubscribed = "userUnsubscribed";
|
184
185
|
this.services = services;
|
185
|
-
this.subscribed =
|
186
|
+
this.subscribed = "initializing";
|
186
187
|
this.setMaxListeners(0);
|
187
188
|
this.state = {
|
188
189
|
identity,
|
@@ -190,7 +191,7 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
190
191
|
friendlyName: null,
|
191
192
|
attributes: {},
|
192
193
|
online: null,
|
193
|
-
notifiable: null
|
194
|
+
notifiable: null,
|
194
195
|
};
|
195
196
|
this._initializationPromise = new Promise((resolve) => {
|
196
197
|
this._resolveInitializationPromise = resolve;
|
@@ -202,63 +203,79 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
202
203
|
/**
|
203
204
|
* User identity.
|
204
205
|
*/
|
205
|
-
get identity() {
|
206
|
-
|
207
|
-
|
206
|
+
get identity() {
|
207
|
+
return this.state.identity;
|
208
|
+
}
|
209
|
+
set identity(identity) {
|
210
|
+
this.state.identity = identity;
|
211
|
+
}
|
212
|
+
set entityName(name) {
|
213
|
+
this.state.entityName = name;
|
214
|
+
}
|
208
215
|
/**
|
209
216
|
* Custom attributes of the user.
|
210
217
|
*/
|
211
|
-
get attributes() {
|
218
|
+
get attributes() {
|
219
|
+
return this.state.attributes;
|
220
|
+
}
|
212
221
|
/**
|
213
222
|
* Friendly name of the user, null if not set.
|
214
223
|
*/
|
215
|
-
get friendlyName() {
|
224
|
+
get friendlyName() {
|
225
|
+
return this.state.friendlyName;
|
226
|
+
}
|
216
227
|
/**
|
217
228
|
* Status of the real-time conversation connection of the user.
|
218
229
|
*/
|
219
|
-
get isOnline() {
|
230
|
+
get isOnline() {
|
231
|
+
return this.state.online;
|
232
|
+
}
|
220
233
|
/**
|
221
234
|
* User push notification registration status.
|
222
235
|
*/
|
223
|
-
get isNotifiable() {
|
236
|
+
get isNotifiable() {
|
237
|
+
return this.state.notifiable;
|
238
|
+
}
|
224
239
|
/**
|
225
240
|
* True if this user is receiving real-time status updates.
|
226
241
|
*/
|
227
|
-
get isSubscribed() {
|
242
|
+
get isSubscribed() {
|
243
|
+
return this.subscribed == "subscribed";
|
244
|
+
}
|
228
245
|
// Handles service updates
|
229
246
|
async _update(key, value) {
|
230
247
|
await this._initializationPromise;
|
231
|
-
|
232
|
-
log.debug(
|
248
|
+
const updateReasons = [];
|
249
|
+
log.debug("User for", this.state.identity, "updated:", key, value);
|
233
250
|
switch (key) {
|
234
|
-
case
|
251
|
+
case "friendlyName":
|
235
252
|
if (this.state.friendlyName !== value.value) {
|
236
|
-
updateReasons.push(
|
253
|
+
updateReasons.push("friendlyName");
|
237
254
|
this.state.friendlyName = value.value;
|
238
255
|
}
|
239
256
|
break;
|
240
|
-
case
|
257
|
+
case "attributes":
|
241
258
|
const updateAttributes = index.parseAttributes(value.value, `Retrieved malformed attributes from the server for user: ${this.state.identity}`, log);
|
242
259
|
if (!isEqual__default['default'](this.state.attributes, updateAttributes)) {
|
243
260
|
this.state.attributes = updateAttributes;
|
244
|
-
updateReasons.push(
|
261
|
+
updateReasons.push("attributes");
|
245
262
|
}
|
246
263
|
break;
|
247
|
-
case
|
264
|
+
case "reachability":
|
248
265
|
if (this.state.online !== value.online) {
|
249
266
|
this.state.online = value.online;
|
250
|
-
updateReasons.push(
|
267
|
+
updateReasons.push("reachabilityOnline");
|
251
268
|
}
|
252
269
|
if (this.state.notifiable !== value.notifiable) {
|
253
270
|
this.state.notifiable = value.notifiable;
|
254
|
-
updateReasons.push(
|
271
|
+
updateReasons.push("reachabilityNotifiable");
|
255
272
|
}
|
256
273
|
break;
|
257
274
|
default:
|
258
275
|
return;
|
259
276
|
}
|
260
277
|
if (updateReasons.length > 0) {
|
261
|
-
this.emit(
|
278
|
+
this.emit("updated", { user: this, updateReasons: updateReasons });
|
262
279
|
}
|
263
280
|
}
|
264
281
|
// Fetch reachability info
|
@@ -267,9 +284,12 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
267
284
|
if (!this.configuration.reachabilityEnabled) {
|
268
285
|
return Promise.resolve();
|
269
286
|
}
|
270
|
-
return map
|
287
|
+
return map
|
288
|
+
.get("reachability")
|
271
289
|
.then(update)
|
272
|
-
.catch(err => {
|
290
|
+
.catch((err) => {
|
291
|
+
log.warn("Failed to get reachability info for ", this.state.identity, err);
|
292
|
+
});
|
273
293
|
}
|
274
294
|
// Fetch user
|
275
295
|
async _fetch() {
|
@@ -277,32 +297,39 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
277
297
|
if (!this.state.entityName) {
|
278
298
|
return this;
|
279
299
|
}
|
280
|
-
this.promiseToFetch = this.services.syncClient
|
300
|
+
this.promiseToFetch = this.services.syncClient
|
301
|
+
.map({
|
281
302
|
id: this.state.entityName,
|
282
|
-
mode:
|
283
|
-
includeItems: true
|
303
|
+
mode: "open_existing",
|
304
|
+
includeItems: true,
|
284
305
|
})
|
285
|
-
.then(map => {
|
306
|
+
.then((map) => {
|
286
307
|
this.entity = map;
|
287
|
-
map.on(
|
288
|
-
log.debug(this.state.entityName +
|
308
|
+
map.on("itemUpdated", (args) => {
|
309
|
+
log.debug(this.state.entityName +
|
310
|
+
" (" +
|
311
|
+
this.state.identity +
|
312
|
+
") itemUpdated: " +
|
313
|
+
args.item.key);
|
289
314
|
return this._update(args.item.key, args.item.data);
|
290
315
|
});
|
291
316
|
return Promise.all([
|
292
|
-
map
|
293
|
-
.
|
294
|
-
|
295
|
-
|
296
|
-
|
317
|
+
map
|
318
|
+
.get("friendlyName")
|
319
|
+
.then((item) => this._update(item.key, item.data)),
|
320
|
+
map
|
321
|
+
.get("attributes")
|
322
|
+
.then((item) => this._update(item.key, item.data)),
|
323
|
+
this._updateReachabilityInfo(map, (item) => this._update(item.key, item.data)),
|
297
324
|
]);
|
298
325
|
})
|
299
326
|
.then(() => {
|
300
|
-
log.debug(
|
301
|
-
this.subscribed =
|
302
|
-
this.emit(
|
327
|
+
log.debug("Fetched for", this.identity);
|
328
|
+
this.subscribed = "subscribed";
|
329
|
+
this.emit("userSubscribed", this);
|
303
330
|
return this;
|
304
331
|
})
|
305
|
-
.catch(err => {
|
332
|
+
.catch((err) => {
|
306
333
|
this.promiseToFetch = null;
|
307
334
|
throw err;
|
308
335
|
});
|
@@ -318,11 +345,11 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
318
345
|
*/
|
319
346
|
async updateAttributes(attributes) {
|
320
347
|
await this._initializationPromise;
|
321
|
-
if (this.subscribed ==
|
322
|
-
throw new Error(
|
348
|
+
if (this.subscribed == "unsubscribed") {
|
349
|
+
throw new Error("Can't modify unsubscribed object");
|
323
350
|
}
|
324
|
-
await this.services.commandExecutor.mutateResource(
|
325
|
-
attributes: JSON.stringify(attributes)
|
351
|
+
await this.services.commandExecutor.mutateResource("post", this.links.self, {
|
352
|
+
attributes: JSON.stringify(attributes),
|
326
353
|
});
|
327
354
|
return this;
|
328
355
|
}
|
@@ -332,11 +359,11 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
332
359
|
*/
|
333
360
|
async updateFriendlyName(friendlyName) {
|
334
361
|
await this._initializationPromise;
|
335
|
-
if (this.subscribed ==
|
336
|
-
throw new Error(
|
362
|
+
if (this.subscribed == "unsubscribed") {
|
363
|
+
throw new Error("Can't modify unsubscribed object");
|
337
364
|
}
|
338
|
-
await this.services.commandExecutor.mutateResource(
|
339
|
-
friendly_name: friendlyName
|
365
|
+
await this.services.commandExecutor.mutateResource("post", this.links.self, {
|
366
|
+
friendly_name: friendlyName,
|
340
367
|
});
|
341
368
|
return this;
|
342
369
|
}
|
@@ -350,8 +377,8 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
350
377
|
await this.promiseToFetch;
|
351
378
|
this.entity.close();
|
352
379
|
this.promiseToFetch = null;
|
353
|
-
this.subscribed =
|
354
|
-
this.emit(
|
380
|
+
this.subscribed = "unsubscribed";
|
381
|
+
this.emit("userUnsubscribed", this);
|
355
382
|
}
|
356
383
|
}
|
357
384
|
_resolveInitialization(configuration, identity, entityName, emitUpdated) {
|
@@ -359,30 +386,30 @@ class User extends replayEventEmitter.ReplayEventEmitter {
|
|
359
386
|
this.identity = identity;
|
360
387
|
this.entityName = entityName;
|
361
388
|
this.links = {
|
362
|
-
self: `${this.configuration.links.users}/${this.identity}
|
389
|
+
self: `${this.configuration.links.users}/${this.identity}`,
|
363
390
|
};
|
364
391
|
this._resolveInitializationPromise();
|
365
392
|
if (emitUpdated) {
|
366
|
-
this.emit(
|
393
|
+
this.emit("updated", {
|
367
394
|
user: this,
|
368
395
|
updateReasons: [
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
]
|
396
|
+
"friendlyName",
|
397
|
+
"attributes",
|
398
|
+
"reachabilityOnline",
|
399
|
+
"reachabilityNotifiable",
|
400
|
+
],
|
374
401
|
});
|
375
402
|
}
|
376
403
|
}
|
377
404
|
}
|
378
405
|
tslib_es6.__decorate([
|
379
|
-
declarativeTypeValidator.validateTypesAsync(
|
406
|
+
declarativeTypeValidator.validateTypesAsync(attributes.attributesValidator),
|
380
407
|
tslib_es6.__metadata("design:type", Function),
|
381
408
|
tslib_es6.__metadata("design:paramtypes", [Object]),
|
382
409
|
tslib_es6.__metadata("design:returntype", Promise)
|
383
410
|
], User.prototype, "updateAttributes", null);
|
384
411
|
tslib_es6.__decorate([
|
385
|
-
declarativeTypeValidator.validateTypesAsync([
|
412
|
+
declarativeTypeValidator.validateTypesAsync(["string"]),
|
386
413
|
tslib_es6.__metadata("design:type", Function),
|
387
414
|
tslib_es6.__metadata("design:paramtypes", [String]),
|
388
415
|
tslib_es6.__metadata("design:returntype", Promise)
|