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