@amityco/ts-sdk 0.0.1-beta.13 → 0.0.1-beta.16

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.
Files changed (190) hide show
  1. package/README.md +3 -3
  2. package/dist/@types/core/cache.d.ts +2 -2
  3. package/dist/@types/core/cache.d.ts.map +1 -1
  4. package/dist/@types/core/events.d.ts +12 -3
  5. package/dist/@types/core/events.d.ts.map +1 -1
  6. package/dist/@types/core/model.d.ts +4 -0
  7. package/dist/@types/core/model.d.ts.map +1 -1
  8. package/dist/@types/core/paging.d.ts +22 -41
  9. package/dist/@types/core/paging.d.ts.map +1 -1
  10. package/dist/@types/core/payload.d.ts +10 -0
  11. package/dist/@types/core/payload.d.ts.map +1 -1
  12. package/dist/@types/core/query.d.ts +13 -4
  13. package/dist/@types/core/query.d.ts.map +1 -1
  14. package/dist/@types/domains/client.d.ts +0 -1
  15. package/dist/@types/domains/client.d.ts.map +1 -1
  16. package/dist/@types/domains/comment.d.ts +1 -2
  17. package/dist/@types/domains/comment.d.ts.map +1 -1
  18. package/dist/@types/domains/follow.d.ts +20 -0
  19. package/dist/@types/domains/follow.d.ts.map +1 -0
  20. package/dist/@types/domains/mention.d.ts +23 -0
  21. package/dist/@types/domains/mention.d.ts.map +1 -0
  22. package/dist/@types/domains/message.d.ts +1 -1
  23. package/dist/@types/domains/message.d.ts.map +1 -1
  24. package/dist/@types/domains/partials.d.ts +0 -10
  25. package/dist/@types/domains/partials.d.ts.map +1 -1
  26. package/dist/@types/domains/post.d.ts +1 -2
  27. package/dist/@types/domains/post.d.ts.map +1 -1
  28. package/dist/@types/index.d.ts +2 -0
  29. package/dist/@types/index.d.ts.map +1 -1
  30. package/dist/category/api/queryCategories.d.ts +2 -2
  31. package/dist/channel/api/createChannel.d.ts +1 -1
  32. package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
  33. package/dist/client/api/activeUser.d.ts +6 -0
  34. package/dist/client/api/activeUser.d.ts.map +1 -0
  35. package/dist/client/api/connectClient.d.ts.map +1 -1
  36. package/dist/client/api/createClient.d.ts +3 -3
  37. package/dist/client/api/createClient.d.ts.map +1 -1
  38. package/dist/client/utils/endpoints.d.ts +12 -0
  39. package/dist/client/utils/endpoints.d.ts.map +1 -0
  40. package/dist/community/api/getRecommendedCommunities.d.ts +32 -0
  41. package/dist/community/api/getRecommendedCommunities.d.ts.map +1 -0
  42. package/dist/community/api/getTopTrendingCommunities.d.ts +32 -0
  43. package/dist/community/api/getTopTrendingCommunities.d.ts.map +1 -0
  44. package/dist/community/api/index.d.ts +2 -0
  45. package/dist/community/api/index.d.ts.map +1 -1
  46. package/dist/core/debug.d.ts.map +1 -1
  47. package/dist/core/device.d.ts.map +1 -1
  48. package/dist/core/events.d.ts +3 -3
  49. package/dist/core/events.d.ts.map +1 -1
  50. package/dist/core/microtasks.d.ts +13 -0
  51. package/dist/core/microtasks.d.ts.map +1 -0
  52. package/dist/core/model/idResolvers.d.ts.map +1 -1
  53. package/dist/core/model/identifyModel.d.ts.map +1 -1
  54. package/dist/core/model/index.d.ts.map +1 -1
  55. package/dist/core/query/paging.d.ts +5 -16
  56. package/dist/core/query/paging.d.ts.map +1 -1
  57. package/dist/core/query/query.d.ts +1 -1
  58. package/dist/core/query/query.d.ts.map +1 -1
  59. package/dist/core/subscription.d.ts +2 -0
  60. package/dist/core/subscription.d.ts.map +1 -1
  61. package/dist/feed/api/queryGlobalFeed.d.ts +22 -4
  62. package/dist/feed/api/queryGlobalFeed.d.ts.map +1 -1
  63. package/dist/follow/api/acceptFollower.d.ts +16 -0
  64. package/dist/follow/api/acceptFollower.d.ts.map +1 -0
  65. package/dist/follow/api/declineFollower.d.ts +16 -0
  66. package/dist/follow/api/declineFollower.d.ts.map +1 -0
  67. package/dist/follow/api/follow.d.ts +16 -0
  68. package/dist/follow/api/follow.d.ts.map +1 -0
  69. package/dist/follow/api/getFollowInfo.d.ts +32 -0
  70. package/dist/follow/api/getFollowInfo.d.ts.map +1 -0
  71. package/dist/follow/api/index.d.ts +8 -0
  72. package/dist/follow/api/index.d.ts.map +1 -0
  73. package/dist/follow/api/queryFollowers.d.ts +36 -0
  74. package/dist/follow/api/queryFollowers.d.ts.map +1 -0
  75. package/dist/follow/api/queryFollowings.d.ts +36 -0
  76. package/dist/follow/api/queryFollowings.d.ts.map +1 -0
  77. package/dist/follow/api/unfollow.d.ts +16 -0
  78. package/dist/follow/api/unfollow.d.ts.map +1 -0
  79. package/dist/follow/api/utils.d.ts +9 -0
  80. package/dist/follow/api/utils.d.ts.map +1 -0
  81. package/dist/follow/events/index.d.ts +9 -0
  82. package/dist/follow/events/index.d.ts.map +1 -0
  83. package/dist/follow/events/onFollowInfoUpdated.d.ts +17 -0
  84. package/dist/follow/events/onFollowInfoUpdated.d.ts.map +1 -0
  85. package/dist/follow/events/onFollowRequestAccepted.d.ts +17 -0
  86. package/dist/follow/events/onFollowRequestAccepted.d.ts.map +1 -0
  87. package/dist/follow/events/onFollowRequestCanceled.d.ts +17 -0
  88. package/dist/follow/events/onFollowRequestCanceled.d.ts.map +1 -0
  89. package/dist/follow/events/onFollowRequestDeclined.d.ts +17 -0
  90. package/dist/follow/events/onFollowRequestDeclined.d.ts.map +1 -0
  91. package/dist/follow/events/onFollowerDeleted.d.ts +17 -0
  92. package/dist/follow/events/onFollowerDeleted.d.ts.map +1 -0
  93. package/dist/follow/events/onFollowerRequested.d.ts +17 -0
  94. package/dist/follow/events/onFollowerRequested.d.ts.map +1 -0
  95. package/dist/follow/events/onUserFollowed.d.ts +17 -0
  96. package/dist/follow/events/onUserFollowed.d.ts.map +1 -0
  97. package/dist/follow/events/onUserUnfollowed.d.ts +17 -0
  98. package/dist/follow/events/onUserUnfollowed.d.ts.map +1 -0
  99. package/dist/follow/events/utils.d.ts +2 -0
  100. package/dist/follow/events/utils.d.ts.map +1 -0
  101. package/dist/follow/observers/index.d.ts +4 -0
  102. package/dist/follow/observers/index.d.ts.map +1 -0
  103. package/dist/follow/observers/observeFollowInfo.d.ts +18 -0
  104. package/dist/follow/observers/observeFollowInfo.d.ts.map +1 -0
  105. package/dist/follow/observers/observeFollowers.d.ts +18 -0
  106. package/dist/follow/observers/observeFollowers.d.ts.map +1 -0
  107. package/dist/follow/observers/observeFollowings.d.ts +18 -0
  108. package/dist/follow/observers/observeFollowings.d.ts.map +1 -0
  109. package/dist/index.cjs.js +845 -74
  110. package/dist/index.d.ts +4 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.esm.js +823 -74
  113. package/dist/index.umd.js +5 -5
  114. package/dist/message/api/createMessage.d.ts +2 -2
  115. package/dist/message/api/createMessage.d.ts.map +1 -1
  116. package/dist/message/api/updateMessage.d.ts +2 -2
  117. package/dist/message/api/updateMessage.d.ts.map +1 -1
  118. package/dist/post/api/queryPosts.d.ts +3 -3
  119. package/dist/post/api/queryPosts.d.ts.map +1 -1
  120. package/dist/utils/env.d.ts +5 -0
  121. package/dist/utils/env.d.ts.map +1 -0
  122. package/dist/version.d.ts.map +1 -1
  123. package/package.json +1 -1
  124. package/src/@types/core/cache.ts +2 -2
  125. package/src/@types/core/events.ts +12 -1
  126. package/src/@types/core/model.ts +6 -0
  127. package/src/@types/core/paging.ts +31 -46
  128. package/src/@types/core/payload.ts +14 -0
  129. package/src/@types/core/query.ts +11 -3
  130. package/src/@types/domains/client.ts +0 -1
  131. package/src/@types/domains/comment.ts +1 -3
  132. package/src/@types/domains/follow.ts +25 -0
  133. package/src/@types/domains/mention.ts +26 -0
  134. package/src/@types/domains/message.ts +1 -0
  135. package/src/@types/domains/partials.ts +0 -8
  136. package/src/@types/domains/post.ts +1 -3
  137. package/src/@types/index.ts +3 -0
  138. package/src/channel/api/createChannel.ts +1 -1
  139. package/src/channel/api/queryChannelMembers.ts +2 -1
  140. package/src/client/api/activeUser.ts +26 -0
  141. package/src/client/api/connectClient.ts +3 -1
  142. package/src/client/api/createClient.ts +7 -7
  143. package/src/client/utils/endpoints.ts +14 -0
  144. package/src/community/api/getRecommendedCommunities.ts +86 -0
  145. package/src/community/api/getTopTrendingCommunities.ts +86 -0
  146. package/src/community/api/index.ts +3 -0
  147. package/src/community/api/queryCommunityMembers.ts +1 -1
  148. package/src/core/debug.ts +3 -1
  149. package/src/core/device.ts +3 -2
  150. package/src/core/events.ts +3 -1
  151. package/src/core/microtasks.ts +27 -0
  152. package/src/core/model/idResolvers.ts +3 -0
  153. package/src/core/model/identifyModel.ts +2 -0
  154. package/src/core/model/index.ts +3 -0
  155. package/src/core/query/paging.ts +16 -51
  156. package/src/core/query/query.ts +5 -5
  157. package/src/core/subscription.ts +21 -3
  158. package/src/feed/api/queryGlobalFeed.ts +58 -5
  159. package/src/follow/api/acceptFollower.ts +32 -0
  160. package/src/follow/api/declineFollower.ts +32 -0
  161. package/src/follow/api/follow.ts +40 -0
  162. package/src/follow/api/getFollowInfo.ts +75 -0
  163. package/src/follow/api/index.ts +8 -0
  164. package/src/follow/api/queryFollowers.ts +42 -0
  165. package/src/follow/api/queryFollowings.ts +42 -0
  166. package/src/follow/api/unfollow.ts +32 -0
  167. package/src/follow/api/utils.ts +77 -0
  168. package/src/follow/events/index.ts +10 -0
  169. package/src/follow/events/onFollowInfoUpdated.ts +43 -0
  170. package/src/follow/events/onFollowRequestAccepted.ts +19 -0
  171. package/src/follow/events/onFollowRequestCanceled.ts +19 -0
  172. package/src/follow/events/onFollowRequestDeclined.ts +19 -0
  173. package/src/follow/events/onFollowerDeleted.ts +19 -0
  174. package/src/follow/events/onFollowerRequested.ts +19 -0
  175. package/src/follow/events/onUserFollowed.ts +19 -0
  176. package/src/follow/events/onUserUnfollowed.ts +19 -0
  177. package/src/follow/events/utils.ts +38 -0
  178. package/src/follow/observers/index.ts +3 -0
  179. package/src/follow/observers/observeFollowInfo.ts +68 -0
  180. package/src/follow/observers/observeFollowers.ts +78 -0
  181. package/src/follow/observers/observeFollowings.ts +78 -0
  182. package/src/index.ts +5 -0
  183. package/src/message/api/createMessage.ts +2 -2
  184. package/src/message/api/updateMessage.ts +2 -2
  185. package/src/post/api/queryPosts.ts +5 -5
  186. package/src/utils/env.ts +7 -0
  187. package/src/version.ts +3 -1
  188. package/dist/core/network.d.ts +0 -11
  189. package/dist/core/network.d.ts.map +0 -1
  190. package/src/core/network.ts +0 -14
