@pollar/react 0.7.1 → 0.8.1

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
@@ -3,8 +3,13 @@
3
3
  React bindings for [Pollar](https://pollar.xyz) — drop-in authentication UI, transaction modals, and hooks for
4
4
  Stellar-based applications.
5
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.
6
+ > **0.8.0 reshapes `<PollarProvider>` props (breaking).** `config` `client`
7
+ > (now accepts a `PollarClient` instance or a `PollarClientConfig`); `styles`
8
+ > moves under `appConfig.styles`; new `appConfig` prop is the opt-out switch
9
+ > for the remote `/applications/config` fetch (pass it — even `{}` — to skip
10
+ > the fetch); new `ui.renderWallets` slot replaces the default Freighter/Albedo
11
+ > picker. Read the [CHANGELOG](../../CHANGELOG.md) and
12
+ > [UPGRADE.md](../../UPGRADE.md) before upgrading.
8
13
 
9
14
  ## Installation
10
15
 
@@ -27,11 +32,7 @@ import { PollarProvider } from '@pollar/react';
27
32
  import '@pollar/react/styles.css';
28
33
 
29
34
  export default function App({ children }: { children: React.ReactNode }) {
30
- return (
31
- <PollarProvider config={{ apiKey: 'your-api-key' }}>
32
- {children}
33
- </PollarProvider>
34
- );
35
+ return <PollarProvider client={{ apiKey: 'your-api-key' }}>{children}</PollarProvider>;
35
36
  }
36
37
  ```
37
38
 
@@ -42,11 +43,7 @@ export function Profile() {
42
43
  const { isAuthenticated, walletAddress, login, logout, getClient } = usePollar();
43
44
 
44
45
  if (!isAuthenticated) {
45
- return (
46
- <button onClick={() => login({ provider: 'google' })}>
47
- Sign in with Google
48
- </button>
49
- );
46
+ return <button onClick={() => login({ provider: 'google' })}>Sign in with Google</button>;
50
47
  }
51
48
 
52
49
  // PII (email, name, avatar, providers) lives in memory only — fetch it from the client.
@@ -70,29 +67,50 @@ Context provider that initialises the Pollar client and makes it available to ch
70
67
 
71
68
  ```tsx
72
69
  <PollarProvider
73
- config={{
70
+ client={{
74
71
  apiKey: 'your-api-key',
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
72
+ baseUrl: 'https://sdk.api.pollar.xyz', // optional
73
+ stellarNetwork: 'testnet', // optional, default: 'testnet'
74
+ storage, // optional, RN apps inject this
75
+ keyManager, // optional, autodetects on web
76
+ walletAdapter, // optional, external wallet stack
77
+ deviceLabel: 'iPhone — Safari', // optional, shown in SessionsModal
78
+ onStorageDegrade, // optional, telemetry hook
79
+ }}
80
+ // Optional: pass `appConfig` (even `{}`) to skip the remote
81
+ // `/applications/config` fetch and use local-only styles/branding.
82
+ appConfig={{
83
+ styles: {
84
+ /* optional style overrides */
85
+ },
86
+ }}
87
+ ui={{
88
+ // Optional: replace the default Freighter/Albedo wallet picker.
89
+ // See "External wallet stacks" below for a kit-powered example.
90
+ renderWallets: undefined,
83
91
  }}
84
- styles={{ /* optional style overrides */ }}
85
- adapters={{ /* optional named adapter set */ }}
92
+ adapters={
93
+ {
94
+ /* optional named adapter set */
95
+ }
96
+ }
86
97
  >
87
98
  {children}
88
99
  </PollarProvider>
89
100
  ```
90
101
 
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 |
102
+ | Prop | Type | Required | Description |
103
+ | ----------- | --------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
+ | `client` | `PollarClient \| PollarClientConfig` | Yes | Either a pre-built `PollarClient` (testing, reuse outside React) or a `PollarClientConfig` the provider will construct one from. **Locked at first render** — swapping after mount is ignored |
105
+ | `appConfig` | `PollarConfig` | No | Local override of `/applications/config`. **Presence is the opt-out switch**: pass it (even `{}`) and the remote fetch is skipped. Omit it to keep the existing remote-fetch-on-mount behaviour |
106
+ | `ui` | `{ renderWallets?: RenderWalletsSlot }` | No | UI customisation slots. `renderWallets` replaces the default Freighter/Albedo buttons in `LoginModal`'s wallet picker. Receives `{ onConnect, authState }` and is expected to call `onConnect(walletId)` |
107
+ | `adapters` | `PollarAdapters` | No | Named set of `PollarAdapter` objects (e.g. Trustless Work). See below |
108
+
109
+ > **Renamed in 0.8.0** — `config` → `client`, `styles` → `appConfig.styles`.
110
+ > If you were passing `styles={{ ... }}` directly, move it to
111
+ > `appConfig={{ styles: { ... } }}` (which also opts you out of the remote
112
+ > `/applications/config` fetch). See [UPGRADE.md](../../UPGRADE.md) for the
113
+ > full migration matrix.
96
114
 
97
115
  ---
98
116
 
@@ -104,56 +122,56 @@ needed.
104
122
  ```ts
105
123
  const {
106
124
  // Session
107
- isAuthenticated, // boolean — true when a wallet public key is present
108
- walletAddress, // string — '' until authenticated
109
- walletType, // WalletId | null
125
+ isAuthenticated, // boolean — true when a wallet public key is present
126
+ walletAddress, // string — '' until authenticated
127
+ walletType, // WalletId | null
110
128
 
111
129
  // Client escape hatch
112
- getClient, // () => PollarClient — for getUserProfile(), listSessions(), …
130
+ getClient, // () => PollarClient — for getUserProfile(), listSessions(), …
113
131
 
114
132
  // Auth
115
- login, // (options: PollarLoginOptions) => void
116
- logout, // () => void (fire-and-forget; await getClient().logout() if you need the promise)
117
- openLoginModal, // () => void
133
+ login, // (options: PollarLoginOptions) => void
134
+ logout, // () => void (fire-and-forget; await getClient().logout() if you need the promise)
135
+ openLoginModal, // () => void
118
136
 
119
137
  // Sessions (new in 0.7.0)
120
- openSessionsModal, // () => void
138
+ openSessionsModal, // () => void
121
139
 
122
140
  // Transactions
123
- tx, // TransactionState
124
- buildTx, // (operation, params, options?) => Promise<void>
125
- signAndSubmitTx, // (unsignedXdr: string) => Promise<void>
126
- openTxModal, // () => void
141
+ tx, // TransactionState
142
+ buildTx, // (operation, params, options?) => Promise<void>
143
+ signAndSubmitTx, // (unsignedXdr: string) => Promise<void>
144
+ openTxModal, // () => void
127
145
 
128
146
  // Transaction history
129
- txHistory, // TxHistoryState
130
- openTxHistoryModal, // () => void
147
+ txHistory, // TxHistoryState
148
+ openTxHistoryModal, // () => void
131
149
 
132
150
  // Wallet balance
133
- walletBalance, // WalletBalanceState
134
- refreshWalletBalance, // () => Promise<void>
151
+ walletBalance, // WalletBalanceState
152
+ refreshWalletBalance, // () => Promise<void>
135
153
  openWalletBalanceModal, // () => void
136
154
 
137
155
  // Send / Receive
138
- openSendModal, // () => void
139
- openReceiveModal, // () => void
156
+ openSendModal, // () => void
157
+ openReceiveModal, // () => void
140
158
 
141
159
  // Network
142
- network, // StellarNetwork — 'mainnet' | 'testnet'
143
- setNetwork, // (network: StellarNetwork) => void
160
+ network, // StellarNetwork — 'mainnet' | 'testnet'
161
+ setNetwork, // (network: StellarNetwork) => void
144
162
 
145
163
  // KYC (UI ready — backend coming soon)
146
- openKycModal, // (options?: { country?, level?, onApproved? }) => void
164
+ openKycModal, // (options?: { country?, level?, onApproved? }) => void
147
165
 
148
166
  // Ramp (UI ready — backend coming soon)
149
- openRampModal, // () => void
167
+ openRampModal, // () => void
150
168
 
151
169
  // App config / styles served by the Pollar API
152
- appConfig, // PollarConfig
153
- styles, // PollarStyles
170
+ appConfig, // PollarConfig
171
+ styles, // PollarStyles
154
172
 
155
173
  // Adapters (from PollarProvider props)
156
- adapters, // PollarAdapters | undefined
174
+ adapters, // PollarAdapters | undefined
157
175
  } = usePollar();
158
176
  ```
159
177
 
@@ -187,16 +205,16 @@ login({ provider: 'wallet', type: 'lobstr' });
187
205
  Every modal mounts itself when its `openXModal()` action is called. You don't need to render these directly — they're
188
206
  already wired inside `<PollarProvider>` — but they're exported in case you want to mount them yourself.
189
207
 
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)* |
208
+ | Component | Purpose |
209
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
210
+ | `<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 |
211
+ | `<SendModal>` | Full send flow: asset picker, amount, destination, inline build → sign → success/error |
212
+ | `<ReceiveModal>` | Wallet address as QR code with copy-to-clipboard (no external QR dependency required) |
213
+ | `<TxHistoryModal>` | Paginated transaction history with auto-fetch on open and stellar.expert explorer links |
214
+ | `<WalletBalanceModal>` | Stellar account balances with refresh button |
215
+ | `<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 |
216
+ | `<KycModal>` | Identity verification flow — provider selection + status polling _(UI preview — backend coming soon)_ |
217
+ | `<RampWidget>` | Buy/sell crypto — direction tabs, route comparison, payment instructions _(UI preview — backend coming soon)_ |
200
218
 
201
219
  ```tsx
202
220
  import { WalletButton } from '@pollar/react';
@@ -213,22 +231,25 @@ export function Header() {
213
231
  Every modal ships a pure presentational "template" companion — same name with a `Template` suffix. Use these when you
214
232
  want to swap the chrome but keep the data wiring from `usePollar()`.
215
233
 
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>` |
234
+ | Wrapper | Template |
235
+ | ----------------------- | ------------------------------ |
236
+ | `<WalletButton>` | `<WalletButtonTemplate>` |
237
+ | _(internal LoginModal)_ | `<LoginModalTemplate>` |
238
+ | `<SendModal>` | `<SendModalTemplate>` |
239
+ | `<ReceiveModal>` | `<ReceiveModalTemplate>` |
240
+ | `<TransactionModal>` | `<TransactionModalTemplate>` |
241
+ | `<TxHistoryModal>` | `<TxHistoryModalTemplate>` |
242
+ | `<WalletBalanceModal>` | `<WalletBalanceModalTemplate>` |
243
+ | `<KycModal>` | `<KycModalTemplate>` |
244
+ | `<RampWidget>` | `<RampWidgetTemplate>` |
245
+ | `<SessionsModal>` | `<SessionsModalTemplate>` |
228
246
 
229
247
  `<TxStatusView>` is the shared status component (build → sign → success/error) reused by `TransactionModal` and
230
248
  `SendModal`; it's exported on its own for consumers that want to embed the lifecycle elsewhere.
231
249
 
250
+ > **0.8.1** — `onWalletConnect` is now **optional** on `<LoginModalTemplate>` (defaults to a no-op). If you drive the
251
+ > wallet picker entirely through `ui.renderWallets`, you no longer have to pass a handler you don't use.
252
+
232
253
  ---
233
254
 
234
255
  ### Custom adapters
@@ -241,12 +262,12 @@ import type { PollarAdapter } from '@pollar/core';
241
262
 
242
263
  const trustlessWork: PollarAdapter = {
243
264
  initialize: async (params) => ({ unsignedTransaction: '…' }),
244
- release: async (params) => ({ unsignedTransaction: '…' }),
265
+ release: async (params) => ({ unsignedTransaction: '…' }),
245
266
  };
246
267
 
247
- <PollarProvider config={{ apiKey }} adapters={{ trustlessWork }}>
268
+ <PollarProvider client={{ apiKey }} adapters={{ trustlessWork }}>
248
269
 
249
- </PollarProvider>
270
+ </PollarProvider>;
250
271
  ```
251
272
 
252
273
  > **Renamed in 0.7.0** — `EscrowFn` → `AdapterFn` and `EscrowAdapter` → `PollarAdapter`. Runtime contract is
@@ -263,7 +284,9 @@ const useTrustlessWork = createPollarAdapterHook<typeof trustlessWork>('trustles
263
284
 
264
285
  function MyComponent() {
265
286
  const tw = useTrustlessWork();
266
- await tw.initialize({ /* … */ }); // unsigned XDR is built, signed, and submitted automatically
287
+ await tw.initialize({
288
+ /* … */
289
+ }); // unsigned XDR is built, signed, and submitted automatically
267
290
  }
268
291
  ```
269
292
 
@@ -271,7 +294,9 @@ function MyComponent() {
271
294
 
272
295
  ### External wallet stacks (Stellar Wallets Kit, …)
273
296
 
274
- Pass a `WalletAdapterResolver` to `config.walletAdapter` so Pollar can reach wallets that live outside `@pollar/core`:
297
+ Pass a `WalletAdapterResolver` to `client.walletAdapter` so Pollar can reach
298
+ wallets that live outside `@pollar/core`. The signing path alone (no UI
299
+ changes):
275
300
 
276
301
  ```tsx
277
302
  import { PollarProvider } from '@pollar/react';
@@ -279,16 +304,58 @@ import { stellarWalletsKit } from '@pollar/stellar-wallets-kit-adapter';
279
304
  import { Networks } from '@creit.tech/stellar-wallets-kit';
280
305
 
281
306
  <PollarProvider
282
- config={{
307
+ client={{
283
308
  apiKey: 'your-api-key',
284
309
  walletAdapter: stellarWalletsKit({ network: Networks.PUBLIC }),
285
310
  }}
286
311
  >
287
312
  {children}
288
- </PollarProvider>
313
+ </PollarProvider>;
314
+ ```
315
+
316
+ Then any `login({ provider: 'wallet', type: '<kit wallet id>' })` is routed
317
+ through the kit. The built-in `LoginModal` still shows only Freighter / Albedo
318
+ unless you also wire `ui.renderWallets` (next section).
319
+
320
+ #### Showing every kit wallet in `LoginModal`
321
+
322
+ The `ui.renderWallets` slot replaces the hardcoded Freighter+Albedo buttons.
323
+ `@pollar/stellar-wallets-kit-adapter/picker` ships a bundle that builds both
324
+ halves (signing + picker UI) from a single options object — drop both into
325
+ `<PollarProvider>` and the kit's full wallet list (xBull, Lobstr, Rabet, …)
326
+ renders in `LoginModal`:
327
+
328
+ ```tsx
329
+ import { PollarProvider } from '@pollar/react';
330
+ import { createStellarWalletsKitBundle } from '@pollar/stellar-wallets-kit-adapter/picker';
331
+ import { Networks } from '@creit.tech/stellar-wallets-kit';
332
+
333
+ const bundle = createStellarWalletsKitBundle({
334
+ network: Networks.PUBLIC,
335
+ picker: { wallets: ['xbull', 'lobstr', 'freighter'] }, // subset, optional
336
+ });
337
+
338
+ <PollarProvider
339
+ client={{ apiKey: 'your-api-key', walletAdapter: bundle.walletAdapter }}
340
+ ui={{ renderWallets: bundle.renderWallets }}
341
+ >
342
+ {children}
343
+ </PollarProvider>;
289
344
  ```
290
345
 
291
- Then any `login({ provider: 'wallet', type: '<kit wallet id>' })` is routed through the kit.
346
+ If you only want a custom picker (no kit), `renderWallets` is just a function
347
+ of `{ onConnect, authState }` — return whatever JSX you want and call
348
+ `onConnect(walletId)` when the user picks a wallet:
349
+
350
+ ```tsx
351
+ ui={{
352
+ renderWallets: ({ onConnect, authState }) => (
353
+ <button disabled={authState.step !== 'idle'} onClick={() => onConnect('xbull')}>
354
+ xBull
355
+ </button>
356
+ ),
357
+ }}
358
+ ```
292
359
 
293
360
  ---
294
361
 
@@ -317,6 +384,10 @@ import type {
317
384
  PollarConfig,
318
385
  PollarStyles,
319
386
 
387
+ // 0.8.0 — wallet picker slot
388
+ RenderWalletsProps,
389
+ RenderWalletsSlot,
390
+
320
391
  // Template props
321
392
  SendModalTemplateProps,
322
393
  ReceiveModalTemplateProps,
package/dist/index.css CHANGED
@@ -582,8 +582,8 @@
582
582
  padding: 0 1rem;
583
583
  position: relative;
584
584
  text-align: center;
585
- -webkit-transition: transform .15s, box-shadow .15s;
586
- transition: transform .15s, box-shadow .15s;
585
+ -webkit-transition: transform 0.15s, box-shadow 0.15s;
586
+ transition: transform 0.15s, box-shadow 0.15s;
587
587
  vertical-align: middle;
588
588
  white-space: nowrap;
589
589
  width: 100%;
@@ -621,8 +621,8 @@
621
621
  flex-grow: 0;
622
622
  }
623
623
  .gsi-material-button .gsi-material-button-state {
624
- -webkit-transition: opacity .218s;
625
- transition: opacity .218s;
624
+ -webkit-transition: opacity 0.218s;
625
+ transition: opacity 0.218s;
626
626
  bottom: 0;
627
627
  left: 0;
628
628
  opacity: 0;
@@ -754,7 +754,7 @@
754
754
  background-color: var(--pollar-bg);
755
755
  }
756
756
  .pollar-social-list {
757
- margin-bottom: .625rem;
757
+ margin-bottom: 0.625rem;
758
758
  display: flex;
759
759
  flex-direction: column;
760
760
  gap: 0.625rem;