@metamask/social-controllers 3.6.0 → 3.8.0
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/CHANGELOG.md +35 -1
- package/dist/SocialRealtimeService.d.ts +1 -1
- package/dist/SocialRealtimeService.d.ts.map +1 -1
- package/dist/SocialRealtimeService.js +13 -6
- package/dist/SocialRealtimeService.js.map +1 -1
- package/dist/SocialService-method-action-types.d.ts +70 -2
- package/dist/SocialService-method-action-types.d.ts.map +1 -1
- package/dist/SocialService-method-action-types.js.map +1 -1
- package/dist/SocialService.d.ts +58 -2
- package/dist/SocialService.d.ts.map +1 -1
- package/dist/SocialService.js +206 -34
- package/dist/SocialService.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/social-constants.d.ts +6 -0
- package/dist/social-constants.d.ts.map +1 -1
- package/dist/social-constants.js +6 -0
- package/dist/social-constants.js.map +1 -1
- package/dist/social-types.d.ts +94 -6
- package/dist/social-types.d.ts.map +1 -1
- package/dist/social-types.js.map +1 -1
- package/package.json +5 -5
package/dist/SocialService.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var _a;
|
|
2
2
|
import { BaseDataService } from '@metamask/base-data-service';
|
|
3
3
|
import { HttpError } from '@metamask/controller-utils';
|
|
4
|
-
import { array, assign, boolean, enums, is, nullable, number, optional, record, string, type as structType, } from '@metamask/superstruct';
|
|
4
|
+
import { array, assign, boolean, enums, is, literal, nullable, number, optional, record, string, type as structType, union, } from '@metamask/superstruct';
|
|
5
5
|
import { serviceName, SocialServiceErrorMessage } from './social-constants.js';
|
|
6
6
|
import { TRADER_RANKING_TAGS, TradeStruct } from './social-types.js';
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
@@ -33,16 +33,20 @@ const PositionStruct = structType({
|
|
|
33
33
|
chain: string(),
|
|
34
34
|
positionAmount: number(),
|
|
35
35
|
isOpen: optional(boolean()),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// Incomplete Clicker metrics serialize these as `null` (`undefined + n`
|
|
37
|
+
// becomes `NaN`, then JSON `null`). Rejecting the field used to fail the
|
|
38
|
+
// entire feed page, so they are nullable here.
|
|
39
|
+
boughtUsd: nullable(number()),
|
|
40
|
+
soldUsd: nullable(number()),
|
|
41
|
+
realizedPnl: nullable(number()),
|
|
42
|
+
costBasis: nullable(number()),
|
|
40
43
|
trades: array(TradeStruct),
|
|
41
44
|
lastTradeAt: number(),
|
|
42
45
|
tokenImageUrl: optional(nullable(string())),
|
|
43
46
|
currentValueUSD: optional(nullable(number())),
|
|
44
47
|
pnlValueUsd: optional(nullable(number())),
|
|
45
48
|
pnlPercent: optional(nullable(number())),
|
|
49
|
+
copyCount: optional(number()),
|
|
46
50
|
perpPositionType: optional(nullable(enums(['long', 'short']))),
|
|
47
51
|
perpLeverage: optional(nullable(number())),
|
|
48
52
|
positionAmountWithLeverage: optional(nullable(number())),
|
|
@@ -164,12 +168,35 @@ const SwapCommentResponseStruct = structType({
|
|
|
164
168
|
isAppUserComment: boolean(),
|
|
165
169
|
metrics: CommentEngagementStruct,
|
|
166
170
|
});
|
|
171
|
+
const CopyTradeAccountStruct = structType({
|
|
172
|
+
profileId: string(),
|
|
173
|
+
caip10: string(),
|
|
174
|
+
});
|
|
175
|
+
const CopyTradeIntentResponseStruct = structType({
|
|
176
|
+
id: string(),
|
|
177
|
+
origin: structType({
|
|
178
|
+
kind: literal('comment'),
|
|
179
|
+
id: string(),
|
|
180
|
+
}),
|
|
181
|
+
copierAccount: CopyTradeAccountStruct,
|
|
182
|
+
sourceAccount: nullable(CopyTradeAccountStruct),
|
|
183
|
+
execution: union([
|
|
184
|
+
structType({ product: literal('swaps'), quoteId: string() }),
|
|
185
|
+
structType({ product: literal('perps'), oid: string() }),
|
|
186
|
+
]),
|
|
187
|
+
payoutEligibility: structType({
|
|
188
|
+
status: enums(['payable', 'non_payable']),
|
|
189
|
+
reason: string(),
|
|
190
|
+
}),
|
|
191
|
+
createdAt: string(),
|
|
192
|
+
});
|
|
167
193
|
const FeedItemStruct = assign(PositionStruct, structType({
|
|
168
194
|
actor: FeedActorStruct,
|
|
169
195
|
timestamp: number(),
|
|
170
196
|
authorComment: optional(nullable(AuthorCommentStruct)),
|
|
171
197
|
commentCount: optional(number()),
|
|
172
198
|
replyCount: optional(number()),
|
|
199
|
+
copyCount: optional(number()),
|
|
173
200
|
firstTradeAt: optional(nullable(number())),
|
|
174
201
|
holdTimeMs: optional(nullable(number())),
|
|
175
202
|
entryPriceUsd: optional(nullable(number())),
|
|
@@ -182,6 +209,34 @@ const FeedResponseStruct = structType({
|
|
|
182
209
|
items: array(FeedItemStruct),
|
|
183
210
|
pagination: FeedPaginationStruct,
|
|
184
211
|
});
|
|
212
|
+
/**
|
|
213
|
+
* One malformed feed item must not blank the page. When the envelope is
|
|
214
|
+
* well-shaped, keep items that match {@link FeedItemStruct} and drop the rest.
|
|
215
|
+
* Still throw when `items` or `pagination` themselves are unusable.
|
|
216
|
+
*
|
|
217
|
+
* @param feedData - Parsed JSON body from a feed endpoint.
|
|
218
|
+
* @param invalidMessage - Error message to throw when the envelope is unusable.
|
|
219
|
+
* @returns The validated feed response, possibly with invalid items dropped.
|
|
220
|
+
*/
|
|
221
|
+
const parseFeedResponse = (feedData, invalidMessage) => {
|
|
222
|
+
if (is(feedData, FeedResponseStruct)) {
|
|
223
|
+
return feedData;
|
|
224
|
+
}
|
|
225
|
+
if (typeof feedData !== 'object' ||
|
|
226
|
+
feedData === null ||
|
|
227
|
+
!('items' in feedData) ||
|
|
228
|
+
!('pagination' in feedData)) {
|
|
229
|
+
throw new Error(invalidMessage);
|
|
230
|
+
}
|
|
231
|
+
const { items, pagination } = feedData;
|
|
232
|
+
if (!Array.isArray(items) || !is(pagination, FeedPaginationStruct)) {
|
|
233
|
+
throw new Error(invalidMessage);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
items: items.filter((item) => is(item, FeedItemStruct)),
|
|
237
|
+
pagination,
|
|
238
|
+
};
|
|
239
|
+
};
|
|
185
240
|
const FollowersResponseStruct = structType({
|
|
186
241
|
followers: array(ProfileSummaryStruct),
|
|
187
242
|
count: number(),
|
|
@@ -227,7 +282,10 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
227
282
|
'fetchOpenPositions',
|
|
228
283
|
'fetchClosedPositions',
|
|
229
284
|
'fetchFollowers',
|
|
285
|
+
'fetchTraderFollowing',
|
|
230
286
|
'fetchFollowing',
|
|
287
|
+
'fetchMyFollowing',
|
|
288
|
+
'fetchMyFollowers',
|
|
231
289
|
'fetchPositionById',
|
|
232
290
|
'fetchFeed',
|
|
233
291
|
'fetchTraderFeed',
|
|
@@ -235,6 +293,7 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
235
293
|
'reactToComment',
|
|
236
294
|
'removeCommentReaction',
|
|
237
295
|
'createSwapComment',
|
|
296
|
+
'createCopyTradeIntent',
|
|
238
297
|
'follow',
|
|
239
298
|
'unfollow',
|
|
240
299
|
'optOutOfLeaderboard',
|
|
@@ -314,7 +373,7 @@ export class SocialService extends BaseDataService {
|
|
|
314
373
|
headers: authHeaders,
|
|
315
374
|
});
|
|
316
375
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_LEADERBOARD_FAILED);
|
|
317
|
-
const leaderboardData = await response.json();
|
|
376
|
+
const leaderboardData = (await response.json());
|
|
318
377
|
if (!is(leaderboardData, LeaderboardResponseStruct)) {
|
|
319
378
|
throw new Error(SocialServiceErrorMessage.FETCH_LEADERBOARD_INVALID_RESPONSE);
|
|
320
379
|
}
|
|
@@ -341,7 +400,7 @@ export class SocialService extends BaseDataService {
|
|
|
341
400
|
const authHeaders = await this.#getAuthHeaders();
|
|
342
401
|
const response = await fetch(url, { headers: authHeaders });
|
|
343
402
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_PROFILE_FAILED);
|
|
344
|
-
const traderProfileData = await response.json();
|
|
403
|
+
const traderProfileData = (await response.json());
|
|
345
404
|
if (!is(traderProfileData, TraderProfileResponseStruct)) {
|
|
346
405
|
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_PROFILE_INVALID_RESPONSE);
|
|
347
406
|
}
|
|
@@ -385,22 +444,32 @@ export class SocialService extends BaseDataService {
|
|
|
385
444
|
/**
|
|
386
445
|
* Fetches a trader's MetaMask followers.
|
|
387
446
|
*
|
|
388
|
-
* Calls `GET ${baseUrl}/traders/${addressOrId}/followers`.
|
|
447
|
+
* Calls `GET ${baseUrl}/traders/${addressOrId}/followers`. Pass
|
|
448
|
+
* `mutuals: true` to append `?mutuals=true` (profiles that follow both this
|
|
449
|
+
* trader and the authenticated user; `count` is the mutual total). Requires a
|
|
450
|
+
* bearer token; the trader is identified by `addressOrId`, not the JWT
|
|
451
|
+
* subject (see {@link fetchMyFollowers} for the current user's inbound list).
|
|
389
452
|
*
|
|
390
453
|
* @param options - Options bag.
|
|
391
454
|
* @param options.addressOrId - Wallet address or Clicker profile ID.
|
|
455
|
+
* @param options.mutuals - When true, request mutual followers only.
|
|
392
456
|
* @returns The followers response.
|
|
393
457
|
*/
|
|
394
458
|
async fetchFollowers(options) {
|
|
395
|
-
const { addressOrId } = options;
|
|
459
|
+
const { addressOrId, mutuals } = options;
|
|
460
|
+
const mutualsOnly = mutuals === true;
|
|
396
461
|
const followersResponse = await this.fetchQuery({
|
|
397
|
-
queryKey: [`${this.name}:fetchFollowers`, addressOrId],
|
|
462
|
+
queryKey: [`${this.name}:fetchFollowers`, addressOrId, mutualsOnly],
|
|
463
|
+
staleTime: 0,
|
|
398
464
|
queryFn: async () => {
|
|
399
|
-
const url = `${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/followers
|
|
465
|
+
const url = new URL(`${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/followers`);
|
|
466
|
+
if (mutualsOnly) {
|
|
467
|
+
url.searchParams.append('mutuals', 'true');
|
|
468
|
+
}
|
|
400
469
|
const authHeaders = await this.#getAuthHeaders();
|
|
401
|
-
const response = await fetch(url, { headers: authHeaders });
|
|
470
|
+
const response = await fetch(url.toString(), { headers: authHeaders });
|
|
402
471
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FOLLOWERS_FAILED);
|
|
403
|
-
const followersData = await response.json();
|
|
472
|
+
const followersData = (await response.json());
|
|
404
473
|
if (!is(followersData, FollowersResponseStruct)) {
|
|
405
474
|
throw new Error(SocialServiceErrorMessage.FETCH_FOLLOWERS_INVALID_RESPONSE);
|
|
406
475
|
}
|
|
@@ -409,6 +478,36 @@ export class SocialService extends BaseDataService {
|
|
|
409
478
|
});
|
|
410
479
|
return followersResponse;
|
|
411
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Fetches the list of traders a profile follows.
|
|
483
|
+
*
|
|
484
|
+
* Calls `GET ${baseUrl}/traders/${addressOrId}/following`. Requires a bearer
|
|
485
|
+
* token; the trader is identified by `addressOrId`, not the JWT subject (see
|
|
486
|
+
* {@link fetchFollowing} for the current user's outbound list).
|
|
487
|
+
*
|
|
488
|
+
* @param options - Options bag.
|
|
489
|
+
* @param options.addressOrId - Wallet address or Clicker profile ID.
|
|
490
|
+
* @returns The following response.
|
|
491
|
+
*/
|
|
492
|
+
async fetchTraderFollowing(options) {
|
|
493
|
+
const { addressOrId } = options;
|
|
494
|
+
const followingResponse = await this.fetchQuery({
|
|
495
|
+
queryKey: [`${this.name}:fetchTraderFollowing`, addressOrId],
|
|
496
|
+
staleTime: 0,
|
|
497
|
+
queryFn: async () => {
|
|
498
|
+
const url = `${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/following`;
|
|
499
|
+
const authHeaders = await this.#getAuthHeaders();
|
|
500
|
+
const response = await fetch(url, { headers: authHeaders });
|
|
501
|
+
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_FOLLOWING_FAILED);
|
|
502
|
+
const followingData = (await response.json());
|
|
503
|
+
if (!is(followingData, FollowingResponseStruct)) {
|
|
504
|
+
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_FOLLOWING_INVALID_RESPONSE);
|
|
505
|
+
}
|
|
506
|
+
return followingData;
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
return followingResponse;
|
|
510
|
+
}
|
|
412
511
|
/**
|
|
413
512
|
* Fetches a single position by its unique ID.
|
|
414
513
|
*
|
|
@@ -427,7 +526,7 @@ export class SocialService extends BaseDataService {
|
|
|
427
526
|
const authHeaders = await this.#getAuthHeaders();
|
|
428
527
|
const response = await fetch(url, { headers: authHeaders });
|
|
429
528
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_POSITION_BY_ID_FAILED);
|
|
430
|
-
const positionData = await response.json();
|
|
529
|
+
const positionData = (await response.json());
|
|
431
530
|
if (!is(positionData, PositionStruct)) {
|
|
432
531
|
throw new Error(SocialServiceErrorMessage.FETCH_POSITION_BY_ID_INVALID_RESPONSE);
|
|
433
532
|
}
|
|
@@ -484,11 +583,8 @@ export class SocialService extends BaseDataService {
|
|
|
484
583
|
headers: authHeaders,
|
|
485
584
|
});
|
|
486
585
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FEED_FAILED);
|
|
487
|
-
const feedData = await response.json();
|
|
488
|
-
|
|
489
|
-
throw new Error(SocialServiceErrorMessage.FETCH_FEED_INVALID_RESPONSE);
|
|
490
|
-
}
|
|
491
|
-
return feedData;
|
|
586
|
+
const feedData = (await response.json());
|
|
587
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_FEED_INVALID_RESPONSE);
|
|
492
588
|
},
|
|
493
589
|
});
|
|
494
590
|
return feedResponse;
|
|
@@ -541,11 +637,8 @@ export class SocialService extends BaseDataService {
|
|
|
541
637
|
headers: authHeaders,
|
|
542
638
|
});
|
|
543
639
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_FEED_FAILED);
|
|
544
|
-
const feedData = await response.json();
|
|
545
|
-
|
|
546
|
-
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_FEED_INVALID_RESPONSE);
|
|
547
|
-
}
|
|
548
|
-
return feedData;
|
|
640
|
+
const feedData = (await response.json());
|
|
641
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_TRADER_FEED_INVALID_RESPONSE);
|
|
549
642
|
},
|
|
550
643
|
});
|
|
551
644
|
return traderFeedResponse;
|
|
@@ -600,10 +693,7 @@ export class SocialService extends BaseDataService {
|
|
|
600
693
|
});
|
|
601
694
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TOKEN_FEED_FAILED);
|
|
602
695
|
const feedData = (await response.json());
|
|
603
|
-
|
|
604
|
-
throw new Error(SocialServiceErrorMessage.FETCH_TOKEN_FEED_INVALID_RESPONSE);
|
|
605
|
-
}
|
|
606
|
-
return feedData;
|
|
696
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_TOKEN_FEED_INVALID_RESPONSE);
|
|
607
697
|
},
|
|
608
698
|
});
|
|
609
699
|
return tokenFeedResponse;
|
|
@@ -633,7 +723,7 @@ export class SocialService extends BaseDataService {
|
|
|
633
723
|
body: JSON.stringify({ emotion }),
|
|
634
724
|
});
|
|
635
725
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.REACT_TO_COMMENT_FAILED);
|
|
636
|
-
const metrics = await response.json();
|
|
726
|
+
const metrics = (await response.json());
|
|
637
727
|
if (!is(metrics, CommentEngagementStruct)) {
|
|
638
728
|
throw new Error(SocialServiceErrorMessage.REACT_TO_COMMENT_INVALID_RESPONSE);
|
|
639
729
|
}
|
|
@@ -664,7 +754,7 @@ export class SocialService extends BaseDataService {
|
|
|
664
754
|
headers: authHeaders,
|
|
665
755
|
});
|
|
666
756
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.REMOVE_COMMENT_REACTION_FAILED);
|
|
667
|
-
const metrics = await response.json();
|
|
757
|
+
const metrics = (await response.json());
|
|
668
758
|
if (!is(metrics, CommentEngagementStruct)) {
|
|
669
759
|
throw new Error(SocialServiceErrorMessage.REMOVE_COMMENT_REACTION_INVALID_RESPONSE);
|
|
670
760
|
}
|
|
@@ -723,12 +813,57 @@ export class SocialService extends BaseDataService {
|
|
|
723
813
|
},
|
|
724
814
|
});
|
|
725
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Records that the current user is copying a trade from a social post, so
|
|
818
|
+
* the copy can be detected and attributed to the post's author.
|
|
819
|
+
*
|
|
820
|
+
* Calls `POST ${baseUrl}/copy-trade-intents`. Send swaps before submission
|
|
821
|
+
* with the Bridge quote id, and perps right after submission with the
|
|
822
|
+
* returned order id. The endpoint is idempotent per profile, origin, and
|
|
823
|
+
* execution id: it answers 201 for a new intent and 200 with the existing
|
|
824
|
+
* one on a retry.
|
|
825
|
+
*
|
|
826
|
+
* @param options - Options bag.
|
|
827
|
+
* @param options.origin - Comment the trade was copied from.
|
|
828
|
+
* @param options.copierAccount - CAIP-10 account placing the trade.
|
|
829
|
+
* @param options.execution - Swap quote id or perps order id.
|
|
830
|
+
* @returns The created or existing copy-trade intent.
|
|
831
|
+
*/
|
|
832
|
+
async createCopyTradeIntent(options) {
|
|
833
|
+
const { origin, copierAccount, execution } = options;
|
|
834
|
+
return await this.fetchQuery({
|
|
835
|
+
queryKey: [
|
|
836
|
+
`${this.name}:createCopyTradeIntent`,
|
|
837
|
+
{ origin, copierAccount, execution },
|
|
838
|
+
],
|
|
839
|
+
staleTime: 0,
|
|
840
|
+
queryFn: async () => {
|
|
841
|
+
const url = `${this.#v1Url}/copy-trade-intents`;
|
|
842
|
+
const authHeaders = await this.#getAuthHeaders();
|
|
843
|
+
const response = await fetch(url, {
|
|
844
|
+
method: 'POST',
|
|
845
|
+
headers: { ...authHeaders, 'Content-Type': 'application/json' },
|
|
846
|
+
body: JSON.stringify({ origin, copierAccount, execution }),
|
|
847
|
+
});
|
|
848
|
+
_a.#throwIfNotOk(response, SocialServiceErrorMessage.CREATE_COPY_TRADE_INTENT_FAILED);
|
|
849
|
+
const intent = (await response.json());
|
|
850
|
+
if (!is(intent, CopyTradeIntentResponseStruct)) {
|
|
851
|
+
throw new Error(SocialServiceErrorMessage.CREATE_COPY_TRADE_INTENT_INVALID_RESPONSE);
|
|
852
|
+
}
|
|
853
|
+
return intent;
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
}
|
|
726
857
|
/**
|
|
727
858
|
* Fetches the list of traders the current user is following.
|
|
728
859
|
*
|
|
729
860
|
* Calls `GET ${baseUrl}/users/me/following`. The caller is identified
|
|
730
861
|
* server-side from the JWT sub claim carried in the Authorization header.
|
|
731
862
|
*
|
|
863
|
+
* Prefer {@link fetchMyFollowing} in new code (pairs with
|
|
864
|
+
* {@link fetchMyFollowers}). This method is unchanged and remains the
|
|
865
|
+
* `/users/me/following` read.
|
|
866
|
+
*
|
|
732
867
|
* @returns The following response.
|
|
733
868
|
*/
|
|
734
869
|
async fetchFollowing() {
|
|
@@ -740,7 +875,7 @@ export class SocialService extends BaseDataService {
|
|
|
740
875
|
const authHeaders = await this.#getAuthHeaders();
|
|
741
876
|
const response = await fetch(url, { headers: authHeaders });
|
|
742
877
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FOLLOWING_FAILED);
|
|
743
|
-
const followingData = await response.json();
|
|
878
|
+
const followingData = (await response.json());
|
|
744
879
|
if (!is(followingData, FollowingResponseStruct)) {
|
|
745
880
|
throw new Error(SocialServiceErrorMessage.FETCH_FOLLOWING_INVALID_RESPONSE);
|
|
746
881
|
}
|
|
@@ -749,6 +884,43 @@ export class SocialService extends BaseDataService {
|
|
|
749
884
|
});
|
|
750
885
|
return followingResponse;
|
|
751
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Fetches the list of traders the current user is following.
|
|
889
|
+
*
|
|
890
|
+
* Alias of {@link fetchFollowing}. Calls `GET ${baseUrl}/users/me/following`.
|
|
891
|
+
* Preferred name in new code so `/users/me` reads are both `fetchMy*`.
|
|
892
|
+
*
|
|
893
|
+
* @returns The following response.
|
|
894
|
+
*/
|
|
895
|
+
async fetchMyFollowing() {
|
|
896
|
+
return this.fetchFollowing();
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Fetches the list of traders following the current user.
|
|
900
|
+
*
|
|
901
|
+
* Calls `GET ${baseUrl}/users/me/followers`. The caller is identified
|
|
902
|
+
* server-side from the JWT sub claim carried in the Authorization header.
|
|
903
|
+
*
|
|
904
|
+
* @returns The followers response.
|
|
905
|
+
*/
|
|
906
|
+
async fetchMyFollowers() {
|
|
907
|
+
const followersResponse = await this.fetchQuery({
|
|
908
|
+
queryKey: [`${this.name}:fetchMyFollowers`],
|
|
909
|
+
staleTime: 0,
|
|
910
|
+
queryFn: async () => {
|
|
911
|
+
const url = `${this.#v1Url}/users/me/followers`;
|
|
912
|
+
const authHeaders = await this.#getAuthHeaders();
|
|
913
|
+
const response = await fetch(url, { headers: authHeaders });
|
|
914
|
+
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_MY_FOLLOWERS_FAILED);
|
|
915
|
+
const followersData = (await response.json());
|
|
916
|
+
if (!is(followersData, FollowersResponseStruct)) {
|
|
917
|
+
throw new Error(SocialServiceErrorMessage.FETCH_MY_FOLLOWERS_INVALID_RESPONSE);
|
|
918
|
+
}
|
|
919
|
+
return followersData;
|
|
920
|
+
},
|
|
921
|
+
});
|
|
922
|
+
return followersResponse;
|
|
923
|
+
}
|
|
752
924
|
/**
|
|
753
925
|
* Follows one or more traders on behalf of the current user.
|
|
754
926
|
*
|
|
@@ -773,7 +945,7 @@ export class SocialService extends BaseDataService {
|
|
|
773
945
|
body: JSON.stringify({ targets }),
|
|
774
946
|
});
|
|
775
947
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FOLLOW_FAILED);
|
|
776
|
-
const followData = await response.json();
|
|
948
|
+
const followData = (await response.json());
|
|
777
949
|
if (!is(followData, FollowResponseStruct)) {
|
|
778
950
|
throw new Error(SocialServiceErrorMessage.FOLLOW_INVALID_RESPONSE);
|
|
779
951
|
}
|
|
@@ -810,7 +982,7 @@ export class SocialService extends BaseDataService {
|
|
|
810
982
|
headers: authHeaders,
|
|
811
983
|
});
|
|
812
984
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.UNFOLLOW_FAILED);
|
|
813
|
-
const unfollowData = await response.json();
|
|
985
|
+
const unfollowData = (await response.json());
|
|
814
986
|
if (!is(unfollowData, UnfollowResponseStruct)) {
|
|
815
987
|
throw new Error(SocialServiceErrorMessage.UNFOLLOW_INVALID_RESPONSE);
|
|
816
988
|
}
|
|
@@ -1035,7 +1207,7 @@ export class SocialService extends BaseDataService {
|
|
|
1035
1207
|
headers: authHeaders,
|
|
1036
1208
|
});
|
|
1037
1209
|
_a.#throwIfNotOk(response, failedMessage);
|
|
1038
|
-
const positionsData = await response.json();
|
|
1210
|
+
const positionsData = (await response.json());
|
|
1039
1211
|
if (!is(positionsData, PositionsResponseStruct)) {
|
|
1040
1212
|
throw new Error(invalidMessage);
|
|
1041
1213
|
}
|