@amityco/ts-sdk 0.0.1-beta.29 → 0.0.1-beta.30
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/domains/channel.d.ts +11 -0
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts +1 -8
- package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
- package/dist/channel/observers/index.d.ts +1 -0
- package/dist/channel/observers/index.d.ts.map +1 -1
- package/dist/channel/observers/liveChannelMembers.d.ts +20 -0
- package/dist/channel/observers/liveChannelMembers.d.ts.map +1 -0
- package/dist/channel/observers/tests/liveChannelMembers.test.d.ts +2 -0
- package/dist/channel/observers/tests/liveChannelMembers.test.d.ts.map +1 -0
- package/dist/client/api/getToken.d.ts +1 -3
- package/dist/client/api/getToken.d.ts.map +1 -1
- package/dist/core/query/filtering.d.ts +1 -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 +105 -2
- package/dist/index.esm.js +104 -3
- package/dist/index.umd.js +4 -4
- package/dist/utils/tests/dummy/channel.d.ts +29 -3
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
- package/dist/utils/tests/utils.d.ts +1 -0
- package/dist/utils/tests/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/@types/domains/channel.ts +19 -0
- package/src/channel/api/queryChannelMembers.ts +3 -8
- package/src/channel/observers/index.ts +1 -0
- package/src/channel/observers/liveChannelMembers.ts +159 -0
- package/src/channel/observers/tests/liveChannelMembers.test.ts +165 -0
- package/src/channel/observers/tests/liveChannels.test.ts +17 -9
- package/src/client/api/isConnected.ts +1 -1
- package/src/core/query/filtering.ts +6 -0
- package/src/core/query/query.ts +2 -3
- package/src/core/tests/query/query.test.ts +93 -11
- package/src/utils/tests/dummy/channel.ts +57 -17
- package/src/utils/tests/utils.ts +10 -0
|
@@ -6,28 +6,54 @@ export declare const channel12: Amity.Channel;
|
|
|
6
6
|
export declare const channel21: Amity.Channel;
|
|
7
7
|
export declare const channelUser1: Amity.Membership<'channel'>;
|
|
8
8
|
export declare const channelUser2: Amity.Membership<'channel'>;
|
|
9
|
+
export declare const channelUser3: Amity.Membership<'channel'>;
|
|
10
|
+
export declare const channelUser4: Amity.Membership<'channel'>;
|
|
9
11
|
export declare const channelQueryResponse: {
|
|
10
12
|
data: {
|
|
13
|
+
paging: {
|
|
14
|
+
previous: string;
|
|
15
|
+
next: string;
|
|
16
|
+
};
|
|
11
17
|
channels: Amity.Channel<any>[];
|
|
12
18
|
channelUsers: never[];
|
|
13
19
|
files: never[];
|
|
14
20
|
users: never[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare const channelQueryResponsePage2: {
|
|
24
|
+
data: {
|
|
15
25
|
paging: {
|
|
16
26
|
previous: string;
|
|
17
27
|
next: string;
|
|
18
28
|
};
|
|
29
|
+
channels: Amity.Channel<any>[];
|
|
30
|
+
channelUsers: never[];
|
|
31
|
+
files: never[];
|
|
32
|
+
users: never[];
|
|
19
33
|
};
|
|
20
34
|
};
|
|
21
|
-
export declare const
|
|
35
|
+
export declare const channelUserQueryResponse: {
|
|
22
36
|
data: {
|
|
37
|
+
paging: {
|
|
38
|
+
previous: string;
|
|
39
|
+
next: string;
|
|
40
|
+
};
|
|
23
41
|
channels: Amity.Channel<any>[];
|
|
24
|
-
channelUsers:
|
|
25
|
-
files: never[];
|
|
42
|
+
channelUsers: Amity.Membership<"channel">[];
|
|
26
43
|
users: never[];
|
|
44
|
+
files: never[];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export declare const channelUserQueryResponsePage2: {
|
|
48
|
+
data: {
|
|
27
49
|
paging: {
|
|
28
50
|
previous: string;
|
|
29
51
|
next: string;
|
|
30
52
|
};
|
|
53
|
+
channels: Amity.Channel<any>[];
|
|
54
|
+
channelUsers: Amity.Membership<"channel">[];
|
|
55
|
+
users: never[];
|
|
56
|
+
files: never[];
|
|
31
57
|
};
|
|
32
58
|
};
|
|
33
59
|
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../../src/utils/tests/dummy/channel.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../../src/utils/tests/dummy/channel.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,UAO/B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,UAO/B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,UAK/B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,OAK7B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,OAI7B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,OAO7B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS,CAYpD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS,CAKpD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS,CAapD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS,CAGpD,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;;;;CAQhC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;CAQrC,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;;;;;;;;;CAQpC,CAAC;AAEF,eAAO,MAAM,6BAA6B;;;;;;;;;;;CAQzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,UAAW,OAAO,MAAM,EAAE,GAAG,CAAC,WAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,UAAW,OAAO,MAAM,EAAE,GAAG,CAAC,WAAkC,CAAC;AAOxF,eAAO,MAAM,KAAK,mCAGd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amityco/ts-sdk",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.30",
|
|
4
4
|
"license": "CC-BY-ND-4.0",
|
|
5
5
|
"author": "amity.co <developers@amity.co> (https://amity.co)",
|
|
6
6
|
"description": "Amity Social Cloud Typescript SDK",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@react-native-async-storage/async-storage": "^1.17.7",
|
|
56
56
|
"agentkeepalive": "^4.2.1",
|
|
57
|
-
"axios": "0.
|
|
57
|
+
"axios": "^0.27.2",
|
|
58
58
|
"debug": "^4.3.4",
|
|
59
59
|
"js-base64": "^3.7.2",
|
|
60
60
|
"mitt": "^3.0.0",
|
|
@@ -35,6 +35,7 @@ declare global {
|
|
|
35
35
|
|
|
36
36
|
memberCount?: number;
|
|
37
37
|
messageCount?: number;
|
|
38
|
+
moderatorMemberCount?: number;
|
|
38
39
|
|
|
39
40
|
lastActivity?: Amity.timestamp;
|
|
40
41
|
|
|
@@ -64,5 +65,23 @@ declare global {
|
|
|
64
65
|
Amity.Channel['channelId'],
|
|
65
66
|
Pick<QueryChannels, 'page'>
|
|
66
67
|
>;
|
|
68
|
+
|
|
69
|
+
type QueryChannelMembers = {
|
|
70
|
+
channelId: Amity.Channel['channelId'];
|
|
71
|
+
memberships?: Array<Exclude<Amity.Membership<'channel'>['membership'], 'none'> | 'muted'>;
|
|
72
|
+
roles?: string[];
|
|
73
|
+
sortBy?: 'firstCreated' | 'lastCreated';
|
|
74
|
+
search?: string;
|
|
75
|
+
page?: Amity.Page;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type ChannelMembersLiveCollection = Amity.LiveCollectionParams<
|
|
79
|
+
Omit<QueryChannelMembers, 'page'>
|
|
80
|
+
>;
|
|
81
|
+
|
|
82
|
+
type ChannelMembersLiveCollectionCache = Amity.LiveCollectionCache<
|
|
83
|
+
Amity.Membership<'channel'>['userId'],
|
|
84
|
+
Pick<QueryChannelMembers, 'page'>
|
|
85
|
+
>;
|
|
67
86
|
}
|
|
68
87
|
}
|
|
@@ -19,14 +19,9 @@ import { prepareChannelPayload } from '../utils';
|
|
|
19
19
|
* @category Channel API
|
|
20
20
|
* @async
|
|
21
21
|
* */
|
|
22
|
-
export const queryChannelMembers = async (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
roles?: string[];
|
|
26
|
-
sortBy?: 'firstCreated' | 'lastCreated';
|
|
27
|
-
search?: string;
|
|
28
|
-
page?: Amity.Page;
|
|
29
|
-
}): Promise<Amity.Cached<Amity.Paged<Amity.Membership<'channel'>>>> => {
|
|
22
|
+
export const queryChannelMembers = async (
|
|
23
|
+
query: Amity.QueryChannelMembers,
|
|
24
|
+
): Promise<Amity.Cached<Amity.Paged<Amity.Membership<'channel'>>>> => {
|
|
30
25
|
const client = getActiveClient();
|
|
31
26
|
client.log('channel/queryChannelMembers', query);
|
|
32
27
|
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
sortByFirstCreated,
|
|
12
|
+
filterByPropInclusion,
|
|
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 { onChannelMemberAdded, onChannelMemberRemoved } from '../events';
|
|
20
|
+
import { queryChannelMembers } from '../api';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ```js
|
|
24
|
+
* import { liveChannelMembers } from '@amityco/ts-sdk'
|
|
25
|
+
*
|
|
26
|
+
* let channelMembers = []
|
|
27
|
+
* const unsub = liveChannelMembers({
|
|
28
|
+
* channelId: Amity.Channel['channelId'],
|
|
29
|
+
* }, response => merge(channelMembers, response.data))
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Observe all mutations on a list of {@link Amity.ChannelUser}s
|
|
33
|
+
*
|
|
34
|
+
* @param params for querying channel users
|
|
35
|
+
* @param callback the function to call when new data are available
|
|
36
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the channel users
|
|
37
|
+
*
|
|
38
|
+
* @category Channel Live Collection
|
|
39
|
+
*/
|
|
40
|
+
export const liveChannelMembers = (
|
|
41
|
+
params: Amity.ChannelMembersLiveCollection,
|
|
42
|
+
callback: Amity.LiveCollectionCallback<Amity.Membership<'channel'>>,
|
|
43
|
+
config?: Amity.LiveCollectionConfig,
|
|
44
|
+
) => {
|
|
45
|
+
const { log, cache } = getActiveClient();
|
|
46
|
+
|
|
47
|
+
if (!cache) {
|
|
48
|
+
console.log(ENABLE_CACHE_MESSAGE);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const timestamp = Date.now();
|
|
52
|
+
log(`liveChannelMembers(tmpid: ${timestamp}) > listen`);
|
|
53
|
+
|
|
54
|
+
const { limit: queryLimit, ...queryParams } = params;
|
|
55
|
+
|
|
56
|
+
const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
|
|
57
|
+
const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
|
|
58
|
+
|
|
59
|
+
const disposers: Amity.Unsubscriber[] = [];
|
|
60
|
+
const cacheKey = ['channelUsers', 'collection', { channelId: params.channelId }];
|
|
61
|
+
|
|
62
|
+
const responder = (data: Amity.ChannelMembersLiveCollectionCache) => {
|
|
63
|
+
let channelMembers: Amity.Membership<'channel'>[] =
|
|
64
|
+
data.data
|
|
65
|
+
.map(id => pullFromCache<Amity.Membership<'channel'>>(['channelUsers', 'get', id])!)
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.map(({ data }) => data) ?? [];
|
|
68
|
+
|
|
69
|
+
channelMembers = filterByPropEquality(channelMembers, 'roles', params.roles);
|
|
70
|
+
channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
|
|
71
|
+
|
|
72
|
+
if (params.search) {
|
|
73
|
+
const containsMatcher = new RegExp(params.search);
|
|
74
|
+
channelMembers = channelMembers.filter(m => m.userId.match(containsMatcher));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// sort, 'lastCreated' is the default sort order
|
|
78
|
+
const sortBy = params.sortBy ? params.sortBy : 'lastCreated';
|
|
79
|
+
channelMembers = channelMembers.sort(
|
|
80
|
+
sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
callback({
|
|
84
|
+
onNextPage: onFetch,
|
|
85
|
+
data: channelMembers,
|
|
86
|
+
hasNextPage: !!data.params?.page,
|
|
87
|
+
loading: data.loading,
|
|
88
|
+
error: data.error,
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const realtimeRouter = (
|
|
93
|
+
channelMember: Amity.Membership<'channel'>,
|
|
94
|
+
action: 'onMemberAdded' | 'onMemberRemoved',
|
|
95
|
+
) => {
|
|
96
|
+
const collection = pullFromCache<Amity.ChannelMembersLiveCollectionCache>(cacheKey)?.data;
|
|
97
|
+
if (!collection) return;
|
|
98
|
+
|
|
99
|
+
const channelMemberCacheId = getResolver('channelUsers')({
|
|
100
|
+
channelId: params.channelId,
|
|
101
|
+
userId: channelMember.userId,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (action === 'onMemberAdded') {
|
|
105
|
+
collection.data = [...new Set([channelMemberCacheId, ...collection.data])];
|
|
106
|
+
} else {
|
|
107
|
+
collection.data = collection.data.filter(m => m !== channelMemberCacheId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pushToCache(cacheKey, collection);
|
|
111
|
+
responder(collection);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const onFetch = () => {
|
|
115
|
+
const collection = pullFromCache<Amity.ChannelMembersLiveCollectionCache>(cacheKey)?.data;
|
|
116
|
+
|
|
117
|
+
const channelMembers = collection?.data ?? [];
|
|
118
|
+
|
|
119
|
+
if (channelMembers.length > 0 && !collection?.params.page) return;
|
|
120
|
+
|
|
121
|
+
const query = createQuery(queryChannelMembers, {
|
|
122
|
+
...queryParams,
|
|
123
|
+
page: collection?.params.page ?? { limit },
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
runQuery(
|
|
127
|
+
query,
|
|
128
|
+
({ data: result, error, loading, nextPage: page }) => {
|
|
129
|
+
const data = {
|
|
130
|
+
loading,
|
|
131
|
+
error,
|
|
132
|
+
params: { page },
|
|
133
|
+
data: channelMembers,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (result) {
|
|
137
|
+
data.data = [...new Set([...channelMembers, ...result.map(getResolver('channelUsers'))])];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
pushToCache(cacheKey, data);
|
|
141
|
+
|
|
142
|
+
responder(data);
|
|
143
|
+
},
|
|
144
|
+
queryOptions(policy),
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
disposers.push(
|
|
149
|
+
onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')),
|
|
150
|
+
onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
onFetch();
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
log(`liveChannelMembers(tmpid: ${timestamp}) > dispose`);
|
|
157
|
+
disposers.forEach(fn => fn());
|
|
158
|
+
};
|
|
159
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { disableCache, enableCache } from '~/cache/api';
|
|
2
|
+
import {
|
|
3
|
+
client,
|
|
4
|
+
connectClient,
|
|
5
|
+
disconnectClient,
|
|
6
|
+
channelUser1,
|
|
7
|
+
channelUser4,
|
|
8
|
+
channelUserQueryResponse,
|
|
9
|
+
channelUserQueryResponsePage2,
|
|
10
|
+
channel11,
|
|
11
|
+
pause,
|
|
12
|
+
} from '~/utils/tests';
|
|
13
|
+
import { liveChannelMembers } from '../liveChannelMembers';
|
|
14
|
+
import { prepareChannelPayload } from '../../utils';
|
|
15
|
+
|
|
16
|
+
const getSnapshot = () => {
|
|
17
|
+
return {
|
|
18
|
+
data: [] as Amity.Membership<'channel'>[],
|
|
19
|
+
loading: true,
|
|
20
|
+
error: undefined as any,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe('liveChannelMembers', () => {
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
await connectClient();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
await disconnectClient();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
beforeEach(enableCache);
|
|
34
|
+
|
|
35
|
+
afterEach(disableCache);
|
|
36
|
+
|
|
37
|
+
const { channelId } = channel11;
|
|
38
|
+
|
|
39
|
+
test('it should return channel members collection', async () => {
|
|
40
|
+
// mock response of queryChannels and validate
|
|
41
|
+
const callback = jest.fn();
|
|
42
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponse);
|
|
43
|
+
|
|
44
|
+
liveChannelMembers({ channelId }, callback);
|
|
45
|
+
await pause();
|
|
46
|
+
|
|
47
|
+
// check if cache data returned (should be empty)
|
|
48
|
+
expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
|
|
49
|
+
|
|
50
|
+
const snapshot = getSnapshot();
|
|
51
|
+
snapshot.data = channelUserQueryResponse.data.channelUsers;
|
|
52
|
+
snapshot.loading = false;
|
|
53
|
+
|
|
54
|
+
expect(callback).toHaveBeenNthCalledWith(2, expect.objectContaining(snapshot));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const filters: [string, Amity.ChannelMembersLiveCollection, Amity.Membership<'channel'>[]][] = [
|
|
58
|
+
[
|
|
59
|
+
'roles',
|
|
60
|
+
{ channelId, roles: channelUserQueryResponse.data.channelUsers[1].roles },
|
|
61
|
+
[channelUserQueryResponse.data.channelUsers[1]],
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
'searched term',
|
|
65
|
+
{ channelId, search: 'use' },
|
|
66
|
+
[channelUserQueryResponse.data.channelUsers[1]],
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
'memberships',
|
|
70
|
+
{ channelId, memberships: ['member'] },
|
|
71
|
+
[channelUserQueryResponse.data.channelUsers[0]],
|
|
72
|
+
],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
test.each(filters)('it should filter channelUsers by %s', async (filter, params, expected) => {
|
|
76
|
+
const callback = jest.fn();
|
|
77
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponse);
|
|
78
|
+
|
|
79
|
+
liveChannelMembers(params, callback);
|
|
80
|
+
await pause();
|
|
81
|
+
|
|
82
|
+
const snapshot = getSnapshot();
|
|
83
|
+
// check if cache data returned (should be empty)
|
|
84
|
+
expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(snapshot));
|
|
85
|
+
|
|
86
|
+
snapshot.loading = false;
|
|
87
|
+
snapshot.data = expected;
|
|
88
|
+
|
|
89
|
+
expect(callback).toHaveBeenNthCalledWith(2, expect.objectContaining(snapshot));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('it should return method to fetch next page', async () => {
|
|
93
|
+
const callback = jest.fn();
|
|
94
|
+
client.http.get = jest
|
|
95
|
+
.fn()
|
|
96
|
+
.mockResolvedValue(channelUserQueryResponse)
|
|
97
|
+
.mockResolvedValueOnce(channelUserQueryResponsePage2);
|
|
98
|
+
|
|
99
|
+
liveChannelMembers({ channelId }, callback);
|
|
100
|
+
await pause();
|
|
101
|
+
|
|
102
|
+
expect(callback).toHaveBeenCalled();
|
|
103
|
+
expect(callback.mock.lastCall).toHaveLength(1);
|
|
104
|
+
|
|
105
|
+
const { onNextPage, hasNextPage } = callback.mock.lastCall[0];
|
|
106
|
+
|
|
107
|
+
expect(hasNextPage).toBe(true);
|
|
108
|
+
expect(onNextPage).toBeTruthy();
|
|
109
|
+
|
|
110
|
+
onNextPage();
|
|
111
|
+
await pause();
|
|
112
|
+
|
|
113
|
+
const payload = prepareChannelPayload(channelUserQueryResponse.data);
|
|
114
|
+
const payload2 = prepareChannelPayload(channelUserQueryResponsePage2.data);
|
|
115
|
+
|
|
116
|
+
const snapshot = getSnapshot();
|
|
117
|
+
snapshot.loading = false;
|
|
118
|
+
snapshot.data = [...payload2.channelUsers, ...payload.channelUsers];
|
|
119
|
+
|
|
120
|
+
expect(callback).toHaveBeenNthCalledWith(4, expect.objectContaining(snapshot));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const events: [string, keyof Amity.Events, Amity.Membership<'channel'>][] = [
|
|
124
|
+
[
|
|
125
|
+
'it should add new member to collection onMemberAdded',
|
|
126
|
+
'v3.channel.didAddUsers',
|
|
127
|
+
channelUser4,
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
'it should remove channel memeber from collection onMemberRemoved',
|
|
131
|
+
'v3.channel.didRemoveUsers',
|
|
132
|
+
channelUser1,
|
|
133
|
+
],
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
test.each(events)('%s', async (test, event, channelUser) => {
|
|
137
|
+
const snapshot = getSnapshot();
|
|
138
|
+
|
|
139
|
+
const callback = jest.fn();
|
|
140
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponse);
|
|
141
|
+
|
|
142
|
+
liveChannelMembers({ channelId }, callback);
|
|
143
|
+
await pause();
|
|
144
|
+
|
|
145
|
+
client.emitter.emit(event, {
|
|
146
|
+
channels: [channel11],
|
|
147
|
+
channelUsers: [channelUser],
|
|
148
|
+
users: [],
|
|
149
|
+
files: [],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const payload = prepareChannelPayload(channelUserQueryResponse.data);
|
|
153
|
+
|
|
154
|
+
snapshot.loading = false;
|
|
155
|
+
// get data based on event
|
|
156
|
+
if (event === 'v3.channel.didAddUsers') {
|
|
157
|
+
snapshot.data = [channelUser, ...payload.channelUsers];
|
|
158
|
+
} else {
|
|
159
|
+
const deletePayload = payload.channelUsers.filter(u => u.userId !== channelUser.userId);
|
|
160
|
+
snapshot.data = deletePayload;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
expect(callback).toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -2,13 +2,13 @@ import { disableCache, enableCache } from '~/cache/api';
|
|
|
2
2
|
|
|
3
3
|
import { liveChannels } from '../liveChannels';
|
|
4
4
|
import { queryChannels } from '~/channel/api';
|
|
5
|
+
import { prepareChannelPayload } from '../../utils/prepareChannelPayload';
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
client,
|
|
8
9
|
connectClient,
|
|
9
10
|
disconnectClient,
|
|
10
11
|
channel11,
|
|
11
|
-
channel12,
|
|
12
12
|
channelUser1,
|
|
13
13
|
channelQueryResponse,
|
|
14
14
|
channelQueryResponsePage2,
|
|
@@ -53,7 +53,10 @@ describe('liveChannels', () => {
|
|
|
53
53
|
|
|
54
54
|
expect(callback).toHaveBeenCalledTimes(2);
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
// cause we apply prepareChannelPayload on channel raw data from server
|
|
57
|
+
const payload = prepareChannelPayload(channelQueryResponse.data);
|
|
58
|
+
|
|
59
|
+
snapshot.data = payload.channels;
|
|
57
60
|
snapshot.loading = false;
|
|
58
61
|
|
|
59
62
|
expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
|
|
@@ -115,12 +118,12 @@ describe('liveChannels', () => {
|
|
|
115
118
|
// 4 -> becuase 1 local & server call each per call (2)
|
|
116
119
|
expect(callback).toHaveBeenCalledTimes(4);
|
|
117
120
|
|
|
121
|
+
const payload = prepareChannelPayload(channelQueryResponse.data);
|
|
122
|
+
const payload2 = prepareChannelPayload(channelQueryResponsePage2.data);
|
|
123
|
+
|
|
118
124
|
const snapshot = getSnapshot();
|
|
119
125
|
snapshot.loading = false;
|
|
120
|
-
snapshot.data = [
|
|
121
|
-
...channelQueryResponsePage2.data.channels,
|
|
122
|
-
...channelQueryResponse.data.channels,
|
|
123
|
-
];
|
|
126
|
+
snapshot.data = [...payload2.channels, ...payload.channels];
|
|
124
127
|
|
|
125
128
|
expect(callback).toHaveBeenNthCalledWith(4, expect.objectContaining(snapshot));
|
|
126
129
|
});
|
|
@@ -153,19 +156,24 @@ describe('liveChannels', () => {
|
|
|
153
156
|
files: [],
|
|
154
157
|
});
|
|
155
158
|
|
|
159
|
+
const payload = prepareChannelPayload(channelQueryResponse.data);
|
|
160
|
+
const deletePayload = payload.channels.filter(
|
|
161
|
+
channel => channel.channelId !== channel11.channelId,
|
|
162
|
+
);
|
|
163
|
+
|
|
156
164
|
snapshot.loading = false;
|
|
157
165
|
// get data based on event
|
|
158
166
|
switch (event) {
|
|
159
167
|
case 'v3.channel.didCreate':
|
|
160
|
-
snapshot.data = [channel,
|
|
168
|
+
snapshot.data = [channel, ...payload.channels];
|
|
161
169
|
break;
|
|
162
170
|
|
|
163
171
|
case 'v3.channel.didUpdate':
|
|
164
|
-
snapshot.data =
|
|
172
|
+
snapshot.data = payload.channels;
|
|
165
173
|
break;
|
|
166
174
|
|
|
167
175
|
default:
|
|
168
|
-
snapshot.data =
|
|
176
|
+
snapshot.data = deletePayload;
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
expect(callback).toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
|
|
@@ -17,3 +17,9 @@ export const filterByPropEquality = <T = Record<string, unknown>>(
|
|
|
17
17
|
key: keyof T,
|
|
18
18
|
value: any,
|
|
19
19
|
) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
|
|
20
|
+
|
|
21
|
+
export const filterByPropInclusion = <T = Record<string, unknown>>(
|
|
22
|
+
collection: T[],
|
|
23
|
+
key: keyof T,
|
|
24
|
+
value: any[] | undefined,
|
|
25
|
+
) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
|
package/src/core/query/query.ts
CHANGED
|
@@ -129,18 +129,17 @@ export const queryOptions = (
|
|
|
129
129
|
*
|
|
130
130
|
* @hidden
|
|
131
131
|
*/
|
|
132
|
-
const isEmpty = (local:
|
|
132
|
+
const isEmpty = (local: unknown) => {
|
|
133
133
|
// if it's supposed to be a single object, it'd be undefined
|
|
134
134
|
if (!local) return true;
|
|
135
135
|
|
|
136
136
|
// it's a paged query, the 1st cell of the array would be empty
|
|
137
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
138
137
|
if (isPaged(local) && isEmpty(local.data)) {
|
|
139
138
|
return true;
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
// if it's a item collection, it'd have no keys
|
|
143
|
-
if (!Object.keys(local).length) return true;
|
|
142
|
+
if (typeof local === 'object' && !Object.keys(local).length) return true;
|
|
144
143
|
|
|
145
144
|
return false;
|
|
146
145
|
};
|