@@ -0,0 +1,86 @@
1
+ import { getActiveClient } from '~/client/api'
2
+
3
+ import { getResolver } from '~/core/model'
4
+ import { pullFromCache, pushToCache } from '~/cache/api'
5
+ import { ingestInCache } from '~/cache/api/ingestInCache'
6
+
7
+ /**
8
+ * ```js
9
+ * import { getTopTrendingCommunities } from '@amityco/ts-sdk'
10
+ * const trendingCommunities = await getTopTrendingCommunities()
11
+ * ```
12
+ *
13
+ * Gets a list of top trending {@link Amity.Community} objects
14
+ *
15
+ * @param query The query parameters
16
+ * @returns A list of {@link Amity.Community} objects
17
+ *
18
+ * @category Community API
19
+ * @async
20
+ */
21
+ export const getTopTrendingCommunities = async (
22
+ query?: Partial<Amity.PageLimit>
23
+ ): Promise<Amity.Cached<Amity.Community[]>> => {
24
+ const client = getActiveClient()
25
+ client.log('channel/getTopTrendingCommunities', query)
26
+
27
+ const { limit = 5 } = query ?? {}
28
+
29
+ // API-FIX: backend doesnt answer Amity.Response
30
+ // const { data } = await client.http.get<Amity.Response<CommunityPayload>>(
31
+ const { data } = await client.http.get<Amity.CommunityPayload>(
32
+ `/api/v3/communities/top-trending`, { params: { options: { limit }}}
33
+ )
34
+
35
+ const { communities } = data
36
+
37
+ const cachedAt = client.cache && Date.now()
38
+
39
+ if (client.cache) {
40
+ ingestInCache(data, { cachedAt })
41
+
42
+ const cacheKey = ['community', 'top-trending', { params: { options: { limit }} }]
43
+ pushToCache(cacheKey, { communities: communities.map(getResolver('community')) })
44
+ }
45
+
46
+ return { data: communities, cachedAt }
47
+ }
48
+
49
+ /**
50
+ * ```js
51
+ * import { getTopTrendingCommunities } from '@amityco/ts-sdk'
52
+ * const communities = getTopTrendingCommunities.locally()
53
+ * ```
54
+ *
55
+ * Gets a list of top trending {@link Amity.Community} objects from cache
56
+ *
57
+ * @param query The query parameters
58
+ * @returns communities
59
+ *
60
+ * @category Community API
61
+ */
62
+ getTopTrendingCommunities.locally = (
63
+ query: Parameters<typeof getTopTrendingCommunities>[0],
64
+ ): Amity.Cached<Amity.Community[]> | undefined => {
65
+ const client = getActiveClient()
66
+ client.log('community/getTopTrendingCommunities.locally', query)
67
+
68
+ if (!client.cache) return
69
+
70
+ const { limit = 5 } = query ?? {}
71
+
72
+ const queryKey = ['community', 'top-trending', { params: { options: { limit }} }];
73
+ const { data, cachedAt } = pullFromCache<{communities: Pick<Amity.Community, 'communityId'>[]}>(queryKey) ?? {}
74
+
75
+ if (!data?.communities.length)
76
+ return
77
+
78
+ const communities: Amity.Community[] = data.communities
79
+ .map(communityId => pullFromCache<Amity.Community>(['community', 'get', communityId])!)
80
+ .filter(Boolean)
81
+ .map(({ data }) => data)
82
+
83
+ return communities.length === data?.communities?.length
84
+ ? { data: communities, cachedAt }
85
+ : undefined
86
+ }
@@ -15,3 +15,6 @@ export * from './removeCommunityMembers'
15
15
 
