@amityco/ts-sdk 0.0.1-beta.27 → 0.0.1-beta.28
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 +1 -0
- package/dist/@types/core/payload.d.ts +12 -10
- package/dist/@types/core/payload.d.ts.map +1 -1
- package/dist/@types/domains/channel.d.ts +5 -1
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/@types/domains/client.d.ts +13 -0
- package/dist/@types/domains/client.d.ts.map +1 -1
- package/dist/@types/domains/content.d.ts +26 -3
- package/dist/@types/domains/content.d.ts.map +1 -1
- package/dist/@types/domains/file.d.ts +36 -3
- package/dist/@types/domains/file.d.ts.map +1 -1
- package/dist/@types/domains/follow.d.ts +11 -0
- package/dist/@types/domains/follow.d.ts.map +1 -1
- package/dist/@types/domains/group.d.ts +1 -0
- package/dist/@types/domains/group.d.ts.map +1 -1
- package/dist/@types/domains/message.d.ts +13 -0
- package/dist/@types/domains/message.d.ts.map +1 -1
- package/dist/@types/global.d.ts +1 -0
- package/dist/@types/global.d.ts.map +1 -1
- package/dist/@types/index.d.ts +2 -2
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/channel/api/addChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/addChannelMembersRole.d.ts.map +1 -1
- package/dist/channel/api/banChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/createChannel.d.ts.map +1 -1
- package/dist/channel/api/getChannel.d.ts.map +1 -1
- package/dist/channel/api/getChannels.d.ts.map +1 -1
- package/dist/channel/api/joinChannel.d.ts.map +1 -1
- package/dist/channel/api/leaveChannel.d.ts.map +1 -1
- package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/queryChannels.d.ts.map +1 -1
- package/dist/channel/api/removeChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/removeChannelMembersRole.d.ts.map +1 -1
- package/dist/channel/api/unbanChannelMembers.d.ts.map +1 -1
- package/dist/channel/api/updateChannel.d.ts.map +1 -1
- package/dist/channel/events/index.d.ts +1 -0
- package/dist/channel/events/index.d.ts.map +1 -1
- package/dist/channel/events/onChannelCreated.d.ts.map +1 -1
- package/dist/channel/events/onChannelDeleted.d.ts.map +1 -1
- package/dist/channel/events/onChannelJoined.d.ts.map +1 -1
- package/dist/channel/events/onChannelLeft.d.ts.map +1 -1
- package/dist/channel/events/onChannelMemberAdded.d.ts.map +1 -1
- package/dist/channel/events/onChannelMemberRemoved.d.ts.map +1 -1
- package/dist/channel/events/onChannelUpdated.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/observeChannel.d.ts +1 -1
- package/dist/channel/observers/observeChannel.d.ts.map +1 -1
- package/dist/channel/observers/observeChannels.d.ts +17 -0
- package/dist/channel/observers/observeChannels.d.ts.map +1 -0
- package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts +2 -0
- package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +1 -0
- package/dist/channel/utils/index.d.ts +2 -0
- package/dist/channel/utils/index.d.ts.map +1 -0
- package/dist/channel/utils/prepareChannelPayload.d.ts +3 -0
- package/dist/channel/utils/prepareChannelPayload.d.ts.map +1 -0
- package/dist/client/api/connectClient.d.ts +1 -8
- package/dist/client/api/connectClient.d.ts.map +1 -1
- package/dist/client/api/createClient.d.ts +8 -1
- package/dist/client/api/createClient.d.ts.map +1 -1
- package/dist/client/api/getFeedSettings.d.ts +16 -0
- package/dist/client/api/getFeedSettings.d.ts.map +1 -0
- package/dist/client/api/index.d.ts +1 -0
- package/dist/client/api/index.d.ts.map +1 -1
- package/dist/core/events.d.ts +3 -3
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/query/query.d.ts +1 -1
- package/dist/core/tests/query/query/runQuery.test.d.ts +2 -0
- package/dist/core/tests/query/query/runQuery.test.d.ts.map +1 -0
- package/dist/file/api/createVideo.d.ts +2 -1
- package/dist/file/api/createVideo.d.ts.map +1 -1
- package/dist/follow/api/queryFollowers.d.ts +1 -5
- package/dist/follow/api/queryFollowers.d.ts.map +1 -1
- package/dist/follow/api/queryFollowings.d.ts +1 -5
- package/dist/follow/api/queryFollowings.d.ts.map +1 -1
- package/dist/follow/observers/index.d.ts +2 -0
- package/dist/follow/observers/index.d.ts.map +1 -1
- package/dist/follow/observers/liveFollowers.d.ts +20 -0
- package/dist/follow/observers/liveFollowers.d.ts.map +1 -0
- package/dist/follow/observers/liveFollowings.d.ts +20 -0
- package/dist/follow/observers/liveFollowings.d.ts.map +1 -0
- package/dist/index.cjs.js +553 -111
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +543 -112
- package/dist/index.umd.js +4 -4
- package/dist/message/api/queryMessages.d.ts +1 -10
- package/dist/message/api/queryMessages.d.ts.map +1 -1
- package/dist/message/observers/index.d.ts +1 -0
- package/dist/message/observers/index.d.ts.map +1 -1
- package/dist/message/observers/liveMessages.d.ts +20 -0
- package/dist/message/observers/liveMessages.d.ts.map +1 -0
- package/dist/message/observers/observeMessages.d.ts +0 -1
- package/dist/message/observers/observeMessages.d.ts.map +1 -1
- package/dist/message/observers/tests/liveMessages.test.d.ts +2 -0
- package/dist/message/observers/tests/liveMessages.test.d.ts.map +1 -0
- package/dist/post/observers/livePosts.d.ts.map +1 -1
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/tests/client.d.ts +5 -2
- package/dist/utils/tests/client.d.ts.map +1 -1
- package/dist/utils/tests/dummy/channel.d.ts +10 -0
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -0
- package/dist/utils/tests/dummy/index.d.ts +10 -0
- package/dist/utils/tests/dummy/index.d.ts.map +1 -0
- package/dist/utils/tests/dummy/message.d.ts +8 -0
- package/dist/utils/tests/dummy/message.d.ts.map +1 -0
- package/dist/utils/tests/{dummy.d.ts → dummy/post.d.ts} +3 -2
- package/dist/utils/tests/dummy/post.d.ts.map +1 -0
- package/dist/utils/tests/dummy/user.d.ts +2 -0
- package/dist/utils/tests/dummy/user.d.ts.map +1 -0
- package/dist/utils/tests/index.d.ts +2 -2
- package/dist/utils/tests/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/@types/core/payload.ts +16 -10
- package/src/@types/domains/channel.ts +16 -1
- package/src/@types/domains/client.ts +17 -0
- package/src/@types/domains/content.ts +34 -3
- package/src/@types/domains/file.ts +42 -3
- package/src/@types/domains/follow.ts +28 -0
- package/src/@types/domains/group.ts +1 -0
- package/src/@types/domains/message.ts +20 -0
- package/src/@types/global.ts +2 -0
- package/src/@types/index.ts +3 -2
- package/src/channel/api/addChannelMembers.ts +6 -5
- package/src/channel/api/addChannelMembersRole.ts +5 -1
- package/src/channel/api/banChannelMembers.ts +7 -3
- package/src/channel/api/createChannel.ts +6 -2
- package/src/channel/api/getChannel.ts +5 -1
- package/src/channel/api/getChannels.ts +5 -1
- package/src/channel/api/joinChannel.ts +5 -1
- package/src/channel/api/leaveChannel.ts +5 -1
- package/src/channel/api/queryChannelMembers.ts +16 -12
- package/src/channel/api/queryChannels.ts +9 -4
- package/src/channel/api/removeChannelMembers.ts +6 -5
- package/src/channel/api/removeChannelMembersRole.ts +5 -1
- package/src/channel/api/unbanChannelMembers.ts +7 -3
- package/src/channel/api/updateChannel.ts +5 -1
- package/src/channel/events/index.ts +1 -0
- package/src/channel/events/onChannelCreated.ts +6 -2
- package/src/channel/events/onChannelDeleted.ts +6 -2
- package/src/channel/events/onChannelJoined.ts +6 -2
- package/src/channel/events/onChannelLeft.ts +6 -2
- package/src/channel/events/onChannelMemberAdded.ts +6 -2
- package/src/channel/events/onChannelMemberRemoved.ts +6 -2
- package/src/channel/events/onChannelUpdated.ts +6 -2
- package/src/channel/observers/index.ts +1 -0
- package/src/channel/observers/observeChannel.ts +1 -11
- package/src/channel/observers/observeChannels.ts +59 -0
- package/src/channel/tests/utils/prepareChannelPayload.test.ts +120 -0
- package/src/channel/utils/index.ts +1 -0
- package/src/channel/utils/prepareChannelPayload.ts +31 -0
- package/src/client/api/connectClient.ts +4 -9
- package/src/client/api/createClient.ts +19 -4
- package/src/client/api/getFeedSettings.ts +29 -0
- package/src/client/api/index.ts +2 -0
- package/src/core/events.ts +2 -1
- package/src/core/query/query.ts +3 -3
- package/src/core/tests/query/query/runQuery.test.ts +182 -0
- package/src/file/api/createVideo.ts +6 -0
- package/src/follow/api/queryFollowers.ts +3 -5
- package/src/follow/api/queryFollowings.ts +3 -5
- package/src/follow/api/utils.ts +7 -3
- package/src/follow/observers/index.ts +3 -0
- package/src/follow/observers/liveFollowers.ts +149 -0
- package/src/follow/observers/liveFollowings.ts +149 -0
- package/src/index.ts +1 -1
- package/src/message/api/queryMessages.ts +3 -10
- package/src/message/observers/index.ts +1 -0
- package/src/message/observers/liveMessages.ts +141 -0
- package/src/message/observers/observeMessages.ts +0 -1
- package/src/message/observers/tests/liveMessages.test.ts +133 -0
- package/src/post/observers/livePosts.ts +2 -1
- package/src/post/tests/api/getPost.test.ts +3 -3
- package/src/post/tests/api/queryPosts.test.ts +4 -4
- package/src/utils/constants.ts +1 -0
- package/src/utils/tests/client.ts +31 -3
- package/src/utils/tests/dummy/channel.ts +52 -0
- package/src/utils/tests/dummy/index.ts +7 -0
- package/src/utils/tests/dummy/message.ts +21 -0
- package/src/utils/tests/dummy/post.ts +31 -0
- package/src/utils/tests/dummy/user.ts +11 -0
- package/src/utils/tests/index.ts +2 -2
- package/dist/utils/tests/dummy.d.ts.map +0 -1
- package/src/utils/tests/dummy.ts +0 -7
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
import { getResolver } from '~/core/model';
|
|
3
|
+
import { getActiveClient } from '~/client/api';
|
|
4
|
+
import { pushToCache, pullFromCache } from '~/cache/api';
|
|
5
|
+
import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
COLLECTION_DEFAULT_CACHING_POLICY,
|
|
9
|
+
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
10
|
+
} from '~/utils/constants';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
onFollowerRequested,
|
|
14
|
+
onFollowRequestAccepted,
|
|
15
|
+
onFollowRequestDeclined,
|
|
16
|
+
onFollowRequestCanceled,
|
|
17
|
+
onUserFollowed,
|
|
18
|
+
onUserUnfollowed,
|
|
19
|
+
onFollowerDeleted,
|
|
20
|
+
} from '../events';
|
|
21
|
+
import { queryFollowers } from '../api';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* ```js
|
|
25
|
+
* import { liveFollowers } from '@amityco/ts-sdk'
|
|
26
|
+
*
|
|
27
|
+
* let followers = []
|
|
28
|
+
* const unsub = liveFollowers({
|
|
29
|
+
* userId: Amity.User['userId'];
|
|
30
|
+
* }, response => merge(followers, response.data))
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Observe all mutations on a list of {@link Amity.FollowStatus} followers for a given userId
|
|
34
|
+
*
|
|
35
|
+
* @param userId the user
|
|
36
|
+
* @param callback the function to call when new data are available
|
|
37
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
38
|
+
*
|
|
39
|
+
* @category Followers Live Collection
|
|
40
|
+
*/
|
|
41
|
+
export const liveFollowers = (
|
|
42
|
+
params: Amity.FollowerLiveCollection,
|
|
43
|
+
callback: Amity.LiveCollectionCallback<Amity.FollowStatus>,
|
|
44
|
+
config?: Amity.LiveCollectionConfig,
|
|
45
|
+
): Amity.Unsubscriber => {
|
|
46
|
+
const { log, cache } = getActiveClient();
|
|
47
|
+
|
|
48
|
+
if (!cache) {
|
|
49
|
+
console.log('For using Live Collection feature you need to enable Cache!');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const timestamp = Date.now();
|
|
53
|
+
log(`liveFollowers(tmpid: ${timestamp}) > listen`);
|
|
54
|
+
|
|
55
|
+
const { limit: queryLimit, ...queryParams } = params;
|
|
56
|
+
|
|
57
|
+
const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
|
|
58
|
+
const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
|
|
59
|
+
|
|
60
|
+
const disposers: Amity.Unsubscriber[] = [];
|
|
61
|
+
const cacheKey = ['follow', 'collection', { userId: params.userId, type: 'follower' }];
|
|
62
|
+
|
|
63
|
+
const responder = (data: Amity.FollowerLiveCollectionCache) => {
|
|
64
|
+
let followers: Amity.FollowStatus[] =
|
|
65
|
+
data.data
|
|
66
|
+
.map(key => pullFromCache<Amity.FollowStatus>(['follow', 'get', key])!)
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.map(({ data }) => data) ?? [];
|
|
69
|
+
|
|
70
|
+
if (params.status) {
|
|
71
|
+
followers = filterByPropEquality(followers, 'status', params.status);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
callback({
|
|
75
|
+
onNextPage: onFetch,
|
|
76
|
+
data: followers,
|
|
77
|
+
hasNextPage: !!data.params?.page,
|
|
78
|
+
loading: data.loading,
|
|
79
|
+
error: data.error,
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const realtimeRouter = (followStatus: Amity.FollowStatus, action: Amity.FollowActionType) => {
|
|
84
|
+
const collection = pullFromCache<Amity.FollowerLiveCollectionCache>(cacheKey)?.data;
|
|
85
|
+
|
|
86
|
+
if (params.userId !== followStatus.to || !collection) return;
|
|
87
|
+
|
|
88
|
+
if (['onDeclined', 'onCanceled', 'onUnfollowed', 'onDeleted'].includes(action)) {
|
|
89
|
+
collection.data = collection.data.filter(p => p !== getResolver('follow')(followStatus));
|
|
90
|
+
} else if (['onRequested', 'onAccepted', 'onFollowed'].includes(action)) {
|
|
91
|
+
collection.data = [...new Set([getResolver('follow')(followStatus), ...collection.data])];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
pushToCache(cacheKey, collection);
|
|
95
|
+
|
|
96
|
+
responder(collection);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const onFetch = () => {
|
|
100
|
+
const collection = pullFromCache<Amity.FollowerLiveCollectionCache>(cacheKey)?.data;
|
|
101
|
+
|
|
102
|
+
const followers = collection?.data ?? [];
|
|
103
|
+
|
|
104
|
+
if (followers.length > 0 && !collection?.params?.page) return;
|
|
105
|
+
|
|
106
|
+
const query = createQuery(queryFollowers, {
|
|
107
|
+
...queryParams,
|
|
108
|
+
page: collection?.params?.page ?? { limit },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
runQuery(
|
|
112
|
+
query,
|
|
113
|
+
({ data: result, error, loading, nextPage: page }) => {
|
|
114
|
+
const data = {
|
|
115
|
+
loading,
|
|
116
|
+
error,
|
|
117
|
+
params: { page },
|
|
118
|
+
data: followers,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (result) {
|
|
122
|
+
data.data = [...new Set([...followers, ...result.map(getResolver('follow'))])];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pushToCache(cacheKey, data);
|
|
126
|
+
|
|
127
|
+
responder(data);
|
|
128
|
+
},
|
|
129
|
+
queryOptions(policy),
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
disposers.push(
|
|
134
|
+
onFollowerRequested(followStatus => realtimeRouter(followStatus, 'onRequested')),
|
|
135
|
+
onFollowRequestAccepted(followStatus => realtimeRouter(followStatus, 'onAccepted')),
|
|
136
|
+
onFollowRequestDeclined(followStatus => realtimeRouter(followStatus, 'onDeclined')),
|
|
137
|
+
onFollowRequestCanceled(followStatus => realtimeRouter(followStatus, 'onCanceled')),
|
|
138
|
+
onUserFollowed(followStatus => realtimeRouter(followStatus, 'onFollowed')),
|
|
139
|
+
onUserUnfollowed(followStatus => realtimeRouter(followStatus, 'onUnfollowed')),
|
|
140
|
+
onFollowerDeleted(followStatus => realtimeRouter(followStatus, 'onDeleted')),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
onFetch();
|
|
144
|
+
|
|
145
|
+
return () => {
|
|
146
|
+
log(`liveFollowers(tmpid: ${timestamp}) > dispose`);
|
|
147
|
+
disposers.forEach(fn => fn());
|
|
148
|
+
};
|
|
149
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
import { getResolver } from '~/core/model';
|
|
3
|
+
import { getActiveClient } from '~/client/api';
|
|
4
|
+
import { pushToCache, pullFromCache } from '~/cache/api';
|
|
5
|
+
import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
COLLECTION_DEFAULT_CACHING_POLICY,
|
|
9
|
+
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
10
|
+
} from '~/utils/constants';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
onFollowerRequested,
|
|
14
|
+
onFollowRequestAccepted,
|
|
15
|
+
onFollowRequestDeclined,
|
|
16
|
+
onFollowRequestCanceled,
|
|
17
|
+
onUserFollowed,
|
|
18
|
+
onUserUnfollowed,
|
|
19
|
+
onFollowerDeleted,
|
|
20
|
+
} from '../events';
|
|
21
|
+
import { queryFollowings } from '../api';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* ```js
|
|
25
|
+
* import { liveFollowings } from '@amityco/ts-sdk'
|
|
26
|
+
*
|
|
27
|
+
* let followings = []
|
|
28
|
+
* const unsub = liveFollowings({
|
|
29
|
+
* userId: Amity.User['userId'];
|
|
30
|
+
* }, response => merge(followings, response.data))
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Observe all mutations on a list of {@link Amity.FollowStatus} followings for a given userId
|
|
34
|
+
*
|
|
35
|
+
* @param userId the user
|
|
36
|
+
* @param callback the function to call when new data are available
|
|
37
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
38
|
+
*
|
|
39
|
+
* @category Followings Live Collection
|
|
40
|
+
*/
|
|
41
|
+
export const liveFollowings = (
|
|
42
|
+
params: Amity.FollowingLiveCollection,
|
|
43
|
+
callback: Amity.LiveCollectionCallback<Amity.FollowStatus>,
|
|
44
|
+
config?: Amity.LiveCollectionConfig,
|
|
45
|
+
): Amity.Unsubscriber => {
|
|
46
|
+
const { log, cache } = getActiveClient();
|
|
47
|
+
|
|
48
|
+
if (!cache) {
|
|
49
|
+
console.log('For using Live Collection feature you need to enable Cache!');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const timestamp = Date.now();
|
|
53
|
+
log(`liveFollowings(tmpid: ${timestamp}) > listen`);
|
|
54
|
+
|
|
55
|
+
const { limit: queryLimit, ...queryParams } = params;
|
|
56
|
+
|
|
57
|
+
const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
|
|
58
|
+
const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
|
|
59
|
+
|
|
60
|
+
const disposers: Amity.Unsubscriber[] = [];
|
|
61
|
+
const cacheKey = ['follow', 'collection', { userId: params.userId, type: 'following' }];
|
|
62
|
+
|
|
63
|
+
const responder = (data: Amity.FollowingLiveCollectionCache) => {
|
|
64
|
+
let followings: Amity.FollowStatus[] =
|
|
65
|
+
data.data
|
|
66
|
+
.map(key => pullFromCache<Amity.FollowStatus>(['follow', 'get', key])!)
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.map(({ data }) => data) ?? [];
|
|
69
|
+
|
|
70
|
+
if (params.status) {
|
|
71
|
+
followings = filterByPropEquality(followings, 'status', params.status);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
callback({
|
|
75
|
+
onNextPage: onFetch,
|
|
76
|
+
data: followings,
|
|
77
|
+
hasNextPage: !!data.params?.page,
|
|
78
|
+
loading: data.loading,
|
|
79
|
+
error: data.error,
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const realtimeRouter = (followStatus: Amity.FollowStatus, action: Amity.FollowActionType) => {
|
|
84
|
+
const collection = pullFromCache<Amity.FollowingLiveCollectionCache>(cacheKey)?.data;
|
|
85
|
+
|
|
86
|
+
if (params.userId !== followStatus.from || !collection) return;
|
|
87
|
+
|
|
88
|
+
if (['onDeclined', 'onCanceled', 'onUnfollowed', 'onDeleted'].includes(action)) {
|
|
89
|
+
collection.data = collection.data.filter(p => p !== getResolver('follow')(followStatus));
|
|
90
|
+
} else if (['onRequested', 'onAccepted', 'onFollowed'].includes(action)) {
|
|
91
|
+
collection.data = [...new Set([getResolver('follow')(followStatus), ...collection.data])];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
pushToCache(cacheKey, collection);
|
|
95
|
+
|
|
96
|
+
responder(collection);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const onFetch = () => {
|
|
100
|
+
const collection = pullFromCache<Amity.FollowingLiveCollectionCache>(cacheKey)?.data;
|
|
101
|
+
|
|
102
|
+
const followings = collection?.data ?? [];
|
|
103
|
+
|
|
104
|
+
if (followings.length > 0 && !collection?.params?.page) return;
|
|
105
|
+
|
|
106
|
+
const query = createQuery(queryFollowings, {
|
|
107
|
+
...queryParams,
|
|
108
|
+
page: collection?.params?.page ?? { limit },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
runQuery(
|
|
112
|
+
query,
|
|
113
|
+
({ data: result, error, loading, nextPage: page }) => {
|
|
114
|
+
const data = {
|
|
115
|
+
loading,
|
|
116
|
+
error,
|
|
117
|
+
params: { page },
|
|
118
|
+
data: followings,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (result) {
|
|
122
|
+
data.data = [...new Set([...followings, ...result.map(getResolver('follow'))])];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pushToCache(cacheKey, data);
|
|
126
|
+
|
|
127
|
+
responder(data);
|
|
128
|
+
},
|
|
129
|
+
queryOptions(policy),
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
disposers.push(
|
|
134
|
+
onFollowerRequested(followStatus => realtimeRouter(followStatus, 'onRequested')),
|
|
135
|
+
onFollowRequestAccepted(followStatus => realtimeRouter(followStatus, 'onAccepted')),
|
|
136
|
+
onFollowRequestDeclined(followStatus => realtimeRouter(followStatus, 'onDeclined')),
|
|
137
|
+
onFollowRequestCanceled(followStatus => realtimeRouter(followStatus, 'onCanceled')),
|
|
138
|
+
onUserFollowed(followStatus => realtimeRouter(followStatus, 'onFollowed')),
|
|
139
|
+
onUserUnfollowed(followStatus => realtimeRouter(followStatus, 'onUnfollowed')),
|
|
140
|
+
onFollowerDeleted(followStatus => realtimeRouter(followStatus, 'onDeleted')),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
onFetch();
|
|
144
|
+
|
|
145
|
+
return () => {
|
|
146
|
+
log(`liveFollowings(tmpid: ${timestamp}) > dispose`);
|
|
147
|
+
disposers.forEach(fn => fn());
|
|
148
|
+
};
|
|
149
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -19,16 +19,9 @@ import { getResolver } from '~/core/model';
|
|
|
19
19
|
* @category Message API
|
|
20
20
|
* @async
|
|
21
21
|
*/
|
|
22
|
-
export const queryMessages = async (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
parentId?: Amity.Message['parentId'];
|
|
26
|
-
filterByParentId?: boolean;
|
|
27
|
-
isDeleted?: boolean;
|
|
28
|
-
tags?: Amity.Taggable['tags'];
|
|
29
|
-
excludeTags?: Amity.Taggable['tags'];
|
|
30
|
-
page?: Amity.Page;
|
|
31
|
-
}): Promise<Amity.Cached<Amity.Paged<Amity.Message>>> => {
|
|
22
|
+
export const queryMessages = async (
|
|
23
|
+
query: Amity.QueryMessages,
|
|
24
|
+
): Promise<Amity.Cached<Amity.Paged<Amity.Message>>> => {
|
|
32
25
|
const client = getActiveClient();
|
|
33
26
|
client.log('message/queryMessages', query);
|
|
34
27
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
import { getResolver } from '~/core/model';
|
|
3
|
+
import { getActiveClient } from '~/client/api';
|
|
4
|
+
import { pushToCache, pullFromCache } from '~/cache/api';
|
|
5
|
+
import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
COLLECTION_DEFAULT_CACHING_POLICY,
|
|
9
|
+
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
10
|
+
ENABLE_CACHE_MESSAGE,
|
|
11
|
+
} from '~/utils/constants';
|
|
12
|
+
|
|
13
|
+
import { onMessageCreated, onMessageDeleted, onMessageUpdated } from '../events';
|
|
14
|
+
import { queryMessages } from '../api';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ```js
|
|
18
|
+
* import { liveMessages } from '@amityco/ts-sdk'
|
|
19
|
+
*
|
|
20
|
+
* let messages = []
|
|
21
|
+
* const unsub = liveMessages({
|
|
22
|
+
* channelId: Amity.Channel['channelId'];
|
|
23
|
+
* }, response => merge(messages, response.data))
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Observe all mutations on a list of {@link Amity.Message} for a given target object
|
|
27
|
+
*
|
|
28
|
+
* @param params for querying memssages from a channel
|
|
29
|
+
* @param callback the function to call when new data are available
|
|
30
|
+
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
31
|
+
*
|
|
32
|
+
* @category Messages Live Collection
|
|
33
|
+
*/
|
|
34
|
+
export const liveMessages = (
|
|
35
|
+
params: Amity.MessagesLiveCollection,
|
|
36
|
+
callback: Amity.LiveCollectionCallback<Amity.Message>,
|
|
37
|
+
config?: Amity.LiveCollectionConfig,
|
|
38
|
+
): Amity.Unsubscriber => {
|
|
39
|
+
const { log, cache } = getActiveClient();
|
|
40
|
+
|
|
41
|
+
if (!cache) {
|
|
42
|
+
console.log(ENABLE_CACHE_MESSAGE);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const timestamp = Date.now();
|
|
46
|
+
log(`liveMessages(tmpid: ${timestamp}) > listen`);
|
|
47
|
+
|
|
48
|
+
const { limit: queryLimit, ...queryParams } = params;
|
|
49
|
+
|
|
50
|
+
const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
|
|
51
|
+
const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
|
|
52
|
+
|
|
53
|
+
const disposers: Amity.Unsubscriber[] = [];
|
|
54
|
+
const cacheKey = ['message', 'collection', { channelId: params.channelId }];
|
|
55
|
+
|
|
56
|
+
const responder = (data: Amity.MessageLiveCollectionCache) => {
|
|
57
|
+
let messages: Amity.Message[] =
|
|
58
|
+
data.data
|
|
59
|
+
.map(messageId => pullFromCache<Amity.Message>(['message', 'get', messageId])!)
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.map(({ data }) => data) ?? [];
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* for cases when message is deleted via RTE, this flag is used to get
|
|
65
|
+
* items fro cache that are deleted | !deleted
|
|
66
|
+
*/
|
|
67
|
+
messages = filterByPropEquality(messages, 'isDeleted', params.isDeleted);
|
|
68
|
+
|
|
69
|
+
callback({
|
|
70
|
+
onNextPage: onFetch,
|
|
71
|
+
data: messages,
|
|
72
|
+
hasNextPage: !!data.params?.page,
|
|
73
|
+
loading: data.loading,
|
|
74
|
+
error: data.error,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const realtimeRouter = (message: Amity.Message, action: Amity.MessageActionType) => {
|
|
79
|
+
const collection = pullFromCache<Amity.MessageLiveCollectionCache>(cacheKey)?.data;
|
|
80
|
+
|
|
81
|
+
if (params.channelId !== message.channelId || !collection) return;
|
|
82
|
+
|
|
83
|
+
if (action === 'onCreate') {
|
|
84
|
+
collection.data = [...new Set([message.messageId, ...collection.data])];
|
|
85
|
+
} else if (['onDelete'].includes(action)) {
|
|
86
|
+
collection.data = collection.data.filter(m => m !== message.messageId);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
pushToCache(cacheKey, collection);
|
|
90
|
+
|
|
91
|
+
responder(collection);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const onFetch = () => {
|
|
95
|
+
const collection = pullFromCache<Amity.MessageLiveCollectionCache>(cacheKey)?.data;
|
|
96
|
+
|
|
97
|
+
const messages = collection?.data ?? [];
|
|
98
|
+
|
|
99
|
+
if (messages.length > 0 && !collection?.params?.page) return;
|
|
100
|
+
|
|
101
|
+
const query = createQuery(queryMessages, {
|
|
102
|
+
...queryParams,
|
|
103
|
+
page: collection?.params?.page ?? { limit },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
runQuery(
|
|
107
|
+
query,
|
|
108
|
+
({ data: result, error, loading, prevPage: page }) => {
|
|
109
|
+
const data = {
|
|
110
|
+
loading,
|
|
111
|
+
error,
|
|
112
|
+
params: { page },
|
|
113
|
+
data: messages,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (result) {
|
|
117
|
+
data.data = [...new Set([...messages, ...result.map(getResolver('message'))])];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pushToCache(cacheKey, data);
|
|
121
|
+
|
|
122
|
+
responder(data);
|
|
123
|
+
},
|
|
124
|
+
queryOptions(policy),
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
disposers.push(
|
|
129
|
+
onMessageCreated(message => realtimeRouter(message, 'onCreate')),
|
|
130
|
+
onMessageDeleted(message => realtimeRouter(message, 'onDelete')),
|
|
131
|
+
|
|
132
|
+
onMessageUpdated(message => realtimeRouter(message, 'onUpdate')),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
onFetch();
|
|
136
|
+
|
|
137
|
+
return () => {
|
|
138
|
+
log(`liveMessages(tmpid: ${timestamp}) > dispose`);
|
|
139
|
+
disposers.forEach(fn => fn());
|
|
140
|
+
};
|
|
141
|
+
};
|
|
@@ -12,7 +12,6 @@ import { onMessageCreated, onMessageUpdated, onMessageDeleted } from '../events'
|
|
|
12
12
|
*
|
|
13
13
|
* Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
|
|
14
14
|
*
|
|
15
|
-
* @param client the client to observe the message with
|
|
16
15
|
* @param channelId the ID of the channel where to observe the messages
|
|
17
16
|
* @param callback the function to call when new data are available
|
|
18
17
|
* @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { disableCache, enableCache, pushToCache } from '~/cache/api';
|
|
2
|
+
import { ENABLE_CACHE_MESSAGE } from '~/utils/constants';
|
|
3
|
+
|
|
4
|
+
import { liveMessages } from '../liveMessages';
|
|
5
|
+
|
|
6
|
+
import { client, messages } from '~/utils/tests';
|
|
7
|
+
import { queryMessages } from '~/message/api';
|
|
8
|
+
|
|
9
|
+
describe('liveMessages', () => {
|
|
10
|
+
beforeEach(enableCache);
|
|
11
|
+
afterEach(disableCache);
|
|
12
|
+
|
|
13
|
+
test('it should show message if cache not enabled', () => {
|
|
14
|
+
/*
|
|
15
|
+
* NOTE: at the time of writing this test cache is diabled by default, but
|
|
16
|
+
* there is a proposal to enable cache by default. So I'm disabling cache
|
|
17
|
+
* here to ensure cache is disabled
|
|
18
|
+
*/
|
|
19
|
+
disableCache();
|
|
20
|
+
|
|
21
|
+
const callback = jest.fn();
|
|
22
|
+
jest.spyOn(global.console, 'log');
|
|
23
|
+
client.http.get = jest.fn();
|
|
24
|
+
|
|
25
|
+
// call liveMessages check if mocked console get's called with the correct
|
|
26
|
+
// message
|
|
27
|
+
client.use();
|
|
28
|
+
liveMessages({ channelId: messages.channelId }, callback);
|
|
29
|
+
|
|
30
|
+
expect(console.log).toBeCalledWith(ENABLE_CACHE_MESSAGE);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('it should return message collection', async () => {
|
|
34
|
+
const returnValue = [{ messageId: messages.page1[0] }];
|
|
35
|
+
|
|
36
|
+
// mock response of queryMessages and validate
|
|
37
|
+
const callback = jest.fn();
|
|
38
|
+
client.http.get = jest.fn().mockResolvedValue({
|
|
39
|
+
data: {
|
|
40
|
+
messages: returnValue,
|
|
41
|
+
paging: {},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
liveMessages({ channelId: messages.channelId }, callback);
|
|
46
|
+
|
|
47
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
48
|
+
|
|
49
|
+
// check if cache data returned (should be empty)
|
|
50
|
+
expect(callback).toHaveBeenCalledWith(
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
data: [],
|
|
53
|
+
error: undefined,
|
|
54
|
+
loading: true,
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
await expect(queryMessages({ channelId: messages.channelId })).resolves.toBeTruthy();
|
|
59
|
+
|
|
60
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
61
|
+
|
|
62
|
+
expect(callback).toHaveBeenCalledWith(
|
|
63
|
+
expect.objectContaining({
|
|
64
|
+
data: returnValue,
|
|
65
|
+
error: undefined,
|
|
66
|
+
loading: false,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('it should filter deleted messages based on param', async () => {
|
|
72
|
+
// mock response of queryMessages with a deleted message
|
|
73
|
+
const returnValue = [
|
|
74
|
+
{ messageId: messages.page1[0], isDeleted: true },
|
|
75
|
+
{ messageId: messages.page1[1], isDeleted: false },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// mock response of queryMessages and validate
|
|
79
|
+
const callback = jest.fn();
|
|
80
|
+
client.http.get = jest.fn().mockResolvedValue({
|
|
81
|
+
data: {
|
|
82
|
+
messages: returnValue,
|
|
83
|
+
paging: {},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
liveMessages({ channelId: messages.channelId, isDeleted: false }, callback);
|
|
88
|
+
|
|
89
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
90
|
+
|
|
91
|
+
// check if cache data returned (should be empty)
|
|
92
|
+
expect(callback).toHaveBeenCalledWith(
|
|
93
|
+
expect.objectContaining({
|
|
94
|
+
data: [],
|
|
95
|
+
error: undefined,
|
|
96
|
+
loading: true,
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await expect(queryMessages({ channelId: messages.channelId })).resolves.toBeTruthy();
|
|
101
|
+
|
|
102
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
103
|
+
|
|
104
|
+
expect(callback).toHaveBeenCalledWith(
|
|
105
|
+
expect.objectContaining({
|
|
106
|
+
data: [returnValue[1]],
|
|
107
|
+
error: undefined,
|
|
108
|
+
loading: false,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('it should return method to fetch next page', async () => {
|
|
114
|
+
// mock nextPage()
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('it should add new message to collection onCreate', () => {
|
|
118
|
+
// new message should be added to top of collection
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('it should update message in collection onUpdate', () => {
|
|
122
|
+
// updated message should be reflected in collection
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('it should delete message from collection onDelete', () => {
|
|
126
|
+
// should remove message from collection on delete
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('it should get messages from cache if present', () => {
|
|
130
|
+
// push message with Id in collection response to cache
|
|
131
|
+
// get request to get that messageId should not be called
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
COLLECTION_DEFAULT_CACHING_POLICY,
|
|
16
16
|
COLLECTION_DEFAULT_PAGINATION_LIMIT,
|
|
17
|
+
ENABLE_CACHE_MESSAGE,
|
|
17
18
|
} from '~/utils/constants';
|
|
18
19
|
|
|
19
20
|
import {
|
|
@@ -57,7 +58,7 @@ export const livePosts = (
|
|
|
57
58
|
const { log, cache } = getActiveClient();
|
|
58
59
|
|
|
59
60
|
if (!cache) {
|
|
60
|
-
console.log(
|
|
61
|
+
console.log(ENABLE_CACHE_MESSAGE);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
const timestamp = Date.now();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ASCApiError } from '~/core/errors';
|
|
2
|
-
import { client,
|
|
2
|
+
import { client, posts } from '~/utils/tests';
|
|
3
3
|
import { pushToCache, enableCache, disableCache } from '~/cache/api';
|
|
4
4
|
|
|
5
5
|
import { getPost } from '~/post/api';
|
|
6
6
|
|
|
7
7
|
describe('post/api', () => {
|
|
8
|
-
const post = { postId:
|
|
9
|
-
const post2 = { postId:
|
|
8
|
+
const post = { postId: posts.page1[0] };
|
|
9
|
+
const post2 = { postId: posts.page1[1] };
|
|
10
10
|
|
|
11
11
|
describe('getPost.locally', () => {
|
|
12
12
|
test('should return undefined if cache is turned off', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { queryPosts } from '~/post/api';
|
|
2
|
-
import { client,
|
|
2
|
+
import { client, posts } from '~/utils/tests';
|
|
3
3
|
|
|
4
4
|
describe('post/api', () => {
|
|
5
5
|
test('should return a post query', async () => {
|
|
6
|
-
const returnValue = [{ postId:
|
|
6
|
+
const returnValue = [{ postId: posts.page1[0] }];
|
|
7
7
|
|
|
8
8
|
client.http.get = jest.fn().mockResolvedValue({
|
|
9
9
|
data: {
|
|
@@ -13,8 +13,8 @@ describe('post/api', () => {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const data = await queryPosts({
|
|
16
|
-
targetId:
|
|
17
|
-
targetType:
|
|
16
|
+
targetId: posts.targetId,
|
|
17
|
+
targetType: posts.targetType,
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
expect(data).toBeDefined();
|
package/src/utils/constants.ts
CHANGED