@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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { disableCache, enableCache } from '~/cache/api';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
client,
|
|
5
|
+
connectClient,
|
|
6
|
+
disconnectClient,
|
|
7
|
+
channel11,
|
|
8
|
+
channelUser1,
|
|
9
|
+
channelUser3,
|
|
10
|
+
} from '~/utils/tests';
|
|
11
|
+
|
|
12
|
+
import { onChannelMemberUnbanned } from '../onChannelMemberUnbanned';
|
|
13
|
+
|
|
14
|
+
describe('onChannelMemberBanned event', () => {
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
await connectClient();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await disconnectClient();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
enableCache();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(disableCache);
|
|
28
|
+
|
|
29
|
+
const channelPayload: Amity.ChannelMembershipPayload = {
|
|
30
|
+
channelUsers: [channelUser1, channelUser3],
|
|
31
|
+
channels: [channel11],
|
|
32
|
+
files: [],
|
|
33
|
+
users: [],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
test('it should call callback with channel and unbanned member', () => {
|
|
37
|
+
const callback = jest.fn();
|
|
38
|
+
|
|
39
|
+
const unsub = onChannelMemberUnbanned(callback);
|
|
40
|
+
client.emitter.emit('v3.channel.didUnban', channelPayload);
|
|
41
|
+
|
|
42
|
+
unsub();
|
|
43
|
+
|
|
44
|
+
expect(callback).toHaveBeenCalled();
|
|
45
|
+
expect(callback).toHaveBeenCalledWith(channel11, channelUser3);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('it should return an Unsubscriber', () => {
|
|
49
|
+
const callback = jest.fn();
|
|
50
|
+
|
|
51
|
+
const unsub = onChannelMemberUnbanned(callback);
|
|
52
|
+
unsub();
|
|
53
|
+
|
|
54
|
+
client.emitter.emit('v3.channel.didUnban', channelPayload);
|
|
55
|
+
|
|
56
|
+
expect(callback).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -16,7 +16,12 @@ import {
|
|
|
16
16
|
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
17
17
|
ENABLE_CACHE_MESSAGE,
|
|
18
18
|
} from '~/utils/constants';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
onChannelMemberAdded,
|
|
21
|
+
onChannelMemberRemoved,
|
|
22
|
+
onChannelMemberBanned,
|
|
23
|
+
onChannelMemberUnbanned,
|
|
24
|
+
} from '../events';
|
|
20
25
|
import { queryChannelMembers } from '../api';
|
|
21
26
|
|
|
22
27
|
/**
|
|
@@ -67,7 +72,22 @@ export const liveChannelMembers = (
|
|
|
67
72
|
.map(({ data }) => data) ?? [];
|
|
68
73
|
|
|
69
74
|
channelMembers = filterByPropEquality(channelMembers, 'roles', params.roles);
|
|
70
|
-
|
|
75
|
+
|
|
76
|
+
if (params.memberships) {
|
|
77
|
+
/*
|
|
78
|
+
* even though membership includes muted as a possible value
|
|
79
|
+
* when querying the server.
|
|
80
|
+
* Muted is specified under seperarte property namely isMuted
|
|
81
|
+
* Hence why I've seperately checked for it's equality
|
|
82
|
+
*/
|
|
83
|
+
if (params.memberships.some(m => ['member', 'banned'].includes(m))) {
|
|
84
|
+
channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (params.memberships.includes('muted')) {
|
|
88
|
+
channelMembers = filterByPropEquality(channelMembers, 'isMuted', true);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
71
91
|
|
|
72
92
|
if (params.search) {
|
|
73
93
|
const containsMatcher = new RegExp(params.search);
|
|
@@ -91,7 +111,7 @@ export const liveChannelMembers = (
|
|
|
91
111
|
|
|
92
112
|
const realtimeRouter = (
|
|
93
113
|
channelMember: Amity.Membership<'channel'>,
|
|
94
|
-
action:
|
|
114
|
+
action: Amity.ChannelMemberActionType,
|
|
95
115
|
) => {
|
|
96
116
|
const collection = pullFromCache<Amity.ChannelMembersLiveCollectionCache>(cacheKey)?.data;
|
|
97
117
|
if (!collection) return;
|
|
@@ -103,7 +123,9 @@ export const liveChannelMembers = (
|
|
|
103
123
|
|
|
104
124
|
if (action === 'onMemberAdded') {
|
|
105
125
|
collection.data = [...new Set([channelMemberCacheId, ...collection.data])];
|
|
106
|
-
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (action === 'onMemberRemoved') {
|
|
107
129
|
collection.data = collection.data.filter(m => m !== channelMemberCacheId);
|
|
108
130
|
}
|
|
109
131
|
|
|
@@ -148,6 +170,8 @@ export const liveChannelMembers = (
|
|
|
148
170
|
disposers.push(
|
|
149
171
|
onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')),
|
|
150
172
|
onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')),
|
|
173
|
+
onChannelMemberBanned((_, member) => realtimeRouter(member, 'onChannelMemberBanned')),
|
|
174
|
+
onChannelMemberUnbanned((_, member) => realtimeRouter(member, 'onChannelMemberUnbanned')),
|
|
151
175
|
);
|
|
152
176
|
|
|
153
177
|
onFetch();
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
runQuery,
|
|
8
8
|
queryOptions,
|
|
9
9
|
filterByPropEquality,
|
|
10
|
+
filterByChannelMembership,
|
|
10
11
|
sortByLastCreated,
|
|
11
12
|
sortByDisplayName,
|
|
12
13
|
sortByFirstCreated,
|
|
@@ -43,7 +44,7 @@ export const liveChannels = (
|
|
|
43
44
|
callback: Amity.LiveCollectionCallback<Amity.Channel>,
|
|
44
45
|
config?: Amity.LiveCollectionConfig,
|
|
45
46
|
) => {
|
|
46
|
-
const { log, cache } = getActiveClient();
|
|
47
|
+
const { log, cache, userId } = getActiveClient();
|
|
47
48
|
|
|
48
49
|
if (!cache) {
|
|
49
50
|
console.log(ENABLE_CACHE_MESSAGE);
|
|
@@ -82,6 +83,11 @@ export const liveChannels = (
|
|
|
82
83
|
channels = channels.filter(c => !c.tags?.some(t => params.excludeTags?.includes(t)));
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
// userId is required to be able to filter channel by current active user
|
|
87
|
+
if (params.membership && userId) {
|
|
88
|
+
channels = filterByChannelMembership(channels, params.membership, userId);
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
const sortBy = params.sortBy || 'lastCreated';
|
|
86
92
|
|
|
87
93
|
if (sortBy === 'lastCreated' || sortBy === 'firstCreated') {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
channelUser1,
|
|
7
7
|
channelUser4,
|
|
8
8
|
channelUserQueryResponse,
|
|
9
|
+
channelUserQueryResponsePage3,
|
|
9
10
|
channelUserQueryResponsePage2,
|
|
10
11
|
channel11,
|
|
11
12
|
pause,
|
|
@@ -57,24 +58,34 @@ describe('liveChannelMembers', () => {
|
|
|
57
58
|
const filters: [string, Amity.ChannelMembersLiveCollection, Amity.Membership<'channel'>[]][] = [
|
|
58
59
|
[
|
|
59
60
|
'roles',
|
|
60
|
-
{ channelId, roles:
|
|
61
|
-
[
|
|
61
|
+
{ channelId, roles: channelUserQueryResponsePage3.data.channelUsers[0].roles },
|
|
62
|
+
[channelUserQueryResponsePage3.data.channelUsers[0]],
|
|
62
63
|
],
|
|
63
64
|
[
|
|
64
65
|
'searched term',
|
|
65
66
|
{ channelId, search: 'use' },
|
|
66
|
-
[
|
|
67
|
+
[channelUserQueryResponsePage3.data.channelUsers[0]],
|
|
67
68
|
],
|
|
68
69
|
[
|
|
69
|
-
'
|
|
70
|
+
'member',
|
|
70
71
|
{ channelId, memberships: ['member'] },
|
|
71
|
-
[
|
|
72
|
+
[channelUserQueryResponsePage3.data.channelUsers[0]],
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
'muted members',
|
|
76
|
+
{ channelId, memberships: ['muted'] },
|
|
77
|
+
[channelUserQueryResponsePage3.data.channelUsers[1]],
|
|
78
|
+
],
|
|
79
|
+
[
|
|
80
|
+
'banned members',
|
|
81
|
+
{ channelId, memberships: ['banned'] },
|
|
82
|
+
[channelUserQueryResponsePage3.data.channelUsers[1]],
|
|
72
83
|
],
|
|
73
84
|
];
|
|
74
85
|
|
|
75
86
|
test.each(filters)('it should filter channelUsers by %s', async (filter, params, expected) => {
|
|
76
87
|
const callback = jest.fn();
|
|
77
|
-
client.http.get = jest.fn().mockResolvedValue(
|
|
88
|
+
client.http.get = jest.fn().mockResolvedValue(channelUserQueryResponsePage3);
|
|
78
89
|
|
|
79
90
|
liveChannelMembers(params, callback);
|
|
80
91
|
await pause();
|
|
@@ -131,6 +142,16 @@ describe('liveChannelMembers', () => {
|
|
|
131
142
|
'v3.channel.didRemoveUsers',
|
|
132
143
|
channelUser1,
|
|
133
144
|
],
|
|
145
|
+
[
|
|
146
|
+
'it should update channel memeber membership on onChannelMemberBanned',
|
|
147
|
+
'v3.channel.didBan',
|
|
148
|
+
{ ...channelUser1, membership: 'banned', isBanned: true },
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
'it should update channel memeber membership on onChannelMemberUnbanned',
|
|
152
|
+
'v3.channel.didUnban',
|
|
153
|
+
{ ...channelUser1, membership: 'none', isBanned: false },
|
|
154
|
+
],
|
|
134
155
|
];
|
|
135
156
|
|
|
136
157
|
test.each(events)('%s', async (test, event, channelUser) => {
|
|
@@ -153,11 +174,32 @@ describe('liveChannelMembers', () => {
|
|
|
153
174
|
|
|
154
175
|
snapshot.loading = false;
|
|
155
176
|
// get data based on event
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
177
|
+
switch (event) {
|
|
178
|
+
case 'v3.channel.didAddUsers':
|
|
179
|
+
snapshot.data = [channelUser, ...payload.channelUsers];
|
|
180
|
+
break;
|
|
181
|
+
|
|
182
|
+
case 'v3.channel.didBan': {
|
|
183
|
+
const bannedUserIdx = payload.channelUsers.findIndex(u => u.userId === channelUser.userId);
|
|
184
|
+
payload.channelUsers[bannedUserIdx] = channelUser;
|
|
185
|
+
|
|
186
|
+
snapshot.data = payload.channelUsers;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
case 'v3.channel.didUnban': {
|
|
191
|
+
const unbannedUserIdx = payload.channelUsers.findIndex(
|
|
192
|
+
u => u.userId === channelUser.userId,
|
|
193
|
+
);
|
|
194
|
+
payload.channelUsers[unbannedUserIdx] = channelUser;
|
|
195
|
+
|
|
196
|
+
snapshot.data = payload.channelUsers;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
// delete payload
|
|
202
|
+
snapshot.data = payload.channelUsers.filter(u => u.userId !== channelUser.userId);
|
|
161
203
|
}
|
|
162
204
|
|
|
163
205
|
expect(callback).toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { disableCache, enableCache } from '~/cache/api';
|
|
1
|
+
import { disableCache, enableCache, pushToCache } from '~/cache/api';
|
|
2
|
+
import { getResolver } from '~/core/model';
|
|
2
3
|
|
|
3
4
|
import { liveChannels } from '../liveChannels';
|
|
4
5
|
import { queryChannels } from '~/channel/api';
|
|
@@ -6,6 +7,7 @@ import { prepareChannelPayload } from '../../utils/prepareChannelPayload';
|
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
client,
|
|
10
|
+
pause,
|
|
9
11
|
connectClient,
|
|
10
12
|
disconnectClient,
|
|
11
13
|
channel11,
|
|
@@ -49,7 +51,7 @@ describe('liveChannels', () => {
|
|
|
49
51
|
// check if cache data returned (should be empty)
|
|
50
52
|
expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
|
|
51
53
|
|
|
52
|
-
await
|
|
54
|
+
await pause();
|
|
53
55
|
|
|
54
56
|
expect(callback).toHaveBeenCalledTimes(2);
|
|
55
57
|
|
|
@@ -63,33 +65,61 @@ describe('liveChannels', () => {
|
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
// table for channel filters
|
|
66
|
-
const filters: [string, Amity.ChannelLiveCollection][] = [
|
|
68
|
+
const filters: [string, Amity.ChannelLiveCollection, number?][] = [
|
|
67
69
|
['deleted', { isDeleted: false }],
|
|
68
70
|
['tagged', { tags: ['tag2'] }],
|
|
69
71
|
['out excluded tags', { excludeTags: ['tag1'] }],
|
|
72
|
+
['member', { membership: 'member' }],
|
|
73
|
+
['not member', { membership: 'notMember' }, 1],
|
|
74
|
+
['all membership', { membership: 'all' }],
|
|
70
75
|
];
|
|
71
76
|
|
|
72
|
-
test.each(filters)(
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
test.each(filters)(
|
|
78
|
+
'it should filter by %s channels',
|
|
79
|
+
async (filter, params, expectedChannelIdx = 0) => {
|
|
80
|
+
const callback = jest.fn();
|
|
81
|
+
client.http.get = jest.fn().mockResolvedValue(channelQueryResponse);
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
const payload = prepareChannelPayload(channelQueryResponse.data);
|
|
84
|
+
let snapshotData = [payload.channels[expectedChannelIdx]];
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
// add channel users to cache for testing membership filter
|
|
87
|
+
if (params.membership) {
|
|
88
|
+
pushToCache(
|
|
89
|
+
[
|
|
90
|
+
'channelUsers',
|
|
91
|
+
'get',
|
|
92
|
+
getResolver('channelUsers')({
|
|
93
|
+
channelId: channelQueryResponse.data.channels[0].channelId,
|
|
94
|
+
userId: channelUser1.userId,
|
|
95
|
+
}),
|
|
96
|
+
],
|
|
97
|
+
channelUser1,
|
|
98
|
+
);
|
|
79
99
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
if (params.membership === 'all') {
|
|
101
|
+
snapshotData = payload.channels;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
83
104
|
|
|
84
|
-
|
|
105
|
+
liveChannels(params, callback);
|
|
85
106
|
|
|
86
|
-
|
|
107
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
87
108
|
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
const snapshot = getSnapshot();
|
|
110
|
+
// check if cache data returned (should be empty)
|
|
111
|
+
expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
await pause();
|
|
114
|
+
|
|
115
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
116
|
+
|
|
117
|
+
snapshot.loading = false;
|
|
118
|
+
snapshot.data = snapshotData;
|
|
119
|
+
|
|
120
|
+
expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
|
|
121
|
+
},
|
|
122
|
+
);
|
|
93
123
|
|
|
94
124
|
test('it should return method to fetch next page', async () => {
|
|
95
125
|
const callback = jest.fn();
|
|
@@ -102,7 +132,7 @@ describe('liveChannels', () => {
|
|
|
102
132
|
|
|
103
133
|
expect(callback).toHaveBeenCalled();
|
|
104
134
|
|
|
105
|
-
await
|
|
135
|
+
await pause();
|
|
106
136
|
|
|
107
137
|
expect(callback.mock.lastCall).toHaveLength(1);
|
|
108
138
|
|
|
@@ -113,7 +143,7 @@ describe('liveChannels', () => {
|
|
|
113
143
|
|
|
114
144
|
onNextPage();
|
|
115
145
|
|
|
116
|
-
await
|
|
146
|
+
await pause();
|
|
117
147
|
|
|
118
148
|
// 4 -> becuase 1 local & server call each per call (2)
|
|
119
149
|
expect(callback).toHaveBeenCalledTimes(4);
|
|
@@ -146,7 +176,7 @@ describe('liveChannels', () => {
|
|
|
146
176
|
|
|
147
177
|
liveChannels({}, callback);
|
|
148
178
|
|
|
149
|
-
await
|
|
179
|
+
await pause();
|
|
150
180
|
expect(callback).toHaveBeenCalledTimes(2);
|
|
151
181
|
|
|
152
182
|
client.emitter.emit(event, {
|
package/src/core/events.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getResolver } from '~/core/model';
|
|
2
|
+
import { pullFromCache } from '~/cache/api';
|
|
1
3
|
// Note:
|
|
2
4
|
// this file should contain a suite of filtering utilities to help the
|
|
3
5
|
// local version of the query functions.
|
|
@@ -23,3 +25,50 @@ export const filterByPropInclusion = <T = Record<string, unknown>>(
|
|
|
23
25
|
key: keyof T,
|
|
24
26
|
value: any[] | undefined,
|
|
25
27
|
) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Filter a channel collection by membership of the userId
|
|
31
|
+
*
|
|
32
|
+
* @param collection the channel collection to filter
|
|
33
|
+
* @param membership the membership to be filtered by
|
|
34
|
+
* @param userId user id to be filtered by
|
|
35
|
+
* @returns a filtered collection with items only matching the criteria
|
|
36
|
+
*
|
|
37
|
+
* @hidden
|
|
38
|
+
*/
|
|
39
|
+
export const filterByChannelMembership = (
|
|
40
|
+
collection: Amity.Channel[],
|
|
41
|
+
membership: Amity.ChannelLiveCollection['membership'],
|
|
42
|
+
userId: Amity.Membership<'channel'>['userId'],
|
|
43
|
+
): Amity.Channel[] => {
|
|
44
|
+
if (membership === 'all') {
|
|
45
|
+
return collection;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return collection.filter(c => {
|
|
49
|
+
// get resolver for the channel by user
|
|
50
|
+
const channelUserCacheKey = getResolver('channelUsers')({
|
|
51
|
+
channelId: c.channelId,
|
|
52
|
+
userId,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const channelUser = pullFromCache<Amity.Membership<'channel'>>([
|
|
56
|
+
'channelUsers',
|
|
57
|
+
'get',
|
|
58
|
+
channelUserCacheKey,
|
|
59
|
+
])?.data;
|
|
60
|
+
|
|
61
|
+
// if channel user is defined for user, user is a member
|
|
62
|
+
if (membership === 'member' && channelUser) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// if channel user by user id not found, user is not a member of that
|
|
67
|
+
// channel
|
|
68
|
+
if (membership === 'notMember' && !channelUser) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false;
|
|
73
|
+
});
|
|
74
|
+
};
|
package/src/core/query/query.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { isPaged } from './paging';
|
|
3
3
|
import { identifyModelKey } from '~/core/model';
|
|
4
4
|
|
|
5
|
+
import { CACHE_LIFESPAN } from '~/cache/utils';
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Type guard to check and cast that a given async function is has the ".locally" property
|
|
7
9
|
*
|
|
@@ -71,7 +73,7 @@ export const isLocal = (model?: any): model is Amity.Local => {
|
|
|
71
73
|
*
|
|
72
74
|
* @hidden
|
|
73
75
|
*/
|
|
74
|
-
export const isFresh = (model?: Amity.Cachable, lifeSpan =
|
|
76
|
+
export const isFresh = (model?: Amity.Cachable, lifeSpan = CACHE_LIFESPAN) => {
|
|
75
77
|
return Date.now() - (model?.cachedAt ?? 0) <= lifeSpan;
|
|
76
78
|
};
|
|
77
79
|
|
|
@@ -111,12 +113,11 @@ export const createQuery = <Args extends any[], Returned extends any>(
|
|
|
111
113
|
*/
|
|
112
114
|
export const queryOptions = (
|
|
113
115
|
policy: Amity.QueryPolicy,
|
|
114
|
-
lifeSpan: number =
|
|
116
|
+
lifeSpan: number = CACHE_LIFESPAN, // 1mn
|
|
115
117
|
): Amity.QueryOptions => {
|
|
116
118
|
if (policy === 'cache_only') return { lifeSpan: Infinity };
|
|
117
|
-
if (policy === 'server_only') return { lifeSpan: 0 };
|
|
118
119
|
|
|
119
|
-
return { lifeSpan };
|
|
120
|
+
return { lifeSpan: lifeSpan < CACHE_LIFESPAN ? CACHE_LIFESPAN : lifeSpan };
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
/**
|
|
@@ -168,8 +169,10 @@ export const runQuery = <Args extends any[], Returned extends any>(
|
|
|
168
169
|
) => {
|
|
169
170
|
let local: Returned | undefined;
|
|
170
171
|
|
|
172
|
+
const { lifeSpan } = queryOptions('cache_then_server', options.lifeSpan);
|
|
173
|
+
|
|
171
174
|
// offline first
|
|
172
|
-
if (isOffline(func)
|
|
175
|
+
if (isOffline(func)) {
|
|
173
176
|
try {
|
|
174
177
|
local = isMutator(func) ? func.optimistically(...args) : func.locally(...args);
|
|
175
178
|
} catch (error) {
|
|
@@ -182,7 +185,7 @@ export const runQuery = <Args extends any[], Returned extends any>(
|
|
|
182
185
|
);
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
const shouldAbort = isCachable(local) && isFresh(local,
|
|
188
|
+
const shouldAbort = isCachable(local) && isFresh(local, lifeSpan);
|
|
186
189
|
|
|
187
190
|
callback?.(
|
|
188
191
|
createSnapshot<Returned>(local, {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
isFresh,
|
|
8
8
|
queryOptions,
|
|
9
9
|
} from '~/core/query';
|
|
10
|
+
import { CACHE_LIFESPAN } from '~/cache/utils';
|
|
10
11
|
|
|
11
12
|
describe('Core/Query', () => {
|
|
12
13
|
type isFetcherMockType = jest.Mock<any, any> & {
|
|
@@ -90,8 +91,17 @@ describe('Core/Query', () => {
|
|
|
90
91
|
});
|
|
91
92
|
|
|
92
93
|
describe('queryOptions', () => {
|
|
93
|
-
test('should return
|
|
94
|
-
expect(queryOptions('
|
|
94
|
+
test('should return minimum lifeSpan', () => {
|
|
95
|
+
expect(queryOptions('cache_then_server').lifeSpan).toBe(CACHE_LIFESPAN);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('should return minimum lifeSpan if provided lifeSpan is less than cache lifeSpan', () => {
|
|
99
|
+
expect(queryOptions('cache_then_server', 0).lifeSpan).toBe(CACHE_LIFESPAN);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should return provided lifeSpan if provided lifeSpan is more than cache lifeSpan', () => {
|
|
103
|
+
const value = CACHE_LIFESPAN * 2;
|
|
104
|
+
expect(queryOptions('cache_then_server', value).lifeSpan).toBe(value);
|
|
95
105
|
});
|
|
96
106
|
|
|
97
107
|
test('should return lifespan Infinity if cache only', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { uuid } from '~/core/uuid';
|
|
2
|
+
import { fireEvent } from '~/core/events';
|
|
1
3
|
import { getActiveClient } from '~/client/api';
|
|
2
|
-
|
|
3
|
-
import { upsertInCache, pullFromCache, pushToCache } from '~/cache/api';
|
|
4
4
|
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { UNSYNCED_OBJECT_CACHED_AT_VALUE } from '~/utils/constants';
|
|
6
|
+
import { upsertInCache, pullFromCache, pushToCache } from '~/cache/api';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* ```js
|
|
@@ -25,8 +25,11 @@ import { uuid } from '~/core/uuid';
|
|
|
25
25
|
export const createMessage = async <T extends Amity.MessageContentType>(
|
|
26
26
|
bundle: Pick<
|
|
27
27
|
Amity.Message<T>,
|
|
28
|
-
'channelId' | 'parentId' | 'type' | '
|
|
29
|
-
|
|
28
|
+
'channelId' | 'parentId' | 'type' | 'tags' | 'metadata' | 'mentionees'
|
|
29
|
+
> & {
|
|
30
|
+
data?: Amity.Message<T>['data'];
|
|
31
|
+
fileId?: Amity.Message<T>['fileId'];
|
|
32
|
+
},
|
|
30
33
|
): Promise<Amity.Cached<Amity.Message>> => {
|
|
31
34
|
const client = getActiveClient();
|
|
32
35
|
|
|
@@ -36,10 +39,11 @@ export const createMessage = async <T extends Amity.MessageContentType>(
|
|
|
36
39
|
|
|
37
40
|
const cachedAt = client.cache && Date.now();
|
|
38
41
|
if (client.cache) ingestInCache(data, { cachedAt });
|
|
39
|
-
// no need for event, fired by server
|
|
40
42
|
|
|
41
43
|
const { messages } = data;
|
|
42
44
|
|
|
45
|
+
fireEvent('v3.message.didCreate', { messages });
|
|
46
|
+
|
|
43
47
|
return {
|
|
44
48
|
data: messages[0],
|
|
45
49
|
cachedAt,
|
|
@@ -102,11 +106,13 @@ createMessage.optimistically = (
|
|
|
102
106
|
...bundleWithMessageId,
|
|
103
107
|
} as Amity.Message;
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
|
|
110
|
+
|
|
111
|
+
pushToCache(['message', 'get', message.messageId], message, { cachedAt });
|
|
106
112
|
fireEvent('v3.message.didCreate', { messages: [message] });
|
|
107
113
|
|
|
108
114
|
return {
|
|
115
|
+
cachedAt,
|
|
109
116
|
data: message,
|
|
110
|
-
cachedAt: -1,
|
|
111
117
|
};
|
|
112
118
|
};
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { getActiveClient } from '~/client/api';
|
|
2
2
|
|
|
3
|
+
import { fireEvent } from '~/core/events';
|
|
3
4
|
import { pullFromCache } from '~/cache/api';
|
|
4
5
|
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
6
|
|
|
7
|
+
import { isInTombstone } from '~/cache/api/isInTombstone';
|
|
8
|
+
import { checkIfShouldGoesToTombstone } from '~/cache/utils';
|
|
9
|
+
import { pushToTombstone } from '~/cache/api/pushToTombstone';
|
|
10
|
+
|
|
6
11
|
/**
|
|
7
12
|
* ```js
|
|
8
13
|
* import { getMessage } from '@amityco/ts-sdk'
|
|
@@ -19,20 +24,43 @@ import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
|
19
24
|
*/
|
|
20
25
|
export const getMessage = async (
|
|
21
26
|
messageId: Amity.Message['messageId'],
|
|
27
|
+
isLive = false,
|
|
22
28
|
): Promise<Amity.Cached<Amity.Message>> => {
|
|
23
29
|
const client = getActiveClient();
|
|
24
30
|
client.log('message/getMessage', messageId);
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (!isLive) {
|
|
33
|
+
console.log(
|
|
34
|
+
'getMessage will be deprecated on 15th November 2022, please use liveMessage instead.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
isInTombstone('message', messageId);
|
|
39
|
+
|
|
40
|
+
let data: Amity.MessagePayload;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// API-FIX: endpoint should not be /list, parameters should be querystring.
|
|
44
|
+
const response = await client.http.get<Amity.MessagePayload>(
|
|
45
|
+
`/api/v3/messages/${encodeURIComponent(messageId)}`,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
data = response.data;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (checkIfShouldGoesToTombstone((error as Amity.ErrorResponse)?.code)) {
|
|
51
|
+
pushToTombstone('message', messageId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
30
56
|
|
|
31
57
|
const cachedAt = client.cache && Date.now();
|
|
32
58
|
if (client.cache) ingestInCache(data, { cachedAt });
|
|
33
59
|
|
|
34
60
|
const { messages } = data;
|
|
35
61
|
|
|
62
|
+
fireEvent('v3.message.didFetch', { messages });
|
|
63
|
+
|
|
36
64
|
return {
|
|
37
65
|
data: messages.find(message => message.messageId === messageId)!,
|
|
38
66
|
cachedAt,
|