@swype-org/react-sdk 0.1.47 → 0.1.49

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 CHANGED
@@ -669,13 +669,20 @@ function isInCrossOriginIframe() {
669
669
  }
670
670
  }
671
671
  var delegationCounter = 0;
672
+ var DELEGATION_CREATE_TIMEOUT_MS = 6e4;
673
+ var DELEGATION_GET_TIMEOUT_MS = 3e4;
672
674
  function delegatePasskeyCreate(options) {
673
675
  return new Promise((resolve, reject) => {
674
676
  const id = `pc-${++delegationCounter}-${Date.now()}`;
677
+ const timer = setTimeout(() => {
678
+ window.removeEventListener("message", handler);
679
+ reject(new Error("Passkey creation timed out. Please try again."));
680
+ }, DELEGATION_CREATE_TIMEOUT_MS);
675
681
  const handler = (event) => {
676
682
  const data = event.data;
677
683
  if (!data || typeof data !== "object") return;
678
684
  if (data.type !== "swype:passkey-create-response" || data.id !== id) return;
685
+ clearTimeout(timer);
679
686
  window.removeEventListener("message", handler);
680
687
  if (data.error) {
681
688
  reject(new Error(data.error));
@@ -692,10 +699,15 @@ function delegatePasskeyCreate(options) {
692
699
  function delegatePasskeyGet(options) {
693
700
  return new Promise((resolve, reject) => {
694
701
  const id = `pg-${++delegationCounter}-${Date.now()}`;
702
+ const timer = setTimeout(() => {
703
+ window.removeEventListener("message", handler);
704
+ reject(new Error("Passkey verification timed out. Please try again."));
705
+ }, DELEGATION_GET_TIMEOUT_MS);
695
706
  const handler = (event) => {
696
707
  const data = event.data;
697
708
  if (!data || typeof data !== "object") return;
698
709
  if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
710
+ clearTimeout(timer);
699
711
  window.removeEventListener("message", handler);
700
712
  if (data.error) {
701
713
  reject(new Error(data.error));
@@ -2647,7 +2659,6 @@ var hintStyle = (color) => ({
2647
2659
  });
2648
2660
  function CreatePasskeyScreen({
2649
2661
  onCreatePasskey,
2650
- onSkip,
2651
2662
  onBack,
2652
2663
  creating,
2653
2664
  error
@@ -2658,7 +2669,6 @@ function CreatePasskeyScreen({
2658
2669
  {
2659
2670
  footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2660
2671
  /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onCreatePasskey, disabled: creating, loading: creating, children: "Create passkey" }),
2661
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onSkip, style: skipStyle(tokens.textMuted), disabled: creating, children: "Skip for now" }),
2662
2672
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
2663
2673
  ] }),
2664
2674
  children: [
@@ -2713,19 +2723,6 @@ var errorBannerStyle2 = (tokens) => ({
2713
2723
  width: "100%",
2714
2724
  textAlign: "left"
2715
2725
  });
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
2726
  var WALLET_EMOJIS = {
2730
2727
  rabby: "\u{1F430}",
2731
2728
  ora: "\u2666\uFE0F",
@@ -2738,6 +2735,7 @@ function truncateAddress(address) {
2738
2735
  function WalletPickerScreen({
2739
2736
  providers,
2740
2737
  pendingConnections,
2738
+ loading,
2741
2739
  onSelectProvider,
2742
2740
  onContinueConnection,
2743
2741
  onBack
@@ -2751,6 +2749,12 @@ function WalletPickerScreen({
2751
2749
  { id: "ora", name: "Ora" },
2752
2750
  { id: "phantom", name: "Phantom" }
2753
2751
  ];
2752
+ if (loading) {
2753
+ return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
2754
+ /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Set up Swype", onBack }),
2755
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Connecting..." }) })
2756
+ ] });
2757
+ }
2754
2758
  return /* @__PURE__ */ jsxRuntime.jsxs(
2755
2759
  ScreenLayout,
2756
2760
  {
@@ -3851,6 +3855,167 @@ var waitHintStyle = (color) => ({
3851
3855
  color,
3852
3856
  margin: 0
3853
3857
  });
3858
+ function OpenWalletScreen({
3859
+ walletName,
3860
+ deeplinkUri,
3861
+ loading,
3862
+ onLogout
3863
+ }) {
3864
+ const { tokens } = useSwypeConfig();
3865
+ const displayName = walletName ?? "your wallet";
3866
+ const logoSrc = walletName ? KNOWN_LOGOS[walletName.toLowerCase()] : void 0;
3867
+ const handleOpen = react.useCallback(() => {
3868
+ const opened = window.open(deeplinkUri, "_blank");
3869
+ if (!opened) {
3870
+ window.location.href = deeplinkUri;
3871
+ }
3872
+ }, [deeplinkUri]);
3873
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3874
+ ScreenLayout,
3875
+ {
3876
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3877
+ !loading && /* @__PURE__ */ jsxRuntime.jsxs(PrimaryButton, { onClick: handleOpen, children: [
3878
+ "Open ",
3879
+ displayName
3880
+ ] }),
3881
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: hintStyle3(tokens.textMuted), children: loading ? "Preparing authorization..." : "Tap the button to authorize in your wallet app" })
3882
+ ] }),
3883
+ children: [
3884
+ /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
3885
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
3886
+ logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
3887
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
3888
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle8(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
3889
+ !loading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: waitingBadgeStyle(tokens), children: [
3890
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 14 }),
3891
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Waiting for authorization..." })
3892
+ ] })
3893
+ ] })
3894
+ ]
3895
+ }
3896
+ );
3897
+ }
3898
+ var contentStyle6 = {
3899
+ flex: 1,
3900
+ display: "flex",
3901
+ flexDirection: "column",
3902
+ alignItems: "center",
3903
+ justifyContent: "center",
3904
+ textAlign: "center",
3905
+ padding: "0 24px"
3906
+ };
3907
+ var logoStyle = {
3908
+ width: 56,
3909
+ height: 56,
3910
+ borderRadius: 14,
3911
+ objectFit: "contain"
3912
+ };
3913
+ var headingStyle8 = (color) => ({
3914
+ fontSize: "1.45rem",
3915
+ fontWeight: 700,
3916
+ letterSpacing: "-0.02em",
3917
+ color,
3918
+ margin: "20px 0 8px"
3919
+ });
3920
+ var subtitleStyle8 = (color) => ({
3921
+ fontSize: "0.9rem",
3922
+ color,
3923
+ margin: "0 0 24px",
3924
+ lineHeight: 1.5,
3925
+ maxWidth: 280
3926
+ });
3927
+ var waitingBadgeStyle = (tokens) => ({
3928
+ display: "inline-flex",
3929
+ alignItems: "center",
3930
+ gap: 8,
3931
+ padding: "8px 16px",
3932
+ borderRadius: 20,
3933
+ background: tokens.bgInput,
3934
+ border: `1px solid ${tokens.border}`,
3935
+ color: tokens.textMuted,
3936
+ fontSize: "0.82rem"
3937
+ });
3938
+ var hintStyle3 = (color) => ({
3939
+ textAlign: "center",
3940
+ fontSize: "0.82rem",
3941
+ color,
3942
+ margin: "8px 0 0"
3943
+ });
3944
+ var PaymentErrorBoundary = class extends react.Component {
3945
+ constructor(props) {
3946
+ super(props);
3947
+ this.state = { hasError: false };
3948
+ }
3949
+ static getDerivedStateFromError() {
3950
+ return { hasError: true };
3951
+ }
3952
+ componentDidCatch(error, info) {
3953
+ console.error("[SwypePayment] Uncaught error:", error, info.componentStack);
3954
+ }
3955
+ handleReset = () => {
3956
+ this.setState({ hasError: false });
3957
+ this.props.onReset();
3958
+ };
3959
+ render() {
3960
+ if (!this.state.hasError) {
3961
+ return this.props.children;
3962
+ }
3963
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle8, children: [
3964
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: iconStyle4, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", children: [
3965
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#ef4444", strokeWidth: "1.5" }),
3966
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v5", stroke: "#ef4444", strokeWidth: "1.5", strokeLinecap: "round" }),
3967
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "16", r: "0.75", fill: "#ef4444" })
3968
+ ] }) }),
3969
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9, children: "Something went wrong" }),
3970
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: messageStyle, children: "An unexpected error occurred. Please try again." }),
3971
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: this.handleReset, style: buttonStyle3, children: "Try again" })
3972
+ ] });
3973
+ }
3974
+ };
3975
+ var containerStyle8 = {
3976
+ display: "flex",
3977
+ flexDirection: "column",
3978
+ alignItems: "center",
3979
+ justifyContent: "center",
3980
+ textAlign: "center",
3981
+ padding: "48px 24px",
3982
+ minHeight: "100%",
3983
+ maxWidth: 420,
3984
+ margin: "0 auto"
3985
+ };
3986
+ var iconStyle4 = {
3987
+ marginBottom: 20
3988
+ };
3989
+ var headingStyle9 = {
3990
+ fontSize: "1.25rem",
3991
+ fontWeight: 700,
3992
+ color: "#1a1a1a",
3993
+ margin: "0 0 8px"
3994
+ };
3995
+ var messageStyle = {
3996
+ fontSize: "0.9rem",
3997
+ color: "#666",
3998
+ margin: "0 0 28px",
3999
+ lineHeight: 1.5
4000
+ };
4001
+ var buttonStyle3 = {
4002
+ background: "#1a1a1a",
4003
+ color: "#fff",
4004
+ border: "none",
4005
+ borderRadius: 12,
4006
+ padding: "12px 32px",
4007
+ fontSize: "0.95rem",
4008
+ fontWeight: 600,
4009
+ fontFamily: "inherit",
4010
+ cursor: "pointer"
4011
+ };
4012
+ function isInIframe() {
4013
+ try {
4014
+ return typeof window !== "undefined" && window !== window.top;
4015
+ } catch {
4016
+ return true;
4017
+ }
4018
+ }
3854
4019
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
3855
4020
  var MIN_SEND_AMOUNT_USD = 0.25;
