@echoxyz/sonar-react 0.2.1 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @echoxyz/sonar-react
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [affa026]
8
+ - @echoxyz/sonar-core@0.2.1
9
+
10
+ ## 0.3.0
11
+
12
+ ### Minor Changes
13
+
14
+ - b6393ef: Add useSonarEntity hook
15
+
3
16
  ## 0.2.1
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -78,26 +78,69 @@ export default function OAuthCallback() {
78
78
  }
79
79
  ```
80
80
 
81
- 4. Call APIs using the low-level client:
81
+ 4. Load the Sonar entity associated with the user's wallet
82
+
83
+ ```tsx
84
+ import { useSonarEntity } from "./hooks/useSonarEntity";
85
+ import { useAccount } from "wagmi";
86
+
87
+ const ExampleEntityPanel = () => {
88
+ const { address, isConnected } = useAccount();
89
+ const { authenticated, loading, entity, error } = useSonarEntity({
90
+ saleUUID: "<your-sale-uuid>",
91
+ wallet: { address, isConnected },
92
+ });
93
+
94
+ if (!isConnected || !authenticated) {
95
+ return <p>Connect your wallet and Sonar account to continue</p>;
96
+ }
97
+
98
+ if (loading) {
99
+ return <p>Loading...</p>;
100
+ }
101
+
102
+ if (error) {
103
+ return <p>Error: {error.message}</p>;
104
+ }
105
+
106
+ if (!entity) {
107
+ return <p>No entity found for this wallet. Please link your wallet on Sonar to continue.</p>;
108
+ }
109
+
110
+ return (
111
+ <div>
112
+ <span>entity.Label</span>
113
+ <span>entity.EntitySetupState</span>
114
+ <span>entity.EntitySaleEligibility</span>
115
+ </div>
116
+ );
117
+ };
118
+
119
+ ```
120
+
121
+ 5. Optional: call APIs with the low level client to implement the purchase flow:
82
122
 
83
123
  ```tsx
84
124
  import { useEffect } from "react";
85
- import { useSonarAuth, useSonarClient } from "@echoxyz/sonar-react";
125
+ import { useSonarEntity } from "./hooks/useSonarEntity";
126
+ import { useAccount } from "wagmi";
86
127
  import { EntityType } from "@echoxyz/sonar-core";
87
128
 
88
129
  export function Example() {
89
- const { authenticated, ready } = useSonarAuth();
130
+ const { address, isConnected } = useAccount();
131
+ // Would normally need to handle loading and error states like above
132
+ const { entity } = useSonarEntity({
133
+ saleUUID: "<your-sale-uuid>",
134
+ wallet: { address, isConnected },
135
+ });
90
136
  const client = useSonarClient();
91
- const { walletAddress } = useWallet();
92
137
 
93
138
  useEffect(() => {
94
- if (!ready || !authenticated) {
139
+ if (!entity) {
95
140
  return;
96
141
  }
97
142
 
98
143
  (async () => {
99
- const { Entity: entity } = await client.readEntity({ saleUUID: "<your-sale-uuid>", walletAddress });
100
-
101
144
  const pre = await client.prePurchaseCheck({
102
145
  saleUUID: "<your-sale-uuid>",
103
146
  entityUUID: entity.EntityUUID,
@@ -121,7 +164,7 @@ export function Example() {
121
164
  });
122
165
  console.log(alloc);
123
166
  })();
124
- }, [authenticated, client, ready]);
167
+ }, [entity]);
125
168
 
126
169
  return null;
127
170
  }
@@ -140,6 +183,8 @@ export function Example() {
140
183
  - `useSonarAuth()` → `{ authenticated, ready, token?, login(), completeOAuth({ code, state }), logout() }`
141
184
 
142
185
  - `useSonarClient()` → low-level `SonarClient` instance.
186
+
187
+ - `useSonarEntity()` → `{ authenticated, loading, entity?, error? }` high-level convenience hook for fetching a Sonar entity by wallet address.
143
188
 
144
189
  ## Notes
145
190
 
package/dist/index.cjs CHANGED
@@ -22,11 +22,12 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  SonarProvider: () => SonarProvider,
24
24
  useSonarAuth: () => useSonarAuth,
25
- useSonarClient: () => useSonarClient
25
+ useSonarClient: () => useSonarClient,
26
+ useSonarEntity: () => useSonarEntity
26
27
  });