16
16
  export * from './addCommunityMembersRole'
17
17
  export * from './removeCommunityMembersRole'
18
+
19
+ export * from './getTopTrendingCommunities'
20
+ export * from './getRecommendedCommunities'
@@ -51,7 +51,7 @@ export const queryCommunityMembers = async (query: {
51
51
  const nextPage = toPage(paging.next)
52
52
  const prevPage = toPage(paging.previous)
53
53
 
54
- return { data: communityUsers, nextPage, prevPage }
54
+ return { data: communityUsers, cachedAt, nextPage, prevPage }
55
55
  }
56
56
 
57
57
  /**
package/src/core/debug.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import debug from 'debug'
2
2
 
3
+ import { isProduction } from '~/utils/env'
4
+
3
5
  /**
4
6
  * Creates a pre-configured logger instance
5
7
  *
@@ -12,7 +14,7 @@ import debug from 'debug'
12
14
  export const createLogger = (session?: string) => {
13
15
  const active = (
14
16
  `${session?.trim()}`.length > 0
15
- && process?.env?.NODE_ENV !== 'production'
17
+ && !isProduction
16
18
  )
17
19
 
18
20
  const logger = debug(session!)
@@ -1,5 +1,6 @@
1
1
  // TODO: find a way to uniquely identify the device
2
2
  // import { getStorage } from './storage'
3
+ import { safeProcess } from '~/utils/env'
3
4
 
4
5
  /** @hidden */
5
6
  const VERSION = '11.0.0'
@@ -16,8 +17,8 @@ export const getDeviceId = () => {
16
17
 
17
18
  /** @hidden */
18
19
  export const getDeviceModel = () => {
19
- if (process?.versions?.node)
20
- return process.versions.node
20
+ if (safeProcess.versions?.node)
21
+ return safeProcess.versions.node
21
22
 
22
23
  if (navigator)
23
24
  return `${navigator.product.toLowerCase()}#${navigator.userAgent ?? 'unknown_agent'}`
@@ -2,6 +2,8 @@ import mitt from 'mitt'
2
2
 
3
3
  import { getActiveClient } from '~/client/api/activeClient'
4
4
 
5
+ import { scheduleTask } from './microtasks'
6
+
5
7
  // need to sync up the list
6
8
  const WS_EVENTS = [
7
9
  'disconnected',
@@ -134,5 +136,5 @@ export const fireEvent = <T extends keyof Amity.Events>(
134
136
  payload: Amity.Events[T],
135
137
  ) => {
136
138
  const { emitter } = getActiveClient()
137
- setTimeout(() => { emitter.emit(event, payload) }, 0)
139
+ scheduleTask(() => { emitter.emit(event, payload) })
138
140
  }
@@ -0,0 +1,27 @@
1
+ let tasks: Function[] = []
2
+ let timer: NodeJS.Timeout
3
+
4
+ /**
5
+ * Add a function to run just before the next tick
6
+ *
7
+ * @param task function to schedule for later execution
8
+ */
9
+ export const scheduleTask = (task: Function) => {
10
+ clearTimeout(timer)
11
+ tasks.push(task)
12
+
13
+ timer = setTimeout(() => {
14
+ tasks.forEach(fn => fn())
15
+ tasks = []
16
+ }, 0)
17
+ }
18
+
19
+ /**
20
+ * Cancel the scheduling operation
21
+ *
22
+ * @param task function to be removed from later execution
23
+ */
24
+ export const cancelTask = (task: Function) => {
25
+ tasks = tasks.filter(fn => fn !== task)
26
+ if (!tasks.length) clearTimeout(timer)
27
+ }
@@ -24,6 +24,9 @@ const idResolvers: Resolvers = {
24
24
  comment: ({ commentId }) => commentId,
25
25
 
26
26
  stream: ({ streamId }) => streamId,
27
+
28
+ follow: ({ from, to }) => `${from}#${to}`,
29
+ followCount: ({ userId }) => userId,
27
30
  }
28
31
 
29
32
  /**
@@ -31,6 +31,8 @@ const CRITERIAS: Criterias = {
31
31
  post: ['postId', 'feedId'],
32
32
  comment: ['commentId', 'referenceId'],
33
33
  stream: ['streamId'],
34
+ follow: ['from', 'to'],
35
+ followCount: ['userId', 'followerCount']
34
36
  }
35
37
 
36
38
  /** @hidden */
@@ -20,4 +20,7 @@ export * from './identifyModel'
20
20
  postChildren: 'post',
21
21
  comments: 'comment',
22
22
  videoStreamings: 'stream',
23
+
24
+ follows: 'follow',
25
+ followCounts: 'followCount'
23
26
  }
@@ -1,4 +1,3 @@
1
- // @ts-ignore
2
1
  import { encode, decode } from 'js-base64'
3
2
 
4
3
  /**
@@ -22,7 +21,7 @@ export const isSkipLimit = (json: Object): json is Amity.SkipLimitPagination =>
22
21
  * @hidden
23
22
  */
24
23
  export const isAfterBefore = (json: Object): json is Amity.AfterBeforePagination => (
25
- ['after', 'before', 'first', 'last'].some(prop => prop in json)
24
+ ['after', 'before', 'first', 'last', 'limit'].some(prop => prop in json)
26
25
  )
27
26
 
28
27
  /**
@@ -34,7 +33,7 @@ export const isAfterBefore = (json: Object): json is Amity.AfterBeforePagination
34
33
  * @hidden
35
34
  */
36
35
  export const isAfterBeforeRaw = (json: Object): json is Amity.AfterBeforeRawPagination => (
37
- ['after', 'before'].some(prop => prop in json)
36
+ 'limit' in json
38
37
  )
39
38
 
40
39
  /**
@@ -45,7 +44,7 @@ export const isAfterBeforeRaw = (json: Object): json is Amity.AfterBeforeRawPagi
45
44
  *
46
45
  * @hidden
47
46
  */
48
- export const isPaged = <T extends unknown>(payload: any): payload is Amity.Paged<T> => {
47
+ export const isPaged = <T extends unknown>(payload: any): payload is Amity.Paged<T, any> => {
49
48
  return (
50
49
  payload?.hasOwnProperty('data')
51
50
  && payload?.hasOwnProperty('nextPage')
@@ -63,9 +62,8 @@ export const isPaged = <T extends unknown>(payload: any): payload is Amity.Paged
63
62
  * @hidden
64
63
  */
65
64
  export const toToken = (
66
- paging: Amity.Page | undefined,
67
- style: 'skiplimit' | 'afterbefore' | 'afterbeforeraw',
68
-
65
+ paging: Amity.Page | Amity.PageRaw | undefined,
66
+ style: Amity.PaginationStyles,
69
67
  ): Amity.Token | undefined => {
70
68
  if (!paging || !Object.keys(paging).length)
71
69
  return
@@ -79,7 +77,7 @@ export const toToken = (
79
77
  limit: paging.limit,
80
78
  }
81
79
  }
82
- else if (style === 'afterbefore') {
80
+ else if (style === 'afterbefore' && isAfterBefore(paging)) {
83
81
  /*
84
82
  Caution: this testing style is only valid because
85
83
  our backend expects nothing else than a number as
@@ -114,6 +112,7 @@ export const toToken = (
114
112
  return encode(JSON.stringify(payload))
115
113
  }
116
114
 
115
+
117
116
  /**
118
117
  * Converts a b64 string token into a paging object
119
118
  *
@@ -122,8 +121,8 @@ export const toToken = (
122
121
  *
123
122
  * @hidden
124
123
  */
125
- export const toPage = (token?: Amity.Token): Amity.Page | null => {
126
- if (!token) return null
124
+ export const toPage = (token?: Amity.Token): Amity.Page | undefined => {
125
+ if (!token) return undefined
127
126
 
128
127
  // don't try catch, let it throw
129
128
  const json = JSON.parse(decode(token))
@@ -132,22 +131,22 @@ export const toPage = (token?: Amity.Token): Amity.Page | null => {
132
131
  return {
133
132
  after: json.skip,
134
133
  limit: json.limit!,
135
- } as Amity.NextPage
134
+ } as Amity.NextPage<number>
136
135
  } else if (isAfterBefore(json)) {
137
136
  if ('before' in json) {
138
137
  return {
139
138
  before: json.before,
140
139
  limit: json.last,
141
- } as Amity.PrevPage
140
+ } as Amity.PrevPage<number>
142
141
  } else if ('after' in json) {
143
142
  return {
144
143
  after: json.after,
145
144
  limit: json.first,
146
- } as Amity.NextPage
145
+ } as Amity.NextPage<number>
147
146
  }
148
147
  }
149
148
 
150
- return null
149
+ return undefined
151
150
  }
152
151
 
153
152
  /**
@@ -158,8 +157,8 @@ export const toPage = (token?: Amity.Token): Amity.Page | null => {
158
157
  *
159
158
  * @hidden
160
159
  */
161
- export const toPageRaw = (token?: Amity.Token): Amity.PageRaw | null => {
162
- if (!token) return null
160
+ export const toPageRaw = (token?: Amity.Token): Amity.PageRaw | undefined => {
161
+ if (!token) return undefined
163
162
 
164
163
  // don't try catch, let it throw
165
164
  const json = JSON.parse(decode(token))
@@ -168,39 +167,5 @@ export const toPageRaw = (token?: Amity.Token): Amity.PageRaw | null => {
168
167
  return json as Amity.PageRaw
169
168
  }
170
169
 
171
- return null
172
- }
173
-
174
- /**
175
- * Slices a collection according to its paging
176
- *
177
- * @param type the type of collection (for unique id identification with after/before)
178
- * @param collection the collection to slice
179
- * @param paging the paging object
180
- * @returns the sliced collection according to the paging definition
181
- *
182
- * @hidden
183
- */
184
- export const sliceByPaging = <K extends Amity.Domain, T extends Amity.Model[K]>(
185
- type: K, collection: T[], page?: Amity.Page
186
- ) => {
187
- console.log('slice by paging', page)
188
- if (!page) return collection.slice()
189
-
190
- let start: number | undefined = 0
191
- let end: number | undefined = undefined
192
-
193
- // next page behavior
194
- if (page.after) {
195
- start = page.after
196
- end = start + page.limit
197
- // prev page behavior
198
- } else if (page.before) {
199
- end = page.before + 1
200
- start = end > page.limit ? end - page.limit : 0
201
- }
202
-
203
- const paged = collection.slice(start, end)
204
-
205
- return paged.length === page.limit ? paged : undefined
170
+ return undefined
206
171
  }
@@ -170,7 +170,7 @@ const isEmpty = (local: any) => {
170
170
  */
171
171
  export const runQuery = <Args extends any[], Returned extends any>(
172
172
  { func, args }: Amity.Query<Args, Returned>,
173
- callback?: (args: Amity.Snapshot<Returned | undefined>) => void,
173
+ callback?: (args: Amity.Snapshot<Returned>) => void,
174
174
  options: Amity.QueryOptions = queryOptions('cache_then_server')
175
175
  ) => {
176
176
  let local: Returned | undefined
@@ -193,7 +193,7 @@ export const runQuery = <Args extends any[], Returned extends any>(
193
193
 
194
194
  const shouldAbort = isCachable(local) && isFresh(local, options.lifeSpan)
195
195
 
196
- callback?.(createSnapshot(local , {
196
+ callback?.(createSnapshot<Returned>(local , {
197
197
  origin: 'local',
198
198
  loading: !(isFetcher(func) && shouldAbort)
199
199
  }))
@@ -201,7 +201,7 @@ export const runQuery = <Args extends any[], Returned extends any>(
201
201
  if (shouldAbort)
202
202
  return
203
203
  } else {
204
- callback?.(createSnapshot(undefined, {
204
+ callback?.(createSnapshot<Returned>(undefined, {
205
205
  origin: 'local',
206
206
  loading: true
207
207
  }))
@@ -213,7 +213,7 @@ export const runQuery = <Args extends any[], Returned extends any>(
213
213
  // isLocal(local) && syncInCache(local, fresh)
214
214
 
215
215
  callback?.(
216
- createSnapshot(fresh, {
216
+ createSnapshot<Returned>(fresh, {
217
217
  origin: 'server',
218
218
  loading: false,
219
219
  })
@@ -221,7 +221,7 @@ export const runQuery = <Args extends any[], Returned extends any>(
221
221
  })
222
222
  .catch(error => {
223
223
  callback?.(
224
- createSnapshot(undefined, {
224
+ createSnapshot<Returned>(undefined, {
225
225
  origin: 'server',
226
226
  loading: false,
227
227
  error,
@@ -1,6 +1,7 @@
1
1
  import { getActiveClient } from '~/client/api'
2
2
  import { ASCError } from '~/core/errors'
3
3
  import { proxyMqttEvents } from '~/core/events'
4
+ import { getActiveUser } from '~/client/api/activeUser'
4
5
 
5
6
  export enum SubscriptionLevels {
6
7
  COMMUNITY = 'community',
@@ -54,6 +55,22 @@ export const getCommentTopic = ({ path }: Amity.Subscribable): string => {
54
55
  return path;
55
56
  };
56
57
 
58
+ export const getMyFollowersTopic = (): string => {
59
+ const user = getActiveUser()
60
+
61
+ const [networkId] = user.path.split('/user/');
62
+
63
+ return `${networkId}/membership/${user._id}/+/+`;
64
+ }
65
+
66
+ export const getMyFollowingsTopic = (): string => {
67
+ const user = getActiveUser()
68
+
69
+ const [networkId] = user.path.split('/user/');
70
+
71
+ return `${networkId}/membership/+/${user._id}/+`;
72
+ }
73
+
57
74
  let mqttAccessToken: string;
58
75
  let mqttUserId: string;
59
76
 
@@ -61,11 +78,12 @@ export function subscribeTopic(
61
78
  topic: string,
62
79
  callback?: Amity.Listener<ASCError | void>
63
80
  ): Amity.Unsubscriber {
64
- const { mqtt, emitter, accessToken, _internalUserId } = getActiveClient()
81
+ const { mqtt, emitter, accessToken } = getActiveClient()
82
+ const user = getActiveUser();
65
83
 
66
- if (mqttAccessToken !== accessToken || mqttUserId !== _internalUserId) {
84
+ if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
67
85
  mqttAccessToken = accessToken!
68
- mqttUserId = _internalUserId!
86
+ mqttUserId = user._id!
69
87
 
70
88
  mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId })
71
89
  proxyMqttEvents(mqtt, emitter)
@@ -2,6 +2,8 @@ import { getActiveClient } from '~/client/api/activeClient'
2
2
 
3
3
  import { toPage, toToken } from '~/core/query'
4
4
  import { ingestInCache } from '~/cache/api/ingestInCache'
5
+ import { pullFromCache, pushToCache } from '~/cache/api'
6
+ import { getResolver } from '~/core/model'
5
7
 
6
8
  /**
7
9
  * ```js
@@ -19,15 +21,16 @@ import { ingestInCache } from '~/cache/api/ingestInCache'
19
21
  **/
20
22
  export const queryGlobalFeed = async (query?: {
21
23
  dataTypes?: ('video' | 'image' | 'file' | 'liveStream')[]
24
+ useCustomRanking?: boolean
22
25
  page?: Amity.Page
23
- }): Promise<Amity.Paged<Amity.Post>> => {
26
+ }): Promise<Amity.Cached<Amity.Paged<Amity.Post>>> => {
24
27
  const client = getActiveClient()
25
28
  client.log('feed/queryGlobalFeed', query)
26
29
 
27
- const { page, ...params } = query ?? {}
30
+ const { useCustomRanking, page, ...params } = query ?? {}
28
31
 
29
32
  const { data } = await client.http.get<Amity.PostPayload & Amity.Pagination>(
30
- `/api/v4/me/global-feeds`, { params: {
33
+ useCustomRanking ? `/api/v5/me/global-feeds` : `/api/v4/me/global-feeds`, { params: {
31
34
  ...params,
32
35
  options: {
33
36
  token: toToken(page, 'skiplimit')
@@ -38,10 +41,60 @@ export const queryGlobalFeed = async (query?: {
38
41
  const { paging, ...payload } = data
39
42
  const { posts } = payload
40
43
 
41
- if (client.cache) ingestInCache(payload as Amity.PostPayload)
44
+ const cachedAt = client.cache && Date.now()
45
+
46
+ if (client.cache) {
47
+ ingestInCache(payload as Amity.PostPayload)
48
+
49
+ const cacheKey = ['globalFeed', 'query', { ...params, useCustomRanking, options: { ...page } } as Amity.Serializable]
50
+ pushToCache(cacheKey, { posts: posts.map(getResolver('post')), paging })
51
+ }
42
52
 
43
53
  const nextPage = toPage(paging.next)
44
54
  const prevPage = toPage(paging.previous)
45
55
 
46
- return { data: posts, prevPage, nextPage }
56
+ return { data: posts, cachedAt, prevPage, nextPage }
57
+ }
58
+
59
+ /**
60
+ * ```js
61
+ * import { queryGlobalFeed } from '@amityco/ts-sdk'
62
+ * const posts = await queryGlobalFeed.locally()
63
+ * ```
64
+ *
65
+ * Queries a paginable list of {@link Amity.Post} objects from cache
66
+ *
67
+ * @param query The query parameters
68
+ * @returns A page of {@link Amity.Post} objects
69
+ *
70
+ * @category Feed API
71
+ * @async
72
+ **/
73
+ queryGlobalFeed.locally = (
74
+ query: Parameters<typeof queryGlobalFeed>[0],
75
+ ): Amity.Cached<Amity.Paged<Amity.Post>> | undefined => {
76
+ const client = getActiveClient()
77
+ client.log('post/queryGlobalFeed.locally', query)
78
+
79
+ if (!client.cache) return
80
+
81
+ const { useCustomRanking, page, ...params } = query ?? {}
82
+
83
+ const queryKey = ['globalFeed', 'query', { ...params, useCustomRanking, options: { ...page }} as Amity.Serializable];
84
+ const { data, cachedAt } = pullFromCache<{ posts: string[] } & Amity.Pagination>(queryKey) ?? {}
85
+
86
+ if (!data?.posts.length)
87
+ return
88
+
89
+ const posts: Amity.Post[] = data.posts
90
+ .map(postId => pullFromCache<Amity.Post>(['post', 'get', postId])!)
91
+ .filter(Boolean)
92
+ .map(({ data }) => data)
93
+
94
+ const prevPage = toPage(data?.paging.previous)
95
+ const nextPage = toPage(data?.paging.next)
96
+
97
+ return posts.length === data?.posts?.length
98
+ ? { data: posts, cachedAt, prevPage, nextPage }
99
+ : undefined
47
100
  }
@@ -0,0 +1,32 @@
1
+ import { getActiveClient } from '~/client/api'
2
+ import { ingestInCache } from '~/cache/api/ingestInCache'
3
+ import { fireEvent } from '~/core/events'
4
+
5
+ /**
6
+ * ```js
7
+ * import { acceptFollower } from '@amityco/ts-sdk'
8
+ * await acceptFollower('foobar')
9
+ * ```
10
+ *
11
+ * Accept the follow request
12
+ *
13
+ * @param userId the ID of the {@link Amity.User} follower
14
+ * @returns A success boolean if the follow request was accepted
15
+ *
16
+ * @category Follow API
17
+ * @async
18
+ */
19
+ export const acceptFollower = async (userId: Amity.User['userId']): Promise<boolean> => {
20
+ const client = getActiveClient()
21
+ client.log('follow/acceptFollower', userId)
22
+
23
+ const { data } = await client.http.post<Amity.FollowStatusPayload>(`/api/v4/me/followers/${userId}`)
24
+
25
+ if (client.cache) {
26
+ ingestInCache(data)
27
+ }
28
+
29
+ fireEvent('follow.accepted', data.follows[0])
30
+
31
+ return true
32
+ }
@@ -0,0 +1,32 @@
1
+ import { getActiveClient } from '~/client/api'
2
+ import { ingestInCache } from '~/cache/api/ingestInCache'
3
+ import { fireEvent } from '~/core/events'
4
+
5
+ /**
6
+ * ```js
7
+ * import { declineFollower } from '@amityco/ts-sdk'
8
+ * await declineFollower('foobar')
9
+ * ```
10
+ *
11
+ * Decline the follow request or delete the follower
12
+ *
13
+ * @param userId the ID of the {@link Amity.User} follower
14
+ * @returns A success boolean if the follow request was decline
15
+ *
16
+ * @category Follow API
17
+ * @async
18
+ */
19
+ export const declineFollower = async (userId: Amity.User['userId']): Promise<boolean> => {
20
+ const client = getActiveClient()
21
+ client.log('follow/declineFollower', userId)
22
+
23
+ const { data } = await client.http.delete<{ follows: Amity.FollowStatus[] }>(`/api/v4/me/followers/${userId}`)
24
+
25
+ if (client.cache) {
26
+ ingestInCache(data)
27
+ }
28
+
29
+ fireEvent('follow.requestDeclined', data.follows[0])
30
+
31
+ return true
32
+ }
@@ -0,0 +1,40 @@
1
+ import { getActiveClient } from '~/client/api'
2
+ import { ingestInCache } from '~/cache/api/ingestInCache'
3
+ import { fireEvent } from '~/core/events'
4
+
5
+ /**
6
+ * ```js
7
+ * import { follow } from '@amityco/ts-sdk'
8
+ * const status = await follow('foobar')
9
+ * ```
10
+ *
11
+ * Follow the user
12
+ *
13
+ * @param userId the ID of the {@link Amity.User}
14
+ * @returns the status {@link Amity.FollowStatus}
15
+ *
16
+ * @category Follow API
17
+ * @async
18
+ */
19
+ export const follow = async (userId: Amity.User['userId']): Promise<Amity.Cached<Amity.FollowStatus>> => {
20
+ const client = getActiveClient()
21
+ client.log('follow/follow', userId)
22
+
23
+ const { data } = await client.http.post<Amity.FollowStatusPayload>(`/api/v4/me/following/${userId}`)
24
+
25
+ const cachedAt = client.cache && Date.now()
26
+
27
+ if (client.cache) {
28
+ ingestInCache(data, { cachedAt })
29
+ }
30
+
31
+ fireEvent(
32
+ data.follows[0].status === 'accepted' ? 'follow.created' : 'follow.requested',
33
+ data.follows[0],
34
+ )
35
+
36
+ return {
37
+ data: data.follows[0],
38
+ cachedAt,
39
+ }
40
+ }