@swype-org/react-sdk 0.1.14 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -591,6 +591,28 @@ var ACTION_POLL_MAX_RETRIES = 20;
591
591
  var SIGN_PERMIT2_POLL_MS = 1e3;
592
592
  var SIGN_PERMIT2_MAX_POLLS = 15;
593
593
  var TRANSFER_SIGN_MAX_POLLS = 60;
594
+ function waitForDocumentFocus(timeoutMs = 5e3, intervalMs = 100) {
595
+ return new Promise((resolve, reject) => {
596
+ if (typeof document === "undefined") {
597
+ resolve();
598
+ return;
599
+ }
600
+ if (document.hasFocus()) {
601
+ resolve();
602
+ return;
603
+ }
604
+ const deadline = Date.now() + timeoutMs;
605
+ const timer = setInterval(() => {
606
+ if (document.hasFocus()) {
607
+ clearInterval(timer);
608
+ resolve();
609
+ } else if (Date.now() >= deadline) {
610
+ clearInterval(timer);
611
+ resolve();
612
+ }
613
+ }, intervalMs);
614
+ });
615
+ }
594
616
  function actionSuccess(action, message, data) {
595
617
  return { actionId: action.id, type: action.type, status: "success", message, data };
596
618
  }
@@ -682,6 +704,7 @@ async function createPasskeyCredential(userIdentifier) {
682
704
  const challenge = new Uint8Array(32);
683
705
  crypto.getRandomValues(challenge);
684
706
  const rpId = resolvePasskeyRpId();
707
+ await waitForDocumentFocus();
685
708
  const credential = await navigator.credentials.create({
686
709
  publicKey: {
687
710
  challenge,
@@ -715,6 +738,28 @@ async function createPasskeyCredential(userIdentifier) {
715
738
  publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
716
739
  };
717
740
  }
741
+ async function deviceHasPasskey(credentialId) {
742
+ try {
743
+ const challenge = new Uint8Array(32);
744
+ crypto.getRandomValues(challenge);
745
+ await waitForDocumentFocus();
746
+ const assertion = await navigator.credentials.get({
747
+ publicKey: {
748
+ challenge,
749
+ rpId: resolvePasskeyRpId(),
750
+ allowCredentials: [{
751
+ type: "public-key",
752
+ id: base64ToBytes(credentialId)
753
+ }],
754
+ userVerification: "required",
755
+ timeout: 3e4
756
+ }
757
+ });
758
+ return assertion != null;
759
+ } catch {
760
+ return false;
761
+ }
762
+ }
718
763
  function useTransferPolling(intervalMs = 3e3) {
719
764
  const { apiBaseUrl } = useSwypeConfig();
720
765
  const { getAccessToken } = reactAuth.usePrivy();
@@ -1187,6 +1232,7 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1187
1232
  type: "public-key",
1188
1233
  id: base64ToBytes(payload.passkeyCredentialId)
1189
1234
  }] : void 0;
1235
+ await waitForDocumentFocus();
1190
1236
  const assertion = await navigator.credentials.get({
1191
1237
  publicKey: {
1192
1238
  challenge: hashBytes,
@@ -1983,6 +2029,7 @@ function buildProcessingTimeoutMessage(status) {
1983
2029
  return `Payment is taking longer than expected (status: ${status}). Please try again.`;
1984
2030
  }
1985
2031
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
2032
+ var MIN_SEND_AMOUNT_USD = 0.25;
1986
2033
  function isMobile() {
1987
2034
  if (typeof navigator === "undefined") return false;
1988
2035
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
@@ -2116,12 +2163,27 @@ function SwypePayment({
2116
2163
  if (!token || cancelled) return;
2117
2164
  const { config } = await fetchUserConfig(apiBaseUrl, token);
2118
2165
  if (cancelled) return;
2119
- if (!config.passkey || !activeCredentialId) {
2120
- setStep("register-passkey");
2121
- } else if (depositAmount != null && depositAmount > 0) {
2122
- setStep("ready");
2166
+ if (config.passkey?.credentialId) {
2167
+ const hasKey = activeCredentialId ? activeCredentialId === config.passkey.credentialId : await deviceHasPasskey(config.passkey.credentialId);
2168
+ if (cancelled) return;
2169
+ if (hasKey) {
2170
+ if (!activeCredentialId) {
2171
+ setActiveCredentialId(config.passkey.credentialId);
2172
+ window.localStorage.setItem(
2173
+ ACTIVE_CREDENTIAL_STORAGE_KEY,
2174
+ config.passkey.credentialId
2175
+ );
2176
+ }
2177
+ if (depositAmount != null && depositAmount > 0) {
2178
+ setStep("ready");
2179
+ } else {
2180
+ setStep("enter-amount");
2181
+ }
2182
+ } else {
2183
+ setStep("register-passkey");
2184
+ }
2123
2185
  } else {
2124
- setStep("enter-amount");
2186
+ setStep("register-passkey");
2125
2187
  }
2126
2188
  } catch {
2127
2189
  if (!cancelled) {
@@ -2308,8 +2370,8 @@ function SwypePayment({
2308
2370
  }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
2309
2371
  const handlePay = react.useCallback(async () => {
2310
2372
  const parsedAmount = parseFloat(amount);
2311
- if (isNaN(parsedAmount) || parsedAmount <= 0) {
2312
- setError("Enter a valid amount.");
2373
+ if (isNaN(parsedAmount) || parsedAmount < MIN_SEND_AMOUNT_USD) {
2374
+ setError(`Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.`);
2313
2375
  return;
2314
2376
  }
2315
2377
  if (!sourceId) {
@@ -2685,7 +2747,7 @@ function SwypePayment({
2685
2747
  }
2686
2748
  if (step === "enter-amount") {
2687
2749
  const parsedAmount = parseFloat(amount);
2688
- const canContinue = !isNaN(parsedAmount) && parsedAmount > 0;
2750
+ const canContinue = !isNaN(parsedAmount) && parsedAmount >= MIN_SEND_AMOUNT_USD;
2689
2751
  let maxSourceBalance = null;
2690
2752
  for (const acct of accounts) {
2691
2753
  for (const wallet of acct.wallets) {
@@ -2732,7 +2794,7 @@ function SwypePayment({
2732
2794
  "input",
2733
2795
  {
2734
2796
  type: "number",
2735
- min: "0.01",
2797
+ min: MIN_SEND_AMOUNT_USD.toFixed(2),
2736
2798
  step: "0.01",
2737
2799
  value: amount,
2738
2800
  onChange: (e) => setAmount(e.target.value),
@@ -2802,7 +2864,7 @@ function SwypePayment({
2802
2864
  }
2803
2865
  if (step === "ready") {
2804
2866
  const parsedAmount = parseFloat(amount);
2805
- const canPay = !isNaN(parsedAmount) && parsedAmount > 0 && !!sourceId && !loadingData;
2867
+ const canPay = !isNaN(parsedAmount) && parsedAmount >= MIN_SEND_AMOUNT_USD && !!sourceId && !loadingData;
2806
2868
  const noAccounts = !loadingData && accounts.length === 0;
2807
2869
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: cardStyle, children: [
2808
2870
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
@@ -3437,6 +3499,7 @@ exports.SwypePayment = SwypePayment;
3437
3499
  exports.SwypeProvider = SwypeProvider;
3438
3500
  exports.createPasskeyCredential = createPasskeyCredential;
3439
3501
  exports.darkTheme = darkTheme;
3502
+ exports.deviceHasPasskey = deviceHasPasskey;
3440
3503
  exports.getTheme = getTheme;
3441
3504
  exports.lightTheme = lightTheme;
3442
3505
  exports.swypeApi = api_exports;