27
28
  module.exports = __toCommonJS(index_exports);
28
29
 
29
- // src/react.tsx
30
+ // src/provider.tsx
30
31
  var import_sonar_core = require("@echoxyz/sonar-core");
31
32
  var import_react = require("react");
32
33
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -102,23 +103,99 @@ function SonarProvider({ children, config }) {
102
103
  const clientValue = (0, import_react.useMemo)(() => ({ client }), [client]);
103
104
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: authValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClientContext.Provider, { value: clientValue, children }) });
104
105
  }
106
+
107
+ // src/hooks.ts
108
+ var import_sonar_core2 = require("@echoxyz/sonar-core");
109
+ var import_react2 = require("react");
105
110
  function useSonarAuth() {
106
- const ctx = (0, import_react.useContext)(AuthContext);
111
+ const ctx = (0, import_react2.useContext)(AuthContext);
107
112
  if (!ctx) {
108
113
  throw new Error("useSonarAuth must be used within a SonarProvider");
109
114
  }
110
115
  return ctx;
111
116
  }
112
117
  function useSonarClient() {
113
- const ctx = (0, import_react.useContext)(ClientContext);
118
+ const ctx = (0, import_react2.useContext)(ClientContext);
114
119
  if (!ctx) {
115
120
  throw new Error("useSonarClient must be used within a SonarProvider");
116
121
  }
117
122
  return ctx.client;
118
123
  }
124
+ function useSonarEntity(args) {
125
+ const { authenticated, ready } = useSonarAuth();
126
+ const client = useSonarClient();
127
+ if (!args.saleUUID) {
128
+ throw new Error("saleUUID is required");
129
+ }
130
+ const saleUUID = args.saleUUID;
131
+ const activeWallet = args.wallet.address;
132
+ const walletConnected = args.wallet.isConnected;
133
+ const [state, setState] = (0, import_react2.useState)({
134
+ loading: false,
135
+ hasFetched: false
136
+ });
137
+ const fullyConnected = ready && authenticated && Boolean(activeWallet) && walletConnected;
138
+ const refetch = (0, import_react2.useCallback)(async () => {
139
+ if (!saleUUID || !activeWallet || !fullyConnected) {
140
+ return;
141
+ }
142
+ setState((s) => ({ ...s, loading: true }));
143
+ try {
144
+ const resp = await client.readEntity({
145
+ saleUUID,
146
+ walletAddress: activeWallet
147
+ });
148
+ setState({
149
+ loading: false,
150
+ entity: resp.Entity,
151
+ error: void 0,
152
+ hasFetched: true
153
+ });
154
+ } catch (err) {
155
+ if (err instanceof import_sonar_core2.APIError && err.status === 404) {
156
+ setState({
157
+ loading: false,
158
+ entity: void 0,
159
+ error: void 0,
160
+ hasFetched: true
161
+ });
162
+ return;
163
+ }
164
+ const error = err instanceof Error ? err : new Error(String(err));
165
+ setState({ loading: false, entity: void 0, error, hasFetched: true });
166
+ }
167
+ }, [client, saleUUID, activeWallet, fullyConnected]);
168
+ const reset = (0, import_react2.useCallback)(() => {
169
+ setState({
170
+ loading: false,
171
+ hasFetched: false,
172
+ entity: void 0,
173
+ error: void 0
174
+ });
175
+ }, []);
176
+ (0, import_react2.useEffect)(() => {
177
+ if (fullyConnected) {
178
+ if (!state.hasFetched && !state.loading) {
179
+ refetch();
180
+ }
181
+ }
182
+ }, [fullyConnected, state.hasFetched, state.loading, refetch]);
183
+ (0, import_react2.useEffect)(() => {
184
+ if (ready && (!authenticated || !walletConnected || !activeWallet)) {
185
+ reset();
186
+ }
187
+ }, [ready, authenticated, walletConnected, activeWallet, reset]);
188
+ return {
189
+ authenticated,
190
+ loading: state.loading,
191
+ entity: state.entity,
192
+ error: state.error
193
+ };
194
+ }
119
195
  // Annotate the CommonJS export names for ESM import in node:
