@pollar/react 0.4.4 → 0.4.5

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, isValidSession, PollarStateVar, WalletType } from '@pollar/core';
2
+ import { PollarClient, StellarClient, StateStatus, STATE_VAR_CODES, AUTH_ERROR_CODES, WalletType } from '@pollar/core';
3
3
  import { createContext, useState, useEffect, useMemo, useContext, useRef, Component } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -42,52 +42,21 @@ var PollarModalFooter = () => {
42
42
  ] })
43
43
  ] });
44
44
  };
45
- var LOGIN_CODE_MESSAGES = {
46
- NONE: { text: "" },
47
- LOGOUT: { text: "Logged out" },
48
- CREATE_SESSION_START: { text: "Starting session\u2026" },
49
- CREATE_SESSION_ERROR: { text: "Failed to start session" },
50
- CREATE_SESSION_SUCCESS: { text: "Session ready" },
51
- EMAIL_AUTH_START: { text: "Sending code\u2026" },
52
- EMAIL_AUTH_START_ERROR: { text: "Failed to send code" },
53
- EMAIL_AUTH_START_SUCCESS: { text: "Code sent \u2014 check your inbox" },
54
- EMAIL_AUTH_CODE_ERROR: { text: "Invalid code \u2014 try again" },
55
- EMAIL_AUTH_CODE_SUCCESS: { text: "Code verified!" },
56
- WALLET_AUTH_START: { text: "Connecting wallet\u2026" },
57
- WALLET_AUTH_FREIGHTER_NOT_INSTALLED: { text: "Freighter is not installed" },
58
- WALLET_AUTH_ALBEDO_NOT_INSTALLED: { text: "Albedo is not installed" },
59
- WALLET_AUTH_CONNECTED: { text: "Wallet connected" },
60
- WALLET_AUTH_LOGIN_START: { text: "Signing in with wallet\u2026" },
61
- WALLET_AUTH_LOGIN_START_SUCCESS: { text: "Wallet signed in" },
62
- WALLET_AUTH_LOGIN_START_ERROR: { text: "Failed to sign in with wallet" },
63
- WALLET_AUTH_ERROR: { text: "Unknow wallet error" },
64
- STREAM_POLL_START: { text: "Waiting for authentication\u2026" },
65
- STREAM_POLL_EVENT: { text: "Waiting for authentication\u2026" },
66
- STREAM_POLL_READY: { text: "Authenticated!" },
67
- FETCH_SESSION_START: { text: "Loading session\u2026" },
68
- FETCH_SESSION_SUCCESS: { text: "Welcome back!" },
69
- FETCH_SESSION_ERROR: { text: "Failed to load session" },
70
- NO_RESTORED_SESSION: { text: "" },
71
- RESTORED_SESSION_SUCCESS: { text: "Session restored" },
72
- RESTORED_SESSION_ERROR: { text: "Failed to restore session" },
73
- SESSION_STORED: { text: "Session saved" },
74
- ERROR_UNKNOWN: { text: "Something went wrong" },
75
- ABORTED: { text: "Login cancelled" },
76
- // transaction
77
- BUILD_TRANSACTION_START: { text: "Building transaction\u2026" },
78
- BUILD_TRANSACTION_SUCCESS: { text: "Transaction built, ready to sign and send" },
79
- BUILD_TRANSACTION_ERROR: { text: "Failed to build transaction" },
80
- BUILD_TRANSACTION_ERROR_NO_WALLET: { text: "No wallet connected" },
81
- SIGN_SEND_TRANSACTION_START: { text: "Signing and sending transaction\u2026" },
82
- SIGN_SEND_TRANSACTION_SUCCESS: { text: "Transaction signed" },
83
- SIGN_SEND_TRANSACTION_ERROR: { text: "Signing rejected" }
45
+ var TRANSACTION_CODE_MESSAGES = {
46
+ NONE: "",
47
+ BUILD_TRANSACTION_START: "Building transaction\u2026",
48
+ BUILD_TRANSACTION_SUCCESS: "Transaction built, ready to sign and send",
49
+ BUILD_TRANSACTION_ERROR: "Failed to build transaction",
50
+ BUILD_TRANSACTION_ERROR_NO_WALLET: "No wallet connected",
51
+ SIGN_SEND_TRANSACTION_START: "Signing and sending transaction\u2026",
52
+ SIGN_SEND_TRANSACTION_SUCCESS: "Transaction signed",
53
+ SIGN_SEND_TRANSACTION_ERROR: "Signing rejected"
84
54
  };
85
- function ModalStatusBanner({ code, status, onCancel, onRetry }) {
86
- if (!code) {
55
+ function ModalStatusBanner({ message, status, onCancel, onRetry }) {
56
+ if (!message && status === StateStatus.NONE) {
87
57
  return /* @__PURE__ */ jsx("div", { className: "pollar-status" });
88
58
  }
89
- const { text } = LOGIN_CODE_MESSAGES[code] || { text: "" };
90
- const isLoading = status === "LOADING";
59
+ const isLoading = status === StateStatus.LOADING;
91
60
  const icon = status === "ERROR" ? /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
92
61
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
93
62
  /* @__PURE__ */ jsx("path", { d: "M4.5 4.5l5 5M9.5 4.5l-5 5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round" })
@@ -97,7 +66,7 @@ function ModalStatusBanner({ code, status, onCancel, onRetry }) {
97
66
  ] }) : status === "LOADING" ? /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeDasharray: "22 10" }) }) : null;
