@pollar/react 0.3.9 → 0.4.0

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.js CHANGED
@@ -12,6 +12,19 @@ var LOGO_POLLAR = "https://pollar.xyz/assets/logo_pollar.png";
12
12
  var LOGO_GITHUB = "https://pollar.xyz/assets/GitHub_Invertocat_White.png";
13
13
  var LOGO_FREIGHTER = "https://pollar.xyz/assets/logo_freighter.png";
14
14
  var LOGO_ALBEDO = "https://pollar.xyz/assets/logo_albedo.svg";
15
+ var PollarModalFooter = () => {
16
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-footer", children: [
17
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
18
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-footer-brand", children: [
19
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: LOGO_POLLAR, alt: "Pollar", className: "pollar-footer-logo" }),
20
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
21
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
22
+ "v",
23
+ "0.4.0"
24
+ ] })
25
+ ] })
26
+ ] });
27
+ };
15
28
  function EmailCodeInput({ email, onSubmit }) {
16
29
  const [digits, setDigits] = react.useState(["", "", "", "", "", ""]);
17
30
  const inputRefs = react.useRef([]);
@@ -119,6 +132,7 @@ var GoogleButton = ({ disabled, onClick }) => {
119
132
  ] });
120
133
  };
121
134
  var LOGIN_CODE_MESSAGES = {
135
+ NONE: { text: "" },
122
136
  LOGOUT: { text: "Logged out" },
123
137
  CREATE_SESSION_START: { text: "Starting session\u2026" },
124
138
  CREATE_SESSION_ERROR: { text: "Failed to start session" },
@@ -246,21 +260,11 @@ function LoginModalTemplate({
246
260
  ] })
247
261
  ] }),
248
262
  /* @__PURE__ */ jsxRuntime.jsx(LoginStatusBanner, { code: loginStateCode, status, onCancel: () => cancelLoginRef.current?.(), onRetry }),
249
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-footer", children: [
250
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
251
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-footer-brand", children: [
252
- /* @__PURE__ */ jsxRuntime.jsx("img", { src: LOGO_POLLAR, alt: "Pollar", className: "pollar-footer-logo" }),
253
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
254
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
255
- "v",
256
- "0.3.9"
257
- ] })
258
- ] })
259
- ] })
263
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
260
264
  ] });
261
265
  }
262
266
  function isLoginCode(code) {
263
- return Object.values(core.STATE_VAR_CODES[core.PollarStateVar.LOGIN]).includes(code);
267
+ return Object.values(core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION]).includes(code);
264
268
  }
265
269
  function LoginModal({ onClose }) {
266
270
  const [email, setEmail] = react.useState("");
@@ -272,20 +276,20 @@ function LoginModal({ onClose }) {
272
276
  const [clientSessionId, setClientSessionId] = react.useState(null);
273
277
  react.useEffect(() => {
274
278
  return getClient().onStateChange((stateEntry) => {
275
- if (stateEntry.var === core.PollarStateVar.LOGIN && isLoginCode(stateEntry.code)) {
279
+ if (stateEntry.var === core.PollarStateVar.AUTHENTICATION && isLoginCode(stateEntry.code)) {
276
280
  setLoginStateCode(stateEntry.code);
277
281
  setStatus(stateEntry.status);
278
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.LOGIN].STREAM_POLL_START) {
282
+ if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].STREAM_POLL_START) {
279
283
  const data = stateEntry.data;
280
284
  setClientSessionId(data.clientSessionId);
281
285
  }
282
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.LOGIN].STREAM_POLL_EVENT) {
286
+ if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].STREAM_POLL_EVENT) {
283
287
  const data = stateEntry.data;
284
288
  if (data?.status === "AWAITING_EMAIL") {
285
289
  setAwaitingEmailCode(true);
286
290
  }
287
291
  }
288
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.LOGIN].FETCH_SESSION_SUCCESS) {
292
+ if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].FETCH_SESSION_SUCCESS) {
289
293
  setAwaitingEmailCode(false);
290
294
  setTimeout(onClose, 1e3);
291
295
  }
@@ -355,6 +359,163 @@ function LoginModal({ onClose }) {
355
359
  }
356
360
  ) });
357
361
  }