120
196
  0 && (module.exports = {
121
197
  SonarProvider,
122
198
  useSonarAuth,
123
- useSonarClient
199
+ useSonarClient,
200
+ useSonarEntity
124
201
  });
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SonarClient } from '@echoxyz/sonar-core';
3
2
  import React from 'react';
3
+ import { SonarClient, EntityDetails } from '@echoxyz/sonar-core';
4
4
 
5
5
  type SonarProviderProps = {
6
6
  children: React.ReactNode;
@@ -26,7 +26,22 @@ type AuthContextValue = {
26
26
  logout: () => void;
27
27
  };
28
28
  declare function SonarProvider({ children, config }: SonarProviderProps): react_jsx_runtime.JSX.Element;
29
+
29
30
  declare function useSonarAuth(): AuthContextValue;
30
31
  declare function useSonarClient(): SonarClient;
32
+ type WalletConnection = {
33
+ address?: string;
34
+ isConnected: boolean;
35
+ };
36
+ type UseSonarEntityResult = {
37
+ authenticated: boolean;
38
+ loading: boolean;
39
+ entity?: EntityDetails;
40
+ error?: Error;
41
+ };
42
+ declare function useSonarEntity(args: {
43
+ saleUUID: string;
44
+ wallet: WalletConnection;
45
+ }): UseSonarEntityResult;
31
46
 
32
- export { SonarProvider, type SonarProviderConfig, useSonarAuth, useSonarClient };
47
+ export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, type WalletConnection, useSonarAuth, useSonarClient, useSonarEntity };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SonarClient } from '@echoxyz/sonar-core';
3
2
  import React from 'react';
3
+ import { SonarClient, EntityDetails } from '@echoxyz/sonar-core';
4
4
 