98
67
  return /* @__PURE__ */ jsxs("div", { className: "pollar-status", "data-kind": status, children: [
99
68
  icon,
100
- /* @__PURE__ */ jsx("span", { children: text }),
69
+ /* @__PURE__ */ jsx("span", { children: message }),
101
70
  isLoading && onCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onCancel, children: "Cancel" }),
102
71
  status === StateStatus.ERROR && onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onRetry, children: "Retry" })
103
72
  ] });
@@ -208,6 +177,38 @@ var GoogleButton = ({ disabled, onClick }) => {
208
177
  ] })
209
178
  ] });
210
179
  };
180
+ var AUTH_STATE_MESSAGES = {
181
+ idle: "",
182
+ creating_session: "Initializing\u2026",
183
+ entering_email: "",
184
+ sending_email: "Sending\u2026",
185
+ entering_code: "Code sent \u2014 check your inbox",
186
+ verifying_email_code: "Verifying\u2026",
187
+ opening_oauth: "Redirecting\u2026",
188
+ connecting_wallet: "Connecting wallet\u2026",
189
+ wallet_not_installed: "Wallet not installed",
190
+ authenticating_wallet: "Signing in with wallet\u2026",
191
+ authenticating: "Authenticating\u2026",
192
+ authenticated: "Welcome!",
193
+ error: ""
194
+ };
195
+ function authStateToStatus(step) {
196
+ const loading = [
197
+ "creating_session",
198
+ "sending_email",
199
+ "verifying_email_code",
200
+ "opening_oauth",
201
+ "connecting_wallet",
202
+ "authenticating_wallet",
203
+ "authenticating"
204
+ ];
205
+ const success = ["authenticated", "entering_code"];
206
+ const error = ["error", "wallet_not_installed"];
207
+ if (loading.includes(step)) return StateStatus.LOADING;
208
+ if (success.includes(step)) return StateStatus.SUCCESS;
209
+ if (error.includes(step)) return StateStatus.ERROR;
210
+ return StateStatus.NONE;
211
+ }
211
212
  function LoginModalTemplate({
212
213
  theme,
213
214
  accentColor,
@@ -217,17 +218,16 @@ function LoginModalTemplate({
217
218
  providers,
218
219
  appName,
219
220
  email = "",
220
- status,
221
- error,
222
221
  onEmailChange,
223
222
  onEmailSubmit,
224
223
  onSocialLogin,
225
224
  onFreighterConnect,
226
225
  onAlbedoConnect,
227
- loginStateCode,
228
- awaitingEmailCode = false,
226
+ authState,
227
+ codeInputKey,
229
228
  onCodeSubmit,
230
- cancelLoginRef,
229
+ onBack,
230
+ onCancel,
231
231
  onRetry
232
232
  }) {
233
233
  const isDark = theme === "dark";
@@ -245,16 +245,22 @@ function LoginModalTemplate({
245
245
  "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
246
246
  "--pollar-error-text": isDark ? "#f87171" : "#dc2626"
247
247
  };
248
+ const status = authStateToStatus(authState.step);
248
249
  const isLoading = status === StateStatus.LOADING;
250
+ const isEmailCodeError = authState.step === "error" && (authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
251
+ const awaitingEmailCode = authState.step === "entering_code" || authState.step === "verifying_email_code" || isEmailCodeError;
252
+ const statusMessage = authState.step === "error" ? authState.message : AUTH_STATE_MESSAGES[authState.step];
249
253
  return /* @__PURE__ */ jsxs("div", { className: "pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
250
254
  /* @__PURE__ */ jsxs("div", { className: "pollar-header", children: [
251
255
  /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
252
256
  /* @__PURE__ */ jsx("h2", { className: "pollar-title", children: appName }),
253
257
  /* @__PURE__ */ jsx("p", { className: "pollar-subtitle", children: "Log in or sign up" })
254
258
  ] }),
255
- awaitingEmailCode ? /* @__PURE__ */ jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
256
- }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
257
- error && /* @__PURE__ */ jsx("div", { className: "pollar-error", children: error }),
259
+ awaitingEmailCode ? /* @__PURE__ */ jsxs(Fragment, { children: [
260
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-back-btn", onClick: onBack, children: "\u2190 Back" }),
261
+ /* @__PURE__ */ jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
262
+ }) }, codeInputKey)
263
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
258
264
  emailEnabled && /* @__PURE__ */ jsxs("div", { className: "pollar-email-section", children: [
259
265
  /* @__PURE__ */ jsx(
260
266
  "input",
@@ -290,74 +296,68 @@ function LoginModalTemplate({
290
296
  ] })
291
297
  ] })
292
298
  ] }),
293
- /* @__PURE__ */ jsx(ModalStatusBanner, { code: loginStateCode, status, onCancel: () => cancelLoginRef.current?.(), onRetry }),
299
+ /* @__PURE__ */ jsx(
300
+ ModalStatusBanner,
301
+ {
302
+ message: statusMessage,
303
+ status,
304
+ onCancel,
305
+ onRetry: isEmailCodeError ? void 0 : onRetry
306
+ }
307
+ ),
294
308
  /* @__PURE__ */ jsx(PollarModalFooter, {})
295
309
  ] });
