@amityco/ts-sdk 0.0.1-beta.37 → 0.0.1-beta.38
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/.eslintrc.json +3 -5
- package/dist/@types/core/events.d.ts +1 -0
- package/dist/@types/core/events.d.ts.map +1 -1
- package/dist/@types/domains/category.d.ts +7 -0
- package/dist/@types/domains/category.d.ts.map +1 -1
- package/dist/@types/domains/community.d.ts +12 -0
- package/dist/@types/domains/community.d.ts.map +1 -1
- package/dist/@types/global.d.ts +5 -0
- package/dist/@types/global.d.ts.map +1 -1
- package/dist/category/api/queryCategories.d.ts +1 -5
- package/dist/category/api/queryCategories.d.ts.map +1 -1
- package/dist/category/observers/index.d.ts +2 -0
- package/dist/category/observers/index.d.ts.map +1 -0
- package/dist/category/observers/liveCategories.d.ts +18 -0
- package/dist/category/observers/liveCategories.d.ts.map +1 -0
- package/dist/category/observers/tests/liveCategories.test.d.ts +2 -0
- package/dist/category/observers/tests/liveCategories.test.d.ts.map +1 -0
- package/dist/channel/api/tests/getChannels.test.d.ts +2 -0
- package/dist/channel/api/tests/getChannels.test.d.ts.map +1 -0
- package/dist/channel/api/tests/leaveChannel.test.d.ts +2 -0
- package/dist/channel/api/tests/leaveChannel.test.d.ts.map +1 -0
- package/dist/channel/observers/liveChannel.d.ts.map +1 -1
- package/dist/client/api/accessTokenExpiryWatcher.d.ts +4 -1
- package/dist/client/api/accessTokenExpiryWatcher.d.ts.map +1 -1
- package/dist/client/api/getToken.d.ts +3 -1
- package/dist/client/api/getToken.d.ts.map +1 -1
- package/dist/client/utils/setClientToken.d.ts +3 -3
- package/dist/comment/api/deleteComment.d.ts.map +1 -1
- package/dist/comment/api/getComment.d.ts.map +1 -1
- package/dist/comment/observers/index.d.ts +1 -0
- package/dist/comment/observers/index.d.ts.map +1 -1
- package/dist/comment/observers/liveComment.d.ts +21 -0
- package/dist/comment/observers/liveComment.d.ts.map +1 -0
- package/dist/comment/observers/tests/liveComment.test.d.ts +2 -0
- package/dist/comment/observers/tests/liveComment.test.d.ts.map +1 -0
- package/dist/community/api/queryCommunities.d.ts +1 -10
- package/dist/community/api/queryCommunities.d.ts.map +1 -1
- package/dist/community/events/index.d.ts +1 -0
- package/dist/community/events/index.d.ts.map +1 -1
- package/dist/community/events/onCommunityUserChanged.d.ts +17 -0
- package/dist/community/events/onCommunityUserChanged.d.ts.map +1 -0
- package/dist/community/events/utils.d.ts +1 -0
- package/dist/community/events/utils.d.ts.map +1 -1
- package/dist/community/observers/index.d.ts +1 -0
- package/dist/community/observers/index.d.ts.map +1 -1
- package/dist/community/observers/liveCommunities.d.ts +20 -0
- package/dist/community/observers/liveCommunities.d.ts.map +1 -0
- package/dist/community/observers/tests/liveCommunities.test.d.ts +2 -0
- package/dist/community/observers/tests/liveCommunities.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/sorting.d.ts +6 -0
- package/dist/core/query/sorting.d.ts.map +1 -1
- package/dist/core/transports/http.d.ts +0 -5
- package/dist/core/transports/http.d.ts.map +1 -1
- package/dist/file/api/test/createImage.test.d.ts +2 -0
- package/dist/file/api/test/createImage.test.d.ts.map +1 -0
- package/dist/file/api/test/createVideo.test.d.ts +2 -0
- package/dist/file/api/test/createVideo.test.d.ts.map +1 -0
- package/dist/index.cjs.js +441 -195
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +436 -195
- package/dist/index.umd.js +4 -4
- package/dist/message/observers/liveMessage.d.ts.map +1 -1
- package/dist/post/observers/index.d.ts +1 -0
- package/dist/post/observers/index.d.ts.map +1 -1
- package/dist/post/observers/livePost.d.ts +21 -0
- package/dist/post/observers/livePost.d.ts.map +1 -0
- package/dist/post/observers/tests/livePost.test.d.ts +2 -0
- package/dist/post/observers/tests/livePost.test.d.ts.map +1 -0
- package/dist/subChannel/api/tests/deleteSubChannel.test.d.ts +2 -0
- package/dist/subChannel/api/tests/deleteSubChannel.test.d.ts.map +1 -0
- package/dist/subChannel/api/tests/getSubChannel.test.d.ts +2 -0
- package/dist/subChannel/api/tests/getSubChannel.test.d.ts.map +1 -0
- package/dist/subChannel/api/tests/getSubChannels.test.d.ts +2 -0
- package/dist/subChannel/api/tests/getSubChannels.test.d.ts.map +1 -0
- package/dist/subChannel/api/tests/updateSubChannel.test.d.ts +2 -0
- package/dist/subChannel/api/tests/updateSubChannel.test.d.ts.map +1 -0
- package/dist/subChannel/observers/liveSubChannel.d.ts.map +1 -1
- package/dist/user/api/tests/updateUser.test.d.ts +2 -0
- package/dist/user/api/tests/updateUser.test.d.ts.map +1 -0
- package/dist/user/observers/liveUser.d.ts.map +1 -1
- package/dist/utils/liveObject.d.ts +2 -0
- package/dist/utils/liveObject.d.ts.map +1 -0
- package/dist/utils/tests/dummy/category.d.ts +64 -0
- package/dist/utils/tests/dummy/category.d.ts.map +1 -0
- package/dist/utils/tests/dummy/channel.d.ts +50 -0
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
- package/dist/utils/tests/dummy/community.d.ts +132 -0
- package/dist/utils/tests/dummy/community.d.ts.map +1 -1
- package/dist/utils/tests/dummy/file.d.ts +3 -0
- package/dist/utils/tests/dummy/file.d.ts.map +1 -1
- package/dist/utils/tests/dummy/index.d.ts +1 -0
- package/dist/utils/tests/dummy/index.d.ts.map +1 -1
- package/dist/utils/tests/dummy/post.d.ts +2 -0
- package/dist/utils/tests/dummy/post.d.ts.map +1 -1
- package/dist/utils/tests/dummy/subChannel.d.ts +0 -20
- package/dist/utils/tests/dummy/subChannel.d.ts.map +1 -1
- package/dist/utils/tests/dummy/user.d.ts +18 -0
- package/dist/utils/tests/dummy/user.d.ts.map +1 -1
- package/dist/utils/tests/liveObject.test.d.ts +2 -0
- package/dist/utils/tests/liveObject.test.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/@types/core/events.ts +1 -0
- package/src/@types/domains/category.ts +13 -0
- package/src/@types/domains/community.ts +25 -0
- package/src/@types/global.ts +5 -0
- package/src/category/api/queryCategories.ts +3 -5
- package/src/category/observers/index.ts +1 -0
- package/src/category/observers/liveCategories.ts +141 -0
- package/src/category/observers/tests/liveCategories.test.ts +147 -0
- package/src/channel/api/tests/createChannel.test.ts +30 -6
- package/src/channel/api/tests/getChannel.test.ts +3 -1
- package/src/channel/api/tests/getChannels.test.ts +97 -0
- package/src/channel/api/tests/joinChannel.test.ts +62 -3
- package/src/channel/api/tests/leaveChannel.test.ts +90 -0
- package/src/channel/api/tests/queryChannels.test.ts +38 -1
- package/src/channel/observers/liveChannel.ts +8 -80
- package/src/client/api/accessTokenExpiryWatcher.ts +7 -5
- package/src/client/api/tests/accessTokenExpiryWatcher.test.ts +8 -5
- package/src/comment/api/deleteComment.ts +5 -5
- package/src/comment/api/getComment.ts +8 -3
- package/src/comment/observers/index.ts +1 -0
- package/src/comment/observers/liveComment.ts +43 -0
- package/src/comment/observers/tests/liveComment.test.ts +95 -0
- package/src/community/api/queryCommunities.ts +3 -10
- package/src/community/api/test/addCommunityMember.test.ts +14 -2
- package/src/community/api/test/joinCommunity.test.ts +14 -2
- package/src/community/api/test/leaveCommunity.test.ts +11 -1
- package/src/community/api/test/removeCommunityMembers.test.ts +11 -1
- package/src/community/events/index.ts +1 -0
- package/src/community/events/onCommunityUserChanged.ts +20 -0
- package/src/community/events/test/onCommunityJoined.test.ts +1 -1
- package/src/community/events/test/onCommunityUserAdded.test.ts +1 -1
- package/src/community/events/test/onCommunityUserBanned.test.ts +1 -1
- package/src/community/events/test/onCommunityUserUnbaned.test.ts +1 -1
- package/src/community/events/test/utils.test.ts +52 -3
- package/src/community/events/utils.ts +49 -0
- package/src/community/observers/index.ts +1 -0
- package/src/community/observers/liveCommunities.ts +188 -0
- package/src/community/observers/tests/liveCommunities.test.ts +284 -0
- package/src/core/query/sorting.ts +8 -0
- package/src/core/transports/http.ts +17 -13
- package/src/core/transports/test/http.test.ts +49 -60
- package/src/file/api/createFile.ts +1 -1
- package/src/file/api/createImage.ts +1 -1
- package/src/file/api/createVideo.ts +1 -1
- package/src/file/api/test/createImage.test.ts +69 -0
- package/src/file/api/test/createVideo.test.ts +81 -0
- package/src/index.ts +1 -0
- package/src/message/observers/liveMessage.ts +11 -83
- package/src/message/tests/observers/liveMessage.test.ts +1 -143
- package/src/post/observers/index.ts +1 -0
- package/src/post/observers/livePost.ts +47 -0
- package/src/post/observers/tests/livePost.test.ts +97 -0
- package/src/stream/api/tests/createStream.test.ts +3 -11
- package/src/stream/api/tests/deleteStream.test.ts +5 -3
- package/src/subChannel/api/tests/createSubChannel.test.ts +21 -14
- package/src/subChannel/api/tests/deleteSubChannel.test.ts +112 -0
- package/src/subChannel/api/tests/getSubChannel.test.ts +97 -0
- package/src/subChannel/api/tests/getSubChannels.test.ts +97 -0
- package/src/subChannel/api/tests/updateSubChannel.test.ts +86 -0
- package/src/subChannel/observers/liveSubChannel.ts +6 -56
- package/src/subChannel/observers/tests/liveSubChannel.test.ts +0 -82
- package/src/user/api/tests/updateUser.test.ts +60 -0
- package/src/user/observers/liveUser.ts +9 -61
- package/src/user/tests/observers/liveUser.test.ts +1 -154
- package/src/utils/liveObject.ts +86 -0
- package/src/utils/tests/dummy/category.ts +57 -0
- package/src/utils/tests/dummy/channel.ts +51 -1
- package/src/utils/tests/dummy/community.ts +94 -0
- package/src/utils/tests/dummy/file.ts +32 -0
- package/src/utils/tests/dummy/index.ts +1 -0
- package/src/utils/tests/dummy/post.ts +56 -45
- package/src/utils/tests/dummy/stream.ts +73 -73
- package/src/utils/tests/dummy/subChannel.ts +0 -22
- package/src/utils/tests/dummy/user.ts +21 -0
- package/src/utils/tests/liveObject.test.ts +172 -0
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
user11,
|
|
11
11
|
} from '~/utils/tests';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
createCommunityEventSubscriber,
|
|
15
|
+
createCommunityMemberEventSubscriber,
|
|
16
|
+
updateMembershipStatus,
|
|
17
|
+
} from '../utils';
|
|
14
18
|
|
|
15
19
|
const event = 'community.created';
|
|
16
20
|
const communityToCreate = community11;
|
|
@@ -115,7 +119,7 @@ describe('createCommunityMemberEventSubscriber', () => {
|
|
|
115
119
|
unsub();
|
|
116
120
|
|
|
117
121
|
expect(callback).toHaveBeenCalled();
|
|
118
|
-
expect(callback).toHaveBeenCalledWith(communityToAddUser, addedUser);
|
|
122
|
+
expect(callback).toHaveBeenCalledWith({ ...communityToAddUser, isJoined: true }, addedUser);
|
|
119
123
|
});
|
|
120
124
|
|
|
121
125
|
test('it should trigger callback with correctly params when cache is enabled', () => {
|
|
@@ -128,7 +132,7 @@ describe('createCommunityMemberEventSubscriber', () => {
|
|
|
128
132
|
unsub();
|
|
129
133
|
|
|
130
134
|
expect(callback).toHaveBeenCalled();
|
|
131
|
-
expect(callback).toHaveBeenCalledWith(
|
|
135
|
+
expect(callback).toHaveBeenCalledWith({ ...communityToCreate, isJoined: true }, addedUser);
|
|
132
136
|
|
|
133
137
|
disableCache();
|
|
134
138
|
});
|
|
@@ -156,3 +160,48 @@ describe('createCommunityMemberEventSubscriber', () => {
|
|
|
156
160
|
disableCache();
|
|
157
161
|
});
|
|
158
162
|
});
|
|
163
|
+
|
|
164
|
+
describe('updateMembershipStatus', () => {
|
|
165
|
+
beforeAll(connectClient);
|
|
166
|
+
afterAll(disconnectClient);
|
|
167
|
+
|
|
168
|
+
const communities = [
|
|
169
|
+
{
|
|
170
|
+
communityId: 'test-membership',
|
|
171
|
+
} as Amity.Community,
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const getCommunityUsers = (membership = 'member') => [
|
|
175
|
+
{
|
|
176
|
+
communityId: 'test-membership',
|
|
177
|
+
communityMembership: membership,
|
|
178
|
+
userId: client?.userId || 'test',
|
|
179
|
+
} as Amity.Membership<'community'>,
|
|
180
|
+
{
|
|
181
|
+
communityId: 'test-membership',
|
|
182
|
+
communityMembership: 'member',
|
|
183
|
+
userId: 'some-other-user',
|
|
184
|
+
} as Amity.Membership<'community'>,
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
test('it should update isJoined status to true if membership member', () => {
|
|
188
|
+
const expected = { ...communities[0], isJoined: true };
|
|
189
|
+
const recieved = updateMembershipStatus(communities, getCommunityUsers())[0];
|
|
190
|
+
|
|
191
|
+
expect(recieved).toStrictEqual(expected);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('it should update isJoined status to true if membership banned', () => {
|
|
195
|
+
const expected = { ...communities[0], isJoined: true };
|
|
196
|
+
const recieved = updateMembershipStatus(communities, getCommunityUsers('banned'))[0];
|
|
197
|
+
|
|
198
|
+
expect(recieved).toStrictEqual(expected);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('it should update isJoined status to false if membership none', () => {
|
|
202
|
+
const expected = { ...communities[0], isJoined: false };
|
|
203
|
+
const recieved = updateMembershipStatus(communities, getCommunityUsers('none'))[0];
|
|
204
|
+
|
|
205
|
+
expect(recieved).toStrictEqual(expected);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getActiveClient } from '~/client/api';
|
|
2
|
+
import { getActiveUser } from '~/client/api/activeUser';
|
|
2
3
|
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
3
4
|
import { createEventSubscriber } from '~/core/events';
|
|
4
5
|
import { pullFromCache } from '~/cache/api';
|
|
@@ -35,6 +36,43 @@ export const createCommunityEventSubscriber = (
|
|
|
35
36
|
return createEventSubscriber(client, event, event, filter);
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
/*
|
|
40
|
+
* verifies membership status
|
|
41
|
+
*/
|
|
42
|
+
function isMember(membership: Amity.Membership<'community'>['communityMembership']): boolean {
|
|
43
|
+
return membership !== 'none';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* checks if currently logged in user is part of the community
|
|
48
|
+
*/
|
|
49
|
+
function isCurrentUserPartOfCommunity(
|
|
50
|
+
c: Amity.Community,
|
|
51
|
+
m: Amity.Membership<'community'>,
|
|
52
|
+
): boolean {
|
|
53
|
+
const { userId } = getActiveUser();
|
|
54
|
+
|
|
55
|
+
return c.communityId === m.communityId && m.userId === userId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* For mqtt events server will not send user specific data as it's broadcasted
|
|
60
|
+
* to multiple users. Althought this can be calculated, it's not scalable. Hence
|
|
61
|
+
* the client SDK needs to calculate this info
|
|
62
|
+
*/
|
|
63
|
+
export function updateMembershipStatus(
|
|
64
|
+
communities: Amity.Community[],
|
|
65
|
+
communityUsers: Amity.Membership<'community'>[],
|
|
66
|
+
): Amity.Community[] {
|
|
67
|
+
return communities.map(c => {
|
|
68
|
+
const isJoined = communityUsers.some(
|
|
69
|
+
m => isCurrentUserPartOfCommunity(c, m) && isMember(m.communityMembership),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return { ...c, isJoined };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
38
76
|
function hasPermission(
|
|
39
77
|
member: Amity.Membership<'community'>,
|
|
40
78
|
payload: {
|
|
@@ -88,7 +126,18 @@ export const createCommunityMemberEventSubscriber = (
|
|
|
88
126
|
|
|
89
127
|
const filter = (payload: Amity.CommunityMembershipPayload) => {
|
|
90
128
|
const communityPayload = unpackCommunityPayload(payload);
|
|
129
|
+
const { communities } = communityPayload;
|
|
130
|
+
|
|
91
131
|
const preparedPayload = prepareMembershipPayload(communityPayload, 'communityUsers');
|
|
132
|
+
const { communityUsers } = preparedPayload;
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* community.isJoined is not part of the communityMembership payload, and needs
|
|
136
|
+
* to be calculated based on the communityMembership value
|
|
137
|
+
*/
|
|
138
|
+
const communitiesWithMembership = updateMembershipStatus(communities, communityUsers);
|
|
139
|
+
|
|
140
|
+
preparedPayload.communities = communitiesWithMembership;
|
|
92
141
|
|
|
93
142
|
if (!client.cache) {
|
|
94
143
|
// TODO: here we are missing specific properties here!
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
import { getResolver } from '~/core/model';
|
|
3
|
+
import { pullFromCache, pushToCache } from '~/cache/api';
|
|
4
|
+
import { getActiveClient } from '~/client/api';
|
|
5
|
+
import {
|
|
6
|
+
createQuery,
|
|
7
|
+
runQuery,
|
|
8
|
+
queryOptions,
|
|
9
|
+
filterByPropEquality,
|
|
10
|
+
sortByLastCreated,
|
|
11
|
+
sortByDisplayName,
|
|
12
|
+
sortByFirstCreated,
|
|
13
|
+
} from '~/core/query';
|
|
14
|
+
import {
|
|
15
|
+
COLLECTION_DEFAULT_CACHING_POLICY,
|
|
16
|
+
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
17
|
+
ENABLE_CACHE_MESSAGE,
|
|
18
|
+
} from '~/utils/constants';
|
|
19
|
+
import {
|
|
20
|
+
onCommunityCreated,
|
|
21
|
+
onCommunityDeleted,
|
|
22
|
+
onCommunityUpdated,
|
|
23
|
+
onCommunityJoined,
|
|
24
|
+
onCommunityLeft,
|
|
25
|
+
onCommunityUserChanged,
|
|
26
|
+
} from '../events';
|
|
27
|
+
import { queryCommunities } from '../api';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ```js
|
|
31
|
+
* import { liveCommunities } from '@amityco/ts-sdk'
|
|
32
|
+
*
|
|
33
|
+
* let communities = []
|
|
34
|
+
* const unsub = liveCommunities({
|
|
35
|
+
* displayName: Amity.Community['displayName'],
|
|
36
|
+
* }, response => merge(communities, response.data))
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Observe all mutations on a list of {@link Amity.Community}s
|
|
40
|
+
*
|
|
41
|
+
* @param params for querying communities
|
|
42
|
+
* @param callback the function to call when new data are available
|
|
43
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the communities
|
|
44
|
+
*
|
|
45
|
+
* @category Community Live Collection
|
|
46
|
+
*/
|
|
47
|
+
export const liveCommunities = (
|
|
48
|
+
params: Amity.CommunityLiveCollection,
|
|
49
|
+
callback: Amity.LiveCollectionCallback<Amity.Community>,
|
|
50
|
+
config?: Amity.LiveCollectionConfig,
|
|
51
|
+
) => {
|
|
52
|
+
const { log, cache } = getActiveClient();
|
|
53
|
+
|
|
54
|
+
if (!cache) {
|
|
55
|
+
console.log(ENABLE_CACHE_MESSAGE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const timestamp = Date.now();
|
|
59
|
+
log(`liveCommunities(tmpid: ${timestamp}) > listen`);
|
|
60
|
+
|
|
61
|
+
const { limit: queryLimit, ...queryParams } = params;
|
|
62
|
+
|
|
63
|
+
const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
|
|
64
|
+
const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
|
|
65
|
+
|
|
66
|
+
const disposers: Amity.Unsubscriber[] = [];
|
|
67
|
+
const cacheKey = ['community', 'collection', {}];
|
|
68
|
+
|
|
69
|
+
const responder = (data: Amity.CommunityLiveCollectionCache) => {
|
|
70
|
+
let communities: Amity.Community[] =
|
|
71
|
+
data.data
|
|
72
|
+
.map(communityId => pullFromCache<Amity.Community>(['community', 'get', communityId])!)
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.map(({ data }) => data) ?? [];
|
|
75
|
+
|
|
76
|
+
communities = filterByPropEquality(communities, 'isDeleted', params.isDeleted);
|
|
77
|
+
communities = filterByPropEquality(communities, 'displayName', params.displayName);
|
|
78
|
+
|
|
79
|
+
if (params.categoryId) {
|
|
80
|
+
communities = communities.filter(c => c.categoryIds?.includes(params.categoryId!));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (params.tags) {
|
|
84
|
+
communities = communities.filter(c => c.tags?.some(t => params.tags?.includes(t)));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (params.membership === 'member') {
|
|
88
|
+
communities = filterByPropEquality(communities, 'isJoined', true);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (params.membership === 'notMember') {
|
|
92
|
+
communities = filterByPropEquality(communities, 'isJoined', false);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const sortBy = params.sortBy || 'lastCreated';
|
|
96
|
+
|
|
97
|
+
if (sortBy === 'lastCreated' || sortBy === 'firstCreated') {
|
|
98
|
+
communities = communities.sort(
|
|
99
|
+
sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/*
|
|
104
|
+
* The server returns communities with empty | null displayName's first before
|
|
105
|
+
* returning sorted list of communities with displayNames
|
|
106
|
+
*
|
|
107
|
+
* This section needs to be updated as displayNames can be null as well
|
|
108
|
+
*/
|
|
109
|
+
if (sortBy === 'displayName') {
|
|
110
|
+
communities = communities
|
|
111
|
+
// this needs to be aligned with the backend data type
|
|
112
|
+
.map(c => (c.displayName ? c : { ...c, displayName: '' }))
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
.sort(sortByDisplayName);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
callback({
|
|
118
|
+
onNextPage: onFetch,
|
|
119
|
+
data: communities,
|
|
120
|
+
hasNextPage: !!data.params?.page,
|
|
121
|
+
loading: data.loading,
|
|
122
|
+
error: data.error,
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const realtimeRouter = (_: Amity.CommunityActionType) => (community: Amity.Community) => {
|
|
127
|
+
const collection = pullFromCache<Amity.CommunityLiveCollectionCache>(cacheKey)?.data;
|
|
128
|
+
if (!collection) return;
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* Simply update collection and let responder decide what to do with data
|
|
132
|
+
*/
|
|
133
|
+
collection.data = [...new Set([community.communityId, ...collection.data])];
|
|
134
|
+
|
|
135
|
+
pushToCache(cacheKey, collection);
|
|
136
|
+
responder(collection);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const onFetch = () => {
|
|
140
|
+
const collection = pullFromCache<Amity.CommunityLiveCollectionCache>(cacheKey)?.data;
|
|
141
|
+
|
|
142
|
+
const communities = collection?.data ?? [];
|
|
143
|
+
|
|
144
|
+
if (communities.length > 0 && !collection?.params.page) return;
|
|
145
|
+
|
|
146
|
+
const query = createQuery(queryCommunities, {
|
|
147
|
+
...queryParams,
|
|
148
|
+
page: collection?.params.page ?? { limit },
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
runQuery(
|
|
152
|
+
query,
|
|
153
|
+
({ data: result, error, loading, nextPage: page }) => {
|
|
154
|
+
const data = {
|
|
155
|
+
loading,
|
|
156
|
+
error,
|
|
157
|
+
params: { page },
|
|
158
|
+
data: communities,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (result) {
|
|
162
|
+
data.data = [...new Set([...communities, ...result.map(getResolver('community'))])];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
pushToCache(cacheKey, data);
|
|
166
|
+
|
|
167
|
+
responder(data);
|
|
168
|
+
},
|
|
169
|
+
queryOptions(policy),
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
disposers.push(
|
|
174
|
+
onCommunityCreated(realtimeRouter('onCreate')),
|
|
175
|
+
onCommunityDeleted(realtimeRouter('onDelete')),
|
|
176
|
+
onCommunityUpdated(realtimeRouter('onUpdate')),
|
|
177
|
+
onCommunityJoined(realtimeRouter('onJoin')),
|
|
178
|
+
onCommunityLeft(realtimeRouter('onLeft')),
|
|
179
|
+
onCommunityUserChanged(realtimeRouter('onMemberCountChanged')),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
onFetch();
|
|
183
|
+
|
|
184
|
+
return () => {
|
|
185
|
+
log(`liveCommunities(tmpid: ${timestamp}) > dispose`);
|
|
186
|
+
disposers.forEach(fn => fn());
|
|
187
|
+
};
|
|
188
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { disableCache, enableCache } from '~/cache/api';
|
|
2
|
+
import {
|
|
3
|
+
communityQueryResponse,
|
|
4
|
+
communityQueryResponsePage2,
|
|
5
|
+
client,
|
|
6
|
+
connectClient,
|
|
7
|
+
disconnectClient,
|
|
8
|
+
pause,
|
|
9
|
+
communityUser11,
|
|
10
|
+
} from '~/utils/tests';
|
|
11
|
+
|
|
12
|
+
import { liveCommunities } from '../liveCommunities';
|
|
13
|
+
|
|
14
|
+
import { unpackCommunityPayload } from '../../utils';
|
|
15
|
+
|
|
16
|
+
const getSnapshot = (params?: Record<string, any>) => {
|
|
17
|
+
return {
|
|
18
|
+
data: [] as Amity.Community[],
|
|
19
|
+
loading: true,
|
|
20
|
+
error: undefined as any,
|
|
21
|
+
...params,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const { paging, ...payload } = communityQueryResponse.data;
|
|
26
|
+
const { paging: paging2, ...payload2 } = communityQueryResponsePage2.data;
|
|
27
|
+
|
|
28
|
+
const community1 = unpackCommunityPayload(payload).communities[0];
|
|
29
|
+
const community2 = unpackCommunityPayload(payload).communities[1];
|
|
30
|
+
|
|
31
|
+
describe('liveCommunities', () => {
|
|
32
|
+
beforeAll(connectClient);
|
|
33
|
+
afterAll(disconnectClient);
|
|
34
|
+
|
|
35
|
+
beforeEach(enableCache);
|
|
36
|
+
|
|
37
|
+
afterEach(disableCache);
|
|
38
|
+
|
|
39
|
+
test('it should return community collection', async () => {
|
|
40
|
+
const callback = jest.fn();
|
|
41
|
+
client.http.get = jest.fn().mockResolvedValue(communityQueryResponse);
|
|
42
|
+
|
|
43
|
+
liveCommunities({}, callback);
|
|
44
|
+
await pause();
|
|
45
|
+
|
|
46
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
47
|
+
expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
|
|
48
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
49
|
+
2,
|
|
50
|
+
expect.objectContaining(
|
|
51
|
+
getSnapshot({
|
|
52
|
+
data: unpackCommunityPayload(payload).communities,
|
|
53
|
+
loading: false,
|
|
54
|
+
}),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('it should return data from cache', async () => {
|
|
60
|
+
const callback = jest.fn();
|
|
61
|
+
client.http.get = jest.fn().mockResolvedValue(communityQueryResponse);
|
|
62
|
+
|
|
63
|
+
liveCommunities({}, () => undefined);
|
|
64
|
+
await pause();
|
|
65
|
+
liveCommunities({}, callback);
|
|
66
|
+
await pause();
|
|
67
|
+
|
|
68
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
69
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
70
|
+
1,
|
|
71
|
+
expect.objectContaining(
|
|
72
|
+
getSnapshot({
|
|
73
|
+
data: unpackCommunityPayload(payload).communities,
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
78
|
+
2,
|
|
79
|
+
expect.objectContaining(
|
|
80
|
+
getSnapshot({
|
|
81
|
+
data: unpackCommunityPayload(payload).communities,
|
|
82
|
+
loading: false,
|
|
83
|
+
}),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const filters: [string, Amity.CommunityLiveCollection, Amity.Community[]][] = [
|
|
89
|
+
['deleted', { isDeleted: true }, [community1]],
|
|
90
|
+
['categoryId', { categoryId: 'test-category-id' }, [community1]],
|
|
91
|
+
['displayName', { displayName: 'community 1' }, [community1]],
|
|
92
|
+
['tagged', { tags: ['test-community-tag'] }, [community1]],
|
|
93
|
+
['member', { membership: 'member' }, [community1]],
|
|
94
|
+
['not member', { membership: 'notMember' }, [community2]],
|
|
95
|
+
['all membership', { membership: 'all' }, [community1, community2]],
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
test.each(filters)('it should filter by %s communities', async (filter, params, expected) => {
|
|
99
|
+
const callback = jest.fn();
|
|
100
|
+
client.http.get = jest.fn().mockResolvedValue(communityQueryResponse);
|
|
101
|
+
|
|
102
|
+
liveCommunities(params, callback);
|
|
103
|
+
|
|
104
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
105
|
+
// check if cache data returned (should be empty)
|
|
106
|
+
expect(callback).toHaveBeenCalledWith(expect.objectContaining(getSnapshot()));
|
|
107
|
+
|
|
108
|
+
await pause();
|
|
109
|
+
|
|
110
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
111
|
+
expect(callback).toHaveBeenCalledWith(
|
|
112
|
+
expect.objectContaining(
|
|
113
|
+
getSnapshot({
|
|
114
|
+
loading: false,
|
|
115
|
+
data: expected,
|
|
116
|
+
}),
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('it should return method to fetch next page', async () => {
|
|
122
|
+
const callback = jest.fn();
|
|
123
|
+
client.http.get = jest
|
|
124
|
+
.fn()
|
|
125
|
+
.mockResolvedValue(communityQueryResponse)
|
|
126
|
+
.mockResolvedValueOnce(communityQueryResponsePage2);
|
|
127
|
+
|
|
128
|
+
liveCommunities({}, callback);
|
|
129
|
+
await pause();
|
|
130
|
+
|
|
131
|
+
expect(callback).toHaveBeenCalled();
|
|
132
|
+
expect(callback.mock.lastCall).toHaveLength(1);
|
|
133
|
+
|
|
134
|
+
const { onNextPage, hasNextPage } = callback.mock.lastCall[0];
|
|
135
|
+
|
|
136
|
+
expect(hasNextPage).toBe(true);
|
|
137
|
+
expect(onNextPage).toBeTruthy();
|
|
138
|
+
|
|
139
|
+
onNextPage();
|
|
140
|
+
await pause();
|
|
141
|
+
|
|
142
|
+
// 4 -> because 1 local & server call each per call (2)
|
|
143
|
+
expect(callback).toHaveBeenCalledTimes(4);
|
|
144
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
145
|
+
4,
|
|
146
|
+
expect.objectContaining(
|
|
147
|
+
getSnapshot({
|
|
148
|
+
loading: false,
|
|
149
|
+
data: unpackCommunityPayload(payload).communities,
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('events', () => {
|
|
156
|
+
const newCommunityPayload = {
|
|
157
|
+
...communityQueryResponse.data.communities[0],
|
|
158
|
+
communityId: 'new-community',
|
|
159
|
+
isDeleted: false,
|
|
160
|
+
};
|
|
161
|
+
const newCommunity = unpackCommunityPayload({ communities: [newCommunityPayload] })
|
|
162
|
+
.communities[0];
|
|
163
|
+
|
|
164
|
+
const communityPayload = {
|
|
165
|
+
communities: [communityQueryResponse.data.communities[0]],
|
|
166
|
+
communityUsers: [communityUser11],
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const cases: [string, keyof Amity.Events, Amity.CommunityPayload, Amity.Community[]][] = [
|
|
170
|
+
[
|
|
171
|
+
'it should add new community to collection onCreate',
|
|
172
|
+
'community.created',
|
|
173
|
+
{ communities: [newCommunityPayload] },
|
|
174
|
+
[newCommunity, community2],
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
'it should update community in collection onUpdate',
|
|
178
|
+
'community.updated',
|
|
179
|
+
{ communities: [{ ...newCommunityPayload, communityId: community1.communityId }] },
|
|
180
|
+
[{ ...newCommunity, communityId: community1.communityId }, community2],
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
'it should remove community from collection onDelete',
|
|
184
|
+
'community.deleted',
|
|
185
|
+
communityPayload,
|
|
186
|
+
[community2],
|
|
187
|
+
],
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
test.each(cases)('%s', async (test, event, communityPayload, expected) => {
|
|
191
|
+
const callback = jest.fn();
|
|
192
|
+
client.http.get = jest.fn().mockResolvedValue(communityQueryResponse);
|
|
193
|
+
|
|
194
|
+
liveCommunities({ isDeleted: false }, callback);
|
|
195
|
+
await pause();
|
|
196
|
+
|
|
197
|
+
client.emitter.emit(event, communityPayload);
|
|
198
|
+
await pause();
|
|
199
|
+
|
|
200
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
201
|
+
3,
|
|
202
|
+
expect.objectContaining(
|
|
203
|
+
getSnapshot({
|
|
204
|
+
data: expected,
|
|
205
|
+
loading: false,
|
|
206
|
+
}),
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const joinCommunityPayload = {
|
|
212
|
+
...communityQueryResponse.data.communities[0],
|
|
213
|
+
communityId: 'joining-community',
|
|
214
|
+
isDeleted: false,
|
|
215
|
+
};
|
|
216
|
+
const joinedCommunity = unpackCommunityPayload({ communities: [joinCommunityPayload] })
|
|
217
|
+
.communities[0];
|
|
218
|
+
|
|
219
|
+
const joiningUser: Amity.MembershipRaw<'community'> = {
|
|
220
|
+
...communityUser11,
|
|
221
|
+
userId: 'test',
|
|
222
|
+
communityId: 'joining-community',
|
|
223
|
+
communityMembership: 'member',
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const memberEvents: [
|
|
227
|
+
string,
|
|
228
|
+
Amity.CommunityLiveCollection,
|
|
229
|
+
keyof Amity.Events,
|
|
230
|
+
Amity.CommunityPayload,
|
|
231
|
+
Amity.Community[],
|
|
232
|
+
][] = [
|
|
233
|
+
[
|
|
234
|
+
'it should add new community to collection onJoin',
|
|
235
|
+
{ membership: 'member' },
|
|
236
|
+
'community.joined',
|
|
237
|
+
{ communities: [joinCommunityPayload], communityUsers: [joiningUser] },
|
|
238
|
+
[joinedCommunity, community1],
|
|
239
|
+
],
|
|
240
|
+
[
|
|
241
|
+
'it should not add new member to community collection onJoin if membership:not member',
|
|
242
|
+
{ membership: 'notMember' },
|
|
243
|
+
'community.joined',
|
|
244
|
+
{ communities: [joinCommunityPayload], communityUsers: [joiningUser] },
|
|
245
|
+
[],
|
|
246
|
+
],
|
|
247
|
+
[
|
|
248
|
+
'it should update community in collection onLeft',
|
|
249
|
+
{ membership: 'member' },
|
|
250
|
+
'community.left',
|
|
251
|
+
{ ...communityPayload, communities: [{ ...community1, isJoined: false }] },
|
|
252
|
+
[],
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
'it should add new community in collection onLeft and if membership:not member',
|
|
256
|
+
{ membership: 'notMember' },
|
|
257
|
+
'community.left',
|
|
258
|
+
{ ...communityPayload, communities: [{ ...community1, isJoined: false }] },
|
|
259
|
+
[{ ...community1, isJoined: false }],
|
|
260
|
+
],
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
test.each(memberEvents)('%s', async (test, params, event, communityPayload, expected) => {
|
|
264
|
+
const callback = jest.fn();
|
|
265
|
+
client.http.get = jest.fn().mockResolvedValue(communityQueryResponsePage2);
|
|
266
|
+
|
|
267
|
+
liveCommunities(params, callback);
|
|
268
|
+
await pause();
|
|
269
|
+
|
|
270
|
+
client.emitter.emit(event, communityPayload);
|
|
271
|
+
await pause();
|
|
272
|
+
|
|
273
|
+
expect(callback).toHaveBeenNthCalledWith(
|
|
274
|
+
3,
|
|
275
|
+
expect.objectContaining(
|
|
276
|
+
getSnapshot({
|
|
277
|
+
data: expected,
|
|
278
|
+
loading: false,
|
|
279
|
+
}),
|
|
280
|
+
),
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -15,6 +15,14 @@ export const sortByDisplayName: SortingFunc<{ displayName: string }> = (
|
|
|
15
15
|
return a < b ? -1 : 1;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Alphabetic sorting of objects having a name
|
|
20
|
+
*/
|
|
21
|
+
export const sortByName: SortingFunc<{ name: string }> = ({ name: a }, { name: b }) => {
|
|
22
|
+
if (a === b) return 0;
|
|
23
|
+
return a < b ? -1 : 1;
|
|
24
|
+
};
|
|
25
|
+
|
|
18
26
|
/**
|
|
19
27
|
* Sorting a collection by their apparition order (oldest first)
|
|
20
28
|
*/
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import qs from 'qs';
|
|
3
2
|
import HttpAgent, { HttpsAgent } from 'agentkeepalive';
|
|
4
3
|
|
|
5
4
|
import { ASCError } from '~/core/errors';
|
|
@@ -7,14 +6,6 @@ import { ASCError } from '~/core/errors';
|
|
|
7
6
|
import { fireEvent } from '../events';
|
|
8
7
|
import { unwrapPayload } from './utils';
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
* Axios param serializer (for querystring)
|
|
12
|
-
* @hidden
|
|
13
|
-
*/
|
|
14
|
-
export const stringify = (params: Record<string, unknown>): string => {
|
|
15
|
-
return qs.stringify(params, { arrayFormat: 'brackets' });
|
|
16
|
-
};
|
|
17
|
-
|
|
18
9
|
/**
|
|
19
10
|
* Creates a pre-configured axios instance
|
|
20
11
|
*
|
|
@@ -36,7 +27,14 @@ export const createHttpTransport = (endpoint: string) => {
|
|
|
36
27
|
baseURL: endpoint,
|
|
37
28
|
httpAgent: new HttpAgent(options),
|
|
38
29
|
httpsAgent: new HttpsAgent(options),
|
|
39
|
-
|
|
30
|
+
/*
|
|
31
|
+
* If paramSerializer is required, use the serialize option to stringify
|
|
32
|
+
* params. The encode option will pass complex params as string only whereas
|
|
33
|
+
* the serialize option will pass params in the format Record<string, any>
|
|
34
|
+
*
|
|
35
|
+
* For more details:
|
|
36
|
+
* https://github.com/axios/axios#request-config
|
|
37
|
+
*/
|
|
40
38
|
});
|
|
41
39
|
|
|
42
40
|
instance.interceptors.request.use(config => {
|
|
@@ -59,8 +57,6 @@ export const createHttpTransport = (endpoint: string) => {
|
|
|
59
57
|
response => response,
|
|
60
58
|
error => {
|
|
61
59
|
const { response } = error;
|
|
62
|
-
// eslint-disable-next-line no-console
|
|
63
|
-
console.error(response?.data ?? error);
|
|
64
60
|
|
|
65
61
|
// handle unauthorized request
|
|
66
62
|
if (response.data.code === Amity.ServerError.UNAUTHORIZED) {
|
|
@@ -78,7 +74,15 @@ export const createHttpTransport = (endpoint: string) => {
|
|
|
78
74
|
);
|
|
79
75
|
}
|
|
80
76
|
|
|
81
|
-
if
|
|
77
|
+
// if it's an error with status in the response payload,
|
|
78
|
+
// then it's an expected error.
|
|
79
|
+
if (response?.data?.status) {
|
|
80
|
+
unwrapPayload(response?.data);
|
|
81
|
+
}
|
|
82
|
+
// unexpected error.
|
|
83
|
+
else {
|
|
84
|
+
throw new Error(response?.data ?? error);
|
|
85
|
+
}
|
|
82
86
|
},
|
|
83
87
|
);
|
|
84
88
|
|