shogun-button-react 1.0.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 ADDED
@@ -0,0 +1,192 @@
1
+ # Shogun Button React
2
+
3
+ A React component library for seamless integration of Shogun authentication into your applications. This library provides a simple yet powerful way to add Shogun authentication to your React applications.
4
+
5
+ ## Features
6
+
7
+ - 🚀 Easy to integrate
8
+ - 🎨 Customizable UI components
9
+ - 🔒 Secure authentication flow
10
+ - 🌓 Dark mode support
11
+ - 🔌 Multiple authentication methods (Username/Password, MetaMask, WebAuthn)
12
+ - 📱 Responsive design
13
+ - 🌍 TypeScript support
14
+
15
+ ## Quick Start
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import {
20
+ ShogunButton,
21
+ ShogunButtonProvider,
22
+ shogunConnector,
23
+ } from "@shogun/shogun-button-react";
24
+ import "@shogun/shogun-button-react/styles.css";
25
+
26
+ function App() {
27
+ const { sdk, options, setProvider } = shogunConnector({
28
+ appName: "My App",
29
+ appDescription: "An awesome app with Shogun authentication",
30
+ appUrl: "https://myapp.com",
31
+ appIcon: "https://myapp.com/icon.png",
32
+ });
33
+
34
+ return (
35
+ <ShogunButtonProvider
36
+ sdk={sdk}
37
+ options={options}
38
+ onLoginSuccess={(data) => {
39
+ console.log("Login successful!", data);
40
+ }}
41
+ onSignupSuccess={(data) => {
42
+ console.log("Signup successful!", data);
43
+ }}
44
+ onError={(error) => {
45
+ console.error("An error occurred:", error);
46
+ }}
47
+ >
48
+ <div>
49
+ <h1>Welcome to My App</h1>
50
+ <ShogunButton />
51
+ </div>
52
+ </ShogunButtonProvider>
53
+ );
54
+ }
55
+
56
+ export default App;
57
+ ```
58
+
59
+ ## API Reference
60
+
61
+ ### ShogunButtonProvider
62
+
63
+ The provider component that supplies Shogun context to your application.
64
+
65
+ #### Props
66
+
67
+ | Name | Type | Description |
68
+ | --------------- | ------------------------ | ---------------------------------------------- |
69
+ | sdk | ShogunSDK | Shogun SDK instance created by shogunConnector |
70
+ | options | Object | Configuration options |
71
+ | onLoginSuccess | (data: AuthData) => void | Callback fired on successful login |
72
+ | onSignupSuccess | (data: AuthData) => void | Callback fired on successful signup |
73
+ | onError | (error: Error) => void | Callback fired when an error occurs |
74
+
75
+ ### ShogunButton
76
+
77
+ The main button component for triggering Shogun authentication.
78
+
79
+ #### Custom Button
80
+
81
+ You can customize the button appearance using `ShogunButton.Custom`:
82
+
83
+ ```tsx
84
+ <ShogunButton.Custom>
85
+ {({ ready, authenticate }) => (
86
+ <button
87
+ className="my-custom-button"
88
+ disabled={!ready}
89
+ onClick={authenticate}
90
+ >
91
+ Connect with Shogun
92
+ </button>
93
+ )}
94
+ </ShogunButton.Custom>
95
+ ```
96
+
97
+ ### useShogun Hook
98
+
99
+ A hook to access Shogun authentication state and functions.
100
+
101
+ ```tsx
102
+ import { useShogun } from "@shogun/shogun-button-react";
103
+
104
+ function Profile() {
105
+ const {
106
+ isAuthenticated,
107
+ user,
108
+ login,
109
+ signup,
110
+ logout,
111
+ connectWithMetaMask,
112
+ connectWithWebAuthn,
113
+ setProvider,
114
+ } = useShogun();
115
+
116
+ const switchToCustomNetwork = () => {
117
+ setProvider('https://my-custom-rpc.example.com');
118
+ };
119
+
120
+ return isAuthenticated ? (
121
+ <div>
122
+ <h2>Welcome, {user.username}!</h2>
123
+ <button onClick={logout}>Logout</button>
124
+ <button onClick={switchToCustomNetwork}>Switch Network</button>
125
+ </div>
126
+ ) : (
127
+ <div>Please login to continue</div>
128
+ );
129
+ }
130
+ ```
131
+
132
+ ## Configuration Options
133
+
134
+ The `shogunConnector` accepts the following options:
135
+
136
+ ```typescript
137
+ interface ShogunConnectorOptions {
138
+ appName: string;
139
+ appDescription?: string;
140
+ appUrl?: string;
141
+ appIcon?: string;
142
+ showMetamask?: boolean;
143
+ showWebauthn?: boolean;
144
+ darkMode?: boolean;
145
+ websocketSecure?: boolean;
146
+ didRegistryAddress?: string | null;
147
+ providerUrl?: string | null;
148
+ peers?: string[];
149
+ }
150
+ ```
151
+
152
+ The `shogunConnector` returns an object with the following properties:
153
+
154
+ ```typescript
155
+ interface ShogunConnectorResult {
156
+ sdk: ShogunCore;
157
+ options: ShogunConnectorOptions;
158
+ setProvider: (provider: string | EthersProvider) => boolean;
159
+ getCurrentProviderUrl: () => string | null;
160
+ }
161
+ ```
162
+
163
+ > **Note**: The `setProvider` method attempts to update the RPC provider URL used by the SDK. This functionality depends on the specific version of Shogun Core you're using. If the SDK does not have a public `setRpcUrl` method available, the provider URL will still be saved but not applied to the SDK directly. In such cases, the setting will only be available through the `getCurrentProviderUrl` method.
164
+
165
+ ## Styling
166
+
167
+ The component comes with default styling that you can override using CSS variables:
168
+
169
+ ```css
170
+ :root {
171
+ --shogun-button-primary: #5c6bc0;
172
+ --shogun-button-hover: #3f51b5;
173
+ --shogun-text-primary: #333333;
174
+ --shogun-background: #ffffff;
175
+ /* ... other variables */
176
+ }
177
+ ```
178
+
179
+ ## Browser Support
180
+
181
+ - Chrome ≥ 60
182
+ - Firefox ≥ 60
183
+ - Safari ≥ 12
184
+ - Edge ≥ 79
185
+
186
+ ## Contributing
187
+
188
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
189
+
190
+ ## License
191
+
192
+ MIT © [Shogun](https://github.com/shogun)
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { ShogunCore } from "shogun-core";
3
+ import "../types.js";
4
+ import "../styles/index.css";
5
+ export declare const useShogun: () => any;
6
+ type ShogunButtonProviderProps = {
7
+ children: React.ReactNode;
8
+ sdk: ShogunCore;
9
+ options: any;
10
+ onLoginSuccess?: (data: {
11
+ userPub: string;
12
+ username: string;
13
+ password?: string;
14
+ authMethod?: "password" | "web3" | "webauthn" | "nostr" | "oauth";
15
+ }) => void;
16
+ onSignupSuccess?: (data: {
17
+ userPub: string;
18
+ username: string;
19
+ password?: string;
20
+ authMethod?: "password" | "web3" | "webauthn" | "nostr" | "oauth";
21
+ }) => void;
22
+ onError?: (error: string) => void;
23
+ };
24
+ export declare function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, onSignupSuccess, onError, }: ShogunButtonProviderProps): any;
25
+ type ShogunButtonComponent = React.FC & {
26
+ Provider: typeof ShogunButtonProvider;
27
+ useShogun: typeof useShogun;
28
+ };
29
+ export declare const ShogunButton: ShogunButtonComponent;
30
+ export {};
@@ -0,0 +1,451 @@
1
+ import React, { useContext, useState, createContext, useEffect } from "react";
2
+ import { Observable } from "rxjs";
3
+ import "../types.js"; // Import type file to extend definitions
4
+ import "../styles/index.css";
5
+ // Default context
6
+ const defaultShogunContext = {
7
+ sdk: null,
8
+ options: {},
9
+ isLoggedIn: false,
10
+ userPub: null,
11
+ username: null,
12
+ login: async () => ({}),
13
+ signUp: async () => ({}),
14
+ logout: () => { },
15
+ observe: () => new Observable(),
16
+ setProvider: () => false,
17
+ hasPlugin: () => false,
18
+ getPlugin: () => undefined,
19
+ };
20
+ // Create context using React's createContext directly
21
+ const ShogunContext = createContext(defaultShogunContext);
22
+ // Custom hook to access the context
23
+ export const useShogun = () => useContext(ShogunContext);
24
+ // Provider component
25
+ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, onSignupSuccess, onError, }) {
26
+ // Use React's useState directly
27
+ const [isLoggedIn, setIsLoggedIn] = useState((sdk === null || sdk === void 0 ? void 0 : sdk.isLoggedIn()) || false);
28
+ const [userPub, setUserPub] = useState(null);
29
+ const [username, setUsername] = useState(null);
30
+ // Effetto per gestire l'inizializzazione e pulizia
31
+ useEffect(() => {
32
+ // Controlla se l'utente è già autenticato all'avvio
33
+ if (sdk === null || sdk === void 0 ? void 0 : sdk.isLoggedIn()) {
34
+ setIsLoggedIn(true);
35
+ }
36
+ return () => {
37
+ // Pulizia quando il componente si smonta
38
+ };
39
+ }, [sdk]);
40
+ // RxJS observe method
41
+ const observe = (path) => {
42
+ if (!sdk) {
43
+ return new Observable();
44
+ }
45
+ return sdk.observe(path);
46
+ };
47
+ // Unified login
48
+ const login = async (method, ...args) => {
49
+ try {
50
+ if (!sdk) {
51
+ throw new Error("SDK not initialized");
52
+ }
53
+ let result;
54
+ let authMethod = method;
55
+ let username;
56
+ switch (method) {
57
+ case "password":
58
+ username = args[0];
59
+ result = await sdk.login(args[0], args[1]);
60
+ break;
61
+ case "webauthn":
62
+ username = args[0];
63
+ const webauthn = sdk.getPlugin("webauthn");
64
+ if (!webauthn)
65
+ throw new Error("WebAuthn plugin not available");
66
+ result = await webauthn.login(username);
67
+ break;
68
+ case "web3":
69
+ const web3 = sdk.getPlugin("web3");
70
+ if (!web3)
71
+ throw new Error("Web3 plugin not available");
72
+ const connectionResult = await web3.connectMetaMask();
73
+ if (!connectionResult.success || !connectionResult.address) {
74
+ throw new Error(connectionResult.error || "Failed to connect wallet.");
75
+ }
76
+ username = connectionResult.address;
77
+ result = await web3.login(connectionResult.address);
78
+ break;
79
+ case "nostr":
80
+ const nostr = sdk.getPlugin("nostr");
81
+ if (!nostr)
82
+ throw new Error("Nostr plugin not available");
83
+ const nostrResult = await nostr.connectBitcoinWallet();
84
+ if (!nostrResult || !nostrResult.success) {
85
+ throw new Error((nostrResult === null || nostrResult === void 0 ? void 0 : nostrResult.error) || "Connessione al wallet Bitcoin fallita");
86
+ }
87
+ const pubkey = nostrResult.address;
88
+ if (!pubkey)
89
+ throw new Error("Nessuna chiave pubblica ottenuta");
90
+ username = pubkey;
91
+ result = await nostr.login(pubkey);
92
+ break;
93
+ case "oauth":
94
+ const oauth = sdk.getPlugin("oauth");
95
+ if (!oauth)
96
+ throw new Error("OAuth plugin not available");
97
+ const provider = args[0] || "google";
98
+ result = await oauth.login(provider);
99
+ authMethod = "oauth";
100
+ if (result.redirectUrl) {
101
+ return result;
102
+ }
103
+ break;
104
+ default:
105
+ throw new Error("Unsupported login method");
106
+ }
107
+ if (result.success) {
108
+ setIsLoggedIn(true);
109
+ setUserPub(result.userPub || "");
110
+ setUsername(username || "");
111
+ onLoginSuccess === null || onLoginSuccess === void 0 ? void 0 : onLoginSuccess({
112
+ userPub: result.userPub || "",
113
+ username: username || "",
114
+ authMethod: authMethod,
115
+ });
116
+ }
117
+ else {
118
+ onError === null || onError === void 0 ? void 0 : onError(result.error || "Login failed");
119
+ }
120
+ return result;
121
+ }
122
+ catch (error) {
123
+ onError === null || onError === void 0 ? void 0 : onError(error.message || "Error during login");
124
+ return { success: false, error: error.message };
125
+ }
126
+ };
127
+ // Unified signup
128
+ const signUp = async (method, ...args) => {
129
+ try {
130
+ if (!sdk) {
131
+ throw new Error("SDK not initialized");
132
+ }
133
+ let result;
134
+ let authMethod = method;
135
+ let username;
136
+ switch (method) {
137
+ case "password":
138
+ username = args[0];
139
+ if (args[1] !== args[2]) {
140
+ throw new Error("Passwords do not match");
141
+ }
142
+ result = await sdk.signUp(args[0], args[1]);
143
+ break;
144
+ case "webauthn":
145
+ username = args[0];
146
+ const webauthn = sdk.getPlugin("webauthn");
147
+ if (!webauthn)
148
+ throw new Error("WebAuthn plugin not available");
149
+ result = await webauthn.signUp(username);
150
+ break;
151
+ case "web3":
152
+ const web3 = sdk.getPlugin("web3");
153
+ if (!web3)
154
+ throw new Error("Web3 plugin not available");
155
+ const connectionResult = await web3.connectMetaMask();
156
+ if (!connectionResult.success || !connectionResult.address) {
157
+ throw new Error(connectionResult.error || "Failed to connect wallet.");
158
+ }
159
+ username = connectionResult.address;
160
+ result = await web3.signUp(connectionResult.address);
161
+ break;
162
+ case "nostr":
163
+ const nostr = sdk.getPlugin("nostr");
164
+ if (!nostr)
165
+ throw new Error("Nostr plugin not available");
166
+ const nostrResult = await nostr.connectBitcoinWallet();
167
+ if (!nostrResult || !nostrResult.success) {
168
+ throw new Error((nostrResult === null || nostrResult === void 0 ? void 0 : nostrResult.error) || "Connessione al wallet Bitcoin fallita");
169
+ }
170
+ const pubkey = nostrResult.address;
171
+ if (!pubkey)
172
+ throw new Error("Nessuna chiave pubblica ottenuta");
173
+ username = pubkey;
174
+ result = await nostr.signUp(pubkey);
175
+ break;
176
+ case "oauth":
177
+ const oauth = sdk.getPlugin("oauth");
178
+ if (!oauth)
179
+ throw new Error("OAuth plugin not available");
180
+ const provider = args[0] || "google";
181
+ result = await oauth.signUp(provider);
182
+ authMethod = "oauth";
183
+ if (result.redirectUrl) {
184
+ return result;
185
+ }
186
+ break;
187
+ default:
188
+ throw new Error("Unsupported signup method");
189
+ }
190
+ if (result.success) {
191
+ setIsLoggedIn(true);
192
+ const userPub = result.userPub || "";
193
+ setUserPub(userPub);
194
+ setUsername(username || "");
195
+ onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess({
196
+ userPub: userPub,
197
+ username: username || "",
198
+ authMethod: authMethod,
199
+ });
200
+ }
201
+ else {
202
+ onError === null || onError === void 0 ? void 0 : onError(result.error);
203
+ }
204
+ return result;
205
+ }
206
+ catch (error) {
207
+ onError === null || onError === void 0 ? void 0 : onError(error.message || "Error during registration");
208
+ return { success: false, error: error.message };
209
+ }
210
+ };
211
+ // Logout
212
+ const logout = () => {
213
+ sdk.logout();
214
+ setIsLoggedIn(false);
215
+ setUserPub(null);
216
+ setUsername(null);
217
+ };
218
+ // Implementazione del metodo setProvider
219
+ const setProvider = (provider) => {
220
+ if (!sdk) {
221
+ return false;
222
+ }
223
+ try {
224
+ let newProviderUrl = null;
225
+ if (provider && provider.connection && provider.connection.url) {
226
+ newProviderUrl = provider.connection.url;
227
+ }
228
+ else if (typeof provider === 'string') {
229
+ newProviderUrl = provider;
230
+ }
231
+ if (newProviderUrl) {
232
+ if (typeof sdk.setRpcUrl === 'function') {
233
+ return sdk.setRpcUrl(newProviderUrl);
234
+ }
235
+ }
236
+ return false;
237
+ }
238
+ catch (error) {
239
+ console.error("Error setting provider:", error);
240
+ return false;
241
+ }
242
+ };
243
+ const hasPlugin = (name) => {
244
+ return sdk ? sdk.hasPlugin(name) : false;
245
+ };
246
+ const getPlugin = (name) => {
247
+ return sdk ? sdk.getPlugin(name) : undefined;
248
+ };
249
+ // Provide the context value to children
250
+ return (React.createElement(ShogunContext.Provider, { value: {
251
+ sdk,
252
+ options,
253
+ isLoggedIn,
254
+ userPub,
255
+ username,
256
+ login,
257
+ signUp,
258
+ logout,
259
+ observe,
260
+ setProvider,
261
+ hasPlugin,
262
+ getPlugin,
263
+ } }, children));
264
+ }
265
+ // SVG Icons Components
266
+ const WalletIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
267
+ React.createElement("path", { d: "M21 12V7H5a2 2 0 0 1 0-4h14v4" }),
268
+ React.createElement("path", { d: "M3 5v14a2 2 0 0 0 2 2h16v-5" }),
269
+ React.createElement("path", { d: "M18 12a2 2 0 0 0 0 4h4v-4Z" })));
270
+ const KeyIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
271
+ React.createElement("circle", { cx: "7.5", cy: "15.5", r: "5.5" }),
272
+ React.createElement("path", { d: "m21 2-9.6 9.6" }),
273
+ React.createElement("path", { d: "m15.5 7.5 3 3L22 7l-3-3" })));
274
+ const GoogleIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor" },
275
+ React.createElement("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }),
276
+ React.createElement("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
277
+ React.createElement("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }),
278
+ React.createElement("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })));
279
+ const NostrIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
280
+ React.createElement("path", { d: "M19.5 4.5 15 9l-3-3-4.5 4.5L9 12l-1.5 1.5L12 18l4.5-4.5L15 12l1.5-1.5L21 6l-1.5-1.5Z" }),
281
+ React.createElement("path", { d: "M12 12 6 6l-1.5 1.5L9 12l-4.5 4.5L6 18l6-6Z" })));
282
+ const WebAuthnIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
283
+ React.createElement("path", { d: "M7 11v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1h-4" }),
284
+ React.createElement("path", { d: "M14 4V2a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2" })));
285
+ const LogoutIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
286
+ React.createElement("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
287
+ React.createElement("polyline", { points: "16 17 21 12 16 7" }),
288
+ React.createElement("line", { x1: "21", y1: "12", x2: "9", y2: "12" })));
289
+ const UserIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
290
+ React.createElement("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }),
291
+ React.createElement("circle", { cx: "12", cy: "7", r: "4" })));
292
+ const LockIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
293
+ React.createElement("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
294
+ React.createElement("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })));
295
+ const CloseIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
296
+ React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
297
+ React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })));
298
+ // Component for Shogun login button
299
+ export const ShogunButton = (() => {
300
+ const Button = () => {
301
+ const { isLoggedIn, username, logout, login, signUp, sdk, options } = useShogun();
302
+ // Form states
303
+ const [modalIsOpen, setModalIsOpen] = useState(false);
304
+ const [formUsername, setFormUsername] = useState("");
305
+ const [formPassword, setFormPassword] = useState("");
306
+ const [formPasswordConfirm, setFormPasswordConfirm] = useState("");
307
+ const [formMode, setFormMode] = useState("login");
308
+ const [error, setError] = useState("");
309
+ const [loading, setLoading] = useState(false);
310
+ const [dropdownOpen, setDropdownOpen] = useState(false);
311
+ // If already logged in, show only logout button
312
+ if (isLoggedIn && username) {
313
+ return (React.createElement("div", { className: "shogun-logged-in-container" },
314
+ React.createElement("div", { className: "shogun-dropdown" },
315
+ React.createElement("button", { className: "shogun-button shogun-logged-in", onClick: () => setDropdownOpen(!dropdownOpen) },
316
+ React.createElement("div", { className: "shogun-avatar" }, username.substring(0, 2).toUpperCase()),
317
+ React.createElement("span", { className: "shogun-username" }, username.length > 12
318
+ ? `${username.substring(0, 6)}...${username.substring(username.length - 4)}`
319
+ : username)),
320
+ dropdownOpen && (React.createElement("div", { className: "shogun-dropdown-menu" },
321
+ React.createElement("div", { className: "shogun-dropdown-header" },
322
+ React.createElement("div", { className: "shogun-avatar-large" }, username.substring(0, 2).toUpperCase()),
323
+ React.createElement("div", { className: "shogun-user-info" },
324
+ React.createElement("span", { className: "shogun-username-full" }, username.length > 20
325
+ ? `${username.substring(0, 10)}...${username.substring(username.length - 6)}`
326
+ : username))),
327
+ React.createElement("div", { className: "shogun-dropdown-item", onClick: logout },
328
+ React.createElement(LogoutIcon, null),
329
+ React.createElement("span", null, "Disconnect")))))));
330
+ }
331
+ // Event handlers
332
+ const handleAuth = async (method, ...args) => {
333
+ setError("");
334
+ setLoading(true);
335
+ try {
336
+ const action = formMode === "login" ? login : signUp;
337
+ const result = await action(method, ...args);
338
+ if (result && !result.success && result.error) {
339
+ setError(result.error);
340
+ }
341
+ else if (result && result.redirectUrl) {
342
+ window.location.href = result.redirectUrl;
343
+ }
344
+ else {
345
+ setModalIsOpen(false);
346
+ }
347
+ }
348
+ catch (e) {
349
+ setError(e.message || "An unexpected error occurred.");
350
+ }
351
+ finally {
352
+ setLoading(false);
353
+ }
354
+ };
355
+ const handleSubmit = (e) => {
356
+ e.preventDefault();
357
+ handleAuth("password", formUsername, formPassword, formPasswordConfirm);
358
+ };
359
+ const handleWeb3Auth = () => handleAuth("web3");
360
+ const handleWebAuthnAuth = () => {
361
+ if (!(sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("webauthn"))) {
362
+ setError("WebAuthn is not supported in your browser");
363
+ return;
364
+ }
365
+ if (!formUsername) {
366
+ setError("Username required for WebAuthn");
367
+ return;
368
+ }
369
+ handleAuth("webauthn", formUsername);
370
+ };
371
+ const handleNostrAuth = () => handleAuth("nostr");
372
+ const handleOAuth = (provider) => handleAuth("oauth", provider);
373
+ const resetForm = () => {
374
+ setFormUsername("");
375
+ setFormPassword("");
376
+ setFormPasswordConfirm("");
377
+ setError("");
378
+ setLoading(false);
379
+ };
380
+ const openModal = () => {
381
+ resetForm();
382
+ setModalIsOpen(true);
383
+ };
384
+ const closeModal = () => {
385
+ setModalIsOpen(false);
386
+ };
387
+ const toggleMode = () => {
388
+ resetForm();
389
+ setFormMode((prev) => (prev === "login" ? "signup" : "login"));
390
+ };
391
+ // Render logic
392
+ return (React.createElement(React.Fragment, null,
393
+ React.createElement("button", { className: "shogun-connect-button", onClick: openModal },
394
+ React.createElement(WalletIcon, null),
395
+ React.createElement("span", null, "Connect")),
396
+ modalIsOpen && (React.createElement("div", { className: "shogun-modal-overlay", onClick: closeModal },
397
+ React.createElement("div", { className: "shogun-modal", onClick: (e) => e.stopPropagation() },
398
+ React.createElement("div", { className: "shogun-modal-header" },
399
+ React.createElement("h2", null, formMode === "login" ? "Sign In" : "Create Account"),
400
+ React.createElement("button", { className: "shogun-close-button", onClick: closeModal },
401
+ React.createElement(CloseIcon, null))),
402
+ React.createElement("div", { className: "shogun-modal-content" },
403
+ error && React.createElement("div", { className: "shogun-error-message" }, error),
404
+ React.createElement("div", { className: "shogun-auth-options" },
405
+ (options === null || options === void 0 ? void 0 : options.showMetamask) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("web3")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleWeb3Auth, disabled: loading },
406
+ React.createElement(WalletIcon, null),
407
+ React.createElement("span", null, "Continue with Wallet"))),
408
+ (options === null || options === void 0 ? void 0 : options.showWebauthn) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("webauthn")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleWebAuthnAuth, disabled: loading },
409
+ React.createElement(WebAuthnIcon, null),
410
+ React.createElement("span", null, "Continue with Passkey"))),
411
+ (options === null || options === void 0 ? void 0 : options.showNostr) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("nostr")) && (React.createElement("button", { className: "shogun-auth-option-button", onClick: handleNostrAuth, disabled: loading },
412
+ React.createElement(NostrIcon, null),
413
+ React.createElement("span", null, "Continue with Nostr"))),
414
+ (options === null || options === void 0 ? void 0 : options.showOauth) && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("oauth")) && (React.createElement("button", { className: "shogun-auth-option-button shogun-google-button", onClick: () => handleOAuth("google"), disabled: loading },
415
+ React.createElement(GoogleIcon, null),
416
+ React.createElement("span", null, "Continue with Google")))),
417
+ React.createElement("div", { className: "shogun-divider" },
418
+ React.createElement("span", null, "or continue with password")),
419
+ React.createElement("form", { onSubmit: handleSubmit, className: "shogun-auth-form" },
420
+ React.createElement("div", { className: "shogun-form-group" },
421
+ React.createElement("label", { htmlFor: "username" },
422
+ React.createElement(UserIcon, null),
423
+ React.createElement("span", null, "Username")),
424
+ React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username" })),
425
+ React.createElement("div", { className: "shogun-form-group" },
426
+ React.createElement("label", { htmlFor: "password" },
427
+ React.createElement(LockIcon, null),
428
+ React.createElement("span", null, "Password")),
429
+ React.createElement("input", { type: "password", id: "password", value: formPassword, onChange: (e) => setFormPassword(e.target.value), disabled: loading, required: true, placeholder: "Enter your password" })),
430
+ formMode === "signup" && (React.createElement("div", { className: "shogun-form-group" },
431
+ React.createElement("label", { htmlFor: "passwordConfirm" },
432
+ React.createElement(KeyIcon, null),
433
+ React.createElement("span", null, "Confirm Password")),
434
+ React.createElement("input", { type: "password", id: "passwordConfirm", value: formPasswordConfirm, onChange: (e) => setFormPasswordConfirm(e.target.value), disabled: loading, required: true, placeholder: "Confirm your password" }))),
435
+ React.createElement("button", { type: "submit", className: "shogun-submit-button", disabled: loading }, loading
436
+ ? "Processing..."
437
+ : formMode === "login"
438
+ ? "Sign In"
439
+ : "Create Account")),
440
+ React.createElement("div", { className: "shogun-form-footer" },
441
+ formMode === "login"
442
+ ? "Don't have an account?"
443
+ : "Already have an account?",
444
+ React.createElement("button", { className: "shogun-toggle-mode", onClick: toggleMode, disabled: loading }, formMode === "login" ? "Sign Up" : "Sign In"))))))));
445
+ };
446
+ Button.displayName = "ShogunButton";
447
+ return Object.assign(Button, {
448
+ Provider: ShogunButtonProvider,
449
+ useShogun: useShogun,
450
+ });
451
+ })();