3856
4021
  function computeSmartDefaults(accts, transferAmount) {
@@ -3921,7 +4086,14 @@ function buildSelectSourceChoices(options) {
3921
4086
  }
3922
4087
  return chainChoices;
3923
4088
  }
3924
- function SwypePayment({
4089
+ function SwypePayment(props) {
4090
+ const resetKey = react.useRef(0);
4091
+ const handleBoundaryReset = react.useCallback(() => {
4092
+ resetKey.current += 1;
4093
+ }, []);
4094
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
4095
+ }
4096
+ function SwypePaymentInner({
3925
4097
  destination,
3926
4098
  onComplete,
3927
4099
  onError,
@@ -3971,6 +4143,7 @@ function SwypePayment({
3971
4143
  const [otpCode, setOtpCode] = react.useState("");
3972
4144
  const [oneTapLimit, setOneTapLimit] = react.useState(100);
3973
4145
  const [mobileFlow, setMobileFlow] = react.useState(false);
4146
+ const [deeplinkUri, setDeeplinkUri] = react.useState(null);
3974
4147
  const pollingTransferIdRef = react.useRef(null);
3975
4148
  const mobileSigningTransferIdRef = react.useRef(null);
3976
4149
  const mobileSetupFlowRef = react.useRef(false);
@@ -4111,7 +4284,7 @@ function SwypePayment({
4111
4284
  }
4112
4285
  setStep("create-passkey");
4113
4286
  } catch {
4114
- if (!cancelled) setStep("deposit");
4287
+ if (!cancelled) setStep("create-passkey");
4115
4288
  }
4116
4289
  };
4117
4290
  checkPasskey();
@@ -4218,6 +4391,7 @@ function SwypePayment({
4218
4391
  if (mobileSetupFlowRef.current) {
4219
4392
  mobileSetupFlowRef.current = false;
4220
4393
  setMobileFlow(false);
4394
+ setDeeplinkUri(null);
4221
4395
  polling.stopPolling();
4222
4396
  setTransfer(polledTransfer);
4223
4397
  reloadAccounts().catch(() => {
@@ -4358,12 +4532,15 @@ function SwypePayment({
4358
4532
  return;
4359
4533
  }
4360
4534
  const isSetupRedirect = mobileSetupFlowRef.current;
4361
- if (!isSetupRedirect) {
4535
+ if (isSetupRedirect) {
4536
+ setStep("open-wallet");
4537
+ } else {
4362
4538
  setStep("processing");
4363
4539
  }
4364
4540
  processingStartedAtRef.current = Date.now();
4365
4541
  setError(null);
4366
4542
  setCreatingTransfer(true);
4543
+ setDeeplinkUri(null);
4367
4544
  setMobileFlow(false);
4368
4545
  try {
4369
4546
  if (transfer?.status === "AUTHORIZED") {
@@ -4387,7 +4564,7 @@ function SwypePayment({
4387
4564
  const isActiveWallet = effectiveSourceType === "walletId" && accounts.some(
4388
4565
  (a) => a.wallets.some((w) => w.id === effectiveSourceId && w.status === "ACTIVE")
4389
4566
  );
4390
- if (!isActiveWallet) {
4567
+ if (!isActiveWallet && !isSetupRedirect) {
4391
4568
  let found = false;
4392
4569
  for (const acct of accounts) {
4393
4570
  for (const wallet of acct.wallets) {
@@ -4417,11 +4594,14 @@ function SwypePayment({
4417
4594
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
4418
4595
  });
4419
4596
  if (!shouldUseConnector) {
4597
+ const uri = t.authorizationSessions[0].uri;
4420
4598
  setMobileFlow(true);
4421
4599
  pollingTransferIdRef.current = t.id;
4422
4600
  polling.startPolling(t.id);
4423
- if (!isActiveWallet) {
4424
- window.location.href = t.authorizationSessions[0].uri;
4601
+ setDeeplinkUri(uri);
4602
+ setStep("open-wallet");
4603
+ if (!isInIframe()) {
4604
+ window.location.href = uri;
4425
4605
  }
4426
4606
  return;
4427
4607
  } else {
@@ -4435,7 +4615,7 @@ function SwypePayment({
4435
4615
  const msg = err instanceof Error ? err.message : "Transfer failed";
4436
4616
  setError(msg);
4437
4617
  onError?.(msg);
4438
- setStep("deposit");
4618
+ setStep(isSetupRedirect ? "wallet-picker" : "deposit");
4439
4619
  } finally {
4440
4620
  setCreatingTransfer(false);
4441
4621
  }
@@ -4484,16 +4664,6 @@ function SwypePayment({
4484
4664
  setRegisteringPasskey(false);
4485
4665
  }
4486
4666
  }, [getAccessToken, user, apiBaseUrl, accounts]);
4487
- const handleSkipPasskey = react.useCallback(() => {
4488
- const hasActiveWallet = accounts.some(
4489
- (a) => a.wallets.some((w) => w.status === "ACTIVE")
4490
- );
4491
- if (accounts.length === 0 || !hasActiveWallet) {
4492
- setStep("wallet-picker");
4493
- } else {
4494
- setStep("deposit");
4495
- }
4496
- }, [accounts]);
4497
4667
  const handleSelectProvider = react.useCallback((providerId) => {
4498
4668
  setSelectedProviderId(providerId);
4499
4669
  setSelectedAccountId(null);
@@ -4526,6 +4696,7 @@ function SwypePayment({
4526
4696
  setError(null);
4527
4697
  setAmount(depositAmount != null ? depositAmount.toString() : "");
4528
4698
  setMobileFlow(false);
4699
+ setDeeplinkUri(null);
4529
4700
  processingStartedAtRef.current = null;
4530
4701
  pollingTransferIdRef.current = null;
4531
4702
  mobileSigningTransferIdRef.current = null;
@@ -4557,6 +4728,7 @@ function SwypePayment({
4557
4728
  setConnectingNewAccount(false);
4558
4729
  setAmount(depositAmount != null ? depositAmount.toString() : "");
4559
4730
  setMobileFlow(false);
4731
+ setDeeplinkUri(null);
4560
4732
  resetHeadlessLogin();
4561
4733
  }, [logout, polling, depositAmount, resetHeadlessLogin]);
4562
4734
  if (!ready) {
@@ -4607,8 +4779,7 @@ function SwypePayment({
4607
4779
  CreatePasskeyScreen,
4608
4780
  {
4609
4781
  onCreatePasskey: handleRegisterPasskey,
4610
- onSkip: handleSkipPasskey,
4611
- onBack: () => setStep("login"),
4782
+ onBack: handleLogout,
4612
4783
  creating: registeringPasskey,
4613
4784
  error
4614
4785
  }
@@ -4620,9 +4791,22 @@ function SwypePayment({
4620
4791
  {
4621
4792
  providers,
4622
4793
  pendingConnections,
4794
+ loading: creatingTransfer,
4623
4795
  onSelectProvider: handleSelectProvider,
4624
4796
  onContinueConnection: handleContinueConnection,
4625
- onBack: () => setStep("create-passkey")
4797
+ onBack: () => setStep(activeCredentialId ? "deposit" : "create-passkey")
4798
+ }
4799
+ );
4800
+ }
4801
+ if (step === "open-wallet") {
4802
+ const providerName = providers.find((p) => p.id === selectedProviderId)?.name ?? null;
4803
+ return /* @__PURE__ */ jsxRuntime.jsx(
4804
+ OpenWalletScreen,
4805
+ {
4806
+ walletName: providerName,
4807
+ deeplinkUri: deeplinkUri ?? "",
4808
+ loading: creatingTransfer || !deeplinkUri,
4809
+ onLogout: handleLogout
4626
4810
  }
4627
4811
  );
4628
4812
  }