@pollar/react 0.6.0 → 0.7.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/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @pollar/react
2
2
 
3
- React bindings for [Pollar](https://pollar.xyz) — drop-in authentication UI and hooks for Stellar-based applications.
3
+ React bindings for [Pollar](https://pollar.xyz) — drop-in authentication UI, transaction modals, and hooks for
4
+ Stellar-based applications.
5
+
6
+ > **0.7.0 is a breaking change.** The context's session is now `PollarPersistedSession | null` and PII has moved to
7
+ > `client.getUserProfile()`. Read the [CHANGELOG](../../CHANGELOG.md) before upgrading.
4
8
 
5
9
  ## Installation
6
10
 
@@ -12,7 +16,7 @@ pnpm add @pollar/react @pollar/core
12
16
  yarn add @pollar/react @pollar/core
13
17
  ```
14
18
 
15
- **Peer dependencies:** `react >= 18`, `react-dom >= 18`
19
+ **Peer dependencies:** `react >= 18`, `react-dom >= 18`. Node ≥ 20 in toolchains.
16
20
 
17
21
  ## Quick Start
18
22
 
@@ -35,7 +39,7 @@ export default function App({ children }: { children: React.ReactNode }) {
35
39
  import { usePollar } from '@pollar/react';
36
40
 
37
41
  export function Profile() {
38
- const { isAuthenticated, session, login, logout } = usePollar();
42
+ const { isAuthenticated, walletAddress, login, logout, getClient } = usePollar();
39
43
 
40
44
  if (!isAuthenticated) {
41
45
  return (
@@ -45,9 +49,13 @@ export function Profile() {
45
49
  );
46
50
  }
47
51
 
52
+ // PII (email, name, avatar, providers) lives in memory only — fetch it from the client.
53
+ const profile = getClient().getUserProfile();
54
+
48
55
  return (
49
56
  <div>
50
- <p>Wallet: {session?.wallet?.publicKey}</p>
57
+ <p>Wallet: {walletAddress}</p>
58
+ <p>Email: {profile?.mail}</p>
51
59
  <button onClick={logout}>Sign out</button>
52
60
  </div>
53
61
  );
@@ -64,34 +72,94 @@ Context provider that initialises the Pollar client and makes it available to ch
64
72
  <PollarProvider
65
73
  config={{
66
74
  apiKey: 'your-api-key',
67
- baseUrl: 'https://sdk.api.pollar.xyz', // optional
68
- stellarNetwork: 'testnet', // optional, default: 'testnet'
75
+ baseUrl: 'https://sdk.api.pollar.xyz', // optional
76
+ stellarNetwork: 'testnet', // optional, default: 'testnet'
77
+ // 0.7.0 options threaded straight through to PollarClient:
78
+ storage, // optional, RN apps inject this
79
+ keyManager, // optional, autodetects on web
80
+ walletAdapter, // optional, external wallet stack
81
+ deviceLabel: 'iPhone — Safari', // optional, shown in SessionsModal
82
+ onStorageDegrade, // optional, telemetry hook
69
83
  }}
84
+ styles={{ /* optional style overrides */ }}
85
+ adapters={{ /* optional named adapter set */ }}
70
86
  >
71
87
  {children}
72
88
  </PollarProvider>
73
89
  ```
74
90
 
75
- | Prop | Type | Required | Description |
76
- | -------- | ------------------- | -------- | ---------------------------------------- |
77
- | `config` | `PollarClientConfig`| Yes | Configuration passed to `PollarClient` |
91
+ | Prop | Type | Required | Description |
92
+ | ---------- | ------------------- | -------- | -------------------------------------------------------------------------- |
93
+ | `config` | `PollarClientConfig`| Yes | Configuration passed verbatim to `PollarClient` |
94
+ | `styles` | `PollarStyles` | No | Per-app style overrides; merged on top of `appConfig.styles` from the API |
95
+ | `adapters` | `PollarAdapters` | No | Named set of `PollarAdapter` objects (e.g. Trustless Work). See below |
78
96
 
79
97
  ---
80
98
 
81
99
  ### `usePollar()`
82
100
 
83
- Returns the authentication context.
101
+ Returns the full Pollar context. Every modal opener returned here renders an already-wired modal — no extra mounting
102
+ needed.
84
103
 
85
104
  ```ts
86
105
  const {
87
- session, // PollarApplicationConfigContent | null
88
- isLoading, // boolean — true while a login flow is in progress
89
- isAuthenticated, // booleantrue when a valid session exists
90
- login, // (options: PollarLoginOptions) => void
91
- logout, // () => Promise<void>
106
+ // Session
107
+ isAuthenticated, // boolean — true when a wallet public key is present
108
+ walletAddress, // string'' until authenticated
109
+ walletType, // WalletId | null
110
+
111
+ // Client escape hatch
112
+ getClient, // () => PollarClient — for getUserProfile(), listSessions(), …
113
+
114
+ // Auth
115
+ login, // (options: PollarLoginOptions) => void
116
+ logout, // () => void (fire-and-forget; await getClient().logout() if you need the promise)
117
+ openLoginModal, // () => void
118
+
119
+ // Sessions (new in 0.7.0)
120
+ openSessionsModal, // () => void
121
+
122
+ // Transactions
123
+ tx, // TransactionState
124
+ buildTx, // (operation, params, options?) => Promise<void>
125
+ signAndSubmitTx, // (unsignedXdr: string) => Promise<void>
126
+ openTxModal, // () => void
127
+
128
+ // Transaction history
129
+ txHistory, // TxHistoryState
130
+ openTxHistoryModal, // () => void
131
+
132
+ // Wallet balance
133
+ walletBalance, // WalletBalanceState
134
+ refreshWalletBalance, // () => Promise<void>
135
+ openWalletBalanceModal, // () => void
136
+
137
+ // Send / Receive
138
+ openSendModal, // () => void
139
+ openReceiveModal, // () => void
140
+
141
+ // Network
142
+ network, // StellarNetwork — 'mainnet' | 'testnet'
143
+ setNetwork, // (network: StellarNetwork) => void
144
+
145
+ // KYC (UI ready — backend coming soon)
146
+ openKycModal, // (options?: { country?, level?, onApproved? }) => void
147
+
148
+ // Ramp (UI ready — backend coming soon)
149
+ openRampModal, // () => void
150
+
151
+ // App config / styles served by the Pollar API
152
+ appConfig, // PollarConfig
153
+ styles, // PollarStyles
154
+
155
+ // Adapters (from PollarProvider props)
156
+ adapters, // PollarAdapters | undefined
92
157
  } = usePollar();
93
158
  ```
94
159
 
160
+ > **0.6.0 renames** — `transaction` → `tx`, `openTransactionModal` → `openTxModal`, `config` → `appConfig`,
161
+ > `openRampWidget` → `openRampModal`, `refreshBalance` → `refreshWalletBalance`. Existing code on 0.5.x must update.
162
+
95
163
  #### Login options
96
164
 
97
165
  ```ts
@@ -102,17 +170,33 @@ login({ provider: 'github' });
102
170
  // Email OTP
103
171
  login({ provider: 'email', email: 'user@example.com' });
104
172
 
105
- // Stellar wallet
173
+ // Built-in Stellar wallet adapters
106
174
  import { WalletType } from '@pollar/core';
107
175
  login({ provider: 'wallet', type: WalletType.FREIGHTER });
108
176
  login({ provider: 'wallet', type: WalletType.ALBEDO });
177
+
178
+ // Any external adapter (e.g. Stellar Wallets Kit) when `walletAdapter` is set on config
179
+ login({ provider: 'wallet', type: 'xbull' });
180
+ login({ provider: 'wallet', type: 'lobstr' });
109
181
  ```
110
182
 
111
183
  ---
112
184
 
113
- ### `<WalletButton>`
185
+ ### Components
186
+
187
+ Every modal mounts itself when its `openXModal()` action is called. You don't need to render these directly — they're
188
+ already wired inside `<PollarProvider>` — but they're exported in case you want to mount them yourself.
114
189
 
115
- Pre-built button component that opens the Pollar authentication modal.
190
+ | Component | Purpose |
191
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------- |
192
+ | `<WalletButton>` | Drop-in button. Opens login when signed out; signed in, shows the wallet address with a dropdown (Send, Receive, balance, history, sign out). Inline arc spinner during in-progress transactions |
193
+ | `<SendModal>` | Full send flow: asset picker, amount, destination, inline build → sign → success/error |
194
+ | `<ReceiveModal>` | Wallet address as QR code with copy-to-clipboard (no external QR dependency required) |
195
+ | `<TxHistoryModal>` | Paginated transaction history with auto-fetch on open and stellar.expert explorer links |
196
+ | `<WalletBalanceModal>` | Stellar account balances with refresh button |
197
+ | `<SessionsModal>` | **New in 0.7.0.** Lists every active refresh-token family for the current user with device metadata, marks the local session, per-row revoke, and a "Sign out everywhere" button |
198
+ | `<KycModal>` | Identity verification flow — provider selection + status polling *(UI preview — backend coming soon)* |
199
+ | `<RampWidget>` | Buy/sell crypto — direction tabs, route comparison, payment instructions *(UI preview — backend coming soon)* |
116
200
 
117
201
  ```tsx
118
202
  import { WalletButton } from '@pollar/react';
@@ -122,7 +206,89 @@ export function Header() {
122
206
  }
123
207
  ```
124
208
 
125
- The modal handles all login providers, loading states, and error feedback out of the box.
209
+ ---
210
+
211
+ ### Template components
212
+
213
+ Every modal ships a pure presentational "template" companion — same name with a `Template` suffix. Use these when you
214
+ want to swap the chrome but keep the data wiring from `usePollar()`.
215
+
216
+ | Wrapper | Template |
217
+ | ---------------------- | ------------------------------ |
218
+ | `<WalletButton>` | `<WalletButtonTemplate>` |
219
+ | *(internal LoginModal)*| `<LoginModalTemplate>` |
220
+ | `<SendModal>` | `<SendModalTemplate>` |
221
+ | `<ReceiveModal>` | `<ReceiveModalTemplate>` |
222
+ | `<TransactionModal>` | `<TransactionModalTemplate>` |
223
+ | `<TxHistoryModal>` | `<TxHistoryModalTemplate>` |
224
+ | `<WalletBalanceModal>` | `<WalletBalanceModalTemplate>` |
225
+ | `<KycModal>` | `<KycModalTemplate>` |
226
+ | `<RampWidget>` | `<RampWidgetTemplate>` |
227
+ | `<SessionsModal>` | `<SessionsModalTemplate>` |
228
+
229
+ `<TxStatusView>` is the shared status component (build → sign → success/error) reused by `TransactionModal` and
230
+ `SendModal`; it's exported on its own for consumers that want to embed the lifecycle elsewhere.
231
+
232
+ ---
233
+
234
+ ### Custom adapters
235
+
236
+ The `adapters` prop on `<PollarProvider>` accepts any named set of `PollarAdapter` objects. Each adapter function
237
+ receives params, returns an unsigned XDR, and Pollar handles signing and submission automatically.
238
+
239
+ ```tsx
240
+ import type { PollarAdapter } from '@pollar/core';
241
+
242
+ const trustlessWork: PollarAdapter = {
243
+ initialize: async (params) => ({ unsignedTransaction: '…' }),
244
+ release: async (params) => ({ unsignedTransaction: '…' }),
245
+ };
246
+
247
+ <PollarProvider config={{ apiKey }} adapters={{ trustlessWork }}>
248
+
249
+ </PollarProvider>
250
+ ```
251
+
252
+ > **Renamed in 0.7.0** — `EscrowFn` → `AdapterFn` and `EscrowAdapter` → `PollarAdapter`. Runtime contract is
253
+ > unchanged; rename your imports.
254
+
255
+ #### `createPollarAdapterHook(key)`
256
+
257
+ Factory that generates a fully-typed hook mirroring an adapter's API with automatic XDR signing built in:
258
+
259
+ ```ts
260
+ import { createPollarAdapterHook } from '@pollar/react';
261
+
262
+ const useTrustlessWork = createPollarAdapterHook<typeof trustlessWork>('trustlessWork');
263
+
264
+ function MyComponent() {
265
+ const tw = useTrustlessWork();
266
+ await tw.initialize({ /* … */ }); // unsigned XDR is built, signed, and submitted automatically
267
+ }
268
+ ```
269
+
270
+ ---
271
+
272
+ ### External wallet stacks (Stellar Wallets Kit, …)
273
+
274
+ Pass a `WalletAdapterResolver` to `config.walletAdapter` so Pollar can reach wallets that live outside `@pollar/core`:
275
+
276
+ ```tsx
277
+ import { PollarProvider } from '@pollar/react';
278
+ import { stellarWalletsKit } from '@pollar/stellar-wallets-kit-adapter';
279
+ import { Networks } from '@creit.tech/stellar-wallets-kit';
280
+
281
+ <PollarProvider
282
+ config={{
283
+ apiKey: 'your-api-key',
284
+ walletAdapter: stellarWalletsKit({ network: Networks.PUBLIC }),
285
+ }}
286
+ >
287
+ {children}
288
+ </PollarProvider>
289
+ ```
290
+
291
+ Then any `login({ provider: 'wallet', type: '<kit wallet id>' })` is routed through the kit.
126
292
 
127
293
  ---
128
294
 
@@ -150,11 +316,27 @@ import type {
150
316
  AuthModalProps,
151
317
  PollarConfig,
152
318
  PollarStyles,
319
+
320
+ // Template props
321
+ SendModalTemplateProps,
322
+ ReceiveModalTemplateProps,
323
+ TransactionModalTemplateProps,
324
+ TxStatusViewProps,
325
+ WalletBalanceModalTemplateProps,
326
+ SessionsModalTemplateProps,
327
+ SessionsState,
328
+
329
+ // Step unions
330
+ KycStep,
331
+ RampStep,
153
332
  } from '@pollar/react';
154
333
  ```
155
334
 
335
+ The state types (`TransactionState`, `TxHistoryState`, `WalletBalanceState`, `NetworkState`, `AuthState`,
336
+ `PollarPersistedSession`, `PollarUserProfile`, `SessionInfo`, …) are re-exported from `@pollar/core`.
337
+
156
338
  ---
157
339
 
158
340
  ## License
159
341
 
160
- MIT
342
+ MIT
package/dist/index.css CHANGED
@@ -1443,6 +1443,113 @@
1443
1443
  animation: pollar-pulse 1.4s ease-in-out infinite;
1444
1444
  }
1445
1445
 
1446
+ /* src/components/sessions-modal/SessionsModal.css */
1447
+ .pollar-sessions-modal {
1448
+ max-width: 480px;
1449
+ }
1450
+ .pollar-sessions-list {
1451
+ display: flex;
1452
+ flex-direction: column;
1453
+ gap: 0.625rem;
1454
+ max-height: 380px;
1455
+ overflow-y: auto;
1456
+ margin-bottom: 1rem;
1457
+ }
1458
+ .pollar-sessions-item {
1459
+ display: grid;
1460
+ grid-template-columns: 1fr auto;
1461
+ grid-template-rows: auto auto;
1462
+ column-gap: 0.75rem;
1463
+ row-gap: 0.125rem;
1464
+ background: var(--pollar-input-bg);
1465
+ border: 1px solid var(--pollar-border);
1466
+ border-radius: var(--pollar-card-border-radius, 10px);
1467
+ padding: 0.75rem 1rem;
1468
+ }
1469
+ .pollar-sessions-item[data-current] {
1470
+ border-color: var(--pollar-accent);
1471
+ }
1472
+ .pollar-sessions-item-main {
1473
+ display: flex;
1474
+ align-items: center;
1475
+ gap: 0.5rem;
1476
+ min-width: 0;
1477
+ }
1478
+ .pollar-sessions-item-device {
1479
+ font-size: 0.875rem;
1480
+ font-weight: 500;
1481
+ color: var(--pollar-text);
1482
+ overflow: hidden;
1483
+ text-overflow: ellipsis;
1484
+ white-space: nowrap;
1485
+ }
1486
+ .pollar-sessions-item-badge {
1487
+ font-size: 0.6875rem;
1488
+ font-weight: 600;
1489
+ padding: 2px 8px;
1490
+ border-radius: 999px;
1491
+ text-transform: uppercase;
1492
+ letter-spacing: 0.04em;
1493
+ background: rgba(0, 93, 180, 0.12);
1494
+ color: var(--pollar-accent);
1495
+ white-space: nowrap;
1496
+ }
1497
+ .pollar-sessions-item-meta {
1498
+ font-size: 0.75rem;
1499
+ color: var(--pollar-muted);
1500
+ display: flex;
1501
+ gap: 0.5rem;
1502
+ align-items: center;
1503
+ grid-column: 1;
1504
+ }
1505
+ .pollar-sessions-item-revoke {
1506
+ grid-row: 1 / 3;
1507
+ grid-column: 2;
1508
+ align-self: center;
1509
+ background: none;
1510
+ border: 1px solid var(--pollar-border);
1511
+ border-radius: var(--pollar-buttons-border-radius, 6px);
1512
+ padding: 6px 12px;
1513
+ font-size: 0.75rem;
1514
+ font-weight: 500;
1515
+ color: var(--pollar-error-text, #dc2626);
1516
+ cursor: pointer;
1517
+ transition: background 150ms, border-color 150ms;
1518
+ }
1519
+ .pollar-sessions-item-revoke:hover:not(:disabled) {
1520
+ background: var(--pollar-error-bg, #fef2f2);
1521
+ border-color: var(--pollar-error-border, #fecaca);
1522
+ }
1523
+ .pollar-sessions-item-revoke:disabled {
1524
+ opacity: 0.5;
1525
+ cursor: not-allowed;
1526
+ }
1527
+ .pollar-sessions-actions {
1528
+ display: flex;
1529
+ justify-content: center;
1530
+ padding-top: 0.5rem;
1531
+ border-top: 1px solid var(--pollar-border);
1532
+ margin-bottom: 0.75rem;
1533
+ }
1534
+ .pollar-sessions-logout-all {
1535
+ background: none;
1536
+ border: 1px solid var(--pollar-error-border, #fecaca);
1537
+ border-radius: var(--pollar-buttons-border-radius, 6px);
1538
+ padding: 8px 16px;
1539
+ font-size: 0.8125rem;
1540
+ font-weight: 500;
1541
+ color: var(--pollar-error-text, #dc2626);
1542
+ cursor: pointer;
1543
+ transition: background 150ms;
1544
+ }
1545
+ .pollar-sessions-logout-all:hover:not(:disabled) {
1546
+ background: var(--pollar-error-bg, #fef2f2);
1547
+ }
1548
+ .pollar-sessions-logout-all:disabled {
1549
+ opacity: 0.4;
1550
+ cursor: not-allowed;
1551
+ }
1552
+
1446
1553
  /* src/components/tx-history-modal/TxHistoryModal.css */
1447
1554
  .pollar-hist-modal {
1448
1555
  max-width: 480px;