296
310
  }
297
- function isLoginCode(code) {
298
- return Object.values(STATE_VAR_CODES[PollarStateVar.AUTHENTICATION]).some((c) => code.startsWith(c));
299
- }
300
311
  function LoginModal({ onClose }) {
301
312
  const [email, setEmail] = useState("");
302
313
  const { getClient, styles, config } = usePollar();
303
- const [status, setStatus] = useState(StateStatus.NONE);
304
- const [error, setError] = useState(null);
305
- const [loginStateCode, setLoginStateCode] = useState(null);
306
- const [awaitingEmailCode, setAwaitingEmailCode] = useState(false);
307
- const [clientSessionId, setClientSessionId] = useState(null);
314
+ const [authState, setAuthState] = useState(() => getClient().getAuthState());
315
+ const [codeInputKey, setCodeInputKey] = useState(0);
316
+ const pendingEmail = useRef(null);
308
317
  useEffect(() => {
309
- return getClient().onStateChange((stateEntry) => {
310
- if (stateEntry.var === PollarStateVar.AUTHENTICATION && isLoginCode(stateEntry.code)) {
311
- setLoginStateCode(stateEntry.code);
312
- setStatus(stateEntry.status);
313
- if (stateEntry.code === STATE_VAR_CODES[PollarStateVar.AUTHENTICATION].STREAM_POLL_START) {
314
- const data = stateEntry.data;
315
- setClientSessionId(data.clientSessionId);
316
- }
317
- if (stateEntry.code === STATE_VAR_CODES[PollarStateVar.AUTHENTICATION].EMAIL_AUTH_START_SUCCESS) {
318
- const data = stateEntry.data;
319
- if (data?.code === "SDK_EMAIL_CODE_SENT") {
320
- setAwaitingEmailCode(true);
321
- setClientSessionId(data?.content?.clientSessionId);
322
- }
323
- }
324
- if (stateEntry.code === STATE_VAR_CODES[PollarStateVar.AUTHENTICATION].FETCH_SESSION_SUCCESS) {
325
- setAwaitingEmailCode(false);
326
- setTimeout(onClose, 1e3);
327
- }
318
+ return getClient().onAuthStateChange((next) => {
319
+ setAuthState(next);
320
+ if (next.step === "entering_email" && pendingEmail.current) {
321
+ getClient().sendEmailCode(pendingEmail.current);
322
+ pendingEmail.current = null;
323
+ }
324
+ if (next.step === "error" && next.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID) {
325
+ setCodeInputKey((k) => k + 1);
326
+ }
327
+ if (next.step === "authenticated") {
328
+ setTimeout(onClose, 1e3);
328
329
  }
329
330
  });
330
331
  }, []);
331
332
  const { theme = "light", accentColor = "#005DB4", logoUrl, emailEnabled, embeddedWallets, providers } = styles;
332
333
  function handleClose() {
333
334
  setEmail("");
334
- setError(null);
335
- setAwaitingEmailCode(false);
336
- setClientSessionId(null);
335
+ getClient().cancelLogin();
337
336
  onClose();
338
337
  }
339
- const cancelLoginRef = useRef(null);
340
- function handleEmail() {
341
- if (!email) {
342
- return;
343
- }
344
- const { cancelLogin } = getClient().login({ provider: "email", email });
345
- cancelLoginRef.current = cancelLogin;
338
+ function handleEmailSubmit() {
339
+ if (!email) return;
340
+ pendingEmail.current = email;
341
+ getClient().beginEmailLogin();
346
342
  }
347
343
  function handleSocialLogin(provider) {
348
- const { cancelLogin } = getClient().login({ provider });
349
- cancelLoginRef.current = cancelLogin;
344
+ getClient().loginOAuth(provider);
350
345
  }
351
346
  function handleWalletConnect(type) {
352
- const { cancelLogin } = getClient().login({ provider: "wallet", type });
353
- cancelLoginRef.current = cancelLogin;
347
+ getClient().loginWallet(type);
354
348
  }
355
- async function handleVerifyCode(code) {
356
- if (!clientSessionId) return;
357
- void getClient().verifyEmailCode(clientSessionId, code);
349
+ function handleVerifyCode(code) {
350
+ getClient().verifyEmailCode(code);
351
+ }
352
+ function handleBack() {
353
+ setEmail("");
354
+ getClient().cancelLogin();
358
355
  }
359
356
  function handleRetry() {
360
357
  getClient().logout();
358
+ if (styles.emailEnabled) {
359
+ getClient().beginEmailLogin();
360
+ }
361
361
  }
362
362
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: handleClose, children: /* @__PURE__ */ jsx(
363
363
  LoginModalTemplate,
@@ -376,17 +376,16 @@ function LoginModal({ onClose }) {
376
376
  },
377
377
  appName: config.application?.name ?? "Pollar",
378
378
  email,
379
- status,
380
- error,
381
379
  onEmailChange: setEmail,
382
- onEmailSubmit: handleEmail,
380
+ onEmailSubmit: handleEmailSubmit,
383
381
  onSocialLogin: handleSocialLogin,
384
382
  onFreighterConnect: () => handleWalletConnect(WalletType.FREIGHTER),
385
383
  onAlbedoConnect: () => handleWalletConnect(WalletType.ALBEDO),
386
- loginStateCode,
387
- awaitingEmailCode,
384
+ authState,
385
+ codeInputKey,
388
386
  onCodeSubmit: handleVerifyCode,
389
- cancelLoginRef,
387
+ onBack: handleBack,
388
+ onCancel: () => getClient().cancelLogin(),
390
389
  onRetry: handleRetry
391
390
  }
392
391
  ) });
@@ -534,7 +533,7 @@ function TransactionModalTemplate({
534
533
  /* @__PURE__ */ jsx(
535
534
  ModalStatusBanner,
536
535
  {
537
- code: transactionStateCode,
536
+ message: TRANSACTION_CODE_MESSAGES[transactionStateCode] ?? "",
538
537
  status
539
538
  }
540
539
  ),
@@ -618,13 +617,6 @@ function PollarProvider({ config, styles: propStyles, children }) {
618
617
  level: "info",
619
618
  ts: 0
620
619
  },
621
- authentication: {
622
- var: "authentication",
623
- code: STATE_VAR_CODES.authentication.NONE,
624
- status: StateStatus.NONE,
625
- level: "info",
626
- ts: 0
627
- },
628
620
  transaction: {
629
621
  var: "transaction",
630
622
  code: STATE_VAR_CODES.transaction.NONE,
@@ -639,25 +631,18 @@ function PollarProvider({ config, styles: propStyles, children }) {
639
631
  return pollarClient.onStateChange((stateEntry) => {
640
632
  setState((prevState) => {
641
633
  if (JSON.stringify(prevState[stateEntry.var]) !== JSON.stringify(stateEntry)) {
642
- return {
643
- ...prevState,
644
- [stateEntry.var]: stateEntry
645
- };
634
+ return { ...prevState, [stateEntry.var]: stateEntry };
646
635
  }
647
636
  return prevState;
648
637
  });
649
- if (stateEntry.var === "authentication") {
650
- if ((stateEntry.code === STATE_VAR_CODES.authentication.SESSION_STORED || STATE_VAR_CODES.authentication.RESTORED_SESSION_SUCCESS) && isValidSession(stateEntry.data)) {
651
- setSessionState((prevState) => {
652
- if (JSON.stringify(prevState) !== JSON.stringify(stateEntry.data)) {
653
- return stateEntry.data;
654
- }
655
- return prevState;
656
- });
657
- }
658
- if (stateEntry.code === STATE_VAR_CODES.authentication.LOGOUT) {
659
- setSessionState(null);
660
- }
638
+ });
639
+ }, [pollarClient]);
640
+ useEffect(() => {
641
+ return pollarClient.onAuthStateChange((authState) => {
642
+ if (authState.step === "authenticated") {
643
+ setSessionState((prev) => JSON.stringify(prev) !== JSON.stringify(authState.session) ? authState.session : prev);
644
+ } else if (authState.step === "idle") {
645
+ setSessionState(null);
661
646
  }
662
647
  });
663
648
  }, [pollarClient]);
@@ -679,14 +664,12 @@ function PollarProvider({ config, styles: propStyles, children }) {
679
664
  () => ({
680
665
  walletAddress: sessionState?.wallet?.publicKey || "",
681
666
  getClient: () => pollarClient,
682
- // client
683
667
  state,
684
668
  login: (options) => pollarClient.login(options),
685
669
  logout: () => pollarClient.logout(),
686
670
  isAuthenticated: pollarClient.isAuthenticated(),
687
671
  buildTx: (operation, params, options) => pollarClient.buildTx(operation, params, options),
688
672
  submitTx: (signedXdr) => pollarClient.submitTx(signedXdr),
689
- // react
690
673
  sendTransaction: (operation, params, options) => {
691
674
  void pollarClient.buildTx(operation, params, options);
692
675
  setTransactionModalOpen(true);
@@ -695,7 +678,6 @@ function PollarProvider({ config, styles: propStyles, children }) {
695
678
  openLoginModal: () => setLoginModalOpen(true),
696
679
  config: remoteConfig,
697
680
  styles,
698
- // stellar
699
681
  async getBalance(publicKey) {
700
682
  const pk = publicKey || sessionState?.wallet?.publicKey;
701
683
  if (pk) {