362
+ function phaseFromStateCode(stateCode, submitResult) {
363
+ if (stateCode === core.STATE_VAR_CODES.transaction.BUILD_TRANSACTION_ERROR || stateCode === core.STATE_VAR_CODES.transaction.BUILD_TRANSACTION_ERROR_NO_WALLET || stateCode === core.STATE_VAR_CODES.transaction.SIGN_TRANSACTION_ERROR || stateCode === core.STATE_VAR_CODES.transaction.SEND_TRANSACTION_ERROR)
364
+ return "error";
365
+ if (stateCode === core.STATE_VAR_CODES.transaction.SEND_TRANSACTION_SUCCESS || submitResult) return "success";
366
+ if (stateCode === core.STATE_VAR_CODES.transaction.BUILD_TRANSACTION_SUCCESS || stateCode === core.STATE_VAR_CODES.transaction.SIGN_TRANSACTION_START || stateCode === core.STATE_VAR_CODES.transaction.SIGN_TRANSACTION_SUCCESS || stateCode === core.STATE_VAR_CODES.transaction.SEND_TRANSACTION_START)
367
+ return "ready";
368
+ return "building";
369
+ }
370
+ var TX_TITLES = {
371
+ NONE: "Preparing transaction\u2026",
372
+ BUILD_TRANSACTION_START: "Building transaction\u2026",
373
+ BUILD_TRANSACTION_SUCCESS: "Confirm Transaction",
374
+ BUILD_TRANSACTION_ERROR: "Transaction failed",
375
+ BUILD_TRANSACTION_ERROR_NO_WALLET: "No wallet connected",
376
+ SIGN_TRANSACTION_START: "Waiting for wallet\u2026",
377
+ SIGN_TRANSACTION_SUCCESS: "Signed \u2014 submitting\u2026",
378
+ SIGN_TRANSACTION_ERROR: "Signing failed",
379
+ SEND_TRANSACTION_START: "Submitting transaction\u2026",
380
+ SEND_TRANSACTION_SUCCESS: "Transaction sent",
381
+ SEND_TRANSACTION_ERROR: "Transaction failed"
382
+ };
383
+ function TransactionModalTemplate({
384
+ theme,
385
+ accentColor,
386
+ stateCode,
387
+ buildResult,
388
+ submitResult,
389
+ isLoading,
390
+ onClose,
391
+ onSignAndSend,
392
+ onRetry
393
+ }) {
394
+ const isDark = theme === "dark";
395
+ const cssVars = {
396
+ "--pollar-accent": accentColor,
397
+ "--pollar-buttons-border-radius": "8px",
398
+ "--pollar-buttons-height": "44px",
399
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
400
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
401
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
402
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
403
+ "--pollar-input-bg": isDark ? "#374151" : "rgba(0,0,0,0.04)",
404
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
405
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a"
406
+ };
407
+ const [showXdr, setShowXdr] = react.useState(false);
408
+ const phase = phaseFromStateCode(stateCode, submitResult);
409
+ const title = TX_TITLES[stateCode] || "";
410
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
411
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-header", children: [
412
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-tx-title", children: title }),
413
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-close", onClick: onClose, disabled: isLoading, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
414
+ ] }),
415
+ buildResult && phase !== "building" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
416
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-summary", children: [
417
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-tx-summary-title", children: "Details" }),
418
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "pollar-tx-summary-lines", children: buildResult.summary.lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "pollar-tx-summary-line", children: line }, i)) })
419
+ ] }),
420
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta", children: [
421
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta-item", children: [
422
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-label", children: "Network" }),
423
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildResult.summary.network })
424
+ ] }),
425
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta-item", children: [
426
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-label", children: "Fee" }),
427
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildResult.summary.fee })
428
+ ] })
429
+ ] }),
430
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-xdr", children: [
431
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "pollar-tx-xdr-toggle", onClick: () => setShowXdr((v) => !v), children: [
432
+ /* @__PURE__ */ jsxRuntime.jsx(
433
+ "svg",
434
+ {
435
+ width: "12",
436
+ height: "12",
437
+ viewBox: "0 0 12 12",
438
+ fill: "none",
439
+ "aria-hidden": true,
440
+ style: { transform: showXdr ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 150ms" },
441
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 2l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
442
+ }
443
+ ),
444
+ "Raw transaction (XDR)"
445
+ ] }),
446
+ showXdr && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "pollar-tx-xdr-content", children: buildResult.unsignedXdr })
447
+ ] })
448
+ ] }),
449
+ phase === "success" && submitResult && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-result", children: [
450
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-result-label", children: "Transaction hash" }),
451
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-result-hash", children: submitResult.hash })
452
+ ] }),
453
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-status", "data-kind": phase === "error" ? "ERROR" : phase === "success" ? "SUCCESS" : "LOADING", children: [
454
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pollar-tx-spinner", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
455
+ "circle",
456
+ {
457
+ cx: "7",
458
+ cy: "7",
459
+ r: "5.5",
460
+ stroke: "currentColor",
461
+ strokeWidth: "1.5",
462
+ strokeLinecap: "round",
463
+ strokeDasharray: "22 10"
464
+ }
465
+ ) }),
466
+ phase === "error" && /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
467
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
468
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.5 4.5l5 5M9.5 4.5l-5 5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round" })
469
+ ] }),
470
+ phase === "success" && /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
471
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
472
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.5 7l2.5 2.5 4.5-5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
473
+ ] })
474
+ ] }),
475
+ phase === "ready" && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
476
+ phase === "error" && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-sign-btn", onClick: onRetry, children: "Retry" }),
477
+ phase === "success" && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-sign-btn", onClick: onClose, children: "Done" }),
478
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
479
+ ] });
480
+ }
481
+ var isTxBuildResponse = (data) => {
482
+ if (!data || typeof data !== "object") return false;
483
+ const d = data;
484
+ return typeof d.unsignedXdr === "string" && typeof d.networkPassphrase === "string" && typeof d.estimatedFee === "string" && d.summary !== null && typeof d.summary === "object";
485
+ };
486
+ function TransactionModal({ onClose }) {
487
+ const {
488
+ styles,
489
+ state: { transaction }
490
+ } = usePollar();
491
+ const { theme = "light", accentColor = "#005DB4" } = styles;
492
+ const [submitResult, setSubmitResult] = react.useState(null);
493
+ console.log({ transaction });
494
+ async function handleSignAndSend() {
495
+ }
496
+ const isLoading = transaction.status === "LOADING";
497
+ let buildResult = null;
498
+ const stateCode = transaction.code;
499
+ if (isTxBuildResponse(transaction.data)) {
500
+ buildResult = transaction.data;
501
+ }
502
+ console.log({ transaction, buildResult });
503
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
504
+ TransactionModalTemplate,
505
+ {
506
+ theme,
507
+ accentColor,
508
+ stateCode,
509
+ buildResult,
510
+ submitResult,
511
+ isLoading,
512
+ onClose,
513
+ onSignAndSend: handleSignAndSend,
514
+ onRetry: () => {
515
+ }
516
+ }
517
+ ) });
518
+ }
358
519
  var emptyResponse = {
359
520
  application: {
360
521
  name: ""
@@ -363,7 +524,7 @@ var emptyResponse = {
363
524
  };
364
525
  async function fetchRemoteConfig(api) {
365
526
  try {
366
- const { data, error } = await api.GET(`/config`);
527
+ const { data, error } = await api.GET(`/applications/config`);
367
528
  if (!data || error) {
368
529
  return emptyResponse;
369
530
  }
@@ -378,16 +539,16 @@ function PollarProvider({ config, styles: propStyles, children }) {
378
539
  const [stellarClient] = react.useState(() => new core.StellarClient(config.stellarNetwork || "testnet"));
379
540
  const [sessionState, setSessionState] = react.useState(null);
380
541
  const [state, setState] = react.useState({
381
- [core.PollarStateVar.LOGIN]: {
382
- var: core.PollarStateVar.LOGIN,
383
- code: core.STATE_VAR_CODES[core.PollarStateVar.LOGIN].NONE,
542
+ authentication: {
543
+ var: "authentication",
544
+ code: core.STATE_VAR_CODES.authentication.NONE,
384
545
  status: core.StateStatus.NONE,
385
546
  level: "info",
386
547
  ts: 0
387
548
  },
388
- [core.PollarStateVar.WALLET_ADDRESS]: {
389
- var: core.PollarStateVar.WALLET_ADDRESS,
390
- code: core.STATE_VAR_CODES[core.PollarStateVar.WALLET_ADDRESS].NONE,
549
+ transaction: {
550
+ var: "transaction",
551
+ code: core.STATE_VAR_CODES.transaction.NONE,
391
552
  status: core.StateStatus.NONE,
392
553
  level: "info",
393
554
  ts: 0
@@ -406,8 +567,8 @@ function PollarProvider({ config, styles: propStyles, children }) {
406
567
  }
407
568
  return prevState;
408
569
  });
409
- if (stateEntry.var === core.PollarStateVar.WALLET_ADDRESS) {
410
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.WALLET_ADDRESS].UPDATED_ADDRESS && core.isValidSession(stateEntry.data)) {
570
+ if (stateEntry.var === "authentication") {
571
+ if ((stateEntry.code === core.STATE_VAR_CODES.authentication.SESSION_STORED || core.STATE_VAR_CODES.authentication.RESTORED_SESSION_SUCCESS) && core.isValidSession(stateEntry.data)) {
411
572
  setSessionState((prevState) => {
412
573
  if (JSON.stringify(prevState) !== JSON.stringify(stateEntry.data)) {
413
574
  return stateEntry.data;
@@ -415,9 +576,9 @@ function PollarProvider({ config, styles: propStyles, children }) {
415
576
  return prevState;
416
577
  });
417
578
  }
418
- }
419
- if (stateEntry.var === core.PollarStateVar.WALLET_ADDRESS && stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.WALLET_ADDRESS].REMOVED_ADDRESS || stateEntry.var === core.PollarStateVar.LOGIN && stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.LOGIN].LOGOUT) {
420
- setSessionState(null);
579
+ if (stateEntry.code === core.STATE_VAR_CODES.authentication.LOGOUT) {
580
+ setSessionState(null);
581
+ }
421
582
  }
422
583
  });
423
584
  }, [pollarClient]);
@@ -433,31 +594,43 @@ function PollarProvider({ config, styles: propStyles, children }) {
433
594
  setStyles(propStyles ?? {});
434
595
  });
435
596
  }, [pollarClient]);
436
- const [modalOpen, setModalOpen] = react.useState(false);
597
+ const [loginModalOpen, setLoginModalOpen] = react.useState(false);
598
+ const [transactionModalOpen, setTransactionModalOpen] = react.useState(false);
437
599
  const contextValue = react.useMemo(
438
600
  () => ({
439
601
  walletAddress: sessionState?.wallet?.publicKey || "",
440
602
  getClient: () => pollarClient,
441
- openLoginModal: () => setModalOpen(true),
442
- isAuthenticated: pollarClient.isAuthenticated(),
603
+ // client
604
+ state,
443
605
  login: (options) => pollarClient.login(options),
444
606
  logout: () => pollarClient.logout(),
607
+ isAuthenticated: pollarClient.isAuthenticated(),
608
+ buildTx: (operation, params, options) => pollarClient.buildTx(operation, params, options),
609
+ submitTx: (signedXdr) => pollarClient.submitTx(signedXdr),
610
+ // react
611
+ sendTransaction: (operation, params, options) => {
612
+ void pollarClient.buildTx(operation, params, options);
613
+ setTransactionModalOpen(true);
614
+ },
615
+ openTransactionModal: () => setTransactionModalOpen(true),
616
+ openLoginModal: () => setLoginModalOpen(true),
445
617
  config: remoteConfig,
446
- state,
447
618
  styles,
619
+ // stellar
448
620
  async getBalance(publicKey) {
449
621
  const pk = publicKey || sessionState?.wallet?.publicKey;
450
622
  if (pk) {
451
- return stellarClient.getBalances(pk);
623
+ return await stellarClient.getBalances(pk);
452
624
  }
453
- return null;
625
+ return { success: false, errorCode: "NO_WALLET_FOUND", balances: [] };
454
626
  }
455
627
  }),
456
628
  [sessionState, remoteConfig, styles, pollarClient, state]
457
629
  );
458
630
  return /* @__PURE__ */ jsxRuntime.jsxs(PollarContext.Provider, { value: contextValue, children: [
459
631
  children,
460
- modalOpen && /* @__PURE__ */ jsxRuntime.jsx(LoginModal, { onClose: () => setModalOpen(false) })
632
+ loginModalOpen && /* @__PURE__ */ jsxRuntime.jsx(LoginModal, { onClose: () => setLoginModalOpen(false) }),
633
+ transactionModalOpen && /* @__PURE__ */ jsxRuntime.jsx(TransactionModal, { onClose: () => setTransactionModalOpen(false) })
461
634
  ] });
462
635
  }
463
636
  function usePollar() {