5
5
  type SonarProviderProps = {
6
6
  children: React.ReactNode;
@@ -26,7 +26,22 @@ type AuthContextValue = {
26
26
  logout: () => void;
27
27
  };
28
28
  declare function SonarProvider({ children, config }: SonarProviderProps): react_jsx_runtime.JSX.Element;
29
+
29
30
  declare function useSonarAuth(): AuthContextValue;
30
31
  declare function useSonarClient(): SonarClient;
32
+ type WalletConnection = {
33
+ address?: string;
34
+ isConnected: boolean;
35
+ };
36
+ type UseSonarEntityResult = {
37
+ authenticated: boolean;
38
+ loading: boolean;
39
+ entity?: EntityDetails;
40
+ error?: Error;
41
+ };
42
+ declare function useSonarEntity(args: {
43
+ saleUUID: string;
44
+ wallet: WalletConnection;
45
+ }): UseSonarEntityResult;
31
46
 
32
- export { SonarProvider, type SonarProviderConfig, useSonarAuth, useSonarClient };
47
+ export { SonarProvider, type SonarProviderConfig, type UseSonarEntityResult, type WalletConnection, useSonarAuth, useSonarClient, useSonarEntity };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- // src/react.tsx
1
+ // src/provider.tsx
2
2
  import { buildAuthorizationUrl, createClient, generatePKCEParams } from "@echoxyz/sonar-core";
3
- import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
3
+ import { createContext, useCallback, useEffect, useMemo, useState } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  var AuthContext = createContext(void 0);
6
6
  var ClientContext = createContext(void 0);
@@ -74,22 +74,98 @@ function SonarProvider({ children, config }) {
74
74
  const clientValue = useMemo(() => ({ client }), [client]);
75
75
  return /* @__PURE__ */ jsx(AuthContext.Provider, { value: authValue, children: /* @__PURE__ */ jsx(ClientContext.Provider, { value: clientValue, children }) });
76
76
  }
77
+
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";
77
81
  function useSonarAuth() {
78
- const ctx = useContext(AuthContext);
82
+ const ctx = useContext2(AuthContext);
79
83
  if (!ctx) {
80
84
  throw new Error("useSonarAuth must be used within a SonarProvider");
81
85
  }
82
86
  return ctx;
83
87
  }
84
88
  function useSonarClient() {
85
- const ctx = useContext(ClientContext);
89
+ const ctx = useContext2(ClientContext);
86
90
  if (!ctx) {
87
91
  throw new Error("useSonarClient must be used within a SonarProvider");
88
92
  }
89
93
  return ctx.client;
90
94
  }
95
+ function useSonarEntity(args) {
96
+ const { authenticated, ready } = useSonarAuth();
97
+ const client = useSonarClient();
98
+ if (!args.saleUUID) {
99
+ throw new Error("saleUUID is required");
100
+ }
101
+ const saleUUID = args.saleUUID;
102
+ const activeWallet = args.wallet.address;
103
+ const walletConnected = args.wallet.isConnected;
104
+ const [state, setState] = useState2({
105
+ loading: false,
106
+ hasFetched: false
107
+ });
108
+ const fullyConnected = ready && authenticated && Boolean(activeWallet) && walletConnected;
109
+ const refetch = useCallback2(async () => {
110
+ if (!saleUUID || !activeWallet || !fullyConnected) {
111
+ return;
112
+ }
113
+ setState((s) => ({ ...s, loading: true }));
114
+ try {
115
+ const resp = await client.readEntity({
116
+ saleUUID,
117
+ walletAddress: activeWallet
118
+ });
119
+ setState({
120
+ loading: false,
121
+ entity: resp.Entity,
122
+ error: void 0,
123
+ hasFetched: true
124
+ });
125
+ } catch (err) {
126
+ if (err instanceof APIError && err.status === 404) {
127
+ setState({
128
+ loading: false,
129
+ entity: void 0,
130
+ error: void 0,
131
+ hasFetched: true
132
+ });
133
+ return;
134
+ }
135
+ const error = err instanceof Error ? err : new Error(String(err));
136
+ setState({ loading: false, entity: void 0, error, hasFetched: true });
137
+ }
138
+ }, [client, saleUUID, activeWallet, fullyConnected]);
139
+ const reset = useCallback2(() => {
140
+ setState({
141
+ loading: false,
142
+ hasFetched: false,
143
+ entity: void 0,
144
+ error: void 0
145
+ });
146
+ }, []);
147
+ useEffect2(() => {
148
+ if (fullyConnected) {
149
+ if (!state.hasFetched && !state.loading) {
150
+ refetch();
151
+ }
152
+ }
153
+ }, [fullyConnected, state.hasFetched, state.loading, refetch]);
154
+ useEffect2(() => {
155
+ if (ready && (!authenticated || !walletConnected || !activeWallet)) {
156
+ reset();
157
+ }
158
+ }, [ready, authenticated, walletConnected, activeWallet, reset]);
159
+ return {
160
+ authenticated,
161
+ loading: state.loading,
162
+ entity: state.entity,
163
+ error: state.error
164
+ };
165
+ }
91
166
  export {
92
167
  SonarProvider,
93
168
  useSonarAuth,
94
- useSonarClient
169
+ useSonarClient,
170
+ useSonarEntity
95
171
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echoxyz/sonar-react",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "react": ">=18"
17
17
  },
18
18
  "dependencies": {
19
- "@echoxyz/sonar-core": "0.2.0"
19
+ "@echoxyz/sonar-core": "0.2.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@testing-library/react": "^16.0.0",