@echoxyz/sonar-react 0.4.1 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @echoxyz/sonar-react
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2e6e793: Introduce useSonarPurchase hook
8
+
9
+ ## 0.4.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 1102d83: Ensure that entity is refetched whenever the wallet address changes
14
+
3
15
  ## 0.4.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -118,52 +118,76 @@ const ExampleEntityPanel = () => {
118
118
 
119
119
  ```
120
120
 
121
- 5. Optional: call APIs with the low level client to implement the purchase flow:
121
+ 5. Implement the purchase flow
122
122
 
123
123
  ```tsx
124
- import { useEffect } from "react";
125
- import { useSonarEntity } from "./hooks/useSonarEntity";
126
- import { useAccount } from "wagmi";
127
-
128
- export function Example() {
129
- const { address, isConnected } = useAccount();
130
- // Would normally need to handle loading and error states like above
131
- const { entity } = useSonarEntity({
132
- saleUUID: "<your-sale-uuid>",
133
- wallet: { address, isConnected },
134
- });
135
- const client = useSonarClient();
136
-
137
- useEffect(() => {
138
- if (!entity) {
139
- return;
140
- }
124
+ function Example({
125
+ entityUUID,
126
+ walletAddress,
127
+ }: {
128
+ entityUUID: string;
129
+ walletAddress: string;
130
+ }) {
131
+ const sonarPurchaser = useSonarPurchase({
132
+ saleUUID: sonarConfig.saleUUID,
133
+ entityUUID,
134
+ entityType,
135
+ walletAddress,
136
+ });
137
+
138
+ if (sonarPurchaser.loading) {
139
+ return <p>Loading...</p>;
140
+ }
141
+
142
+ if (sonarPurchaser.error) {
143
+ return <p>Error: {error.message}</p>;
144
+ }
145
+
146
+ return (
147
+ <div>
148
+ {sonarPurchaser.readyToPurchase && (
149
+ <PurchaseButton
150
+ generatePurchasePermit={sonarPurchaser.generatePurchasePermit}
151
+ />
152
+ )}
153
+
154
+ {!sonarPurchaser.readyToPurchase &&
155
+ sonarPurchaser.failureReason ===
156
+ PrePurchaseFailureReason.REQUIRES_LIVENESS && (
157
+ <button
158
+ onClick={() => {
159
+ window.open(prePurchaseCheckResult.LivenessCheckURL, "_blank");
160
+ }}
161
+ >
162
+ Complete liveness check to purchase
163
+ </button>
164
+ )}
165
+ </div>
166
+ );
167
+ }
141
168
 
142
- (async () => {
143
- const pre = await client.prePurchaseCheck({
144
- saleUUID: "<your-sale-uuid>",
145
- entityUUID: entity.EntityUUID,
146
- walletAddress: "0x1234...abcd" as `0x${string}`,
147
- });
148
-
149
- if (pre.ReadyToPurchase) {
150
- const permit = await client.generatePurchasePermit({
151
- saleUUID: "<your-sale-uuid>",
152
- entityUUID: entity.EntityUUID,
153
- walletAddress: "0x1234...abcd" as `0x${string}`,
154
- });
155
- console.log(permit.Signature, permit.Permit);
156
- }
157
-
158
- const alloc = await client.fetchAllocation({
159
- saleUUID: "<your-sale-uuid>",
160
- walletAddress,
161
- });
162
- console.log(alloc);
163
- })();
164
- }, [entity]);
169
+ function PurchaseButton({
170
+ generatePurchasePermit,
171
+ }: {
172
+ generatePurchasePermit: () => Promise<GeneratePurchasePermitResponse>;
173
+ }) {
174
+ const purchase = async () => {
175
+ const response = await generatePurchasePermit();
176
+ const r = response as unknown as {
177
+ Signature: string;
178
+ PermitJSON: AllocationPermit;
179
+ };
180
+ if (r.Signature && r.PermitJSON) {
181
+ console.log(permit.Signature, permit.Permit);
182
+ return;
183
+ }
184
+ };
165
185
 
166
- return null;
186
+ return (
187
+ <button onClick={purchase}>
188
+ Purchase
189
+ </button>
190
+ );
167
191
  }
168
192
  ```
169
193
 
package/dist/index.cjs CHANGED
@@ -23,7 +23,8 @@ __export(index_exports, {
23
23
  SonarProvider: () => SonarProvider,
24
24
  useSonarAuth: () => useSonarAuth,
25
25
  useSonarClient: () => useSonarClient,
26
- useSonarEntity: () => useSonarEntity
26
+ useSonarEntity: () => useSonarEntity,
27
+ useSonarPurchase: () => useSonarPurchase
27
28
  });
28
29
  module.exports = __toCommonJS(index_exports);
29
30
 
@@ -147,6 +148,7 @@ function useSonarEntity(args) {
147
148
  setState({
148
149
  loading: false,
149
150
  entity: resp.Entity,
151
+ walletAddress,
150
152
  error: void 0,
151
153
  hasFetched: true
152
154
  });
@@ -155,13 +157,14 @@ function useSonarEntity(args) {
155
157
  setState({
156
158
  loading: false,
157
159
  entity: void 0,
160
+ walletAddress: void 0,
158
161
  error: void 0,
159
162
  hasFetched: true
160
163
  });
161
164
  return;
162
165
  }
163
166
  const error = err instanceof Error ? err : new Error(String(err));
164
- setState({ loading: false, entity: void 0, error, hasFetched: true });
167
+ setState({ loading: false, entity: void 0, walletAddress: void 0, error, hasFetched: true });
165
168
  }
166
169
  }, [client, saleUUID, walletAddress, fullyConnected]);
167
170
  const reset = (0, import_react2.useCallback)(() => {
@@ -169,16 +172,15 @@ function useSonarEntity(args) {
169
172
  loading: false,
170
173
  hasFetched: false,
171
174
  entity: void 0,
175
+ walletAddress: void 0,
172
176
  error: void 0
173
177
  });
174
178
  }, []);
175
179
  (0, import_react2.useEffect)(() => {
176
- if (fullyConnected) {
177
- if (!state.hasFetched && !state.loading) {
178
- refetch();
179
- }
180
+ if (fullyConnected && state.walletAddress !== walletAddress) {
181
+ refetch();
180
182
  }
181
- }, [fullyConnected, state.hasFetched, state.loading, refetch]);
183
+ }, [fullyConnected, state.walletAddress, walletAddress, refetch]);
182
184
  (0, import_react2.useEffect)(() => {
183
185
  if (ready && (!authenticated || !walletAddress)) {
184
186
  reset();
@@ -191,10 +193,70 @@ function useSonarEntity(args) {
191
193
  error: state.error
192
194
  };
193
195
  }
196
+ function useSonarPurchase(args) {
197
+ const saleUUID = args.saleUUID;
198
+ const entityUUID = args.entityUUID;
199
+ const walletAddress = args.walletAddress;
200
+ const client = useSonarClient();
201
+ const [state, setState] = (0, import_react2.useState)({
202
+ loading: true,
203
+ readyToPurchase: false,
204
+ error: void 0
205
+ });
206
+ const generatePurchasePermit = (0, import_react2.useCallback)(() => {
207
+ return client.generatePurchasePermit({
208
+ saleUUID,
209
+ entityUUID,
210
+ walletAddress
211
+ });
212
+ }, [client, saleUUID, entityUUID, walletAddress]);
213
+ (0, import_react2.useEffect)(() => {
214
+ const fetchPurchaseData = async () => {
215
+ setState({
216
+ loading: true,
217
+ readyToPurchase: false,
218
+ error: void 0
219
+ });
220
+ try {
221
+ const response = await client.prePurchaseCheck({
222
+ saleUUID,
223
+ entityUUID,
224
+ walletAddress
225
+ });
226
+ if (response.ReadyToPurchase) {
227
+ setState({
228
+ loading: false,
229
+ readyToPurchase: true,
230
+ generatePurchasePermit,
231
+ error: void 0
232
+ });
233
+ } else {
234
+ setState({
235
+ loading: false,
236
+ readyToPurchase: false,
237
+ failureReason: response.FailureReason,
238
+ livenessCheckURL: response.LivenessCheckURL,
239
+ error: void 0
240
+ });
241
+ }
242
+ } catch (err) {
243
+ const error = err instanceof Error ? err : new Error(String(err));
244
+ setState({
245
+ loading: false,
246
+ readyToPurchase: false,
247
+ error
248
+ });
249
+ }
250
+ };
251
+ fetchPurchaseData();
252
+ }, [saleUUID, entityUUID, walletAddress, client, generatePurchasePermit]);
253
+ return state;
254
+ }
194
255
  // Annotate the CommonJS export names for ESM import in node:
195
256
  0 && (module.exports = {
196
257
  SonarProvider,
197
258
  useSonarAuth,
198
259
  useSonarClient,
199
- useSonarEntity
260
+ useSonarEntity,
261
+ useSonarPurchase
200
262
  });
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { SonarClient, EntityDetails } from '@echoxyz/sonar-core';
3
+ import { SonarClient, EntityDetails, GeneratePurchasePermitResponse, PrePurchaseFailureReason } from '@echoxyz/sonar-core';
4
4
 
5
5
  type SonarProviderProps = {
6
6
  children: React.ReactNode;
@@ -39,5 +39,34 @@ declare function useSonarEntity(args: {
39
39
  saleUUID: string;
40
40
  walletAddress?: string;
41
41
  }): UseSonarEntityResult;
42
+ type UseSonarPurchaseResultReadyToPurchase = {
43
+ loading: false;
44
+ readyToPurchase: true;
45
+ error: undefined;
46
+ generatePurchasePermit: () => Promise<GeneratePurchasePermitResponse>;
47
+ };
48
+ type UseSonarPurchaseResultNotReadyToPurchase = {
49
+ loading: false;
50
+ readyToPurchase: false;
51
+ error: undefined;
52
+ failureReason: PrePurchaseFailureReason;
53
+ livenessCheckURL: string;
54
+ };
55
+ type UseSonarPurchaseResultError = {
56
+ loading: false;
57
+ readyToPurchase: false;
58
+ error: Error;
59
+ };
60
+ type UseSonarPurchaseResultLoading = {
61
+ loading: true;
62
+ readyToPurchase: false;
63
+ error: undefined;
64
+ };
65
+ type UseSonarPurchaseResult = UseSonarPurchaseResultLoading | UseSonarPurchaseResultReadyToPurchase | UseSonarPurchaseResultNotReadyToPurchase | UseSonarPurchaseResultError;
66
+ declare function useSonarPurchase(args: {
67
+ saleUUID: string;
68
+ entityUUID: string;
69
+ walletAddress: string;
70
+ }): UseSonarPurchaseResult;
42
71
 
43
- export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, useSonarAuth, useSonarClient, useSonarEntity };
72
+ export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, type UseSonarPurchaseResult, type UseSonarPurchaseResultError, type UseSonarPurchaseResultLoading, type UseSonarPurchaseResultNotReadyToPurchase, type UseSonarPurchaseResultReadyToPurchase, useSonarAuth, useSonarClient, useSonarEntity, useSonarPurchase };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { SonarClient, EntityDetails } from '@echoxyz/sonar-core';
3
+ import { SonarClient, EntityDetails, GeneratePurchasePermitResponse, PrePurchaseFailureReason } from '@echoxyz/sonar-core';
4
4
 
5
5
  type SonarProviderProps = {
6
6
  children: React.ReactNode;
@@ -39,5 +39,34 @@ declare function useSonarEntity(args: {
39
39
  saleUUID: string;
40
40
  walletAddress?: string;
41
41
  }): UseSonarEntityResult;
42
+ type UseSonarPurchaseResultReadyToPurchase = {
43
+ loading: false;
44
+ readyToPurchase: true;
45
+ error: undefined;
46
+ generatePurchasePermit: () => Promise<GeneratePurchasePermitResponse>;
47
+ };
48
+ type UseSonarPurchaseResultNotReadyToPurchase = {
49
+ loading: false;
50
+ readyToPurchase: false;
51
+ error: undefined;
52
+ failureReason: PrePurchaseFailureReason;
53
+ livenessCheckURL: string;
54
+ };
55
+ type UseSonarPurchaseResultError = {
56
+ loading: false;
57
+ readyToPurchase: false;
58
+ error: Error;
59
+ };
60
+ type UseSonarPurchaseResultLoading = {
61
+ loading: true;
62
+ readyToPurchase: false;
63
+ error: undefined;
64
+ };
65
+ type UseSonarPurchaseResult = UseSonarPurchaseResultLoading | UseSonarPurchaseResultReadyToPurchase | UseSonarPurchaseResultNotReadyToPurchase | UseSonarPurchaseResultError;
66
+ declare function useSonarPurchase(args: {
67
+ saleUUID: string;
68
+ entityUUID: string;
69
+ walletAddress: string;
70
+ }): UseSonarPurchaseResult;
42
71
 
43
- export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, useSonarAuth, useSonarClient, useSonarEntity };
72
+ export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, type UseSonarPurchaseResult, type UseSonarPurchaseResultError, type UseSonarPurchaseResultLoading, type UseSonarPurchaseResultNotReadyToPurchase, type UseSonarPurchaseResultReadyToPurchase, useSonarAuth, useSonarClient, useSonarEntity, useSonarPurchase };
package/dist/index.js CHANGED
@@ -76,17 +76,19 @@ function SonarProvider({ children, config }) {
76
76
  }
77
77
 
78
78
  // src/hooks.ts
79
- import { APIError } from "@echoxyz/sonar-core";
80
- import { useCallback as useCallback2, useContext as useContext2, useEffect as useEffect2, useState as useState2 } from "react";
79
+ import {
80
+ APIError
81
+ } from "@echoxyz/sonar-core";
82
+ import { useCallback as useCallback2, useContext, useEffect as useEffect2, useState as useState2 } from "react";
81
83
  function useSonarAuth() {
82
- const ctx = useContext2(AuthContext);
84
+ const ctx = useContext(AuthContext);
83
85
  if (!ctx) {
84
86
  throw new Error("useSonarAuth must be used within a SonarProvider");
85
87
  }
86
88
  return ctx;
87
89
  }
88
90
  function useSonarClient() {
89
- const ctx = useContext2(ClientContext);
91
+ const ctx = useContext(ClientContext);
90
92
  if (!ctx) {
91
93
  throw new Error("useSonarClient must be used within a SonarProvider");
92
94
  }
@@ -118,6 +120,7 @@ function useSonarEntity(args) {
118
120
  setState({
119
121
  loading: false,
120
122
  entity: resp.Entity,
123
+ walletAddress,
121
124
  error: void 0,
122
125
  hasFetched: true
123
126
  });
@@ -126,13 +129,14 @@ function useSonarEntity(args) {
126
129
  setState({
127
130
  loading: false,
128
131
  entity: void 0,
132
+ walletAddress: void 0,
129
133
  error: void 0,
130
134
  hasFetched: true
131
135
  });
132
136
  return;
133
137
  }
134
138
  const error = err instanceof Error ? err : new Error(String(err));
135
- setState({ loading: false, entity: void 0, error, hasFetched: true });
139
+ setState({ loading: false, entity: void 0, walletAddress: void 0, error, hasFetched: true });
136
140
  }
137
141
  }, [client, saleUUID, walletAddress, fullyConnected]);
138
142
  const reset = useCallback2(() => {
@@ -140,16 +144,15 @@ function useSonarEntity(args) {
140
144
  loading: false,
141
145
  hasFetched: false,
142
146
  entity: void 0,
147
+ walletAddress: void 0,
143
148
  error: void 0
144
149
  });
145
150
  }, []);
146
151
  useEffect2(() => {
147
- if (fullyConnected) {
148
- if (!state.hasFetched && !state.loading) {
149
- refetch();
150
- }
152
+ if (fullyConnected && state.walletAddress !== walletAddress) {
153
+ refetch();
151
154
  }
152
- }, [fullyConnected, state.hasFetched, state.loading, refetch]);
155
+ }, [fullyConnected, state.walletAddress, walletAddress, refetch]);
153
156
  useEffect2(() => {
154
157
  if (ready && (!authenticated || !walletAddress)) {
155
158
  reset();
@@ -162,9 +165,69 @@ function useSonarEntity(args) {
162
165
  error: state.error
163
166
  };
164
167
  }
168
+ function useSonarPurchase(args) {
169
+ const saleUUID = args.saleUUID;
170
+ const entityUUID = args.entityUUID;
171
+ const walletAddress = args.walletAddress;
172
+ const client = useSonarClient();
173
+ const [state, setState] = useState2({
174
+ loading: true,
175
+ readyToPurchase: false,
176
+ error: void 0
177
+ });
178
+ const generatePurchasePermit = useCallback2(() => {
179
+ return client.generatePurchasePermit({
180
+ saleUUID,
181
+ entityUUID,
182
+ walletAddress
183
+ });
184
+ }, [client, saleUUID, entityUUID, walletAddress]);
185
+ useEffect2(() => {
186
+ const fetchPurchaseData = async () => {
187
+ setState({
188
+ loading: true,
189
+ readyToPurchase: false,
190
+ error: void 0
191
+ });
192
+ try {
193
+ const response = await client.prePurchaseCheck({
194
+ saleUUID,
195
+ entityUUID,
196
+ walletAddress
197
+ });
198
+ if (response.ReadyToPurchase) {
199
+ setState({
200
+ loading: false,
201
+ readyToPurchase: true,
202
+ generatePurchasePermit,
203
+ error: void 0
204
+ });
205
+ } else {
206
+ setState({
207
+ loading: false,
208
+ readyToPurchase: false,
209
+ failureReason: response.FailureReason,
210
+ livenessCheckURL: response.LivenessCheckURL,
211
+ error: void 0
212
+ });
213
+ }
214
+ } catch (err) {
215
+ const error = err instanceof Error ? err : new Error(String(err));
216
+ setState({
217
+ loading: false,
218
+ readyToPurchase: false,
219
+ error
220
+ });
221
+ }
222
+ };
223
+ fetchPurchaseData();
224
+ }, [saleUUID, entityUUID, walletAddress, client, generatePurchasePermit]);
225
+ return state;
226
+ }
165
227
  export {
166
228
  SonarProvider,
167
229
  useSonarAuth,
168
230
  useSonarClient,
169
- useSonarEntity
231
+ useSonarEntity,
232
+ useSonarPurchase
170
233
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echoxyz/sonar-react",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",