@amityco/ts-sdk 0.0.1-beta.30 → 0.0.1-beta.31
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/dist/@types/core/cache.d.ts +3 -0
- package/dist/@types/core/cache.d.ts.map +1 -1
- package/dist/@types/core/errors.d.ts +2 -1
- package/dist/@types/core/errors.d.ts.map +1 -1
- package/dist/@types/core/events.d.ts +2 -0
- package/dist/@types/core/events.d.ts.map +1 -1
- package/dist/@types/core/live.d.ts +8 -4
- package/dist/@types/core/live.d.ts.map +1 -1
- package/dist/@types/core/query.d.ts +1 -1
- package/dist/@types/core/query.d.ts.map +1 -1
- package/dist/@types/domains/channel.d.ts +1 -0
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/@types/domains/message.d.ts +13 -6
- package/dist/@types/domains/message.d.ts.map +1 -1
- package/dist/@types/domains/post.d.ts +10 -3
- package/dist/@types/domains/post.d.ts.map +1 -1
- package/dist/@types/domains/user.d.ts +1 -0
- package/dist/@types/domains/user.d.ts.map +1 -1
- package/dist/@types/index.d.ts +2 -2
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/cache/api/isInTombstone.d.ts +18 -0
- package/dist/cache/api/isInTombstone.d.ts.map +1 -0
- package/dist/cache/api/pushToTombstone.d.ts +17 -0
- package/dist/cache/api/pushToTombstone.d.ts.map +1 -0
- package/dist/cache/utils/index.d.ts +17 -0
- package/dist/cache/utils/index.d.ts.map +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/queryChannels.d.ts +1 -1
- package/dist/channel/api/queryChannels.d.ts.map +1 -1
- package/dist/channel/api/tests/queryChannelMembers.test.d.ts +2 -0
- package/dist/channel/api/tests/queryChannelMembers.test.d.ts.map +1 -0
- package/dist/channel/api/tests/queryChannels.test.d.ts +2 -0
- package/dist/channel/api/tests/queryChannels.test.d.ts.map +1 -0
- package/dist/channel/events/index.d.ts +2 -0
- package/dist/channel/events/index.d.ts.map +1 -1
- package/dist/channel/events/onChannelMemberBanned.d.ts +17 -0
- package/dist/channel/events/onChannelMemberBanned.d.ts.map +1 -0
- package/dist/channel/events/onChannelMemberUnbanned.d.ts +17 -0
- package/dist/channel/events/onChannelMemberUnbanned.d.ts.map +1 -0
- package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts +2 -0
- package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts.map +1 -0
- package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts +2 -0
- package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts.map +1 -0
- package/dist/channel/observers/liveChannelMembers.d.ts.map +1 -1
- package/dist/channel/observers/liveChannels.d.ts.map +1 -1
- package/dist/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.d.ts +0 -0
- package/dist/channel/utils/tests/prepareChannelPayload.test.d.ts.map +1 -0
- package/dist/core/events.d.ts +3 -3
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/query/filtering.d.ts +11 -0
- package/dist/core/query/filtering.d.ts.map +1 -1
- package/dist/core/query/query.d.ts.map +1 -1
- package/dist/index.cjs.js +963 -507
- package/dist/index.esm.js +946 -499
- package/dist/index.umd.js +4 -4
- package/dist/message/api/createMessage.d.ts +4 -1
- package/dist/message/api/createMessage.d.ts.map +1 -1
- package/dist/message/api/deleteMessage.d.ts.map +1 -1
- package/dist/message/api/getMessage.d.ts +1 -1
- package/dist/message/api/getMessage.d.ts.map +1 -1
- package/dist/message/api/getMessages.d.ts.map +1 -1
- package/dist/message/api/queryMessages.d.ts.map +1 -1
- package/dist/message/api/tests/createMessage.test.d.ts +2 -0
- package/dist/message/api/tests/createMessage.test.d.ts.map +1 -0
- package/dist/message/api/updateMessage.d.ts.map +1 -1
- package/dist/message/events/index.d.ts +1 -0
- package/dist/message/events/index.d.ts.map +1 -1
- package/dist/message/events/onMessageFetched.d.ts +17 -0
- package/dist/message/events/onMessageFetched.d.ts.map +1 -0
- package/dist/message/observers/index.d.ts +2 -1
- package/dist/message/observers/index.d.ts.map +1 -1
- package/dist/message/observers/liveMessage.d.ts +18 -0
- package/dist/message/observers/liveMessage.d.ts.map +1 -0
- package/dist/message/observers/liveMessages.d.ts.map +1 -1
- package/dist/message/observers/observeMessage.d.ts.map +1 -1
- package/dist/message/tests/api/getMessage.test.d.ts +2 -0
- package/dist/message/tests/api/getMessage.test.d.ts.map +1 -0
- package/dist/message/tests/observers/liveMessage.test.d.ts +2 -0
- package/dist/message/tests/observers/liveMessage.test.d.ts.map +1 -0
- package/dist/message/{observers/tests → tests/observers}/liveMessages.test.d.ts +0 -0
- package/dist/message/tests/observers/liveMessages.test.d.ts.map +1 -0
- package/dist/post/api/deletePost.d.ts.map +1 -1
- package/dist/post/api/getPost.d.ts.map +1 -1
- package/dist/post/api/updatePost.d.ts +6 -1
- package/dist/post/api/updatePost.d.ts.map +1 -1
- package/dist/user/api/getUser.d.ts +1 -1
- package/dist/user/api/getUser.d.ts.map +1 -1
- package/dist/user/api/getUsers.d.ts.map +1 -1
- package/dist/user/api/queryUsers.d.ts.map +1 -1
- package/dist/user/api/updateUser.d.ts.map +1 -1
- package/dist/user/events/index.d.ts +1 -0
- package/dist/user/events/index.d.ts.map +1 -1
- package/dist/user/events/onUserFetched.d.ts +17 -0
- package/dist/user/events/onUserFetched.d.ts.map +1 -0
- package/dist/user/observers/index.d.ts +1 -0
- package/dist/user/observers/index.d.ts.map +1 -1
- package/dist/user/observers/liveUser.d.ts +18 -0
- package/dist/user/observers/liveUser.d.ts.map +1 -0
- package/dist/user/observers/observeUser.d.ts.map +1 -1
- package/dist/user/tests/api/getUser.test.d.ts +2 -0
- package/dist/user/tests/api/getUser.test.d.ts.map +1 -0
- package/dist/user/tests/observers/liveUser.test.d.ts +2 -0
- package/dist/user/tests/observers/liveUser.test.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +3 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/tests/dummy/channel.d.ts +15 -0
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
- package/dist/utils/tests/dummy/message.d.ts +12 -1
- package/dist/utils/tests/dummy/message.d.ts.map +1 -1
- package/dist/utils/tests/dummy/user.d.ts +10 -0
- package/dist/utils/tests/dummy/user.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/@types/core/cache.ts +4 -0
- package/src/@types/core/errors.ts +1 -0
- package/src/@types/core/events.ts +2 -0
- package/src/@types/core/live.ts +10 -4
- package/src/@types/core/query.ts +1 -1
- package/src/@types/domains/channel.ts +6 -0
- package/src/@types/domains/message.ts +13 -10
- package/src/@types/domains/post.ts +10 -3
- package/src/@types/domains/user.ts +8 -0
- package/src/@types/index.ts +2 -2
- package/src/cache/api/index.ts +2 -1
- package/src/cache/api/isInTombstone.ts +41 -0
- package/src/cache/api/pushToTombstone.ts +33 -0
- package/src/cache/utils/index.ts +22 -0
- package/src/channel/api/queryChannelMembers.ts +53 -5
- package/src/channel/api/queryChannels.ts +31 -4
- package/src/channel/api/tests/queryChannelMembers.test.ts +115 -0
- package/src/channel/api/tests/queryChannels.test.ts +101 -0
- package/src/channel/events/index.ts +5 -0
- package/src/channel/events/onChannelMemberBanned.ts +36 -0
- package/src/channel/events/onChannelMemberUnbanned.ts +36 -0
- package/src/channel/events/tests/onChannelMemberBanned.test.ts +58 -0
- package/src/channel/events/tests/onChannelMemberUnbanned.test.ts +58 -0
- package/src/channel/observers/liveChannelMembers.ts +28 -4
- package/src/channel/observers/liveChannels.ts +7 -1
- package/src/channel/observers/tests/liveChannelMembers.test.ts +53 -11
- package/src/channel/observers/tests/liveChannels.test.ts +50 -20
- package/src/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.ts +1 -1
- package/src/core/events.ts +1 -0
- package/src/core/query/filtering.ts +49 -0
- package/src/core/query/query.ts +9 -6
- package/src/core/tests/query/query.test.ts +12 -2
- package/src/message/api/createMessage.ts +15 -9
- package/src/message/api/deleteMessage.ts +2 -1
- package/src/message/api/getMessage.ts +32 -4
- package/src/message/api/getMessages.ts +6 -1
- package/src/message/api/queryMessages.ts +3 -0
- package/src/message/api/tests/createMessage.test.ts +76 -0
- package/src/message/api/updateMessage.ts +2 -0
- package/src/message/events/index.ts +1 -0
- package/src/message/events/onMessageFetched.ts +30 -0
- package/src/message/observers/index.ts +2 -1
- package/src/message/observers/liveMessage.ts +100 -0
- package/src/message/observers/liveMessages.ts +3 -1
- package/src/message/observers/observeMessage.ts +4 -0
- package/src/message/tests/api/getMessage.test.ts +96 -0
- package/src/message/tests/observers/liveMessage.test.ts +248 -0
- package/src/message/{observers/tests → tests/observers}/liveMessages.test.ts +29 -3
- package/src/post/api/deletePost.ts +4 -3
- package/src/post/api/getPost.ts +8 -4
- package/src/post/api/updatePost.ts +6 -1
- package/src/user/api/getUser.ts +34 -6
- package/src/user/api/getUsers.ts +3 -0
- package/src/user/api/queryUsers.ts +3 -0
- package/src/user/api/updateUser.ts +3 -0
- package/src/user/events/index.ts +1 -0
- package/src/user/events/onUserFetched.ts +19 -0
- package/src/user/observers/index.ts +1 -0
- package/src/user/observers/liveUser.ts +89 -0
- package/src/user/observers/observeUser.ts +2 -0
- package/src/user/tests/api/getUser.test.ts +112 -0
- package/src/user/tests/observers/liveUser.test.ts +224 -0
- package/src/utils/constants.ts +5 -0
- package/src/utils/tests/dummy/channel.ts +43 -0
- package/src/utils/tests/dummy/message.ts +16 -2
- package/src/utils/tests/dummy/user.ts +11 -0
- package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +0 -1
- package/dist/message/observers/tests/liveMessages.test.d.ts.map +0 -1
package/dist/index.cjs.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var jsBase64 = require('js-base64');
|
|
6
|
-
var mitt = require('mitt');
|
|
7
6
|
var AsyncStorage = require('@react-native-async-storage/async-storage');
|
|
7
|
+
var mitt = require('mitt');
|
|
8
8
|
var debug = require('debug');
|
|
9
9
|
var axios = require('axios');
|
|
10
10
|
var HttpAgent = require('agentkeepalive');
|
|
@@ -13,8 +13,8 @@ var nativeIdGenerator = require('react-native-uuid');
|
|
|
13
13
|
|
|
14
14
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
15
|
|
|
16
|
-
var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);
|
|
17
16
|
var AsyncStorage__default = /*#__PURE__*/_interopDefaultLegacy(AsyncStorage);
|
|
17
|
+
var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);
|
|
18
18
|
var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
|
|
19
19
|
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
20
20
|
var HttpAgent__default = /*#__PURE__*/_interopDefaultLegacy(HttpAgent);
|
|
@@ -75,6 +75,24 @@ const VideoSize = Object.freeze({
|
|
|
75
75
|
ORIGINAL: 'original',
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
const MessageContentType = Object.freeze({
|
|
79
|
+
TEXT: 'text',
|
|
80
|
+
IMAGE: 'image',
|
|
81
|
+
FILE: 'file',
|
|
82
|
+
VIDEO: 'video',
|
|
83
|
+
AUDIO: 'audio',
|
|
84
|
+
CUSTOM: 'custom',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const PostContentType = Object.freeze({
|
|
88
|
+
TEXT: 'text',
|
|
89
|
+
IMAGE: 'image',
|
|
90
|
+
FILE: 'file',
|
|
91
|
+
VIDEO: 'video',
|
|
92
|
+
LIVESTREAM: 'liveStream',
|
|
93
|
+
POLL: 'poll',
|
|
94
|
+
});
|
|
95
|
+
|
|
78
96
|
var _a$1;
|
|
79
97
|
/* eslint-disable no-undef */
|
|
80
98
|
const theGlobal =
|
|
@@ -350,6 +368,78 @@ const PAYLOAD2MODEL = {
|
|
|
350
368
|
followCounts: 'followCount',
|
|
351
369
|
};
|
|
352
370
|
|
|
371
|
+
// cache constants
|
|
372
|
+
const CACHE_KEY_GET = 'get';
|
|
373
|
+
const CACHE_KEY_TOMBSTONE = 'dead';
|
|
374
|
+
const CACHE_LIFESPAN = 1 * 60 * 1000; // 1 minute
|
|
375
|
+
const CACHE_LIFESPAN_TOMBSTONE = 3 * 60 * 1000; // 3 minutes
|
|
376
|
+
/**
|
|
377
|
+
* Shallow-clones an object and sort its keys
|
|
378
|
+
*
|
|
379
|
+
* @param source a plain object to clone
|
|
380
|
+
* @returns a clone of source, with keys sorted with default javascript sorting
|
|
381
|
+
*
|
|
382
|
+
* @category Cache
|
|
383
|
+
* @hidden
|
|
384
|
+
*/
|
|
385
|
+
const normalize = (source) => Object.keys(source)
|
|
386
|
+
.sort()
|
|
387
|
+
.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
|
|
388
|
+
/**
|
|
389
|
+
* Encodes a given {@link Amity.CacheKey} to a plain string
|
|
390
|
+
*
|
|
391
|
+
* @param key the key to encode
|
|
392
|
+
* @returns an encoded string
|
|
393
|
+
*
|
|
394
|
+
* @category Cache
|
|
395
|
+
* @hidden
|
|
396
|
+
*/
|
|
397
|
+
const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
|
|
398
|
+
/**
|
|
399
|
+
* Decodes a string back into a {@link Amity.CacheKey}
|
|
400
|
+
|
|
401
|
+
*
|
|
402
|
+
* @param key the string key to decode
|
|
403
|
+
* @returns a plain Amity.CacheKey object.
|
|
404
|
+
*
|
|
405
|
+
* @category Cache
|
|
406
|
+
* @hidden
|
|
407
|
+
*/
|
|
408
|
+
const decodeKey = (key) => JSON.parse(key);
|
|
409
|
+
/**
|
|
410
|
+
* Performs a recursive partial deep equal check on two objects.
|
|
411
|
+
*
|
|
412
|
+
* @param predicate the reference object containing the partial information
|
|
413
|
+
* @param candidate the object to check against
|
|
414
|
+
* @returns true if the candidate contains all the values of the predicate
|
|
415
|
+
*
|
|
416
|
+
* @category Cache
|
|
417
|
+
*/
|
|
418
|
+
const partialMatch = (predicate, candidate) => {
|
|
419
|
+
if (predicate === candidate)
|
|
420
|
+
return true;
|
|
421
|
+
// if one or the other is nullish, there can't be equality
|
|
422
|
+
if (!predicate || !candidate)
|
|
423
|
+
return false;
|
|
424
|
+
// we only perform recursive check on objects
|
|
425
|
+
if (typeof predicate !== 'object') {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
// recursively call for partial match based on the predicate
|
|
429
|
+
return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* checks if passed error code is included in
|
|
433
|
+
* Tombstone errors list
|
|
434
|
+
*
|
|
435
|
+
* @param errorCode as {@link Amity.ServerError}
|
|
436
|
+
* @returns success boolean if the given errorCode
|
|
437
|
+
* is included in Tombstone errors list
|
|
438
|
+
*
|
|
439
|
+
* @category Cache
|
|
440
|
+
*/
|
|
441
|
+
const checkIfShouldGoesToTombstone = (errorCode) => [400400 /* Amity.ServerError.ITEM_NOT_FOUND */, 400300 /* Amity.ServerError.FORBIDDEN */].includes(errorCode);
|
|
442
|
+
|
|
353
443
|
/* eslint-disable no-use-before-define */
|
|
354
444
|
/**
|
|
355
445
|
* Type guard to check and cast that a given async function is has the ".locally" property
|
|
@@ -408,7 +498,7 @@ const isLocal = (model) => {
|
|
|
408
498
|
*
|
|
409
499
|
* @hidden
|
|
410
500
|
*/
|
|
411
|
-
const isFresh = (model, lifeSpan =
|
|
501
|
+
const isFresh = (model, lifeSpan = CACHE_LIFESPAN) => {
|
|
412
502
|
var _a;
|
|
413
503
|
return Date.now() - ((_a = model === null || model === void 0 ? void 0 : model.cachedAt) !== null && _a !== void 0 ? _a : 0) <= lifeSpan;
|
|
414
504
|
};
|
|
@@ -442,12 +532,10 @@ const createQuery = (func, ...args) => ({ func, args });
|
|
|
442
532
|
*
|
|
443
533
|
* @category Query
|
|
444
534
|
*/
|
|
445
|
-
const queryOptions = (policy, lifeSpan =
|
|
535
|
+
const queryOptions = (policy, lifeSpan = CACHE_LIFESPAN) => {
|
|
446
536
|
if (policy === 'cache_only')
|
|
447
537
|
return { lifeSpan: Infinity };
|
|
448
|
-
|
|
449
|
-
return { lifeSpan: 0 };
|
|
450
|
-
return { lifeSpan };
|
|
538
|
+
return { lifeSpan: lifeSpan < CACHE_LIFESPAN ? CACHE_LIFESPAN : lifeSpan };
|
|
451
539
|
};
|
|
452
540
|
/**
|
|
453
541
|
* ```js
|
|
@@ -468,8 +556,9 @@ const queryOptions = (policy, lifeSpan = 2 * 60 * 1000) => {
|
|
|
468
556
|
*/
|
|
469
557
|
const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_server')) => {
|
|
470
558
|
let local;
|
|
559
|
+
const { lifeSpan } = queryOptions('cache_then_server', options.lifeSpan);
|
|
471
560
|
// offline first
|
|
472
|
-
if (isOffline(func)
|
|
561
|
+
if (isOffline(func)) {
|
|
473
562
|
try {
|
|
474
563
|
local = isMutator(func) ? func.optimistically(...args) : func.locally(...args);
|
|
475
564
|
}
|
|
@@ -480,7 +569,7 @@ const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_s
|
|
|
480
569
|
error,
|
|
481
570
|
}));
|
|
482
571
|
}
|
|
483
|
-
const shouldAbort = isCachable(local) && isFresh(local,
|
|
572
|
+
const shouldAbort = isCachable(local) && isFresh(local, lifeSpan);
|
|
484
573
|
callback === null || callback === void 0 ? void 0 : callback(createSnapshot(local, {
|
|
485
574
|
origin: 'local',
|
|
486
575
|
loading: !(isFetcher(func) && shouldAbort),
|
|
@@ -547,54 +636,6 @@ function createSnapshot(data, options) {
|
|
|
547
636
|
return Object.assign(Object.assign({}, options), { data });
|
|
548
637
|
}
|
|
549
638
|
|
|
550
|
-
// Note:
|
|
551
|
-
// this file should contain a suite of filtering utilities to help the
|
|
552
|
-
// local version of the query functions.
|
|
553
|
-
/**
|
|
554
|
-
* Filter a given collection with strict equality against a param
|
|
555
|
-
*
|
|
556
|
-
* @param collection the collection to filter
|
|
557
|
-
* @param key the key of the collection's items to challenge
|
|
558
|
-
* @param value the expected value
|
|
559
|
-
* @returns a filtered collection with items only matching the criteria
|
|
560
|
-
*
|
|
561
|
-
* @hidden
|
|
562
|
-
*/
|
|
563
|
-
const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
|
|
564
|
-
const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
|
|
565
|
-
|
|
566
|
-
// Note:
|
|
567
|
-
// this file should contain a suite of sorting utilities to help the
|
|
568
|
-
// local version of the query functions.
|
|
569
|
-
/**
|
|
570
|
-
* Alphabetic sorting of objects having a displayName
|
|
571
|
-
*/
|
|
572
|
-
const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
|
|
573
|
-
if (a === b)
|
|
574
|
-
return 0;
|
|
575
|
-
return a < b ? -1 : 1;
|
|
576
|
-
};
|
|
577
|
-
/**
|
|
578
|
-
* Sorting a collection by their apparition order (oldest first)
|
|
579
|
-
*/
|
|
580
|
-
const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
|
|
581
|
-
/**
|
|
582
|
-
* Sorting a collection by their apparition order (oldest first)
|
|
583
|
-
*/
|
|
584
|
-
const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
|
|
585
|
-
/**
|
|
586
|
-
* Sorting a collection by its oldest items
|
|
587
|
-
*/
|
|
588
|
-
const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
|
|
589
|
-
/**
|
|
590
|
-
* Sorting a collection by its newest items
|
|
591
|
-
*/
|
|
592
|
-
const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
|
|
593
|
-
/**
|
|
594
|
-
* Sorting a collection by the items with most recent activity
|
|
595
|
-
*/
|
|
596
|
-
const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
|
|
597
|
-
|
|
598
639
|
/* eslint-disable max-classes-per-file */
|
|
599
640
|
/**
|
|
600
641
|
* Generic ASC error
|
|
@@ -684,265 +725,70 @@ const setActiveClient = (client) => {
|
|
|
684
725
|
activeClient = client;
|
|
685
726
|
};
|
|
686
727
|
|
|
687
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
688
|
-
let tasks = [];
|
|
689
|
-
let timer;
|
|
690
728
|
/**
|
|
691
|
-
*
|
|
729
|
+
* ```js
|
|
730
|
+
* import { enableCache } from '@amityco/ts-sdk'
|
|
731
|
+
* enableCache()
|
|
732
|
+
* ```
|
|
692
733
|
*
|
|
693
|
-
*
|
|
734
|
+
* Adds a new {@link Amity.Cache} object to
|
|
735
|
+
* an {@link Amity.Client} instance
|
|
736
|
+
*
|
|
737
|
+
* @param prevState a previous state of cache instance (useful for SSR)
|
|
738
|
+
* @param persistIf a function to determine if an entry inserted in cache
|
|
739
|
+
* is destined to be also saved in the persistent storage when
|
|
740
|
+
* calling {@link backupCache}
|
|
741
|
+
*
|
|
742
|
+
* @category Cache API
|
|
694
743
|
*/
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}, 0);
|
|
744
|
+
const enableCache = (prevState = {}, persistIf) => {
|
|
745
|
+
const client = getActiveClient();
|
|
746
|
+
if (client.cache)
|
|
747
|
+
return;
|
|
748
|
+
client.log('cache/api/enableCache');
|
|
749
|
+
client.cache = { data: prevState, persistIf };
|
|
702
750
|
};
|
|
703
751
|
|
|
704
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
705
|
-
// need to sync up the list
|
|
706
|
-
const WS_EVENTS = [
|
|
707
|
-
'disconnected',
|
|
708
|
-
'error',
|
|
709
|
-
'connect_error',
|
|
710
|
-
'reconnect_error',
|
|
711
|
-
'reconnect_failed',
|
|
712
|
-
'v3.channel.didCreate',
|
|
713
|
-
'v3.channel.didUpdate',
|
|
714
|
-
'v3.channel.didDelete',
|
|
715
|
-
'v3.channel.didJoin',
|
|
716
|
-
'v3.channel.didLeave',
|
|
717
|
-
'v3.channel.didAddUsers',
|
|
718
|
-
'v3.channel.didRemoveUsers',
|
|
719
|
-
'v3.channel.didBan',
|
|
720
|
-
'v3.channel.didUnban',
|
|
721
|
-
'v3.message.didCreate',
|
|
722
|
-
'v3.message.didUpdate',
|
|
723
|
-
'v3.message.didDelete',
|
|
724
|
-
];
|
|
725
|
-
const MQTT_EVENTS = [
|
|
726
|
-
'connect',
|
|
727
|
-
'message',
|
|
728
|
-
'disconnect',
|
|
729
|
-
'error',
|
|
730
|
-
'close',
|
|
731
|
-
'end',
|
|
732
|
-
'reconnect',
|
|
733
|
-
];
|
|
734
|
-
/** @hidden */
|
|
735
|
-
const createEventEmitter = () => {
|
|
736
|
-
return mitt__default["default"]();
|
|
737
|
-
};
|
|
738
752
|
/**
|
|
739
|
-
*
|
|
740
|
-
*
|
|
753
|
+
* ```js
|
|
754
|
+
* import { disableCache } from '@amityco/ts-sdk'
|
|
755
|
+
* disableCache()
|
|
756
|
+
* ```
|
|
741
757
|
*
|
|
742
|
-
*
|
|
743
|
-
* @
|
|
758
|
+
* Wipes the existing {@link Amity.Cache} object attached to
|
|
759
|
+
* an {@link Amity.Client} instance
|
|
744
760
|
*
|
|
745
|
-
* @category
|
|
746
|
-
* @hidden
|
|
761
|
+
* @category Cache API
|
|
747
762
|
*/
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
mqttClient.on('message', (topic, payload) => {
|
|
759
|
-
const message = JSON.parse(payload.toString());
|
|
760
|
-
emitter.emit(message.eventType, message.data);
|
|
761
|
-
});
|
|
762
|
-
};
|
|
763
|
+
const disableCache = () => {
|
|
764
|
+
const client = getActiveClient();
|
|
765
|
+
if (!client.cache)
|
|
766
|
+
return;
|
|
767
|
+
client.log('cache/api/disableCache');
|
|
768
|
+
// we do this so that testing if cache is enabled
|
|
769
|
+
// is only `if (client.cache)
|
|
770
|
+
delete client.cache;
|
|
771
|
+
};
|
|
772
|
+
|
|
763
773
|
/**
|
|
764
|
-
*
|
|
774
|
+
* ```js
|
|
775
|
+
* import { restoreCache } from '@amityco/ts-sdk'
|
|
776
|
+
* const success = await restoreCache()
|
|
777
|
+
* ```
|
|
765
778
|
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
* @param event The websocket event name
|
|
769
|
-
* @param fn A wrapper for the callback.
|
|
770
|
-
* @returns A dispose function to unsubscribe to the event
|
|
779
|
+
* Reads a previously saved {@link Amity.Cache} from a persistent storage,
|
|
780
|
+
* and inserts it into the current {@link Amity.Cache} instance.
|
|
771
781
|
*
|
|
772
|
-
*
|
|
773
|
-
* @
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
};
|
|
783
|
-
emitter.on(event, handler);
|
|
784
|
-
return () => {
|
|
785
|
-
log(`${namespace}(tmpid: ${timestamp}) > dispose`);
|
|
786
|
-
emitter.off(event, handler);
|
|
787
|
-
};
|
|
788
|
-
};
|
|
789
|
-
/**
|
|
790
|
-
* Wrapper around dispatch event
|
|
791
|
-
*
|
|
792
|
-
* @hidden
|
|
793
|
-
*/
|
|
794
|
-
const fireEvent = (event, payload) => {
|
|
795
|
-
const { emitter } = getActiveClient();
|
|
796
|
-
scheduleTask(() => {
|
|
797
|
-
emitter.emit(event, payload);
|
|
798
|
-
});
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
let activeUser = null;
|
|
802
|
-
/**
|
|
803
|
-
* for internal use
|
|
804
|
-
*/
|
|
805
|
-
const getActiveUser = () => {
|
|
806
|
-
if (!activeUser) {
|
|
807
|
-
throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
|
|
808
|
-
}
|
|
809
|
-
return activeUser;
|
|
810
|
-
};
|
|
811
|
-
const setActiveUser = (user) => {
|
|
812
|
-
activeUser = {
|
|
813
|
-
_id: user._id,
|
|
814
|
-
userId: user.userId,
|
|
815
|
-
path: user.path,
|
|
816
|
-
};
|
|
817
|
-
};
|
|
818
|
-
|
|
819
|
-
exports.SubscriptionLevels = void 0;
|
|
820
|
-
(function (SubscriptionLevels) {
|
|
821
|
-
SubscriptionLevels["COMMUNITY"] = "community";
|
|
822
|
-
SubscriptionLevels["POST"] = "post";
|
|
823
|
-
SubscriptionLevels["COMMENT"] = "comment";
|
|
824
|
-
SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
|
|
825
|
-
SubscriptionLevels["USER"] = "user";
|
|
826
|
-
})(exports.SubscriptionLevels || (exports.SubscriptionLevels = {}));
|
|
827
|
-
const getCommunityUserTopic = (path, level) => {
|
|
828
|
-
switch (level) {
|
|
829
|
-
case 'post':
|
|
830
|
-
return `${path}/post/+`;
|
|
831
|
-
case 'comment':
|
|
832
|
-
return `${path}/post/+/comment/+`;
|
|
833
|
-
case 'post_and_comment':
|
|
834
|
-
return `${path}/post/#`;
|
|
835
|
-
default:
|
|
836
|
-
return path;
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
const getCommunityTopic = ({ path }, level = exports.SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
|
|
840
|
-
const getUserTopic = ({ path }, level = exports.SubscriptionLevels.USER) => getCommunityUserTopic(level === exports.SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
|
|
841
|
-
const getPostTopic = ({ path }, level = exports.SubscriptionLevels.POST) => {
|
|
842
|
-
switch (level) {
|
|
843
|
-
case 'comment':
|
|
844
|
-
return `${path}/comment/+`;
|
|
845
|
-
default:
|
|
846
|
-
return path;
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
const getCommentTopic = ({ path }) => {
|
|
850
|
-
return path;
|
|
851
|
-
};
|
|
852
|
-
const getMyFollowersTopic = () => {
|
|
853
|
-
const user = getActiveUser();
|
|
854
|
-
const [networkId] = user.path.split('/user/');
|
|
855
|
-
return `${networkId}/membership/${user._id}/+/+`;
|
|
856
|
-
};
|
|
857
|
-
const getMyFollowingsTopic = () => {
|
|
858
|
-
const user = getActiveUser();
|
|
859
|
-
const [networkId] = user.path.split('/user/');
|
|
860
|
-
return `${networkId}/membership/+/${user._id}/+`;
|
|
861
|
-
};
|
|
862
|
-
/** @hidden */
|
|
863
|
-
const getNetworkTopic = () => {
|
|
864
|
-
const user = getActiveUser();
|
|
865
|
-
const [networkId] = user.path.split('/user/');
|
|
866
|
-
return networkId;
|
|
867
|
-
};
|
|
868
|
-
let mqttAccessToken;
|
|
869
|
-
let mqttUserId;
|
|
870
|
-
function subscribeTopic(topic, callback) {
|
|
871
|
-
const { mqtt, emitter, accessToken } = getActiveClient();
|
|
872
|
-
const user = getActiveUser();
|
|
873
|
-
if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
|
|
874
|
-
mqttAccessToken = accessToken;
|
|
875
|
-
mqttUserId = user._id;
|
|
876
|
-
mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
|
|
877
|
-
proxyMqttEvents(mqtt, emitter);
|
|
878
|
-
}
|
|
879
|
-
return mqtt.subscribe(topic, callback);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* ```js
|
|
884
|
-
* import { enableCache } from '@amityco/ts-sdk'
|
|
885
|
-
* enableCache()
|
|
886
|
-
* ```
|
|
887
|
-
*
|
|
888
|
-
* Adds a new {@link Amity.Cache} object to
|
|
889
|
-
* an {@link Amity.Client} instance
|
|
890
|
-
*
|
|
891
|
-
* @param prevState a previous state of cache instance (useful for SSR)
|
|
892
|
-
* @param persistIf a function to determine if an entry inserted in cache
|
|
893
|
-
* is destined to be also saved in the persistent storage when
|
|
894
|
-
* calling {@link backupCache}
|
|
895
|
-
*
|
|
896
|
-
* @category Cache API
|
|
897
|
-
*/
|
|
898
|
-
const enableCache = (prevState = {}, persistIf) => {
|
|
899
|
-
const client = getActiveClient();
|
|
900
|
-
if (client.cache)
|
|
901
|
-
return;
|
|
902
|
-
client.log('cache/api/enableCache');
|
|
903
|
-
client.cache = { data: prevState, persistIf };
|
|
904
|
-
};
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* ```js
|
|
908
|
-
* import { disableCache } from '@amityco/ts-sdk'
|
|
909
|
-
* disableCache()
|
|
910
|
-
* ```
|
|
911
|
-
*
|
|
912
|
-
* Wipes the existing {@link Amity.Cache} object attached to
|
|
913
|
-
* an {@link Amity.Client} instance
|
|
914
|
-
*
|
|
915
|
-
* @category Cache API
|
|
916
|
-
*/
|
|
917
|
-
const disableCache = () => {
|
|
918
|
-
const client = getActiveClient();
|
|
919
|
-
if (!client.cache)
|
|
920
|
-
return;
|
|
921
|
-
client.log('cache/api/disableCache');
|
|
922
|
-
// we do this so that testing if cache is enabled
|
|
923
|
-
// is only `if (client.cache)
|
|
924
|
-
delete client.cache;
|
|
925
|
-
};
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
* ```js
|
|
929
|
-
* import { restoreCache } from '@amityco/ts-sdk'
|
|
930
|
-
* const success = await restoreCache()
|
|
931
|
-
* ```
|
|
932
|
-
*
|
|
933
|
-
* Reads a previously saved {@link Amity.Cache} from a persistent storage,
|
|
934
|
-
* and inserts it into the current {@link Amity.Cache} instance.
|
|
935
|
-
*
|
|
936
|
-
* The strategy for persistent storage will depend on the runtime,
|
|
937
|
-
* which is supported by @react-native-async-storage/async-storage.
|
|
938
|
-
*
|
|
939
|
-
* The current userId will be appended to the given storageKey to ensures
|
|
940
|
-
* the cached data concerns only the current user.
|
|
941
|
-
*
|
|
942
|
-
* @param storageKey the name of the persistent storage
|
|
943
|
-
* @returns a success boolean if the cache was dumped to persistent storage
|
|
944
|
-
*
|
|
945
|
-
* @category Cache API
|
|
782
|
+
* The strategy for persistent storage will depend on the runtime,
|
|
783
|
+
* which is supported by @react-native-async-storage/async-storage.
|
|
784
|
+
*
|
|
785
|
+
* The current userId will be appended to the given storageKey to ensures
|
|
786
|
+
* the cached data concerns only the current user.
|
|
787
|
+
*
|
|
788
|
+
* @param storageKey the name of the persistent storage
|
|
789
|
+
* @returns a success boolean if the cache was dumped to persistent storage
|
|
790
|
+
*
|
|
791
|
+
* @category Cache API
|
|
946
792
|
*/
|
|
947
793
|
const restoreCache = async (storageKey = 'amitySdk') => {
|
|
948
794
|
var _a;
|
|
@@ -1030,62 +876,6 @@ const wipeCache = async (storageKey = 'amitySdk') => {
|
|
|
1030
876
|
return true;
|
|
1031
877
|
};
|
|
1032
878
|
|
|
1033
|
-
/**
|
|
1034
|
-
* Shallow-clones an object and sort its keys
|
|
1035
|
-
*
|
|
1036
|
-
* @param source a plain object to clone
|
|
1037
|
-
* @returns a clone of source, with keys sorted with default javascript sorting
|
|
1038
|
-
*
|
|
1039
|
-
* @category Cache
|
|
1040
|
-
* @hidden
|
|
1041
|
-
*/
|
|
1042
|
-
const normalize = (source) => Object.keys(source)
|
|
1043
|
-
.sort()
|
|
1044
|
-
.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
|
|
1045
|
-
/**
|
|
1046
|
-
* Encodes a given {@link Amity.CacheKey} to a plain string
|
|
1047
|
-
*
|
|
1048
|
-
* @param key the key to encode
|
|
1049
|
-
* @returns an encoded string
|
|
1050
|
-
*
|
|
1051
|
-
* @category Cache
|
|
1052
|
-
* @hidden
|
|
1053
|
-
*/
|
|
1054
|
-
const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
|
|
1055
|
-
/**
|
|
1056
|
-
* Decodes a string back into a {@link Amity.CacheKey}
|
|
1057
|
-
|
|
1058
|
-
*
|
|
1059
|
-
* @param key the string key to decode
|
|
1060
|
-
* @returns a plain Amity.CacheKey object.
|
|
1061
|
-
*
|
|
1062
|
-
* @category Cache
|
|
1063
|
-
* @hidden
|
|
1064
|
-
*/
|
|
1065
|
-
const decodeKey = (key) => JSON.parse(key);
|
|
1066
|
-
/**
|
|
1067
|
-
* Performs a recursive partial deep equal check on two objects.
|
|
1068
|
-
*
|
|
1069
|
-
* @param predicate the reference object containing the partial information
|
|
1070
|
-
* @param candidate the object to check against
|
|
1071
|
-
* @returns true if the candidate contains all the values of the predicate
|
|
1072
|
-
*
|
|
1073
|
-
* @category Cache
|
|
1074
|
-
*/
|
|
1075
|
-
const partialMatch = (predicate, candidate) => {
|
|
1076
|
-
if (predicate === candidate)
|
|
1077
|
-
return true;
|
|
1078
|
-
// if one or the other is nullish, there can't be equality
|
|
1079
|
-
if (!predicate || !candidate)
|
|
1080
|
-
return false;
|
|
1081
|
-
// we only perform recursive check on objects
|
|
1082
|
-
if (typeof predicate !== 'object') {
|
|
1083
|
-
return false;
|
|
1084
|
-
}
|
|
1085
|
-
// recursively call for partial match based on the predicate
|
|
1086
|
-
return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
|
|
1087
|
-
};
|
|
1088
|
-
|
|
1089
879
|
/**
|
|
1090
880
|
* ```js
|
|
1091
881
|
* import { queryCache } from '@amityco/ts-sdk'
|
|
@@ -1182,86 +972,368 @@ const pushToCache = (key, data, options = { cachedAt: Date.now() }) => {
|
|
|
1182
972
|
*
|
|
1183
973
|
* Merges a new {@link Amity.Cache} object to an {@link Amity.Client} instance
|
|
1184
974
|
*
|
|
1185
|
-
* @param key the key matching the object to retrieve from cache
|
|
1186
|
-
* @param mutation either a plain object to shallow merge, or a function.
|
|
1187
|
-
* @returns a success boolean if the object was updated
|
|
975
|
+
* @param key the key matching the object to retrieve from cache
|
|
976
|
+
* @param mutation either a plain object to shallow merge, or a function.
|
|
977
|
+
* @returns a success boolean if the object was updated
|
|
978
|
+
*
|
|
979
|
+
* @category Cache API
|
|
980
|
+
*/
|
|
981
|
+
const mergeInCache = (key, mutation, options) => {
|
|
982
|
+
const { log, cache } = getActiveClient();
|
|
983
|
+
if (!cache)
|
|
984
|
+
return false;
|
|
985
|
+
log('cache/api/mergeInCache', { key, mutation });
|
|
986
|
+
const oldVal = pullFromCache(key);
|
|
987
|
+
if (!oldVal)
|
|
988
|
+
return false;
|
|
989
|
+
const newVal = typeof mutation === 'function' ? mutation(oldVal.data) : Object.assign(Object.assign({}, oldVal.data), mutation);
|
|
990
|
+
pushToCache(key, newVal, options);
|
|
991
|
+
return true;
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* ```js
|
|
996
|
+
* import { upsertInCache } from '@amityco/ts-sdk'
|
|
997
|
+
* upsertInCache<Amity.User>(["user", "foobar"], user)
|
|
998
|
+
* ```
|
|
999
|
+
*
|
|
1000
|
+
* Insert or update any provided value as {@link Amity.CacheEntry} for
|
|
1001
|
+
* the matching {@link Amity.CacheKey}
|
|
1002
|
+
*
|
|
1003
|
+
* @param key the key to save the object to
|
|
1004
|
+
* @param data the object to save
|
|
1005
|
+
* @param options customisation object around cache behavior (default gives a 2mn lifespan)
|
|
1006
|
+
* @returns a success boolean if the object was saved in cache
|
|
1007
|
+
*
|
|
1008
|
+
* @category Cache API
|
|
1009
|
+
* @hidden
|
|
1010
|
+
*/
|
|
1011
|
+
const upsertInCache = (key, data, options = { cachedAt: Date.now() }) => {
|
|
1012
|
+
const { log, cache } = getActiveClient();
|
|
1013
|
+
if (!cache)
|
|
1014
|
+
return false;
|
|
1015
|
+
log('cache/api/upsertInCache', { key, data, options });
|
|
1016
|
+
const cached = pullFromCache(key);
|
|
1017
|
+
return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* ```js
|
|
1022
|
+
* import { dropFromCache } from '@amityco/ts-sdk'
|
|
1023
|
+
* const success = dropFromCache(['user', 'foobar'])
|
|
1024
|
+
* ```
|
|
1025
|
+
*
|
|
1026
|
+
* Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
|
|
1027
|
+
* {@link Amity.Cache} from a given {@link Amity.CacheKey}
|
|
1028
|
+
*
|
|
1029
|
+
* @param key The key of the object to delete
|
|
1030
|
+
* @param exact If false, the function will delete all keys satisfying the given key
|
|
1031
|
+
* @returns A success boolean if the object was deleted
|
|
1032
|
+
*
|
|
1033
|
+
* @category Cache API
|
|
1034
|
+
*/
|
|
1035
|
+
const dropFromCache = (key, exact = false) => {
|
|
1036
|
+
const { log, cache } = getActiveClient();
|
|
1037
|
+
if (!cache)
|
|
1038
|
+
return false;
|
|
1039
|
+
log('cache/api/dropFromCache', { key, exact });
|
|
1040
|
+
if (!exact) {
|
|
1041
|
+
return Object.keys(cache.data)
|
|
1042
|
+
.map(stringKey => decodeKey(stringKey))
|
|
1043
|
+
.filter(candidate => partialMatch(key, candidate))
|
|
1044
|
+
.map(filteredKey => dropFromCache(filteredKey, true))
|
|
1045
|
+
.every(returned => returned);
|
|
1046
|
+
}
|
|
1047
|
+
const encodedKey = encodeKey(key);
|
|
1048
|
+
if (encodedKey in cache.data) {
|
|
1049
|
+
delete cache.data[encodedKey];
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
return false;
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
// Note:
|
|
1056
|
+
// this file should contain a suite of filtering utilities to help the
|
|
1057
|
+
// local version of the query functions.
|
|
1058
|
+
/**
|
|
1059
|
+
* Filter a given collection with strict equality against a param
|
|
1060
|
+
*
|
|
1061
|
+
* @param collection the collection to filter
|
|
1062
|
+
* @param key the key of the collection's items to challenge
|
|
1063
|
+
* @param value the expected value
|
|
1064
|
+
* @returns a filtered collection with items only matching the criteria
|
|
1065
|
+
*
|
|
1066
|
+
* @hidden
|
|
1067
|
+
*/
|
|
1068
|
+
const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
|
|
1069
|
+
const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
|
|
1070
|
+
/**
|
|
1071
|
+
* Filter a channel collection by membership of the userId
|
|
1072
|
+
*
|
|
1073
|
+
* @param collection the channel collection to filter
|
|
1074
|
+
* @param membership the membership to be filtered by
|
|
1075
|
+
* @param userId user id to be filtered by
|
|
1076
|
+
* @returns a filtered collection with items only matching the criteria
|
|
1077
|
+
*
|
|
1078
|
+
* @hidden
|
|
1079
|
+
*/
|
|
1080
|
+
const filterByChannelMembership = (collection, membership, userId) => {
|
|
1081
|
+
if (membership === 'all') {
|
|
1082
|
+
return collection;
|
|
1083
|
+
}
|
|
1084
|
+
return collection.filter(c => {
|
|
1085
|
+
var _a;
|
|
1086
|
+
// get resolver for the channel by user
|
|
1087
|
+
const channelUserCacheKey = getResolver('channelUsers')({
|
|
1088
|
+
channelId: c.channelId,
|
|
1089
|
+
userId,
|
|
1090
|
+
});
|
|
1091
|
+
const channelUser = (_a = pullFromCache([
|
|
1092
|
+
'channelUsers',
|
|
1093
|
+
'get',
|
|
1094
|
+
channelUserCacheKey,
|
|
1095
|
+
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
1096
|
+
// if channel user is defined for user, user is a member
|
|
1097
|
+
if (membership === 'member' && channelUser) {
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
// if channel user by user id not found, user is not a member of that
|
|
1101
|
+
// channel
|
|
1102
|
+
if (membership === 'notMember' && !channelUser) {
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
return false;
|
|
1106
|
+
});
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
// Note:
|
|
1110
|
+
// this file should contain a suite of sorting utilities to help the
|
|
1111
|
+
// local version of the query functions.
|
|
1112
|
+
/**
|
|
1113
|
+
* Alphabetic sorting of objects having a displayName
|
|
1114
|
+
*/
|
|
1115
|
+
const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
|
|
1116
|
+
if (a === b)
|
|
1117
|
+
return 0;
|
|
1118
|
+
return a < b ? -1 : 1;
|
|
1119
|
+
};
|
|
1120
|
+
/**
|
|
1121
|
+
* Sorting a collection by their apparition order (oldest first)
|
|
1122
|
+
*/
|
|
1123
|
+
const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
|
|
1124
|
+
/**
|
|
1125
|
+
* Sorting a collection by their apparition order (oldest first)
|
|
1126
|
+
*/
|
|
1127
|
+
const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
|
|
1128
|
+
/**
|
|
1129
|
+
* Sorting a collection by its oldest items
|
|
1130
|
+
*/
|
|
1131
|
+
const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
|
|
1132
|
+
/**
|
|
1133
|
+
* Sorting a collection by its newest items
|
|
1134
|
+
*/
|
|
1135
|
+
const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
|
|
1136
|
+
/**
|
|
1137
|
+
* Sorting a collection by the items with most recent activity
|
|
1138
|
+
*/
|
|
1139
|
+
const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
|
|
1140
|
+
|
|
1141
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
1142
|
+
let tasks = [];
|
|
1143
|
+
let timer;
|
|
1144
|
+
/**
|
|
1145
|
+
* Add a function to run just before the next tick
|
|
1146
|
+
*
|
|
1147
|
+
* @param task function to schedule for later execution
|
|
1148
|
+
*/
|
|
1149
|
+
const scheduleTask = (task) => {
|
|
1150
|
+
clearTimeout(timer);
|
|
1151
|
+
tasks.push(task);
|
|
1152
|
+
timer = setTimeout(() => {
|
|
1153
|
+
tasks.forEach(fn => fn());
|
|
1154
|
+
tasks = [];
|
|
1155
|
+
}, 0);
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1159
|
+
// need to sync up the list
|
|
1160
|
+
const WS_EVENTS = [
|
|
1161
|
+
'disconnected',
|
|
1162
|
+
'error',
|
|
1163
|
+
'connect_error',
|
|
1164
|
+
'reconnect_error',
|
|
1165
|
+
'reconnect_failed',
|
|
1166
|
+
'v3.channel.didCreate',
|
|
1167
|
+
'v3.channel.didUpdate',
|
|
1168
|
+
'v3.channel.didDelete',
|
|
1169
|
+
'v3.channel.didJoin',
|
|
1170
|
+
'v3.channel.didLeave',
|
|
1171
|
+
'v3.channel.didAddUsers',
|
|
1172
|
+
'v3.channel.didRemoveUsers',
|
|
1173
|
+
'v3.channel.didBan',
|
|
1174
|
+
'v3.channel.didUnban',
|
|
1175
|
+
'v3.message.didFetch',
|
|
1176
|
+
'v3.message.didCreate',
|
|
1177
|
+
'v3.message.didUpdate',
|
|
1178
|
+
'v3.message.didDelete',
|
|
1179
|
+
];
|
|
1180
|
+
const MQTT_EVENTS = [
|
|
1181
|
+
'connect',
|
|
1182
|
+
'message',
|
|
1183
|
+
'disconnect',
|
|
1184
|
+
'error',
|
|
1185
|
+
'close',
|
|
1186
|
+
'end',
|
|
1187
|
+
'reconnect',
|
|
1188
|
+
];
|
|
1189
|
+
/** @hidden */
|
|
1190
|
+
const createEventEmitter = () => {
|
|
1191
|
+
return mitt__default["default"]();
|
|
1192
|
+
};
|
|
1193
|
+
/**
|
|
1194
|
+
* Wraps the websocket events into the client's event emitter for
|
|
1195
|
+
* abstraction of transport.
|
|
1196
|
+
*
|
|
1197
|
+
* @param ws A websocket connection to listen from
|
|
1198
|
+
* @param emitter An event emitter to wire the events to
|
|
1199
|
+
*
|
|
1200
|
+
* @category Transport
|
|
1201
|
+
* @hidden
|
|
1202
|
+
*/
|
|
1203
|
+
const proxyWebsocketEvents = (ws, emitter) => {
|
|
1204
|
+
WS_EVENTS.forEach(event => {
|
|
1205
|
+
ws.on(event, (param) => emitter.emit(event, param));
|
|
1206
|
+
});
|
|
1207
|
+
};
|
|
1208
|
+
const proxyMqttEvents = (mqttClient, emitter) => {
|
|
1209
|
+
MQTT_EVENTS.forEach(event => {
|
|
1210
|
+
mqttClient.on(event, (...params) => emitter.emit(event, params.length === 1 ? params[0] : params));
|
|
1211
|
+
});
|
|
1212
|
+
// @ts-ignore
|
|
1213
|
+
mqttClient.on('message', (topic, payload) => {
|
|
1214
|
+
const message = JSON.parse(payload.toString());
|
|
1215
|
+
emitter.emit(message.eventType, message.data);
|
|
1216
|
+
});
|
|
1217
|
+
};
|
|
1218
|
+
/**
|
|
1219
|
+
* Standardize the subscription of SSE through web sockets
|
|
1220
|
+
*
|
|
1221
|
+
* @param client The current client for which to subscribe the event to
|
|
1222
|
+
* @param namespace A unique name for the logger
|
|
1223
|
+
* @param event The websocket event name
|
|
1224
|
+
* @param fn A wrapper for the callback.
|
|
1225
|
+
* @returns A dispose function to unsubscribe to the event
|
|
1188
1226
|
*
|
|
1189
|
-
* @category
|
|
1227
|
+
* @category Transport
|
|
1228
|
+
* @hidden
|
|
1190
1229
|
*/
|
|
1191
|
-
const
|
|
1192
|
-
const { log,
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1230
|
+
const createEventSubscriber = (client, namespace, event, fn) => {
|
|
1231
|
+
const { log, emitter } = client;
|
|
1232
|
+
const timestamp = Date.now();
|
|
1233
|
+
log(`${namespace}(tmpid: ${timestamp}) > listen`);
|
|
1234
|
+
const handler = (...payload) => {
|
|
1235
|
+
log(`${namespace}(tmpid: ${timestamp}) > trigger`, payload);
|
|
1236
|
+
fn(...payload);
|
|
1237
|
+
};
|
|
1238
|
+
emitter.on(event, handler);
|
|
1239
|
+
return () => {
|
|
1240
|
+
log(`${namespace}(tmpid: ${timestamp}) > dispose`);
|
|
1241
|
+
emitter.off(event, handler);
|
|
1242
|
+
};
|
|
1243
|
+
};
|
|
1204
1244
|
/**
|
|
1205
|
-
*
|
|
1206
|
-
* import { upsertInCache } from '@amityco/ts-sdk'
|
|
1207
|
-
* upsertInCache<Amity.User>(["user", "foobar"], user)
|
|
1208
|
-
* ```
|
|
1209
|
-
*
|
|
1210
|
-
* Insert or update any provided value as {@link Amity.CacheEntry} for
|
|
1211
|
-
* the matching {@link Amity.CacheKey}
|
|
1212
|
-
*
|
|
1213
|
-
* @param key the key to save the object to
|
|
1214
|
-
* @param data the object to save
|
|
1215
|
-
* @param options customisation object around cache behavior (default gives a 2mn lifespan)
|
|
1216
|
-
* @returns a success boolean if the object was saved in cache
|
|
1245
|
+
* Wrapper around dispatch event
|
|
1217
1246
|
*
|
|
1218
|
-
* @category Cache API
|
|
1219
1247
|
* @hidden
|
|
1220
1248
|
*/
|
|
1221
|
-
const
|
|
1222
|
-
const {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
const cached = pullFromCache(key);
|
|
1227
|
-
return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
|
|
1249
|
+
const fireEvent = (event, payload) => {
|
|
1250
|
+
const { emitter } = getActiveClient();
|
|
1251
|
+
scheduleTask(() => {
|
|
1252
|
+
emitter.emit(event, payload);
|
|
1253
|
+
});
|
|
1228
1254
|
};
|
|
1229
1255
|
|
|
1256
|
+
let activeUser = null;
|
|
1230
1257
|
/**
|
|
1231
|
-
*
|
|
1232
|
-
* import { dropFromCache } from '@amityco/ts-sdk'
|
|
1233
|
-
* const success = dropFromCache(['user', 'foobar'])
|
|
1234
|
-
* ```
|
|
1235
|
-
*
|
|
1236
|
-
* Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
|
|
1237
|
-
* {@link Amity.Cache} from a given {@link Amity.CacheKey}
|
|
1238
|
-
*
|
|
1239
|
-
* @param key The key of the object to delete
|
|
1240
|
-
* @param exact If false, the function will delete all keys satisfying the given key
|
|
1241
|
-
* @returns A success boolean if the object was deleted
|
|
1242
|
-
*
|
|
1243
|
-
* @category Cache API
|
|
1258
|
+
* for internal use
|
|
1244
1259
|
*/
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
return false;
|
|
1249
|
-
log('cache/api/dropFromCache', { key, exact });
|
|
1250
|
-
if (!exact) {
|
|
1251
|
-
return Object.keys(cache.data)
|
|
1252
|
-
.map(stringKey => decodeKey(stringKey))
|
|
1253
|
-
.filter(candidate => partialMatch(key, candidate))
|
|
1254
|
-
.map(filteredKey => dropFromCache(filteredKey, true))
|
|
1255
|
-
.every(returned => returned);
|
|
1256
|
-
}
|
|
1257
|
-
const encodedKey = encodeKey(key);
|
|
1258
|
-
if (encodedKey in cache.data) {
|
|
1259
|
-
delete cache.data[encodedKey];
|
|
1260
|
-
return true;
|
|
1260
|
+
const getActiveUser = () => {
|
|
1261
|
+
if (!activeUser) {
|
|
1262
|
+
throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
|
|
1261
1263
|
}
|
|
1262
|
-
return
|
|
1264
|
+
return activeUser;
|
|
1265
|
+
};
|
|
1266
|
+
const setActiveUser = (user) => {
|
|
1267
|
+
activeUser = {
|
|
1268
|
+
_id: user._id,
|
|
1269
|
+
userId: user.userId,
|
|
1270
|
+
path: user.path,
|
|
1271
|
+
};
|
|
1263
1272
|
};
|
|
1264
1273
|
|
|
1274
|
+
exports.SubscriptionLevels = void 0;
|
|
1275
|
+
(function (SubscriptionLevels) {
|
|
1276
|
+
SubscriptionLevels["COMMUNITY"] = "community";
|
|
1277
|
+
SubscriptionLevels["POST"] = "post";
|
|
1278
|
+
SubscriptionLevels["COMMENT"] = "comment";
|
|
1279
|
+
SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
|
|
1280
|
+
SubscriptionLevels["USER"] = "user";
|
|
1281
|
+
})(exports.SubscriptionLevels || (exports.SubscriptionLevels = {}));
|
|
1282
|
+
const getCommunityUserTopic = (path, level) => {
|
|
1283
|
+
switch (level) {
|
|
1284
|
+
case 'post':
|
|
1285
|
+
return `${path}/post/+`;
|
|
1286
|
+
case 'comment':
|
|
1287
|
+
return `${path}/post/+/comment/+`;
|
|
1288
|
+
case 'post_and_comment':
|
|
1289
|
+
return `${path}/post/#`;
|
|
1290
|
+
default:
|
|
1291
|
+
return path;
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
const getCommunityTopic = ({ path }, level = exports.SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
|
|
1295
|
+
const getUserTopic = ({ path }, level = exports.SubscriptionLevels.USER) => getCommunityUserTopic(level === exports.SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
|
|
1296
|
+
const getPostTopic = ({ path }, level = exports.SubscriptionLevels.POST) => {
|
|
1297
|
+
switch (level) {
|
|
1298
|
+
case 'comment':
|
|
1299
|
+
return `${path}/comment/+`;
|
|
1300
|
+
default:
|
|
1301
|
+
return path;
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
const getCommentTopic = ({ path }) => {
|
|
1305
|
+
return path;
|
|
1306
|
+
};
|
|
1307
|
+
const getMyFollowersTopic = () => {
|
|
1308
|
+
const user = getActiveUser();
|
|
1309
|
+
const [networkId] = user.path.split('/user/');
|
|
1310
|
+
return `${networkId}/membership/${user._id}/+/+`;
|
|
1311
|
+
};
|
|
1312
|
+
const getMyFollowingsTopic = () => {
|
|
1313
|
+
const user = getActiveUser();
|
|
1314
|
+
const [networkId] = user.path.split('/user/');
|
|
1315
|
+
return `${networkId}/membership/+/${user._id}/+`;
|
|
1316
|
+
};
|
|
1317
|
+
/** @hidden */
|
|
1318
|
+
const getNetworkTopic = () => {
|
|
1319
|
+
const user = getActiveUser();
|
|
1320
|
+
const [networkId] = user.path.split('/user/');
|
|
1321
|
+
return networkId;
|
|
1322
|
+
};
|
|
1323
|
+
let mqttAccessToken;
|
|
1324
|
+
let mqttUserId;
|
|
1325
|
+
function subscribeTopic(topic, callback) {
|
|
1326
|
+
const { mqtt, emitter, accessToken } = getActiveClient();
|
|
1327
|
+
const user = getActiveUser();
|
|
1328
|
+
if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
|
|
1329
|
+
mqttAccessToken = accessToken;
|
|
1330
|
+
mqttUserId = user._id;
|
|
1331
|
+
mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
|
|
1332
|
+
proxyMqttEvents(mqtt, emitter);
|
|
1333
|
+
}
|
|
1334
|
+
return mqtt.subscribe(topic, callback);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1265
1337
|
/**
|
|
1266
1338
|
* Creates a pre-configured logger instance
|
|
1267
1339
|
*
|
|
@@ -20578,6 +20650,61 @@ const isConnected = () => {
|
|
|
20578
20650
|
client.ws.connected);
|
|
20579
20651
|
};
|
|
20580
20652
|
|
|
20653
|
+
/**
|
|
20654
|
+
* ```js
|
|
20655
|
+
* import { isInTombstone } from '@amityco/ts-sdk'
|
|
20656
|
+
* const user = isInTombstone(["message", "messageId"])
|
|
20657
|
+
* ```
|
|
20658
|
+
*
|
|
20659
|
+
* Checks if the {@link Amity.TombstoneCacheOptions} exists
|
|
20660
|
+
* in cache and it's not expired means it's in tombstone
|
|
20661
|
+
* and we throw an Error
|
|
20662
|
+
*
|
|
20663
|
+
* @param model the model to check
|
|
20664
|
+
* @param modelId the object id to check
|
|
20665
|
+
* @returns the matching cache entry, or undefined.
|
|
20666
|
+
*
|
|
20667
|
+
* @category Cache API
|
|
20668
|
+
*/
|
|
20669
|
+
const isInTombstone = (model, modelId) => {
|
|
20670
|
+
const { log, cache } = getActiveClient();
|
|
20671
|
+
const key = [model, CACHE_KEY_TOMBSTONE, modelId];
|
|
20672
|
+
if (!cache)
|
|
20673
|
+
return;
|
|
20674
|
+
log('cache/api/isInTombstone', key);
|
|
20675
|
+
const isInTombstone = pullFromCache(key);
|
|
20676
|
+
const { lifeSpan } = queryOptions('cache_then_server', CACHE_LIFESPAN_TOMBSTONE);
|
|
20677
|
+
if (isInTombstone && isFresh(isInTombstone.data, lifeSpan)) {
|
|
20678
|
+
throw new ASCApiError('Item not found!', 400400 /* Amity.ServerError.ITEM_NOT_FOUND */, "error" /* Amity.ErrorLevel.ERROR */);
|
|
20679
|
+
}
|
|
20680
|
+
};
|
|
20681
|
+
|
|
20682
|
+
/**
|
|
20683
|
+
* ```js
|
|
20684
|
+
* import { pushToTombstone } from '@amityco/ts-sdk'
|
|
20685
|
+
* pushToTombstone(["message", "messageId"], { cachedAt: Date.now() })
|
|
20686
|
+
* ```
|
|
20687
|
+
*
|
|
20688
|
+
* Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
|
|
20689
|
+
* {@link Amity.Cache} from a given {@link Amity.CacheKey}
|
|
20690
|
+
*
|
|
20691
|
+
* @param model the model to remove and push to Tombstone
|
|
20692
|
+
* @param modelId the related model id
|
|
20693
|
+
*
|
|
20694
|
+
* @category Cache API
|
|
20695
|
+
* @hidden
|
|
20696
|
+
*/
|
|
20697
|
+
const pushToTombstone = (model, modelId) => {
|
|
20698
|
+
const { log, cache } = getActiveClient();
|
|
20699
|
+
const cacheKey = [model, CACHE_KEY_TOMBSTONE, modelId];
|
|
20700
|
+
const data = { cachedAt: Date.now() };
|
|
20701
|
+
if (!cache)
|
|
20702
|
+
return;
|
|
20703
|
+
log('cache/api/pushToTombstone', { cacheKey, data });
|
|
20704
|
+
dropFromCache([model, CACHE_KEY_GET, modelId], true);
|
|
20705
|
+
pushToCache(cacheKey, data);
|
|
20706
|
+
};
|
|
20707
|
+
|
|
20581
20708
|
/**
|
|
20582
20709
|
* ```js
|
|
20583
20710
|
* import { getUser } from '@amityco/ts-sdk'
|
|
@@ -20592,15 +20719,30 @@ const isConnected = () => {
|
|
|
20592
20719
|
* @category User API
|
|
20593
20720
|
* @async
|
|
20594
20721
|
*/
|
|
20595
|
-
const getUser = async (userId) => {
|
|
20722
|
+
const getUser = async (userId, isLive = false) => {
|
|
20596
20723
|
const client = getActiveClient();
|
|
20597
20724
|
client.log('user/getUser', userId);
|
|
20598
|
-
|
|
20599
|
-
|
|
20725
|
+
if (!isLive) {
|
|
20726
|
+
console.log('getUser will be deprecated on 25th November 2022, please use liveUser instead.');
|
|
20727
|
+
}
|
|
20728
|
+
isInTombstone('user', userId);
|
|
20729
|
+
let data;
|
|
20730
|
+
try {
|
|
20731
|
+
// API-FIX: endpoint should not be /list, parameters should be querystring.
|
|
20732
|
+
const response = await client.http.get(`/api/v3/users/${encodeURIComponent(userId)}`);
|
|
20733
|
+
data = response.data;
|
|
20734
|
+
}
|
|
20735
|
+
catch (error) {
|
|
20736
|
+
if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
|
|
20737
|
+
pushToTombstone('user', userId);
|
|
20738
|
+
}
|
|
20739
|
+
throw error;
|
|
20740
|
+
}
|
|
20600
20741
|
const cachedAt = client.cache && Date.now();
|
|
20601
20742
|
if (client.cache)
|
|
20602
20743
|
ingestInCache(data, { cachedAt });
|
|
20603
20744
|
const { users } = data;
|
|
20745
|
+
fireEvent('user.fetched', data);
|
|
20604
20746
|
return {
|
|
20605
20747
|
data: users.find(user => user.userId === userId),
|
|
20606
20748
|
cachedAt,
|
|
@@ -20650,6 +20792,7 @@ const getUsers = async (userIds) => {
|
|
|
20650
20792
|
const cachedAt = client.cache && Date.now();
|
|
20651
20793
|
if (client.cache)
|
|
20652
20794
|
ingestInCache(data, { cachedAt });
|
|
20795
|
+
fireEvent('user.fetched', data);
|
|
20653
20796
|
return {
|
|
20654
20797
|
data: data.users,
|
|
20655
20798
|
cachedAt,
|
|
@@ -20749,6 +20892,7 @@ const queryUsers = async (query = {}) => {
|
|
|
20749
20892
|
const cacheKey = ['user', 'query', query];
|
|
20750
20893
|
pushToCache(cacheKey, { users: users.map(getResolver('user')), paging });
|
|
20751
20894
|
}
|
|
20895
|
+
fireEvent('user.fetched', data);
|
|
20752
20896
|
const prevPage = toPage(paging.previous);
|
|
20753
20897
|
const nextPage = toPage(paging.next);
|
|
20754
20898
|
return { data: users, cachedAt, prevPage, nextPage };
|
|
@@ -20812,12 +20956,30 @@ const updateUser = async (userId, patch) => {
|
|
|
20812
20956
|
if (client.cache)
|
|
20813
20957
|
ingestInCache(data, { cachedAt });
|
|
20814
20958
|
const { users } = data;
|
|
20959
|
+
fireEvent('user.updated', data);
|
|
20815
20960
|
return {
|
|
20816
20961
|
data: users.find(user => user.userId === userId),
|
|
20817
20962
|
cachedAt,
|
|
20818
20963
|
};
|
|
20819
20964
|
};
|
|
20820
20965
|
|
|
20966
|
+
/**
|
|
20967
|
+
* ```js
|
|
20968
|
+
* import { onUserFetched } from '@amityco/ts-sdk'
|
|
20969
|
+
* const dispose = onUserFetched(user => {
|
|
20970
|
+
* // ...
|
|
20971
|
+
* })
|
|
20972
|
+
* ```
|
|
20973
|
+
*
|
|
20974
|
+
* Fired when a {@link Amity.User} has been fetched
|
|
20975
|
+
*
|
|
20976
|
+
* @param callback The function to call when the event was fired
|
|
20977
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
20978
|
+
*
|
|
20979
|
+
* @category User Events
|
|
20980
|
+
*/
|
|
20981
|
+
const onUserFetched = (callback) => createUserEventSubscriber('user.fetched', callback);
|
|
20982
|
+
|
|
20821
20983
|
/**
|
|
20822
20984
|
* ```js
|
|
20823
20985
|
* import { onUserUpdated } from '@amityco/ts-sdk'
|
|
@@ -20886,6 +21048,68 @@ const onUserUnflagged = (callback) => createUserEventSubscriber('user.unflagged'
|
|
|
20886
21048
|
*/
|
|
20887
21049
|
const onUserFlagCleared = (callback) => createUserEventSubscriber('user.flagCleared', callback);
|
|
20888
21050
|
|
|
21051
|
+
const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
|
|
21052
|
+
const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
|
|
21053
|
+
const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
|
|
21054
|
+
const LIVE_OBJECT_ENABLE_CACHE_MESSAGE = 'For using Live Object feature you need to enable Cache!';
|
|
21055
|
+
const UNSYNCED_OBJECT_CACHED_AT_MESSAGE = 'Observing unsynced object is not supported by Live Object.';
|
|
21056
|
+
const UNSYNCED_OBJECT_CACHED_AT_VALUE = -5;
|
|
21057
|
+
|
|
21058
|
+
/**
|
|
21059
|
+
* ```js
|
|
21060
|
+
* import { liveUser } from '@amityco/ts-sdk'
|
|
21061
|
+
*
|
|
21062
|
+
* let users = []
|
|
21063
|
+
* const unsub = liveUser(userId, response => merge(users, response.data))
|
|
21064
|
+
* ```
|
|
21065
|
+
*
|
|
21066
|
+
* Observe all mutation on a given {@link Amity.User}
|
|
21067
|
+
*
|
|
21068
|
+
* @param userId the ID of the user to observe
|
|
21069
|
+
* @param callback the function to call when new data are available
|
|
21070
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
|
|
21071
|
+
*
|
|
21072
|
+
* @category Message Live Object
|
|
21073
|
+
*/
|
|
21074
|
+
const liveUser = (userId, callback) => {
|
|
21075
|
+
const { log, cache } = getActiveClient();
|
|
21076
|
+
let user;
|
|
21077
|
+
if (!cache) {
|
|
21078
|
+
console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
|
|
21079
|
+
}
|
|
21080
|
+
const timestamp = Date.now();
|
|
21081
|
+
log(`liveUser(tmpid: ${timestamp}) > listen`);
|
|
21082
|
+
const disposers = [];
|
|
21083
|
+
const dispatcher = (data) => {
|
|
21084
|
+
var _a;
|
|
21085
|
+
// we check updatedAt to see if the current snapshot is new so
|
|
21086
|
+
// we won't send old data back, only fresh new data
|
|
21087
|
+
if (user && ((_a = data.data) === null || _a === void 0 ? void 0 : _a.updatedAt) === (user === null || user === void 0 ? void 0 : user.updatedAt) && data.origin !== 'server')
|
|
21088
|
+
return;
|
|
21089
|
+
callback(data);
|
|
21090
|
+
user = data.data;
|
|
21091
|
+
};
|
|
21092
|
+
const realtimeRouter = (user, action) => {
|
|
21093
|
+
dispatcher({ loading: false, data: user, origin: 'event' });
|
|
21094
|
+
};
|
|
21095
|
+
const onFetch = () => {
|
|
21096
|
+
const query = createQuery(getUser, userId, true);
|
|
21097
|
+
runQuery(query, ({ loading, data, error, origin }) => {
|
|
21098
|
+
dispatcher({ loading, data, origin, error });
|
|
21099
|
+
if (error) {
|
|
21100
|
+
// Terminate observe functionality on error
|
|
21101
|
+
disposers.forEach(fn => fn());
|
|
21102
|
+
}
|
|
21103
|
+
});
|
|
21104
|
+
};
|
|
21105
|
+
disposers.push(onUserFetched(user => realtimeRouter(user)), onUserUpdated(user => realtimeRouter(user)), onUserDeleted(user => realtimeRouter(user)), onUserFlagged(user => realtimeRouter(user)), onUserUnflagged(user => realtimeRouter(user)), onUserFlagCleared(user => realtimeRouter(user)));
|
|
21106
|
+
onFetch();
|
|
21107
|
+
return () => {
|
|
21108
|
+
log(`liveUser(tmpid: ${timestamp}) > dispose`);
|
|
21109
|
+
disposers.forEach(fn => fn());
|
|
21110
|
+
};
|
|
21111
|
+
};
|
|
21112
|
+
|
|
20889
21113
|
/**
|
|
20890
21114
|
* ```js
|
|
20891
21115
|
* import { observeUser } from '@amityco/ts-sdk'
|
|
@@ -20905,6 +21129,7 @@ const observeUser = (userId, callback) => {
|
|
|
20905
21129
|
const { log } = getActiveClient();
|
|
20906
21130
|
const timestamp = Date.now();
|
|
20907
21131
|
log(`observeUser(tmpid: ${timestamp}) > listen`);
|
|
21132
|
+
console.log('observeUser will be deprecated on 25th November 2022, please use liveUser instead.');
|
|
20908
21133
|
// wrapper function to make sure
|
|
20909
21134
|
const router = (result, action) => {
|
|
20910
21135
|
var _a, _b, _c;
|
|
@@ -21314,15 +21539,30 @@ const dispatchReactable = (referenceType, model) => {
|
|
|
21314
21539
|
* @category Message API
|
|
21315
21540
|
* @async
|
|
21316
21541
|
*/
|
|
21317
|
-
const getMessage = async (messageId) => {
|
|
21542
|
+
const getMessage = async (messageId, isLive = false) => {
|
|
21318
21543
|
const client = getActiveClient();
|
|
21319
21544
|
client.log('message/getMessage', messageId);
|
|
21320
|
-
|
|
21321
|
-
|
|
21545
|
+
if (!isLive) {
|
|
21546
|
+
console.log('getMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
|
|
21547
|
+
}
|
|
21548
|
+
isInTombstone('message', messageId);
|
|
21549
|
+
let data;
|
|
21550
|
+
try {
|
|
21551
|
+
// API-FIX: endpoint should not be /list, parameters should be querystring.
|
|
21552
|
+
const response = await client.http.get(`/api/v3/messages/${encodeURIComponent(messageId)}`);
|
|
21553
|
+
data = response.data;
|
|
21554
|
+
}
|
|
21555
|
+
catch (error) {
|
|
21556
|
+
if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
|
|
21557
|
+
pushToTombstone('message', messageId);
|
|
21558
|
+
}
|
|
21559
|
+
throw error;
|
|
21560
|
+
}
|
|
21322
21561
|
const cachedAt = client.cache && Date.now();
|
|
21323
21562
|
if (client.cache)
|
|
21324
21563
|
ingestInCache(data, { cachedAt });
|
|
21325
21564
|
const { messages } = data;
|
|
21565
|
+
fireEvent('v3.message.didFetch', { messages });
|
|
21326
21566
|
return {
|
|
21327
21567
|
data: messages.find(message => message.messageId === messageId),
|
|
21328
21568
|
cachedAt,
|
|
@@ -21379,8 +21619,10 @@ const getMessages = async (messageIds) => {
|
|
|
21379
21619
|
const cachedAt = client.cache && Date.now();
|
|
21380
21620
|
if (client.cache)
|
|
21381
21621
|
ingestInCache(data, { cachedAt });
|
|
21622
|
+
const { messages } = data;
|
|
21623
|
+
fireEvent('v3.message.didFetch', { messages });
|
|
21382
21624
|
return {
|
|
21383
|
-
data:
|
|
21625
|
+
data: messages,
|
|
21384
21626
|
cachedAt,
|
|
21385
21627
|
};
|
|
21386
21628
|
};
|
|
@@ -21457,6 +21699,7 @@ const queryMessages = async (query) => {
|
|
|
21457
21699
|
];
|
|
21458
21700
|
pushToCache(cacheKey, { messages: messages.map(getResolver('message')), paging });
|
|
21459
21701
|
}
|
|
21702
|
+
fireEvent('v3.message.didFetch', { messages });
|
|
21460
21703
|
const nextPage = toPage(paging.next);
|
|
21461
21704
|
const prevPage = toPage(paging.previous);
|
|
21462
21705
|
return { data: messages, cachedAt, prevPage, nextPage };
|
|
@@ -21522,8 +21765,8 @@ const createMessage = async (bundle) => {
|
|
|
21522
21765
|
const cachedAt = client.cache && Date.now();
|
|
21523
21766
|
if (client.cache)
|
|
21524
21767
|
ingestInCache(data, { cachedAt });
|
|
21525
|
-
// no need for event, fired by server
|
|
21526
21768
|
const { messages } = data;
|
|
21769
|
+
fireEvent('v3.message.didCreate', { messages });
|
|
21527
21770
|
return {
|
|
21528
21771
|
data: messages[0],
|
|
21529
21772
|
cachedAt,
|
|
@@ -21565,11 +21808,12 @@ createMessage.optimistically = (bundle) => {
|
|
|
21565
21808
|
// as reused to update created and updated time, which should be the same
|
|
21566
21809
|
const createdTime = new Date().toISOString();
|
|
21567
21810
|
const message = Object.assign({ userId: client.userId, channelSegment: ((_b = channel === null || channel === void 0 ? void 0 : channel.data.messageCount) !== null && _b !== void 0 ? _b : 0) + 1, createdAt: createdTime, updatedAt: createdTime }, bundleWithMessageId);
|
|
21568
|
-
|
|
21811
|
+
const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
|
|
21812
|
+
pushToCache(['message', 'get', message.messageId], message, { cachedAt });
|
|
21569
21813
|
fireEvent('v3.message.didCreate', { messages: [message] });
|
|
21570
21814
|
return {
|
|
21815
|
+
cachedAt,
|
|
21571
21816
|
data: message,
|
|
21572
|
-
cachedAt: -1,
|
|
21573
21817
|
};
|
|
21574
21818
|
};
|
|
21575
21819
|
|
|
@@ -21598,6 +21842,7 @@ const updateMessage = async (messageId, patch) => {
|
|
|
21598
21842
|
if (client.cache)
|
|
21599
21843
|
ingestInCache(data, { cachedAt });
|
|
21600
21844
|
const { messages } = data;
|
|
21845
|
+
fireEvent('v3.message.didUpdate', { messages });
|
|
21601
21846
|
return {
|
|
21602
21847
|
data: messages.find(message => message.messageId === messageId),
|
|
21603
21848
|
cachedAt,
|
|
@@ -21657,7 +21902,7 @@ const deleteMessage = async (messageId) => {
|
|
|
21657
21902
|
// API-FIX: This endpoint has not been implemented yet.
|
|
21658
21903
|
await client.http.delete(`/api/v3/messages/${encodeURIComponent(messageId)}`);
|
|
21659
21904
|
const deleted = await getMessage(messageId);
|
|
21660
|
-
|
|
21905
|
+
fireEvent('v3.message.didDelete', { messages: [deleted.data] });
|
|
21661
21906
|
return deleted.data;
|
|
21662
21907
|
};
|
|
21663
21908
|
/**
|
|
@@ -21844,6 +22089,7 @@ getPosts.locally = (postIds) => {
|
|
|
21844
22089
|
const getPost = async (postId) => {
|
|
21845
22090
|
const client = getActiveClient();
|
|
21846
22091
|
client.log('post/getPost', postId);
|
|
22092
|
+
isInTombstone('post', postId);
|
|
21847
22093
|
let data;
|
|
21848
22094
|
try {
|
|
21849
22095
|
// API-FIX: endpoint should not be /list, parameters should be querystring.
|
|
@@ -21851,8 +22097,8 @@ const getPost = async (postId) => {
|
|
|
21851
22097
|
data = response.data;
|
|
21852
22098
|
}
|
|
21853
22099
|
catch (error) {
|
|
21854
|
-
if ((error === null || error === void 0 ? void 0 : error.code)
|
|
21855
|
-
|
|
22100
|
+
if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
|
|
22101
|
+
pushToTombstone('post', postId);
|
|
21856
22102
|
}
|
|
21857
22103
|
throw error;
|
|
21858
22104
|
}
|
|
@@ -22632,7 +22878,7 @@ const deletePost = async (postId, permanent = false) => {
|
|
|
22632
22878
|
fireEvent('post.deleted', { posts: [deleted] });
|
|
22633
22879
|
if (permanent) {
|
|
22634
22880
|
setTimeout(() => {
|
|
22635
|
-
|
|
22881
|
+
pushToTombstone('post', postId);
|
|
22636
22882
|
}, 0);
|
|
22637
22883
|
}
|
|
22638
22884
|
else {
|
|
@@ -23886,10 +24132,6 @@ const onReactorRemoved = (referenceType, referenceId, callback) => {
|
|
|
23886
24132
|
return createEventSubscriber(client, 'comment.removeReaction', 'comment.removeReaction', filter);
|
|
23887
24133
|
};
|
|
23888
24134
|
|
|
23889
|
-
const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
|
|
23890
|
-
const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
|
|
23891
|
-
const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
|
|
23892
|
-
|
|
23893
24135
|
/**
|
|
23894
24136
|
* ```js
|
|
23895
24137
|
* import { liveReactions } from '@amityco/ts-sdk'
|
|
@@ -24166,9 +24408,24 @@ const queryChannels = async (query) => {
|
|
|
24166
24408
|
* @category Channel API
|
|
24167
24409
|
*/
|
|
24168
24410
|
queryChannels.locally = (query) => {
|
|
24411
|
+
var _a, _b;
|
|
24169
24412
|
const client = getActiveClient();
|
|
24170
24413
|
client.log('channel/queryChannels.locally', query);
|
|
24171
|
-
|
|
24414
|
+
const _c = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _c, params = __rest(_c, ["page"]);
|
|
24415
|
+
const cacheKey = ['channel', 'query', Object.assign(Object.assign({}, params), { options: Object.assign({}, page) })];
|
|
24416
|
+
const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
|
|
24417
|
+
if (!(data === null || data === void 0 ? void 0 : data.channels.length))
|
|
24418
|
+
return;
|
|
24419
|
+
const channels = data.channels
|
|
24420
|
+
// ck -> cacheKey, abbrevated to avoid confusion from the above cache key
|
|
24421
|
+
.map(ck => pullFromCache(['channel', 'get', ck]))
|
|
24422
|
+
.filter(Boolean)
|
|
24423
|
+
.map(({ data }) => data);
|
|
24424
|
+
const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
|
|
24425
|
+
const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
|
|
24426
|
+
return channels.length === ((_b = data === null || data === void 0 ? void 0 : data.channels) === null || _b === void 0 ? void 0 : _b.length)
|
|
24427
|
+
? { data: channels, cachedAt, prevPage, nextPage }
|
|
24428
|
+
: undefined;
|
|
24172
24429
|
};
|
|
24173
24430
|
|
|
24174
24431
|
/**
|
|
@@ -24445,8 +24702,8 @@ const leaveChannel = async (channelId) => {
|
|
|
24445
24702
|
const queryChannelMembers = async (query) => {
|
|
24446
24703
|
const client = getActiveClient();
|
|
24447
24704
|
client.log('channel/queryChannelMembers', query);
|
|
24448
|
-
const _a = query !== null && query !== void 0 ? query : {}, { page
|
|
24449
|
-
const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(channelId)}/users`, {
|
|
24705
|
+
const _a = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _a, params = __rest(_a, ["page"]);
|
|
24706
|
+
const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(params.channelId)}/users`, {
|
|
24450
24707
|
params: Object.assign(Object.assign({}, params), { options: {
|
|
24451
24708
|
token: toToken(page, 'skiplimit'),
|
|
24452
24709
|
} }),
|
|
@@ -24457,6 +24714,15 @@ const queryChannelMembers = async (query) => {
|
|
|
24457
24714
|
const cachedAt = client.cache && Date.now();
|
|
24458
24715
|
if (client.cache) {
|
|
24459
24716
|
ingestInCache(data);
|
|
24717
|
+
const cacheKey = [
|
|
24718
|
+
'channelUsers',
|
|
24719
|
+
'query',
|
|
24720
|
+
Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
|
|
24721
|
+
];
|
|
24722
|
+
pushToCache(cacheKey, {
|
|
24723
|
+
channelUsers: channelUsers.map(({ channelId, userId }) => getResolver('channelUsers')({ channelId, userId })),
|
|
24724
|
+
paging,
|
|
24725
|
+
});
|
|
24460
24726
|
}
|
|
24461
24727
|
const nextPage = toPage(paging.next);
|
|
24462
24728
|
const prevPage = toPage(paging.previous);
|
|
@@ -24476,9 +24742,30 @@ const queryChannelMembers = async (query) => {
|
|
|
24476
24742
|
* @category Post API
|
|
24477
24743
|
*/
|
|
24478
24744
|
queryChannelMembers.locally = (query) => {
|
|
24745
|
+
var _a, _b;
|
|
24479
24746
|
const client = getActiveClient();
|
|
24480
|
-
client.log('channel/queryChannelMembers', query);
|
|
24481
|
-
|
|
24747
|
+
client.log('channel/queryChannelMembers.locally', query);
|
|
24748
|
+
if (!client.cache)
|
|
24749
|
+
return;
|
|
24750
|
+
const { page = { limit: 10 } } = query, params = __rest(query, ["page"]);
|
|
24751
|
+
const cacheKey = [
|
|
24752
|
+
'channelUsers',
|
|
24753
|
+
'query',
|
|
24754
|
+
Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
|
|
24755
|
+
];
|
|
24756
|
+
const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
|
|
24757
|
+
if (!(data === null || data === void 0 ? void 0 : data.channelUsers.length))
|
|
24758
|
+
return;
|
|
24759
|
+
const channelUsers = data.channelUsers
|
|
24760
|
+
// ck -> cacheKey, abbrevated to avoid confusion from the above cache key
|
|
24761
|
+
.map(ck => pullFromCache(['channelUsers', 'get', ck]))
|
|
24762
|
+
.filter(Boolean)
|
|
24763
|
+
.map(({ data }) => data);
|
|
24764
|
+
const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
|
|
24765
|
+
const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
|
|
24766
|
+
return channelUsers.length === ((_b = data === null || data === void 0 ? void 0 : data.channelUsers) === null || _b === void 0 ? void 0 : _b.length)
|
|
24767
|
+
? { data: channelUsers, cachedAt, prevPage, nextPage }
|
|
24768
|
+
: undefined;
|
|
24482
24769
|
};
|
|
24483
24770
|
|
|
24484
24771
|
/**
|
|
@@ -24826,6 +25113,56 @@ const onChannelMemberRemoved = (callback) => {
|
|
|
24826
25113
|
return createEventSubscriber(client, 'onChannelMemberRemoved', 'v3.channel.didRemoveUsers', filter);
|
|
24827
25114
|
};
|
|
24828
25115
|
|
|
25116
|
+
/**
|
|
25117
|
+
* ```js
|
|
25118
|
+
* import { onChannelMemberBanned } from '@amityco/ts-sdk'
|
|
25119
|
+
* const dispose = onChannelMemberBanned((channel, member) => {
|
|
25120
|
+
* // ...
|
|
25121
|
+
* })
|
|
25122
|
+
* ```
|
|
25123
|
+
*
|
|
25124
|
+
* Fired when any {@link Amity.User} has been banned from any {@link Amity.Channel}
|
|
25125
|
+
*
|
|
25126
|
+
* @param callback The function to call when the event was fired
|
|
25127
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
25128
|
+
*
|
|
25129
|
+
* @category Channel Events
|
|
25130
|
+
*/
|
|
25131
|
+
const onChannelMemberBanned = (callback) => {
|
|
25132
|
+
const client = getActiveClient();
|
|
25133
|
+
const filter = (payload) => {
|
|
25134
|
+
const data = prepareChannelPayload(payload);
|
|
25135
|
+
ingestInCache(data);
|
|
25136
|
+
callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'banned'));
|
|
25137
|
+
};
|
|
25138
|
+
return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didBan', filter);
|
|
25139
|
+
};
|
|
25140
|
+
|
|
25141
|
+
/**
|
|
25142
|
+
* ```js
|
|
25143
|
+
* import { onChannelMemberUnbanned } from '@amityco/ts-sdk'
|
|
25144
|
+
* const dispose = onChannelMemberUnbanned((channel, member) => {
|
|
25145
|
+
* // ...
|
|
25146
|
+
* })
|
|
25147
|
+
* ```
|
|
25148
|
+
*
|
|
25149
|
+
* Fired when any {@link Amity.User} has been unbanned from any {@link Amity.Channel}
|
|
25150
|
+
*
|
|
25151
|
+
* @param callback The function to call when the event was fired
|
|
25152
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
25153
|
+
*
|
|
25154
|
+
* @category Channel Events
|
|
25155
|
+
*/
|
|
25156
|
+
const onChannelMemberUnbanned = (callback) => {
|
|
25157
|
+
const client = getActiveClient();
|
|
25158
|
+
const filter = (payload) => {
|
|
25159
|
+
const data = prepareChannelPayload(payload);
|
|
25160
|
+
ingestInCache(data);
|
|
25161
|
+
callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'none'));
|
|
25162
|
+
};
|
|
25163
|
+
return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didUnban', filter);
|
|
25164
|
+
};
|
|
25165
|
+
|
|
24829
25166
|
/**
|
|
24830
25167
|
* ```js
|
|
24831
25168
|
* import { observeChannel } from '@amityco/ts-sdk'
|
|
@@ -24920,7 +25257,7 @@ const observeChannels = (callback) => {
|
|
|
24920
25257
|
* @category Channel Live Collection
|
|
24921
25258
|
*/
|
|
24922
25259
|
const liveChannels = (params, callback, config) => {
|
|
24923
|
-
const { log, cache } = getActiveClient();
|
|
25260
|
+
const { log, cache, userId } = getActiveClient();
|
|
24924
25261
|
if (!cache) {
|
|
24925
25262
|
console.log(ENABLE_CACHE_MESSAGE);
|
|
24926
25263
|
}
|
|
@@ -24948,6 +25285,10 @@ const liveChannels = (params, callback, config) => {
|
|
|
24948
25285
|
if (params.excludeTags) {
|
|
24949
25286
|
channels = channels.filter(c => { var _a; return !((_a = c.tags) === null || _a === void 0 ? void 0 : _a.some(t => { var _a; return (_a = params.excludeTags) === null || _a === void 0 ? void 0 : _a.includes(t); })); });
|
|
24950
25287
|
}
|
|
25288
|
+
// userId is required to be able to filter channel by current active user
|
|
25289
|
+
if (params.membership && userId) {
|
|
25290
|
+
channels = filterByChannelMembership(channels, params.membership, userId);
|
|
25291
|
+
}
|
|
24951
25292
|
const sortBy = params.sortBy || 'lastCreated';
|
|
24952
25293
|
if (sortBy === 'lastCreated' || sortBy === 'firstCreated') {
|
|
24953
25294
|
channels = channels.sort(sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated);
|
|
@@ -25057,7 +25398,20 @@ const liveChannelMembers = (params, callback, config) => {
|
|
|
25057
25398
|
.filter(Boolean)
|
|
25058
25399
|
.map(({ data }) => data)) !== null && _a !== void 0 ? _a : [];
|
|
25059
25400
|
channelMembers = filterByPropEquality(channelMembers, 'roles', params.roles);
|
|
25060
|
-
|
|
25401
|
+
if (params.memberships) {
|
|
25402
|
+
/*
|
|
25403
|
+
* even though membership includes muted as a possible value
|
|
25404
|
+
* when querying the server.
|
|
25405
|
+
* Muted is specified under seperarte property namely isMuted
|
|
25406
|
+
* Hence why I've seperately checked for it's equality
|
|
25407
|
+
*/
|
|
25408
|
+
if (params.memberships.some(m => ['member', 'banned'].includes(m))) {
|
|
25409
|
+
channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
|
|
25410
|
+
}
|
|
25411
|
+
if (params.memberships.includes('muted')) {
|
|
25412
|
+
channelMembers = filterByPropEquality(channelMembers, 'isMuted', true);
|
|
25413
|
+
}
|
|
25414
|
+
}
|
|
25061
25415
|
if (params.search) {
|
|
25062
25416
|
const containsMatcher = new RegExp(params.search);
|
|
25063
25417
|
channelMembers = channelMembers.filter(m => m.userId.match(containsMatcher));
|
|
@@ -25085,7 +25439,7 @@ const liveChannelMembers = (params, callback, config) => {
|
|
|
25085
25439
|
if (action === 'onMemberAdded') {
|
|
25086
25440
|
collection.data = [...new Set([channelMemberCacheId, ...collection.data])];
|
|
25087
25441
|
}
|
|
25088
|
-
|
|
25442
|
+
if (action === 'onMemberRemoved') {
|
|
25089
25443
|
collection.data = collection.data.filter(m => m !== channelMemberCacheId);
|
|
25090
25444
|
}
|
|
25091
25445
|
pushToCache(cacheKey, collection);
|
|
@@ -25112,7 +25466,7 @@ const liveChannelMembers = (params, callback, config) => {
|
|
|
25112
25466
|
responder(data);
|
|
25113
25467
|
}, queryOptions(policy));
|
|
25114
25468
|
};
|
|
25115
|
-
disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')));
|
|
25469
|
+
disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')), onChannelMemberBanned((_, member) => realtimeRouter(member, 'onChannelMemberBanned')), onChannelMemberUnbanned((_, member) => realtimeRouter(member, 'onChannelMemberUnbanned')));
|
|
25116
25470
|
onFetch();
|
|
25117
25471
|
return () => {
|
|
25118
25472
|
log(`liveChannelMembers(tmpid: ${timestamp}) > dispose`);
|
|
@@ -25199,82 +25553,92 @@ const onMessageDeleted = (callback) => {
|
|
|
25199
25553
|
|
|
25200
25554
|
/**
|
|
25201
25555
|
* ```js
|
|
25202
|
-
* import {
|
|
25203
|
-
*
|
|
25204
|
-
*
|
|
25205
|
-
*
|
|
25556
|
+
* import { onMessageFetched } from '@amityco/ts-sdk'
|
|
25557
|
+
* const dispose = onMessageFetched(message => {
|
|
25558
|
+
* // ...
|
|
25559
|
+
* })
|
|
25206
25560
|
* ```
|
|
25207
25561
|
*
|
|
25208
|
-
*
|
|
25562
|
+
* Fired when an {@link Amity.Message} has been fetched
|
|
25209
25563
|
*
|
|
25210
|
-
* @param
|
|
25211
|
-
* @
|
|
25212
|
-
* @param callback the function to call when new data are available
|
|
25213
|
-
* @param policy a query policy option for this observer
|
|
25214
|
-
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
|
|
25564
|
+
* @param callback The function to call when the event was fired
|
|
25565
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
25215
25566
|
*
|
|
25216
|
-
* @category Message
|
|
25567
|
+
* @category Message Events
|
|
25217
25568
|
*/
|
|
25218
|
-
const
|
|
25219
|
-
const
|
|
25220
|
-
const
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
const router = (result, action) => {
|
|
25224
|
-
var _a, _b, _c;
|
|
25225
|
-
// filter function
|
|
25226
|
-
if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
|
|
25227
|
-
return;
|
|
25228
|
-
if (callback instanceof Function)
|
|
25229
|
-
return callback(result);
|
|
25230
|
-
if (action !== 'onFetch')
|
|
25231
|
-
(_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
|
|
25232
|
-
(_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
|
|
25233
|
-
};
|
|
25234
|
-
const disposers = [];
|
|
25235
|
-
disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
|
|
25236
|
-
if (policy !== 'no_fetch') {
|
|
25237
|
-
runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
|
|
25238
|
-
}
|
|
25239
|
-
return () => {
|
|
25240
|
-
log(`observeMessages(tmpid: ${timestamp}) > dispose`);
|
|
25241
|
-
disposers.forEach(fn => fn());
|
|
25569
|
+
const onMessageFetched = (callback) => {
|
|
25570
|
+
const client = getActiveClient();
|
|
25571
|
+
const filter = (payload) => {
|
|
25572
|
+
ingestInCache(payload);
|
|
25573
|
+
callback(payload.messages[0]);
|
|
25242
25574
|
};
|
|
25575
|
+
return createEventSubscriber(client, 'message/onMessageFetched', 'v3.message.didFetch', filter);
|
|
25243
25576
|
};
|
|
25244
25577
|
|
|
25245
25578
|
/**
|
|
25246
25579
|
* ```js
|
|
25247
|
-
* import {
|
|
25580
|
+
* import { liveMessage } from '@amityco/ts-sdk'
|
|
25248
25581
|
*
|
|
25249
25582
|
* let messages = []
|
|
25250
|
-
* const unsub =
|
|
25583
|
+
* const unsub = liveMessage(messageId, response => merge(messages, response.data))
|
|
25251
25584
|
* ```
|
|
25252
25585
|
*
|
|
25253
|
-
* Observe all
|
|
25586
|
+
* Observe all mutation on a given {@link Amity.Message}
|
|
25254
25587
|
*
|
|
25255
|
-
* @param
|
|
25588
|
+
* @param messageId the ID of the message to observe
|
|
25256
25589
|
* @param callback the function to call when new data are available
|
|
25257
25590
|
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
25258
25591
|
*
|
|
25259
|
-
* @category Message
|
|
25592
|
+
* @category Message Live Object
|
|
25260
25593
|
*/
|
|
25261
|
-
const
|
|
25262
|
-
const { log } = getActiveClient();
|
|
25594
|
+
const liveMessage = (messageId, callback) => {
|
|
25595
|
+
const { log, cache } = getActiveClient();
|
|
25596
|
+
let message;
|
|
25597
|
+
let isUnsyncedObject = false;
|
|
25598
|
+
if (!cache) {
|
|
25599
|
+
console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
|
|
25600
|
+
}
|
|
25263
25601
|
const timestamp = Date.now();
|
|
25264
|
-
log(`
|
|
25602
|
+
log(`liveMessage(tmpid: ${timestamp}) > listen`);
|
|
25265
25603
|
const disposers = [];
|
|
25266
|
-
const
|
|
25267
|
-
var _a
|
|
25268
|
-
|
|
25604
|
+
const dispatcher = (data) => {
|
|
25605
|
+
var _a;
|
|
25606
|
+
// we check updatedAt to see if the current snapshot is new so
|
|
25607
|
+
// we won't send old data back, only fresh new data
|
|
25608
|
+
if (message && ((_a = data.data) === null || _a === void 0 ? void 0 : _a.updatedAt) === (message === null || message === void 0 ? void 0 : message.updatedAt) && data.origin !== 'server')
|
|
25269
25609
|
return;
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
(_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
|
|
25273
|
-
(_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
|
|
25610
|
+
callback(data);
|
|
25611
|
+
message = data.data;
|
|
25274
25612
|
};
|
|
25275
|
-
|
|
25613
|
+
const realtimeRouter = (message, action) => {
|
|
25614
|
+
dispatcher({ loading: false, data: message, origin: 'event' });
|
|
25615
|
+
};
|
|
25616
|
+
const onFetch = () => {
|
|
25617
|
+
const query = createQuery(getMessage, messageId, true);
|
|
25618
|
+
runQuery(query, ({ loading, data, error, origin, cachedAt }) => {
|
|
25619
|
+
if (cachedAt === UNSYNCED_OBJECT_CACHED_AT_VALUE) {
|
|
25620
|
+
dispatcher({
|
|
25621
|
+
data,
|
|
25622
|
+
origin,
|
|
25623
|
+
loading: false,
|
|
25624
|
+
error: new ASCApiError(UNSYNCED_OBJECT_CACHED_AT_MESSAGE, 800800 /* Amity.ClientError.DISALOOW_UNSYNCED_OBJECT */, "error" /* Amity.ErrorLevel.ERROR */),
|
|
25625
|
+
});
|
|
25626
|
+
isUnsyncedObject = true;
|
|
25627
|
+
disposers.forEach(fn => fn());
|
|
25628
|
+
}
|
|
25629
|
+
else if (!isUnsyncedObject) {
|
|
25630
|
+
dispatcher({ loading, data, origin, error });
|
|
25631
|
+
}
|
|
25632
|
+
if (error) {
|
|
25633
|
+
// Terminate observe functionality on error
|
|
25634
|
+
disposers.forEach(fn => fn());
|
|
25635
|
+
}
|
|
25636
|
+
});
|
|
25637
|
+
};
|
|
25638
|
+
disposers.push(onMessageFetched(message => realtimeRouter(message)), onMessageUpdated(message => realtimeRouter(message)), onMessageDeleted(message => realtimeRouter(message)));
|
|
25639
|
+
onFetch();
|
|
25276
25640
|
return () => {
|
|
25277
|
-
log(`
|
|
25641
|
+
log(`liveMessage(tmpid: ${timestamp}) > dispose`);
|
|
25278
25642
|
disposers.forEach(fn => fn());
|
|
25279
25643
|
};
|
|
25280
25644
|
};
|
|
@@ -25363,7 +25727,7 @@ const liveMessages = (params, callback, config) => {
|
|
|
25363
25727
|
responder(data);
|
|
25364
25728
|
}, queryOptions(policy));
|
|
25365
25729
|
};
|
|
25366
|
-
disposers.push(onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
|
|
25730
|
+
disposers.push(onMessageFetched(message => realtimeRouter(message, 'onFetch')), onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
|
|
25367
25731
|
onFetch();
|
|
25368
25732
|
return () => {
|
|
25369
25733
|
log(`liveMessages(tmpid: ${timestamp}) > dispose`);
|
|
@@ -25371,6 +25735,89 @@ const liveMessages = (params, callback, config) => {
|
|
|
25371
25735
|
};
|
|
25372
25736
|
};
|
|
25373
25737
|
|
|
25738
|
+
/**
|
|
25739
|
+
* ```js
|
|
25740
|
+
* import { observeMessage } from '@amityco/ts-sdk'
|
|
25741
|
+
*
|
|
25742
|
+
* let message = {}
|
|
25743
|
+
* const unsub = observeMessage(messageId, updated => message = updated)
|
|
25744
|
+
* ```
|
|
25745
|
+
*
|
|
25746
|
+
* Observe all mutations on a {@link Amity.Message} given a single messageId
|
|
25747
|
+
*
|
|
25748
|
+
* @param client the client to observe the message with
|
|
25749
|
+
* @param messageId the ID of the message to observe
|
|
25750
|
+
* @param callback the function to call when new data are available
|
|
25751
|
+
* @param policy a query policy option for this observer
|
|
25752
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
|
|
25753
|
+
*
|
|
25754
|
+
* @category Message Observer
|
|
25755
|
+
*/
|
|
25756
|
+
const observeMessage = (messageId, callback, policy = 'cache_then_server') => {
|
|
25757
|
+
const { log } = getActiveClient();
|
|
25758
|
+
const timestamp = Date.now();
|
|
25759
|
+
log(`observeMessage(tmpid: ${timestamp}) > listen`);
|
|
25760
|
+
console.log('observeMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
|
|
25761
|
+
// wrapper function to make sure
|
|
25762
|
+
const router = (result, action) => {
|
|
25763
|
+
var _a, _b, _c;
|
|
25764
|
+
// filter function
|
|
25765
|
+
if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
|
|
25766
|
+
return;
|
|
25767
|
+
if (callback instanceof Function)
|
|
25768
|
+
return callback(result);
|
|
25769
|
+
if (action !== 'onFetch')
|
|
25770
|
+
(_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
|
|
25771
|
+
(_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
|
|
25772
|
+
};
|
|
25773
|
+
const disposers = [];
|
|
25774
|
+
disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
|
|
25775
|
+
if (policy !== 'no_fetch') {
|
|
25776
|
+
runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
|
|
25777
|
+
}
|
|
25778
|
+
return () => {
|
|
25779
|
+
log(`observeMessages(tmpid: ${timestamp}) > dispose`);
|
|
25780
|
+
disposers.forEach(fn => fn());
|
|
25781
|
+
};
|
|
25782
|
+
};
|
|
25783
|
+
|
|
25784
|
+
/**
|
|
25785
|
+
* ```js
|
|
25786
|
+
* import { observeMessages } from '@amityco/ts-sdk'
|
|
25787
|
+
*
|
|
25788
|
+
* let messages = []
|
|
25789
|
+
* const unsub = observeMessages(channelId, message => merge(messages, message))
|
|
25790
|
+
* ```
|
|
25791
|
+
*
|
|
25792
|
+
* Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
|
|
25793
|
+
*
|
|
25794
|
+
* @param channelId the ID of the channel where to observe the messages
|
|
25795
|
+
* @param callback the function to call when new data are available
|
|
25796
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
25797
|
+
*
|
|
25798
|
+
* @category Message Observer
|
|
25799
|
+
*/
|
|
25800
|
+
const observeMessages = (channelId, callback) => {
|
|
25801
|
+
const { log } = getActiveClient();
|
|
25802
|
+
const timestamp = Date.now();
|
|
25803
|
+
log(`observeMessages(tmpid: ${timestamp}) > listen`);
|
|
25804
|
+
const disposers = [];
|
|
25805
|
+
const router = (message, action) => {
|
|
25806
|
+
var _a, _b, _c;
|
|
25807
|
+
if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.channelId) !== channelId)
|
|
25808
|
+
return;
|
|
25809
|
+
if (callback instanceof Function)
|
|
25810
|
+
return callback(message);
|
|
25811
|
+
(_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
|
|
25812
|
+
(_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
|
|
25813
|
+
};
|
|
25814
|
+
disposers.push(onMessageCreated(data => router({ data, loading: false, origin: 'event' }, 'onCreate')), onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
|
|
25815
|
+
return () => {
|
|
25816
|
+
log(`observeMessages(tmpid: ${timestamp}) > dispose`);
|
|
25817
|
+
disposers.forEach(fn => fn());
|
|
25818
|
+
};
|
|
25819
|
+
};
|
|
25820
|
+
|
|
25374
25821
|
const createCommunityEventSubscriber = (event, callback) => {
|
|
25375
25822
|
const client = getActiveClient();
|
|
25376
25823
|
const filter = (payload) => {
|
|
@@ -27559,6 +28006,8 @@ exports.CommunityPostSettings = CommunityPostSettings;
|
|
|
27559
28006
|
exports.ContentFeedType = ContentFeedType;
|
|
27560
28007
|
exports.DefaultCommunityPostSetting = DefaultCommunityPostSetting;
|
|
27561
28008
|
exports.FileType = FileType;
|
|
28009
|
+
exports.MessageContentType = MessageContentType;
|
|
28010
|
+
exports.PostContentType = PostContentType;
|
|
27562
28011
|
exports.VERSION = VERSION$1;
|
|
27563
28012
|
exports.VideoResolution = VideoResolution;
|
|
27564
28013
|
exports.VideoSize = VideoSize;
|
|
@@ -27605,6 +28054,7 @@ exports.disposeStream = disposeStream;
|
|
|
27605
28054
|
exports.dropFromCache = dropFromCache;
|
|
27606
28055
|
exports.enableCache = enableCache;
|
|
27607
28056
|
exports.fileUrlWithSize = fileUrlWithSize;
|
|
28057
|
+
exports.filterByChannelMembership = filterByChannelMembership;
|
|
27608
28058
|
exports.filterByPropEquality = filterByPropEquality;
|
|
27609
28059
|
exports.filterByPropInclusion = filterByPropInclusion;
|
|
27610
28060
|
exports.follow = follow;
|
|
@@ -27658,9 +28108,11 @@ exports.liveChannels = liveChannels;
|
|
|
27658
28108
|
exports.liveComments = liveComments;
|
|
27659
28109
|
exports.liveFollowers = liveFollowers;
|
|
27660
28110
|
exports.liveFollowings = liveFollowings;
|
|
28111
|
+
exports.liveMessage = liveMessage;
|
|
27661
28112
|
exports.liveMessages = liveMessages;
|
|
27662
28113
|
exports.livePosts = livePosts;
|
|
27663
28114
|
exports.liveReactions = liveReactions;
|
|
28115
|
+
exports.liveUser = liveUser;
|
|
27664
28116
|
exports.mergeInCache = mergeInCache;
|
|
27665
28117
|
exports.observeChannel = observeChannel;
|
|
27666
28118
|
exports.observeChannels = observeChannels;
|
|
@@ -27682,7 +28134,9 @@ exports.onChannelDeleted = onChannelDeleted;
|
|
|
27682
28134
|
exports.onChannelJoined = onChannelJoined;
|
|
27683
28135
|
exports.onChannelLeft = onChannelLeft;
|
|
27684
28136
|
exports.onChannelMemberAdded = onChannelMemberAdded;
|
|
28137
|
+
exports.onChannelMemberBanned = onChannelMemberBanned;
|
|
27685
28138
|
exports.onChannelMemberRemoved = onChannelMemberRemoved;
|
|
28139
|
+
exports.onChannelMemberUnbanned = onChannelMemberUnbanned;
|
|
27686
28140
|
exports.onChannelUpdated = onChannelUpdated;
|
|
27687
28141
|
exports.onClientBanned = onClientBanned;
|
|
27688
28142
|
exports.onClientDisconnected = onClientDisconnected;
|
|
@@ -27711,6 +28165,7 @@ exports.onFollowerDeleted = onFollowerDeleted;
|
|
|
27711
28165
|
exports.onFollowerRequested = onFollowerRequested;
|
|
27712
28166
|
exports.onMessageCreated = onMessageCreated;
|
|
27713
28167
|
exports.onMessageDeleted = onMessageDeleted;
|
|
28168
|
+
exports.onMessageFetched = onMessageFetched;
|
|
27714
28169
|
exports.onMessageUpdated = onMessageUpdated;
|
|
27715
28170
|
exports.onPollDeleted = onPollDeleted;
|
|
27716
28171
|
exports.onPollUpdated = onPollUpdated;
|
|
@@ -27731,6 +28186,7 @@ exports.onStreamRecorded = onStreamRecorded;
|
|
|
27731
28186
|
exports.onStreamStarted = onStreamStarted;
|
|
27732
28187
|
exports.onStreamStopped = onStreamStopped;
|
|
27733
28188
|
exports.onUserDeleted = onUserDeleted;
|
|
28189
|
+
exports.onUserFetched = onUserFetched;
|
|
27734
28190
|
exports.onUserFlagCleared = onUserFlagCleared;
|
|
27735
28191
|
exports.onUserFlagged = onUserFlagged;
|
|
27736
28192
|
exports.onUserFollowed = onUserFollowed;
|