@pollar/react 0.11.4-rc.4 → 0.11.4

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.mjs CHANGED
@@ -1036,7 +1036,7 @@ var PollarModalFooter = ({ shield = false }) => {
1036
1036
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1037
1037
  /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1038
1038
  "v",
1039
- "0.11.4-rc.4"
1039
+ "0.11.4"
1040
1040
  ] })
1041
1041
  ] })
1042
1042
  ] });
@@ -2262,15 +2262,35 @@ function initialValues(fields, answers) {
2262
2262
  return values;
2263
2263
  }
2264
2264
  var DATE = /^\d{4}-\d{2}-\d{2}$/;
2265
+ function localFieldError(field, value) {
2266
+ if (typeof value !== "string" || !value.trim()) return null;
2267
+ const text = value.trim();
2268
+ const rules = field.validation ?? {};
2269
+ if (field.type === "number") {
2270
+ const number = Number(text);
2271
+ if (!Number.isFinite(number)) return "invalid_type";
2272
+ if (rules.min !== void 0 && number < rules.min) return "too_small";
2273
+ if (rules.max !== void 0 && number > rules.max) return "too_large";
2274
+ return null;
2275
+ }
2276
+ if (field.type === "date") {
2277
+ return DATE.test(text) && !Number.isNaN(Date.parse(`${text}T00:00:00Z`)) ? null : "invalid_format";
2278
+ }
2279
+ if (field.type === "select" || field.type === "multiselect" || field.type === "checkbox") return null;
2280
+ if (rules.maxLength !== void 0 && text.length > rules.maxLength) return "too_long";
2281
+ if (rules.pattern) {
2282
+ try {
2283
+ if (!new RegExp(rules.pattern).test(text)) return "pattern";
2284
+ } catch {
2285
+ }
2286
+ }
2287
+ return null;
2288
+ }
2265
2289
  function localFieldErrors(fields, values) {
2266
2290
  const errors = {};
2267
2291
  for (const field of fields) {
2268
- const value = values[field.key];
2269
- if (typeof value !== "string" || !value.trim()) continue;
2270
- if (field.type === "number" && !Number.isFinite(Number(value))) errors[field.key] = "invalid_type";
2271
- if (field.type === "date" && (!DATE.test(value.trim()) || Number.isNaN(Date.parse(`${value.trim()}T00:00:00Z`)))) {
2272
- errors[field.key] = "invalid_format";
2273
- }
2292
+ const error = localFieldError(field, values[field.key]);
2293
+ if (error) errors[field.key] = error;
2274
2294
  }
2275
2295
  return errors;
2276
2296
  }
@@ -2350,7 +2370,8 @@ function FieldInput({
2350
2370
  value,
2351
2371
  language,
2352
2372
  disabled,
2353
- onChange
2373
+ onChange,
2374
+ onBlur
2354
2375
  }) {
2355
2376
  const id = `pollar-form-${field.key}`;
2356
2377
  const text = typeof value === "string" ? value : "";
@@ -2384,7 +2405,8 @@ function FieldInput({
2384
2405
  value: text,
2385
2406
  disabled,
2386
2407
  maxLength: field.validation?.maxLength,
2387
- onChange: (e) => onChange(e.target.value)
2408
+ onChange: (e) => onChange(e.target.value),
2409
+ onBlur
2388
2410
  }
2389
2411
  );
2390
2412
  default:
@@ -2400,7 +2422,8 @@ function FieldInput({
2400
2422
  min: field.validation?.min,
2401
2423
  max: field.validation?.max,
2402
2424
  maxLength: field.validation?.maxLength,
2403
- onChange: (e) => onChange(e.target.value)
2425
+ onChange: (e) => onChange(e.target.value),
2426
+ onBlur
2404
2427
  }
2405
2428
  );
2406
2429
  }
@@ -2431,6 +2454,24 @@ function RequirementFormModal({ formId, progress, onClose, onSubmitted }) {
2431
2454
  setValues(initialValues(loaded.fields, loaded.answers));
2432
2455
  }).catch(() => mounted.current && setError(copy.loadError));
2433
2456
  }, [formId]);
2457
+ function recheck(field, value, onlyIfShown) {
2458
+ if (typeof value === "string" && !value.trim()) return;
2459
+ setFieldErrors((errors) => {
2460
+ if (onlyIfShown && !(field.key in errors)) return errors;
2461
+ const next = { ...errors };
2462
+ const error2 = localFieldError(field, value);
2463
+ if (error2) next[field.key] = error2;
2464
+ else delete next[field.key];
2465
+ return next;
2466
+ });
2467
+ }
2468
+ function change(field, value) {
2469
+ setValues((v) => ({ ...v, [field.key]: value }));
2470
+ recheck(field, value, true);
2471
+ }
2472
+ function blur(field) {
2473
+ recheck(field, values[field.key], false);
2474
+ }
2434
2475
  async function submit() {
2435
2476
  if (!form) return;
2436
2477
  setError(null);
@@ -2510,7 +2551,8 @@ function RequirementFormModal({ formId, progress, onClose, onSubmitted }) {
2510
2551
  value: values[field.key] ?? "",
2511
2552
  language,
2512
2553
  disabled: submitting,
2513
- onChange: (value) => setValues((v) => ({ ...v, [field.key]: value }))
2554
+ onChange: (value) => change(field, value),
2555
+ onBlur: () => blur(field)
2514
2556
  }
2515
2557
  )
