@layers/amba-react-native 3.0.3 → 4.0.1
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/index.cjs +306 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +500 -10
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +500 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +306 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
|
|
2
2
|
//#region src/async-storage-adapter.ts
|
|
3
|
-
let cachedStorage =
|
|
3
|
+
let cachedStorage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Late-imports `@react-native-async-storage/async-storage` and returns
|
|
6
|
+
* it ONLY if the resolved module exposes the three methods we use
|
|
7
|
+
* (`getItem` / `setItem` / `removeItem`). Otherwise returns null and
|
|
8
|
+
* the persistence layer falls back to memory-only.
|
|
9
|
+
*
|
|
10
|
+
* Why the shape check: pnpm with auto-install-peers (default since
|
|
11
|
+
* pnpm 7) installs declared peer deps even in non-RN scratch
|
|
12
|
+
* directories (e.g., this SDK's docs-smoke harness). The installed
|
|
13
|
+
* package's Node entry point doesn't expose AsyncStorage's methods
|
|
14
|
+
* — it's a React Native native module — so the `await import(...)`
|
|
15
|
+
* succeeds (no catch fires) but the returned object has no
|
|
16
|
+
* `getItem`. Calling it throws `TypeError: storage.getItem is not a
|
|
17
|
+
* function` from inside `read()` (verified at PR #246 docs-react-native
|
|
18
|
+
* / docs-expo run on 547a1ff4). Checking method presence here turns
|
|
19
|
+
* that into the same fall-through as "peer dep not installed at all."
|
|
20
|
+
*/
|
|
21
|
+
function isAsyncStorageLike(mod) {
|
|
22
|
+
return typeof mod === "object" && mod !== null && typeof mod.getItem === "function" && typeof mod.setItem === "function" && typeof mod.removeItem === "function";
|
|
23
|
+
}
|
|
4
24
|
async function getStorage() {
|
|
5
|
-
if (cachedStorage !==
|
|
25
|
+
if (cachedStorage !== void 0) return cachedStorage;
|
|
6
26
|
try {
|
|
7
27
|
const mod = await import("@react-native-async-storage/async-storage");
|
|
8
|
-
|
|
28
|
+
const candidate = mod.default ?? mod;
|
|
29
|
+
if (!isAsyncStorageLike(candidate)) {
|
|
30
|
+
cachedStorage = null;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
cachedStorage = candidate;
|
|
9
34
|
return cachedStorage;
|
|
10
35
|
} catch {
|
|
36
|
+
cachedStorage = null;
|
|
11
37
|
return null;
|
|
12
38
|
}
|
|
13
39
|
}
|
|
@@ -325,7 +351,7 @@ function freshSeed() {
|
|
|
325
351
|
//#endregion
|
|
326
352
|
//#region src/index.ts
|
|
327
353
|
/** SDK semver — burned in at build time. Bump alongside package.json. */
|
|
328
|
-
const SDK_VERSION = "
|
|
354
|
+
const SDK_VERSION = "4.0.1";
|
|
329
355
|
let state = null;
|
|
330
356
|
function getState() {
|
|
331
357
|
if (!state) throw new AmbaApiError("NOT_INITIALIZED", "@layers/amba-react-native: SDK not configured. Call Amba.configure({ projectId, clientKey }) at app start.");
|
|
@@ -369,6 +395,24 @@ async function postAuth(state_, path, body) {
|
|
|
369
395
|
await notifyFromAuthResult(result);
|
|
370
396
|
return result;
|
|
371
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* POST helper for endpoints that return `{ data: { user } }` WITHOUT
|
|
400
|
+
* minting new tokens. Used by `linkSmsOtp` / `linkEmailOtp` / `unlink*`
|
|
401
|
+
* — these are "decorate the current user," not "re-authenticate."
|
|
402
|
+
* Existing session tokens stay untouched.
|
|
403
|
+
*/
|
|
404
|
+
async function postUserNoRotate(state_, path, body) {
|
|
405
|
+
return (await state_.http.post(path, body)).data.user;
|
|
406
|
+
}
|
|
407
|
+
async function unlinkProvider(provider) {
|
|
408
|
+
const s = getState();
|
|
409
|
+
const sessionToken = s.identity.getSessionToken();
|
|
410
|
+
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
411
|
+
return postUserNoRotate(s, P.authUnlink, {
|
|
412
|
+
provider,
|
|
413
|
+
session_token: sessionToken
|
|
414
|
+
});
|
|
415
|
+
}
|
|
372
416
|
function requireEmail(email) {
|
|
373
417
|
const trimmed = email.trim();
|
|
374
418
|
if (trimmed === "") throw new AmbaApiError("VALIDATION_ERROR", "email must not be empty");
|
|
@@ -377,15 +421,36 @@ function requireEmail(email) {
|
|
|
377
421
|
function requirePassword(password) {
|
|
378
422
|
if (password.length < 8) throw new AmbaApiError("VALIDATION_ERROR", "password must be at least 8 characters");
|
|
379
423
|
}
|
|
424
|
+
function requireOtpCode(code) {
|
|
425
|
+
if (code.length !== 6 || !/^[0-9]{6}$/.test(code)) throw new AmbaApiError("VALIDATION_ERROR", "code must be 6 digits");
|
|
426
|
+
}
|
|
427
|
+
const E164_RE = /^\+[1-9][0-9]{6,14}$/;
|
|
428
|
+
function requireE164Phone(phone) {
|
|
429
|
+
if (!E164_RE.test(phone)) throw new AmbaApiError("VALIDATION_ERROR", "phone must be E.164 (starts with +, 8–15 digits)");
|
|
430
|
+
}
|
|
380
431
|
const P = {
|
|
381
432
|
authAnonymous: "/v1/client/auth/anonymous",
|
|
382
433
|
authEmailLogin: "/v1/client/auth/email/login",
|
|
383
434
|
authEmailSignup: "/v1/client/auth/email/signup",
|
|
435
|
+
authEmailOtpRequest: "/v1/client/auth/email-otp/request",
|
|
436
|
+
authSmsOtpRequest: "/v1/client/auth/sms-otp/request",
|
|
437
|
+
authSmsOtpVerify: "/v1/client/auth/sms-otp/verify",
|
|
438
|
+
authEmailOtpVerify: "/v1/client/auth/email-otp/verify",
|
|
384
439
|
authSocial: "/v1/client/auth/social",
|
|
440
|
+
authMagicLinkRequest: "/v1/client/auth/magic-link/request",
|
|
441
|
+
authMagicLinkVerify: "/v1/client/auth/magic-link/verify",
|
|
442
|
+
authLink: "/v1/client/auth/link",
|
|
443
|
+
authUnlink: "/v1/client/auth/unlink",
|
|
444
|
+
authIdentifiers: "/v1/client/auth/identifiers",
|
|
385
445
|
authRefresh: "/v1/client/auth/refresh",
|
|
386
446
|
authLogout: "/v1/client/auth/logout",
|
|
387
447
|
authMe: "/v1/client/auth/me",
|
|
388
448
|
events: "/v1/client/events",
|
|
449
|
+
users: "/v1/client/users",
|
|
450
|
+
usersMe: "/v1/client/users/me",
|
|
451
|
+
sessions: "/v1/client/sessions",
|
|
452
|
+
sync: "/v1/client/sync",
|
|
453
|
+
leagues: "/v1/client/leagues",
|
|
389
454
|
collections: "/v1/client/collections",
|
|
390
455
|
storage: "/v1/client/media",
|
|
391
456
|
storagePresign: "/v1/client/media/presign",
|
|
@@ -400,6 +465,7 @@ const P = {
|
|
|
400
465
|
aiAnthropic: "/v1/client/ai/anthropic/messages",
|
|
401
466
|
config: "/v1/client/config",
|
|
402
467
|
flags: "/v1/client/flags",
|
|
468
|
+
diagnosticsPing: "/v1/client/diagnostics/ping",
|
|
403
469
|
achievements: "/v1/client/achievements",
|
|
404
470
|
challenges: "/v1/client/challenges",
|
|
405
471
|
catalog: "/v1/client/catalog",
|
|
@@ -468,14 +534,16 @@ const Amba = {
|
|
|
468
534
|
setDebug: (enabled) => {
|
|
469
535
|
getState().debug = enabled;
|
|
470
536
|
},
|
|
471
|
-
events: { track: async (event, properties) => {
|
|
537
|
+
events: { track: async (event, properties, options) => {
|
|
472
538
|
const s = getState();
|
|
473
|
-
|
|
474
|
-
|
|
539
|
+
const body = {
|
|
540
|
+
event,
|
|
475
541
|
properties: properties ?? void 0,
|
|
476
542
|
anonymous_id: s.identity.getAnonymousId(),
|
|
477
543
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
478
|
-
}
|
|
544
|
+
};
|
|
545
|
+
if (options?.telemetry) body["telemetry"] = true;
|
|
546
|
+
await s.http.post(P.events, body);
|
|
479
547
|
} },
|
|
480
548
|
auth: {
|
|
481
549
|
signInAnonymously: async () => {
|
|
@@ -498,13 +566,100 @@ const Amba = {
|
|
|
498
566
|
password
|
|
499
567
|
});
|
|
500
568
|
},
|
|
501
|
-
|
|
569
|
+
signInWithApple: async (identityToken) => {
|
|
570
|
+
if (identityToken.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "identity_token must not be empty");
|
|
571
|
+
return postAuth(getState(), P.authSocial, {
|
|
572
|
+
provider: "apple",
|
|
573
|
+
id_token: identityToken
|
|
574
|
+
});
|
|
575
|
+
},
|
|
576
|
+
signInWithGoogle: async (idToken) => {
|
|
502
577
|
if (idToken.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "id_token must not be empty");
|
|
503
578
|
return postAuth(getState(), P.authSocial, {
|
|
504
|
-
provider,
|
|
579
|
+
provider: "google",
|
|
505
580
|
id_token: idToken
|
|
506
581
|
});
|
|
507
582
|
},
|
|
583
|
+
requestEmailOtp: async (email) => {
|
|
584
|
+
requireEmail(email);
|
|
585
|
+
await getState().http.post(P.authEmailOtpRequest, { email });
|
|
586
|
+
},
|
|
587
|
+
verifyEmailOtp: async (email, code) => {
|
|
588
|
+
requireEmail(email);
|
|
589
|
+
requireOtpCode(code);
|
|
590
|
+
return postAuth(getState(), P.authEmailOtpVerify, {
|
|
591
|
+
email,
|
|
592
|
+
code
|
|
593
|
+
});
|
|
594
|
+
},
|
|
595
|
+
requestSmsOtp: async (phone) => {
|
|
596
|
+
requireE164Phone(phone);
|
|
597
|
+
await getState().http.post(P.authSmsOtpRequest, { phone });
|
|
598
|
+
},
|
|
599
|
+
verifySmsOtp: async (phone, code) => {
|
|
600
|
+
requireE164Phone(phone);
|
|
601
|
+
requireOtpCode(code);
|
|
602
|
+
return postAuth(getState(), P.authSmsOtpVerify, {
|
|
603
|
+
phone,
|
|
604
|
+
code
|
|
605
|
+
});
|
|
606
|
+
},
|
|
607
|
+
requestMagicLink: async (email) => {
|
|
608
|
+
requireEmail(email);
|
|
609
|
+
await getState().http.post(P.authMagicLinkRequest, { email });
|
|
610
|
+
},
|
|
611
|
+
verifyMagicLink: async (token) => {
|
|
612
|
+
if (token.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "token must not be empty");
|
|
613
|
+
return postAuth(getState(), P.authMagicLinkVerify, { token });
|
|
614
|
+
},
|
|
615
|
+
linkAccount: async (provider, credential) => {
|
|
616
|
+
const providerTrimmed = provider?.trim() ?? "";
|
|
617
|
+
if (providerTrimmed === "") throw new AmbaApiError("VALIDATION_ERROR", "provider must not be empty");
|
|
618
|
+
if (typeof credential !== "string" || credential.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "credential must not be empty");
|
|
619
|
+
const s = getState();
|
|
620
|
+
const sessionToken = s.identity.getSessionToken();
|
|
621
|
+
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
622
|
+
return postAuth(s, P.authLink, {
|
|
623
|
+
provider: providerTrimmed,
|
|
624
|
+
token: credential,
|
|
625
|
+
session_token: sessionToken
|
|
626
|
+
});
|
|
627
|
+
},
|
|
628
|
+
linkSmsOtp: async (phone, code) => {
|
|
629
|
+
requireE164Phone(phone);
|
|
630
|
+
requireOtpCode(code);
|
|
631
|
+
const s = getState();
|
|
632
|
+
const sessionToken = s.identity.getSessionToken();
|
|
633
|
+
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
634
|
+
return postUserNoRotate(s, P.authLink, {
|
|
635
|
+
provider: "sms_otp",
|
|
636
|
+
phone,
|
|
637
|
+
code,
|
|
638
|
+
session_token: sessionToken
|
|
639
|
+
});
|
|
640
|
+
},
|
|
641
|
+
linkEmailOtp: async (email, code) => {
|
|
642
|
+
requireEmail(email);
|
|
643
|
+
requireOtpCode(code);
|
|
644
|
+
const s = getState();
|
|
645
|
+
const sessionToken = s.identity.getSessionToken();
|
|
646
|
+
if (!sessionToken) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
647
|
+
return postUserNoRotate(s, P.authLink, {
|
|
648
|
+
provider: "email_otp",
|
|
649
|
+
email,
|
|
650
|
+
code,
|
|
651
|
+
session_token: sessionToken
|
|
652
|
+
});
|
|
653
|
+
},
|
|
654
|
+
unlinkSmsOtp: async () => unlinkProvider("sms_otp"),
|
|
655
|
+
unlinkEmailOtp: async () => unlinkProvider("email_otp"),
|
|
656
|
+
unlinkApple: async () => unlinkProvider("apple"),
|
|
657
|
+
unlinkGoogle: async () => unlinkProvider("google"),
|
|
658
|
+
getCurrentIdentifiers: async () => {
|
|
659
|
+
const s = getState();
|
|
660
|
+
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
661
|
+
return (await s.http.get(P.authIdentifiers)).data.identifiers;
|
|
662
|
+
},
|
|
508
663
|
signOut: async (rotateAnonymousId = false) => {
|
|
509
664
|
const s = getState();
|
|
510
665
|
if (s.identity.getSessionToken()) try {
|
|
@@ -541,6 +696,41 @@ const Amba = {
|
|
|
541
696
|
};
|
|
542
697
|
}
|
|
543
698
|
},
|
|
699
|
+
users: {
|
|
700
|
+
get: async (userId) => {
|
|
701
|
+
const s = getState();
|
|
702
|
+
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
703
|
+
const path = userId === void 0 || userId === null || userId === "" ? P.usersMe : `${P.users}/${encodePathSegment(userId)}`;
|
|
704
|
+
return (await s.http.get(path)).data;
|
|
705
|
+
},
|
|
706
|
+
update: ((arg1, arg2) => {
|
|
707
|
+
const s = getState();
|
|
708
|
+
if (!s.identity.getSessionToken()) throw new AmbaApiError("UNAUTHORIZED", "no active session — call signIn* first");
|
|
709
|
+
const isById = typeof arg1 === "string";
|
|
710
|
+
const patch = isById ? arg2 ?? {} : arg1;
|
|
711
|
+
const path = isById ? `${P.users}/${encodePathSegment(arg1)}` : `${P.users}/me`;
|
|
712
|
+
return s.http.patch(path, patch).then((env) => env.data);
|
|
713
|
+
})
|
|
714
|
+
},
|
|
715
|
+
sessions: {
|
|
716
|
+
list: async () => {
|
|
717
|
+
return (await getState().http.get(P.sessions)).data;
|
|
718
|
+
},
|
|
719
|
+
revoke: async (sessionId) => {
|
|
720
|
+
await getState().http.post(`${P.sessions}/${encodePathSegment(sessionId)}/end`, {});
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
sync: {
|
|
724
|
+
pushChanges: async (changes) => {
|
|
725
|
+
return (await getState().http.post(P.sync, { changes })).data;
|
|
726
|
+
},
|
|
727
|
+
pullChanges: async (since) => {
|
|
728
|
+
if (!since.entity_type || since.entity_type.trim() === "") throw new AmbaApiError("VALIDATION_ERROR", "pullChanges requires a non-empty entity_type");
|
|
729
|
+
const query = { entity_type: since.entity_type };
|
|
730
|
+
if (since.checkpoint_token !== void 0) query["checkpoint_token"] = since.checkpoint_token;
|
|
731
|
+
return (await getState().http.get(P.sync, query)).data;
|
|
732
|
+
}
|
|
733
|
+
},
|
|
544
734
|
collections: {
|
|
545
735
|
find: async (name, options = {}) => {
|
|
546
736
|
return getState().http.post(`${P.collections}/${encodePathSegment(name)}/find`, options);
|
|
@@ -548,6 +738,20 @@ const Amba = {
|
|
|
548
738
|
findOne: async (name, id) => {
|
|
549
739
|
return (await getState().http.get(`${P.collections}/${encodePathSegment(name)}/${encodePathSegment(id)}`)).data;
|
|
550
740
|
},
|
|
741
|
+
findNearest: async (name, vector, k, where) => {
|
|
742
|
+
const s = getState();
|
|
743
|
+
const body = {
|
|
744
|
+
to_vector: vector,
|
|
745
|
+
k
|
|
746
|
+
};
|
|
747
|
+
if (where !== void 0) body.where = where;
|
|
748
|
+
return (await s.http.post(`${P.collections}/${encodePathSegment(name)}/find-nearest`, body)).data;
|
|
749
|
+
},
|
|
750
|
+
count: async (name, where) => {
|
|
751
|
+
const s = getState();
|
|
752
|
+
const query = where !== void 0 ? { query: JSON.stringify({ where }) } : void 0;
|
|
753
|
+
return (await s.http.get(`${P.collections}/${encodePathSegment(name)}/count`, query)).data.count;
|
|
754
|
+
},
|
|
551
755
|
insert: async (name, row) => {
|
|
552
756
|
return (await getState().http.post(`${P.collections}/${encodePathSegment(name)}`, row)).data;
|
|
553
757
|
},
|
|
@@ -636,6 +840,33 @@ const Amba = {
|
|
|
636
840
|
upload_id: uploadId,
|
|
637
841
|
asset_id: assetId
|
|
638
842
|
})).data;
|
|
843
|
+
},
|
|
844
|
+
list: async (prefix) => {
|
|
845
|
+
const env = await getState().http.get(P.storage, prefix !== void 0 ? { prefix } : void 0);
|
|
846
|
+
return {
|
|
847
|
+
data: env.data ?? [],
|
|
848
|
+
next_cursor: env.next_cursor ?? null
|
|
849
|
+
};
|
|
850
|
+
},
|
|
851
|
+
delete: async (assetId) => {
|
|
852
|
+
await getState().http.delete(`${P.storage}/${encodePathSegment(assetId)}`);
|
|
853
|
+
},
|
|
854
|
+
download: async (assetId) => {
|
|
855
|
+
const assetEnv = await getState().http.get(`${P.storage}/${encodePathSegment(assetId)}`);
|
|
856
|
+
const cdnUrl = assetEnv.data.url ?? assetEnv.data.storage_url ?? "";
|
|
857
|
+
if (!cdnUrl) throw new AmbaApiError("NOT_FOUND", "asset has no resolvable CDN url");
|
|
858
|
+
let response;
|
|
859
|
+
try {
|
|
860
|
+
response = await (globalThis.fetch ?? fetch)(cdnUrl, { method: "GET" });
|
|
861
|
+
} catch (err) {
|
|
862
|
+
throw new AmbaApiError("NETWORK_ERROR", `Network error: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
863
|
+
}
|
|
864
|
+
if (!response.ok) {
|
|
865
|
+
const text = await response.text();
|
|
866
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
867
|
+
throw mapHttpError(response, text, ct.includes("application/json"));
|
|
868
|
+
}
|
|
869
|
+
return response.blob();
|
|
639
870
|
}
|
|
640
871
|
},
|
|
641
872
|
push: {
|
|
@@ -706,8 +937,21 @@ const Amba = {
|
|
|
706
937
|
values: parsed.data ?? {}
|
|
707
938
|
};
|
|
708
939
|
} },
|
|
709
|
-
flags: {
|
|
710
|
-
|
|
940
|
+
flags: {
|
|
941
|
+
fetch: async () => {
|
|
942
|
+
return (await getState().http.get(P.flags)).data;
|
|
943
|
+
},
|
|
944
|
+
get: async (key) => {
|
|
945
|
+
try {
|
|
946
|
+
return (await getState().http.get(`${P.flags}/${encodePathSegment(key)}`)).data;
|
|
947
|
+
} catch (err) {
|
|
948
|
+
if (err instanceof AmbaApiError && err.code === "NOT_FOUND") return null;
|
|
949
|
+
throw err;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
diagnostics: { ping: async () => {
|
|
954
|
+
return await getState().http.post(P.diagnosticsPing, {});
|
|
711
955
|
} },
|
|
712
956
|
achievements: {
|
|
713
957
|
getAll: async () => {
|
|
@@ -765,6 +1009,14 @@ const Amba = {
|
|
|
765
1009
|
return (await getState().http.get(`${P.leaderboards}/${encodePathSegment(key)}/me`)).data;
|
|
766
1010
|
}
|
|
767
1011
|
},
|
|
1012
|
+
leagues: {
|
|
1013
|
+
me: async () => {
|
|
1014
|
+
return (await getState().http.get(`${P.leagues}/me`)).data;
|
|
1015
|
+
},
|
|
1016
|
+
cohort: async () => {
|
|
1017
|
+
return (await getState().http.get(`${P.leagues}/me/cohort`)).data;
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
768
1020
|
stores: {
|
|
769
1021
|
list: async () => {
|
|
770
1022
|
return (await getState().http.get(P.stores)).data;
|
|
@@ -809,6 +1061,15 @@ const Amba = {
|
|
|
809
1061
|
getFriends: async () => {
|
|
810
1062
|
return (await getState().http.get(P.friends, { state: "accepted" })).data;
|
|
811
1063
|
},
|
|
1064
|
+
sendRequest: async (userId) => {
|
|
1065
|
+
return (await getState().http.post(`${P.friends}/request`, { user_id: userId })).data;
|
|
1066
|
+
},
|
|
1067
|
+
acceptRequest: async (friendshipId) => {
|
|
1068
|
+
return (await getState().http.post(`${P.friends}/${encodePathSegment(friendshipId)}/accept`, {})).data;
|
|
1069
|
+
},
|
|
1070
|
+
declineRequest: async (friendshipId) => {
|
|
1071
|
+
await getState().http.post(`${P.friends}/${encodePathSegment(friendshipId)}/reject`, {});
|
|
1072
|
+
},
|
|
812
1073
|
blockUser: async (userId) => {
|
|
813
1074
|
return (await getState().http.post(`${P.friends}/block`, { user_id: userId })).data;
|
|
814
1075
|
},
|
|
@@ -817,6 +1078,9 @@ const Amba = {
|
|
|
817
1078
|
},
|
|
818
1079
|
removeBlock: async (friendshipId) => {
|
|
819
1080
|
await getState().http.delete(`${P.friends}/${encodePathSegment(friendshipId)}`);
|
|
1081
|
+
},
|
|
1082
|
+
removeFriend: async (userId) => {
|
|
1083
|
+
await getState().http.delete(`${P.friends}/by-user/${encodePathSegment(userId)}`);
|
|
820
1084
|
}
|
|
821
1085
|
},
|
|
822
1086
|
groups: {
|
|
@@ -849,10 +1113,7 @@ const Amba = {
|
|
|
849
1113
|
createConversation: async (request) => {
|
|
850
1114
|
return (await getState().http.post(`${P.messaging}/conversations`, request)).data;
|
|
851
1115
|
},
|
|
852
|
-
|
|
853
|
-
return (await getState().http.get(`${P.messaging}/conversations`)).data;
|
|
854
|
-
},
|
|
855
|
-
listConversations: async () => {
|
|
1116
|
+
conversations: async () => {
|
|
856
1117
|
return (await getState().http.get(`${P.messaging}/conversations`)).data;
|
|
857
1118
|
},
|
|
858
1119
|
sendMessage: async (conversationId, request) => {
|
|
@@ -866,6 +1127,24 @@ const Amba = {
|
|
|
866
1127
|
},
|
|
867
1128
|
markRead: async (conversationId) => {
|
|
868
1129
|
return (await getState().http.post(`${P.messaging}/conversations/${encodePathSegment(conversationId)}/read`, {})).data;
|
|
1130
|
+
},
|
|
1131
|
+
getMessage: async (conversationId, messageId) => {
|
|
1132
|
+
const PAGE_SIZE = 100;
|
|
1133
|
+
const MAX_PAGES = 50;
|
|
1134
|
+
const s = getState();
|
|
1135
|
+
const path = `${P.messaging}/conversations/${encodePathSegment(conversationId)}/messages`;
|
|
1136
|
+
let offset = 0;
|
|
1137
|
+
for (let i = 0; i < MAX_PAGES; i++) {
|
|
1138
|
+
const page = (await s.http.get(path, {
|
|
1139
|
+
limit: PAGE_SIZE,
|
|
1140
|
+
offset
|
|
1141
|
+
})).data;
|
|
1142
|
+
const hit = page.find((m) => m.id === messageId);
|
|
1143
|
+
if (hit) return hit;
|
|
1144
|
+
if (page.length < PAGE_SIZE) return null;
|
|
1145
|
+
offset += PAGE_SIZE;
|
|
1146
|
+
}
|
|
1147
|
+
return null;
|
|
869
1148
|
}
|
|
870
1149
|
},
|
|
871
1150
|
moderation: {
|
|
@@ -918,20 +1197,25 @@ const Amba = {
|
|
|
918
1197
|
return (await getState().http.post(P.referrals, body)).data;
|
|
919
1198
|
}
|
|
920
1199
|
},
|
|
921
|
-
catalog: {
|
|
922
|
-
|
|
923
|
-
|
|
1200
|
+
catalog: {
|
|
1201
|
+
list: async () => {
|
|
1202
|
+
return (await getState().http.get(P.catalog)).data;
|
|
1203
|
+
},
|
|
1204
|
+
get: async (id) => {
|
|
1205
|
+
return (await getState().http.get(`${P.catalog}/${encodePathSegment(id)}`)).data;
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
924
1208
|
content: {
|
|
925
|
-
|
|
1209
|
+
today: async (channel = "default") => {
|
|
926
1210
|
return (await getState().http.get(`${P.content}/today/${encodePathSegment(channel)}`)).data ?? null;
|
|
927
1211
|
},
|
|
928
|
-
|
|
1212
|
+
library: async (channel = "default", options) => {
|
|
929
1213
|
return (await getState().http.get(`${P.content}/library/${encodePathSegment(channel)}`, {
|
|
930
1214
|
limit: options?.limit,
|
|
931
1215
|
cursor: options?.cursor
|
|
932
1216
|
})).data;
|
|
933
1217
|
},
|
|
934
|
-
|
|
1218
|
+
item: async (id) => {
|
|
935
1219
|
return (await getState().http.get(`${P.content}/${encodePathSegment(id)}`)).data;
|
|
936
1220
|
},
|
|
937
1221
|
updateItem: async (id, state_) => {
|