@swype-org/react-sdk 0.1.48 → 0.1.50
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 +236 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -2
- package/dist/index.d.ts +64 -2
- package/dist/index.js +235 -98
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -658,6 +658,12 @@ function normalizeSignature(sig) {
|
|
|
658
658
|
}
|
|
659
659
|
|
|
660
660
|
// src/passkey-delegation.ts
|
|
661
|
+
var PasskeyIframeBlockedError = class extends Error {
|
|
662
|
+
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
663
|
+
super(message);
|
|
664
|
+
this.name = "PasskeyIframeBlockedError";
|
|
665
|
+
}
|
|
666
|
+
};
|
|
661
667
|
function isInCrossOriginIframe() {
|
|
662
668
|
if (typeof window === "undefined") return false;
|
|
663
669
|
if (window.parent === window) return false;
|
|
@@ -669,44 +675,73 @@ function isInCrossOriginIframe() {
|
|
|
669
675
|
}
|
|
670
676
|
}
|
|
671
677
|
var delegationCounter = 0;
|
|
672
|
-
|
|
678
|
+
var DELEGATION_GET_TIMEOUT_MS = 3e4;
|
|
679
|
+
function delegatePasskeyGet(options) {
|
|
673
680
|
return new Promise((resolve, reject) => {
|
|
674
|
-
const id = `
|
|
681
|
+
const id = `pg-${++delegationCounter}-${Date.now()}`;
|
|
682
|
+
const timer = setTimeout(() => {
|
|
683
|
+
window.removeEventListener("message", handler);
|
|
684
|
+
reject(new Error("Passkey verification timed out. Please try again."));
|
|
685
|
+
}, DELEGATION_GET_TIMEOUT_MS);
|
|
675
686
|
const handler = (event) => {
|
|
676
687
|
const data = event.data;
|
|
677
688
|
if (!data || typeof data !== "object") return;
|
|
678
|
-
if (data.type !== "swype:passkey-
|
|
689
|
+
if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
|
|
690
|
+
clearTimeout(timer);
|
|
679
691
|
window.removeEventListener("message", handler);
|
|
680
692
|
if (data.error) {
|
|
681
693
|
reject(new Error(data.error));
|
|
682
694
|
} else if (data.result) {
|
|
683
695
|
resolve(data.result);
|
|
684
696
|
} else {
|
|
685
|
-
reject(new Error("Invalid passkey
|
|
697
|
+
reject(new Error("Invalid passkey get response."));
|
|
686
698
|
}
|
|
687
699
|
};
|
|
688
700
|
window.addEventListener("message", handler);
|
|
689
|
-
window.parent.postMessage({ type: "swype:passkey-
|
|
701
|
+
window.parent.postMessage({ type: "swype:passkey-get-request", id, options }, "*");
|
|
690
702
|
});
|
|
691
703
|
}
|
|
692
|
-
|
|
704
|
+
var POPUP_RESULT_TIMEOUT_MS = 12e4;
|
|
705
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
706
|
+
function createPasskeyViaPopup(options) {
|
|
693
707
|
return new Promise((resolve, reject) => {
|
|
694
|
-
const
|
|
708
|
+
const encoded = btoa(JSON.stringify(options));
|
|
709
|
+
const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
|
|
710
|
+
const popup = window.open(popupUrl, "swype-passkey", "width=460,height=600");
|
|
711
|
+
if (!popup) {
|
|
712
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const timer = setTimeout(() => {
|
|
716
|
+
cleanup();
|
|
717
|
+
reject(new Error("Passkey creation timed out. Please try again."));
|
|
718
|
+
}, POPUP_RESULT_TIMEOUT_MS);
|
|
719
|
+
const closedPoll = setInterval(() => {
|
|
720
|
+
if (popup.closed) {
|
|
721
|
+
cleanup();
|
|
722
|
+
reject(new Error("Passkey setup window was closed before completing."));
|
|
723
|
+
}
|
|
724
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
695
725
|
const handler = (event) => {
|
|
726
|
+
if (event.source !== popup) return;
|
|
696
727
|
const data = event.data;
|
|
697
728
|
if (!data || typeof data !== "object") return;
|
|
698
|
-
if (data.type !== "swype:passkey-
|
|
699
|
-
|
|
729
|
+
if (data.type !== "swype:passkey-popup-result") return;
|
|
730
|
+
cleanup();
|
|
700
731
|
if (data.error) {
|
|
701
732
|
reject(new Error(data.error));
|
|
702
733
|
} else if (data.result) {
|
|
703
734
|
resolve(data.result);
|
|
704
735
|
} else {
|
|
705
|
-
reject(new Error("Invalid passkey
|
|
736
|
+
reject(new Error("Invalid passkey popup response."));
|
|
706
737
|
}
|
|
707
738
|
};
|
|
739
|
+
function cleanup() {
|
|
740
|
+
clearTimeout(timer);
|
|
741
|
+
clearInterval(closedPoll);
|
|
742
|
+
window.removeEventListener("message", handler);
|
|
743
|
+
}
|
|
708
744
|
window.addEventListener("message", handler);
|
|
709
|
-
window.parent.postMessage({ type: "swype:passkey-get-request", id, options }, "*");
|
|
710
745
|
});
|
|
711
746
|
}
|
|
712
747
|
|
|
@@ -831,53 +866,51 @@ async function createPasskeyCredential(params) {
|
|
|
831
866
|
const challenge = new Uint8Array(32);
|
|
832
867
|
crypto.getRandomValues(challenge);
|
|
833
868
|
const rpId = resolvePasskeyRpId();
|
|
869
|
+
const publicKeyOptions = {
|
|
870
|
+
challenge,
|
|
871
|
+
rp: { name: "Swype", id: rpId },
|
|
872
|
+
user: {
|
|
873
|
+
id: new TextEncoder().encode(params.userId),
|
|
874
|
+
name: params.displayName,
|
|
875
|
+
displayName: params.displayName
|
|
876
|
+
},
|
|
877
|
+
pubKeyCredParams: [
|
|
878
|
+
{ alg: -7, type: "public-key" },
|
|
879
|
+
{ alg: -257, type: "public-key" }
|
|
880
|
+
],
|
|
881
|
+
authenticatorSelection: {
|
|
882
|
+
authenticatorAttachment: "platform",
|
|
883
|
+
residentKey: "preferred",
|
|
884
|
+
userVerification: "required"
|
|
885
|
+
},
|
|
886
|
+
timeout: 6e4
|
|
887
|
+
};
|
|
834
888
|
if (isInCrossOriginIframe()) {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
userVerification: "required"
|
|
850
|
-
},
|
|
851
|
-
timeout: 6e4
|
|
852
|
-
});
|
|
889
|
+
try {
|
|
890
|
+
await waitForDocumentFocus();
|
|
891
|
+
const credential2 = await navigator.credentials.create({
|
|
892
|
+
publicKey: publicKeyOptions
|
|
893
|
+
});
|
|
894
|
+
if (!credential2) {
|
|
895
|
+
throw new Error("Passkey creation was cancelled.");
|
|
896
|
+
}
|
|
897
|
+
return extractPasskeyResult(credential2);
|
|
898
|
+
} catch (err) {
|
|
899
|
+
if (err instanceof PasskeyIframeBlockedError) throw err;
|
|
900
|
+
if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
|
|
901
|
+
throw new PasskeyIframeBlockedError();
|
|
902
|
+
}
|
|
853
903
|
}
|
|
854
904
|
await waitForDocumentFocus();
|
|
855
905
|
const credential = await navigator.credentials.create({
|
|
856
|
-
publicKey:
|
|
857
|
-
challenge,
|
|
858
|
-
rp: { name: "Swype", id: rpId },
|
|
859
|
-
user: {
|
|
860
|
-
id: new TextEncoder().encode(params.userId),
|
|
861
|
-
name: params.displayName,
|
|
862
|
-
displayName: params.displayName
|
|
863
|
-
},
|
|
864
|
-
pubKeyCredParams: [
|
|
865
|
-
{ alg: -7, type: "public-key" },
|
|
866
|
-
// ES256 (P-256)
|
|
867
|
-
{ alg: -257, type: "public-key" }
|
|
868
|
-
// RS256
|
|
869
|
-
],
|
|
870
|
-
authenticatorSelection: {
|
|
871
|
-
authenticatorAttachment: "platform",
|
|
872
|
-
residentKey: "preferred",
|
|
873
|
-
userVerification: "required"
|
|
874
|
-
},
|
|
875
|
-
timeout: 6e4
|
|
876
|
-
}
|
|
906
|
+
publicKey: publicKeyOptions
|
|
877
907
|
});
|
|
878
908
|
if (!credential) {
|
|
879
909
|
throw new Error("Passkey creation was cancelled.");
|
|
880
910
|
}
|
|
911
|
+
return extractPasskeyResult(credential);
|
|
912
|
+
}
|
|
913
|
+
function extractPasskeyResult(credential) {
|
|
881
914
|
const response = credential.response;
|
|
882
915
|
const publicKeyBytes = response.getPublicKey?.();
|
|
883
916
|
return {
|
|
@@ -885,6 +918,29 @@ async function createPasskeyCredential(params) {
|
|
|
885
918
|
publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
|
|
886
919
|
};
|
|
887
920
|
}
|
|
921
|
+
function buildPasskeyPopupOptions(params) {
|
|
922
|
+
const challenge = new Uint8Array(32);
|
|
923
|
+
crypto.getRandomValues(challenge);
|
|
924
|
+
const rpId = resolvePasskeyRpId();
|
|
925
|
+
return {
|
|
926
|
+
challenge: toBase64(challenge),
|
|
927
|
+
rpId,
|
|
928
|
+
rpName: "Swype",
|
|
929
|
+
userId: toBase64(new TextEncoder().encode(params.userId)),
|
|
930
|
+
userName: params.displayName,
|
|
931
|
+
userDisplayName: params.displayName,
|
|
932
|
+
pubKeyCredParams: [
|
|
933
|
+
{ alg: -7, type: "public-key" },
|
|
934
|
+
{ alg: -257, type: "public-key" }
|
|
935
|
+
],
|
|
936
|
+
authenticatorSelection: {
|
|
937
|
+
authenticatorAttachment: "platform",
|
|
938
|
+
residentKey: "preferred",
|
|
939
|
+
userVerification: "required"
|
|
940
|
+
},
|
|
941
|
+
timeout: 6e4
|
|
942
|
+
};
|
|
943
|
+
}
|
|
888
944
|
async function deviceHasPasskey(credentialId) {
|
|
889
945
|
const found = await findDevicePasskey([credentialId]);
|
|
890
946
|
return found != null;
|
|
@@ -2647,18 +2703,20 @@ var hintStyle = (color) => ({
|
|
|
2647
2703
|
});
|
|
2648
2704
|
function CreatePasskeyScreen({
|
|
2649
2705
|
onCreatePasskey,
|
|
2650
|
-
onSkip,
|
|
2651
2706
|
onBack,
|
|
2652
2707
|
creating,
|
|
2653
|
-
error
|
|
2708
|
+
error,
|
|
2709
|
+
popupFallback = false,
|
|
2710
|
+
onCreatePasskeyViaPopup
|
|
2654
2711
|
}) {
|
|
2655
2712
|
const { tokens } = useSwypeConfig();
|
|
2713
|
+
const handleCreate = popupFallback && onCreatePasskeyViaPopup ? onCreatePasskeyViaPopup : onCreatePasskey;
|
|
2714
|
+
const buttonLabel = popupFallback ? "Open passkey setup" : "Create passkey";
|
|
2656
2715
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2657
2716
|
ScreenLayout,
|
|
2658
2717
|
{
|
|
2659
2718
|
footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2660
|
-
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick:
|
|
2661
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onSkip, style: skipStyle(tokens.textMuted), disabled: creating, children: "Skip for now" }),
|
|
2719
|
+
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleCreate, disabled: creating, loading: creating, children: buttonLabel }),
|
|
2662
2720
|
/* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
|
|
2663
2721
|
] }),
|
|
2664
2722
|
children: [
|
|
@@ -2671,7 +2729,7 @@ function CreatePasskeyScreen({
|
|
|
2671
2729
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 14c0 1.5 1.34 2.5 3 2.5s3-1 3-2.5", stroke: tokens.accent, strokeWidth: "1.2", strokeLinecap: "round" })
|
|
2672
2730
|
] }) }),
|
|
2673
2731
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle3(tokens.text), children: "Create your passkey" }),
|
|
2674
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle3(tokens.textSecondary), children: "Use Face ID to sign in instantly \u2014 no passwords, no codes." }),
|
|
2732
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle3(tokens.textSecondary), children: popupFallback ? "Your browser requires a separate window for passkey setup. Tap the button below to continue." : "Use Face ID to sign in instantly \u2014 no passwords, no codes." }),
|
|
2675
2733
|
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle2(tokens), children: error }),
|
|
2676
2734
|
/* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: "Your passkey is stored securely on your device. Swype never sees your biometric data." })
|
|
2677
2735
|
] })
|
|
@@ -2713,19 +2771,6 @@ var errorBannerStyle2 = (tokens) => ({
|
|
|
2713
2771
|
width: "100%",
|
|
2714
2772
|
textAlign: "left"
|
|
2715
2773
|
});
|
|
2716
|
-
var skipStyle = (color) => ({
|
|
2717
|
-
background: "transparent",
|
|
2718
|
-
border: "none",
|
|
2719
|
-
color,
|
|
2720
|
-
cursor: "pointer",
|
|
2721
|
-
fontFamily: "inherit",
|
|
2722
|
-
fontSize: "0.88rem",
|
|
2723
|
-
fontWeight: 500,
|
|
2724
|
-
display: "block",
|
|
2725
|
-
width: "100%",
|
|
2726
|
-
textAlign: "center",
|
|
2727
|
-
padding: "12px 0 0"
|
|
2728
|
-
});
|
|
2729
2774
|
var WALLET_EMOJIS = {
|
|
2730
2775
|
rabby: "\u{1F430}",
|
|
2731
2776
|
ora: "\u2666\uFE0F",
|
|
@@ -3944,6 +3989,74 @@ var hintStyle3 = (color) => ({
|
|
|
3944
3989
|
color,
|
|
3945
3990
|
margin: "8px 0 0"
|
|
3946
3991
|
});
|
|
3992
|
+
var PaymentErrorBoundary = class extends react.Component {
|
|
3993
|
+
constructor(props) {
|
|
3994
|
+
super(props);
|
|
3995
|
+
this.state = { hasError: false };
|
|
3996
|
+
}
|
|
3997
|
+
static getDerivedStateFromError() {
|
|
3998
|
+
return { hasError: true };
|
|
3999
|
+
}
|
|
4000
|
+
componentDidCatch(error, info) {
|
|
4001
|
+
console.error("[SwypePayment] Uncaught error:", error, info.componentStack);
|
|
4002
|
+
}
|
|
4003
|
+
handleReset = () => {
|
|
4004
|
+
this.setState({ hasError: false });
|
|
4005
|
+
this.props.onReset();
|
|
4006
|
+
};
|
|
4007
|
+
render() {
|
|
4008
|
+
if (!this.state.hasError) {
|
|
4009
|
+
return this.props.children;
|
|
4010
|
+
}
|
|
4011
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle8, children: [
|
|
4012
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: iconStyle4, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", children: [
|
|
4013
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#ef4444", strokeWidth: "1.5" }),
|
|
4014
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v5", stroke: "#ef4444", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
4015
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "16", r: "0.75", fill: "#ef4444" })
|
|
4016
|
+
] }) }),
|
|
4017
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9, children: "Something went wrong" }),
|
|
4018
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: messageStyle, children: "An unexpected error occurred. Please try again." }),
|
|
4019
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: this.handleReset, style: buttonStyle3, children: "Try again" })
|
|
4020
|
+
] });
|
|
4021
|
+
}
|
|
4022
|
+
};
|
|
4023
|
+
var containerStyle8 = {
|
|
4024
|
+
display: "flex",
|
|
4025
|
+
flexDirection: "column",
|
|
4026
|
+
alignItems: "center",
|
|
4027
|
+
justifyContent: "center",
|
|
4028
|
+
textAlign: "center",
|
|
4029
|
+
padding: "48px 24px",
|
|
4030
|
+
minHeight: "100%",
|
|
4031
|
+
maxWidth: 420,
|
|
4032
|
+
margin: "0 auto"
|
|
4033
|
+
};
|
|
4034
|
+
var iconStyle4 = {
|
|
4035
|
+
marginBottom: 20
|
|
4036
|
+
};
|
|
4037
|
+
var headingStyle9 = {
|
|
4038
|
+
fontSize: "1.25rem",
|
|
4039
|
+
fontWeight: 700,
|
|
4040
|
+
color: "#1a1a1a",
|
|
4041
|
+
margin: "0 0 8px"
|
|
4042
|
+
};
|
|
4043
|
+
var messageStyle = {
|
|
4044
|
+
fontSize: "0.9rem",
|
|
4045
|
+
color: "#666",
|
|
4046
|
+
margin: "0 0 28px",
|
|
4047
|
+
lineHeight: 1.5
|
|
4048
|
+
};
|
|
4049
|
+
var buttonStyle3 = {
|
|
4050
|
+
background: "#1a1a1a",
|
|
4051
|
+
color: "#fff",
|
|
4052
|
+
border: "none",
|
|
4053
|
+
borderRadius: 12,
|
|
4054
|
+
padding: "12px 32px",
|
|
4055
|
+
fontSize: "0.95rem",
|
|
4056
|
+
fontWeight: 600,
|
|
4057
|
+
fontFamily: "inherit",
|
|
4058
|
+
cursor: "pointer"
|
|
4059
|
+
};
|
|
3947
4060
|
function isInIframe() {
|
|
3948
4061
|
try {
|
|
3949
4062
|
return typeof window !== "undefined" && window !== window.top;
|
|
@@ -4021,7 +4134,14 @@ function buildSelectSourceChoices(options) {
|
|
|
4021
4134
|
}
|
|
4022
4135
|
return chainChoices;
|
|
4023
4136
|
}
|
|
4024
|
-
function SwypePayment({
|
|
4137
|
+
function SwypePayment(props) {
|
|
4138
|
+
const resetKey = react.useRef(0);
|
|
4139
|
+
const handleBoundaryReset = react.useCallback(() => {
|
|
4140
|
+
resetKey.current += 1;
|
|
4141
|
+
}, []);
|
|
4142
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
|
|
4143
|
+
}
|
|
4144
|
+
function SwypePaymentInner({
|
|
4025
4145
|
destination,
|
|
4026
4146
|
onComplete,
|
|
4027
4147
|
onError,
|
|
@@ -4062,6 +4182,7 @@ function SwypePayment({
|
|
|
4062
4182
|
const [transfer, setTransfer] = react.useState(null);
|
|
4063
4183
|
const [creatingTransfer, setCreatingTransfer] = react.useState(false);
|
|
4064
4184
|
const [registeringPasskey, setRegisteringPasskey] = react.useState(false);
|
|
4185
|
+
const [passkeyPopupNeeded, setPasskeyPopupNeeded] = react.useState(false);
|
|
4065
4186
|
const [activeCredentialId, setActiveCredentialId] = react.useState(() => {
|
|
4066
4187
|
if (typeof window === "undefined") return null;
|
|
4067
4188
|
return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
|
|
@@ -4212,7 +4333,7 @@ function SwypePayment({
|
|
|
4212
4333
|
}
|
|
4213
4334
|
setStep("create-passkey");
|
|
4214
4335
|
} catch {
|
|
4215
|
-
if (!cancelled) setStep("
|
|
4336
|
+
if (!cancelled) setStep("create-passkey");
|
|
4216
4337
|
}
|
|
4217
4338
|
};
|
|
4218
4339
|
checkPasskey();
|
|
@@ -4564,44 +4685,59 @@ function SwypePayment({
|
|
|
4564
4685
|
merchantAuthorization,
|
|
4565
4686
|
transfer
|
|
4566
4687
|
]);
|
|
4688
|
+
const completePasskeyRegistration = react.useCallback(async (credentialId, publicKey) => {
|
|
4689
|
+
const token = await getAccessToken();
|
|
4690
|
+
if (!token) throw new Error("Not authenticated");
|
|
4691
|
+
await registerPasskey(apiBaseUrl, token, credentialId, publicKey);
|
|
4692
|
+
setActiveCredentialId(credentialId);
|
|
4693
|
+
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
4694
|
+
setPasskeyPopupNeeded(false);
|
|
4695
|
+
const hasActiveWallet = accounts.some(
|
|
4696
|
+
(a) => a.wallets.some((w) => w.status === "ACTIVE")
|
|
4697
|
+
);
|
|
4698
|
+
if (accounts.length === 0 || !hasActiveWallet) {
|
|
4699
|
+
setStep("wallet-picker");
|
|
4700
|
+
} else {
|
|
4701
|
+
setStep("deposit");
|
|
4702
|
+
}
|
|
4703
|
+
}, [getAccessToken, apiBaseUrl, accounts]);
|
|
4567
4704
|
const handleRegisterPasskey = react.useCallback(async () => {
|
|
4568
4705
|
setRegisteringPasskey(true);
|
|
4569
4706
|
setError(null);
|
|
4570
4707
|
try {
|
|
4571
|
-
const token = await getAccessToken();
|
|
4572
|
-
if (!token) throw new Error("Not authenticated");
|
|
4573
4708
|
const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
|
|
4574
4709
|
const { credentialId, publicKey } = await createPasskeyCredential({
|
|
4575
4710
|
userId: user?.id ?? "unknown",
|
|
4576
4711
|
displayName: passkeyDisplayName
|
|
4577
4712
|
});
|
|
4578
|
-
await
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
(a) => a.wallets.some((w) => w.status === "ACTIVE")
|
|
4583
|
-
);
|
|
4584
|
-
if (accounts.length === 0 || !hasActiveWallet) {
|
|
4585
|
-
setStep("wallet-picker");
|
|
4713
|
+
await completePasskeyRegistration(credentialId, publicKey);
|
|
4714
|
+
} catch (err) {
|
|
4715
|
+
if (err instanceof PasskeyIframeBlockedError) {
|
|
4716
|
+
setPasskeyPopupNeeded(true);
|
|
4586
4717
|
} else {
|
|
4587
|
-
|
|
4718
|
+
setError(err instanceof Error ? err.message : "Failed to register passkey");
|
|
4588
4719
|
}
|
|
4720
|
+
} finally {
|
|
4721
|
+
setRegisteringPasskey(false);
|
|
4722
|
+
}
|
|
4723
|
+
}, [user, completePasskeyRegistration]);
|
|
4724
|
+
const handleCreatePasskeyViaPopup = react.useCallback(async () => {
|
|
4725
|
+
setRegisteringPasskey(true);
|
|
4726
|
+
setError(null);
|
|
4727
|
+
try {
|
|
4728
|
+
const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
|
|
4729
|
+
const popupOptions = buildPasskeyPopupOptions({
|
|
4730
|
+
userId: user?.id ?? "unknown",
|
|
4731
|
+
displayName: passkeyDisplayName
|
|
4732
|
+
});
|
|
4733
|
+
const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
|
|
4734
|
+
await completePasskeyRegistration(credentialId, publicKey);
|
|
4589
4735
|
} catch (err) {
|
|
4590
4736
|
setError(err instanceof Error ? err.message : "Failed to register passkey");
|
|
4591
4737
|
} finally {
|
|
4592
4738
|
setRegisteringPasskey(false);
|
|
4593
4739
|
}
|
|
4594
|
-
}, [
|
|
4595
|
-
const handleSkipPasskey = react.useCallback(() => {
|
|
4596
|
-
const hasActiveWallet = accounts.some(
|
|
4597
|
-
(a) => a.wallets.some((w) => w.status === "ACTIVE")
|
|
4598
|
-
);
|
|
4599
|
-
if (accounts.length === 0 || !hasActiveWallet) {
|
|
4600
|
-
setStep("wallet-picker");
|
|
4601
|
-
} else {
|
|
4602
|
-
setStep("deposit");
|
|
4603
|
-
}
|
|
4604
|
-
}, [accounts]);
|
|
4740
|
+
}, [user, completePasskeyRegistration]);
|
|
4605
4741
|
const handleSelectProvider = react.useCallback((providerId) => {
|
|
4606
4742
|
setSelectedProviderId(providerId);
|
|
4607
4743
|
setSelectedAccountId(null);
|
|
@@ -4717,10 +4853,11 @@ function SwypePayment({
|
|
|
4717
4853
|
CreatePasskeyScreen,
|
|
4718
4854
|
{
|
|
4719
4855
|
onCreatePasskey: handleRegisterPasskey,
|
|
4720
|
-
|
|
4721
|
-
onBack: () => setStep("login"),
|
|
4856
|
+
onBack: handleLogout,
|
|
4722
4857
|
creating: registeringPasskey,
|
|
4723
|
-
error
|
|
4858
|
+
error,
|
|
4859
|
+
popupFallback: passkeyPopupNeeded,
|
|
4860
|
+
onCreatePasskeyViaPopup: handleCreatePasskeyViaPopup
|
|
4724
4861
|
}
|
|
4725
4862
|
);
|
|
4726
4863
|
}
|
|
@@ -4733,7 +4870,7 @@ function SwypePayment({
|
|
|
4733
4870
|
loading: creatingTransfer,
|
|
4734
4871
|
onSelectProvider: handleSelectProvider,
|
|
4735
4872
|
onContinueConnection: handleContinueConnection,
|
|
4736
|
-
onBack: () => setStep("create-passkey")
|
|
4873
|
+
onBack: () => setStep(activeCredentialId ? "deposit" : "create-passkey")
|
|
4737
4874
|
}
|
|
4738
4875
|
);
|
|
4739
4876
|
}
|
|
@@ -4855,6 +4992,7 @@ function SwypePayment({
|
|
|
4855
4992
|
|
|
4856
4993
|
exports.IconCircle = IconCircle;
|
|
4857
4994
|
exports.OutlineButton = OutlineButton;
|
|
4995
|
+
exports.PasskeyIframeBlockedError = PasskeyIframeBlockedError;
|
|
4858
4996
|
exports.PoweredByFooter = PoweredByFooter;
|
|
4859
4997
|
exports.PrimaryButton = PrimaryButton;
|
|
4860
4998
|
exports.ScreenHeader = ScreenHeader;
|
|
@@ -4866,7 +5004,9 @@ exports.Spinner = Spinner;
|
|
|
4866
5004
|
exports.StepList = StepList;
|
|
4867
5005
|
exports.SwypePayment = SwypePayment;
|
|
4868
5006
|
exports.SwypeProvider = SwypeProvider;
|
|
5007
|
+
exports.buildPasskeyPopupOptions = buildPasskeyPopupOptions;
|
|
4869
5008
|
exports.createPasskeyCredential = createPasskeyCredential;
|
|
5009
|
+
exports.createPasskeyViaPopup = createPasskeyViaPopup;
|
|
4870
5010
|
exports.darkTheme = darkTheme;
|
|
4871
5011
|
exports.deviceHasPasskey = deviceHasPasskey;
|
|
4872
5012
|
exports.findDevicePasskey = findDevicePasskey;
|