2516
2558
  ] }),
@@ -3969,6 +4011,7 @@ var REGISTRATION_COPY = {
3969
4011
  incomplete: "Your registration with the provider did not finish. Register again to complete it.",
3970
4012
  providerDown: "The provider is not responding right now. Please try again in a few minutes.",
3971
4013
  ipNotSupported: "The provider cannot accept your current connection (IPv6). Please try again from another network, for example Wi-Fi.",
4014
+ emailTaken: "This email is already registered with the provider through another app. Use a different email to register here.",
3972
4015
  loadError: "Could not load the registration. Please try again.",
3973
4016
  submitError: "Could not register you. Please try again.",
3974
4017
  step: "Step {n} of {total}",
@@ -3988,6 +4031,7 @@ var REGISTRATION_COPY = {
3988
4031
  incomplete: "Tu registro con el proveedor no se complet\xF3. Reg\xEDstrate de nuevo para terminarlo.",
3989
4032
  providerDown: "El proveedor no responde en este momento. Intenta de nuevo en unos minutos.",
3990
4033
  ipNotSupported: "El proveedor no acepta tu conexi\xF3n actual (IPv6). Intenta desde otra red, por ejemplo Wi-Fi.",
4034
+ emailTaken: "Este email ya est\xE1 registrado con el proveedor desde otra app. Usa otro email para registrarte aqu\xED.",
3991
4035
  loadError: "No se pudo cargar el registro. Intenta de nuevo.",
3992
4036
  submitError: "No se pudo completar el registro. Intenta de nuevo.",
3993
4037
  step: "Paso {n} de {total}",
@@ -4310,6 +4354,7 @@ function ProviderRegistrationModal({
4310
4354
  } else if (code === "KYC_REGISTRATION_STEPS_PENDING") setError(copy.notReady);
4311
4355
  else if (code === "SDK_CARDS_PROVIDER_ERROR") setError(copy.providerDown);
4312
4356
  else if (code === "SDK_CARDS_IP_NOT_SUPPORTED") setError(copy.ipNotSupported);
4357
+ else if (code === "SDK_CARDS_PROVIDER_USER_TAKEN") setError(copy.emailTaken);
4313
4358
  else setError(copy.submitError);
4314
4359
  } finally {
4315
4360
  if (mounted.current) setSubmitting(false);
@@ -7015,7 +7060,8 @@ function groupPan(pan) {
7015
7060
  }
7016
7061
  var CARD_ERROR_COPY = {
7017
7062
  SDK_CARDS_PROVIDER_ERROR: "The card provider is not responding right now. Please try again in a few minutes.",
7018
- SDK_CARDS_IP_NOT_SUPPORTED: "The card provider cannot accept your current connection (IPv6). Please try again from another network, for example Wi-Fi."
7063
+ SDK_CARDS_IP_NOT_SUPPORTED: "The card provider cannot accept your current connection (IPv6). Please try again from another network, for example Wi-Fi.",
7064
+ SDK_CARDS_PROVIDER_USER_TAKEN: "This email is already registered with the card provider through another app. Use a different email to get a card here."
7019
7065
  };
7020
7066
  function errorText(e, fallback) {
7021
7067
  if (isPollarApiError(e)) {
@@ -8631,7 +8677,7 @@ function PollarProvider({
8631
8677
  triggered = false;
8632
8678
  return;
8633
8679
  }
8634
- if (triggered || sessionRef.current?.wallet?.address) return;
8680
+ if (triggered || sessionRef.current) return;
8635
8681
  triggered = true;
8636
8682
  pollarClient.login({ provider: id });
8637
8683
  })
@@ -8710,6 +8756,7 @@ function PollarProvider({
8710
8756
  );
8711
8757
  const walletAddress = sessionState?.wallet?.address || "";
8712
8758
  const walletProvisioning = sessionState?.wallet?.provisioning;
8759
+ const hasSession = sessionState !== null;
8713
8760
  const getClient = useCallback(() => pollarClient, [pollarClient]);
8714
8761
  const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
8715
8762
  const refreshAssets = useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
@@ -8719,7 +8766,9 @@ function PollarProvider({
8719
8766
  // session
8720
8767
  wallet: pollarClient.getWallet(),
8721
8768
  wallets: pollarClient.getWallets(),
8722
- isAuthenticated: !!walletAddress,
8769
+ isAuthenticated: hasSession,
8770
+ hasWallet: pollarClient.hasWallet(),
8771
+ createWallet: () => pollarClient.createWallet(),
8723
8772
  verified,
8724
8773
  // client
8725
8774
  getClient,
@@ -8807,6 +8856,7 @@ function PollarProvider({
8807
8856
  }, [
8808
8857
  walletAddress,
8809
8858
  walletProvisioning,
8859
+ hasSession,
8810
8860
  verified,
8811
8861
  pollarClient,
8812
8862
  getClient,
@@ -8922,8 +8972,54 @@ function WalletButtonTemplate({
8922
8972
  onRamp,
8923
8973
  onDistributionRules,
8924
8974
  onLogout,
8925
- onLogin
8975
+ onLogin,
8976
+ signedIn,
8977
+ creatingWallet,
8978
+ createWalletError,
8979
+ onCreateWallet
8926
8980
  }) {
8981
+ if (!walletAddress && signedIn) {
8982
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, children: [
8983
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-btn", style: { backgroundColor: accentColor }, onClick: onToggleOpen, children: [
8984
+ /* @__PURE__ */ jsx("span", { className: "pollar-wallet-btn-label", children: onCreateWallet ? "No wallet" : "Signed in" }),
8985
+ /* @__PURE__ */ jsx(
8986
+ "svg",
8987
+ {
8988
+ className: `pollar-wallet-chevron${open ? " pollar-open" : ""}`,
8989
+ viewBox: "0 0 12 12",
8990
+ fill: "none",
8991
+ stroke: "currentColor",
8992
+ strokeWidth: "2",
8993
+ strokeLinecap: "round",
8994
+ strokeLinejoin: "round",
8995
+ children: /* @__PURE__ */ jsx("polyline", { points: "2,4 6,8 10,4" })
8996
+ }
8997
+ )
8998
+ ] }),
8999
+ open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
9000
+ createWalletError && /* @__PURE__ */ jsxs(Fragment, { children: [
9001
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: createWalletError }),
9002
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
9003
+ ] }),
9004
+ onCreateWallet && /* @__PURE__ */ jsxs(Fragment, { children: [
9005
+ /* @__PURE__ */ jsx(
9006
+ "button",
9007
+ {
9008
+ className: "pollar-wallet-dropdown-item",
9009
+ style: { color: itemColor },
9010
+ onClick: onCreateWallet,
9011
+ disabled: creatingWallet,
9012
+ children: creatingWallet ? "Creating wallet\u2026" : "Create wallet"
9013
+ }
9014
+ ),
9015
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
9016
+ ] }),
9017
+ /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSessions, children: "Active sessions" }),
9018
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
9019
+ /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: "Log out" })
9020
+ ] })
9021
+ ] });
9022
+ }
8927
9023
  if (!walletAddress) {
8928
9024
  return /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-wallet-login-btn", style: { backgroundColor: accentColor }, onClick: onLogin, children: [
8929
9025
  /* @__PURE__ */ jsx(ButtonLogo, {}),
@@ -9244,7 +9340,10 @@ function WalletButton() {
9244
9340
  openKycModal,
9245
9341
  openRampModal,
9246
9342
  openDistributionRulesModal,
9247
- tx: transaction
9343
+ tx: transaction,
9344
+ isAuthenticated,
9345
+ createWallet,
9346
+ appConfig
9248
9347
  } = usePollar();
9249
9348
  const { primaryChain, primaryAddress } = useChains();
9250
9349
  const walletAddress = primaryAddress || (wallet?.address ?? "");
@@ -9252,6 +9351,8 @@ function WalletButton() {
9252
9351
  const [copied, setCopied] = useState(false);
9253
9352
  const [creating, setCreating] = useState(false);
9254
9353
  const [created, setCreated] = useState(false);
9354
+ const [creatingWallet, setCreatingWallet] = useState(false);
9355
+ const [createWalletError, setCreateWalletError] = useState(null);
9255
9356
  const wrapperRef = useRef(null);
9256
9357
  const copyTimerRef = useRef(null);
9257
9358
  const isInProgress = transaction.step === "building" || transaction.step === "signing";
@@ -9305,6 +9406,21 @@ function WalletButton() {
9305
9406
  setCreating(false);
9306
9407
  }
9307
9408
  }
9409
+ async function handleCreateWallet() {
9410
+ if (creatingWallet) return;
9411
+ setCreatingWallet(true);
9412
+ setCreateWalletError(null);
9413
+ try {
9414
+ await createWallet();
9415
+ setOpen(false);
9416
+ } catch (err) {
9417
+ setCreateWalletError(
9418
+ isPollarApiError(err) && err.code === "SDK_WALLET_PROVISIONING_DISABLED" ? "This app does not create wallets." : "Could not create the wallet. Try again."
9419
+ );
9420
+ } finally {
9421
+ setCreatingWallet(false);
9422
+ }
9423
+ }
9308
9424
  function handleWalletBalance() {
9309
9425
  setOpen(false);
9310
9426
  openWalletBalanceModal();
@@ -9350,6 +9466,10 @@ function WalletButton() {
9350
9466
  wrapperRef,
9351
9467
  isInProgress,
9352
9468
  showCreateAccount: canCreateAccount,
9469
+ signedIn: isAuthenticated,
9470
+ creatingWallet,
9471
+ createWalletError,
9472
+ ...appConfig.application?.walletProvisioning === "NONE" ? {} : { onCreateWallet: handleCreateWallet },
9353
9473
  notReadyReason,
9354
9474
  creatingAccount: creating,
9355
9475
  onToggleOpen: () => setOpen((v) => !v),