shogun-button-react 1.3.2 → 1.3.5
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 +54 -0
- package/dist/components/ShogunButton.js +122 -76
- package/dist/styles/index.css +13 -315
- package/package.json +1 -1
- package/src/styles/index.css +13 -315
- package/dist/index.css +0 -756
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Version 1.3.4 (Latest)
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
- **Export Gun Pair Fix**: Fixed issue where "Export Pair" option was not accessible from user dropdown - now works correctly without requiring disconnect
|
|
7
|
+
- **Modal Logic Improvement**: Enhanced modal rendering logic to allow export functionality when user is already authenticated
|
|
8
|
+
- **UX Enhancement**: Improved back button behavior in export modal - now properly closes modal when user is logged in instead of showing unnecessary auth options
|
|
9
|
+
|
|
10
|
+
### 🔧 Technical Changes
|
|
11
|
+
- Modified component render logic to show modal even when user is logged in
|
|
12
|
+
- Improved export form navigation for better user experience
|
|
13
|
+
- Reordered dropdown menu items for better flow (Export Pair before Disconnect)
|
|
14
|
+
|
|
15
|
+
## Version 1.3.3
|
|
16
|
+
|
|
17
|
+
### ✨ New Features
|
|
18
|
+
- **Import Gun Pair Login**: Aggiunta possibilità di effettuare login tramite importazione di un Gun pair esistente
|
|
19
|
+
- **Export Gun Pair**: Funzionalità per esportare il proprio Gun pair con opzione di crittografia tramite password
|
|
20
|
+
- **Improved UX**: Migliorata l'interfaccia utente con feedback visivi e messaggi informativi
|
|
21
|
+
|
|
22
|
+
### 🔧 Improvements
|
|
23
|
+
- **Navigation Fix**: Il toggle "Don't have account? Sign up" ora porta alla selezione dei metodi di autenticazione invece che direttamente al form password
|
|
24
|
+
- **Visual Feedback**: Sostituiti gli alert con feedback visivi eleganti per export/import
|
|
25
|
+
- **Better Icons**: Aggiunte icone SVG personalizzate per import/export
|
|
26
|
+
- **Auto-copy**: L'export del pair viene automaticamente copiato negli appunti (quando supportato dal browser)
|
|
27
|
+
- **Enhanced Security**: Messaggi informativi per guidare l'utente nell'uso sicuro delle funzionalità
|
|
28
|
+
|
|
29
|
+
### 🛠 Technical Changes
|
|
30
|
+
- Rimosso l'uso del metodo `on` non disponibile in ShogunCore
|
|
31
|
+
- Definiti tipi locali per `AuthResult` per compatibilità
|
|
32
|
+
- Migliorata gestione degli stati nel provider
|
|
33
|
+
- Aggiunto reset completo degli stati quando si chiude il modal
|
|
34
|
+
|
|
35
|
+
### 🎨 UI/UX Enhancements
|
|
36
|
+
- Box informativi colorati per import/export
|
|
37
|
+
- Feedback di successo con timer automatico
|
|
38
|
+
- Indicatori di caricamento migliorati
|
|
39
|
+
- Messaggi di fallback per browser senza supporto clipboard
|
|
40
|
+
|
|
41
|
+
## Version 1.3.2
|
|
42
|
+
|
|
43
|
+
### Features
|
|
44
|
+
- Basic Gun pair export/import functionality
|
|
45
|
+
- Multi-authentication support (Password, MetaMask, WebAuthn, Nostr, OAuth)
|
|
46
|
+
- Dark mode support
|
|
47
|
+
- Responsive design
|
|
48
|
+
|
|
49
|
+
## Version 1.3.1
|
|
50
|
+
|
|
51
|
+
### Features
|
|
52
|
+
- Initial release
|
|
53
|
+
- Basic authentication flow
|
|
54
|
+
- Provider integration
|
|
@@ -34,39 +34,17 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
34
34
|
var _a, _b;
|
|
35
35
|
if (!sdk)
|
|
36
36
|
return;
|
|
37
|
-
|
|
38
|
-
var _a, _b;
|
|
39
|
-
const pub = authResult.pub || ((_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub);
|
|
40
|
-
if (pub) {
|
|
41
|
-
setIsLoggedIn(true);
|
|
42
|
-
setUserPub(pub);
|
|
43
|
-
setUsername(authResult.alias || pub.slice(0, 8) + '...');
|
|
44
|
-
if (onLoginSuccess && authResult.method !== 'recall') {
|
|
45
|
-
onLoginSuccess({
|
|
46
|
-
userPub: pub,
|
|
47
|
-
username: authResult.alias || pub.slice(0, 8) + '...',
|
|
48
|
-
authMethod: authResult.method,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const handleLogout = () => {
|
|
54
|
-
setIsLoggedIn(false);
|
|
55
|
-
setUserPub(null);
|
|
56
|
-
setUsername(null);
|
|
57
|
-
};
|
|
37
|
+
// Verifichiamo se l'utente è già loggato all'inizializzazione
|
|
58
38
|
if (sdk.isLoggedIn()) {
|
|
59
39
|
const pub = (_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub;
|
|
60
40
|
if (pub) {
|
|
61
|
-
|
|
41
|
+
setIsLoggedIn(true);
|
|
42
|
+
setUserPub(pub);
|
|
43
|
+
setUsername(pub.slice(0, 8) + '...');
|
|
62
44
|
}
|
|
63
45
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return () => {
|
|
67
|
-
sdk.off('auth:login', handleLogin);
|
|
68
|
-
sdk.off('auth:logout', handleLogout);
|
|
69
|
-
};
|
|
46
|
+
// Poiché il metodo 'on' non esiste su ShogunCore,
|
|
47
|
+
// gestiamo gli stati direttamente nei metodi di login/logout
|
|
70
48
|
}, [sdk, onLoginSuccess]);
|
|
71
49
|
// RxJS observe method
|
|
72
50
|
const observe = (path) => {
|
|
@@ -77,6 +55,7 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
77
55
|
};
|
|
78
56
|
// Unified login
|
|
79
57
|
const login = async (method, ...args) => {
|
|
58
|
+
var _a, _b;
|
|
80
59
|
try {
|
|
81
60
|
if (!sdk) {
|
|
82
61
|
throw new Error("SDK not initialized");
|
|
@@ -161,12 +140,14 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
161
140
|
throw new Error("Unsupported login method");
|
|
162
141
|
}
|
|
163
142
|
if (result.success) {
|
|
143
|
+
const userPub = result.userPub || ((_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub) || "";
|
|
144
|
+
const displayName = result.alias || username || userPub.slice(0, 8) + '...';
|
|
164
145
|
setIsLoggedIn(true);
|
|
165
|
-
setUserPub(
|
|
166
|
-
setUsername(
|
|
146
|
+
setUserPub(userPub);
|
|
147
|
+
setUsername(displayName);
|
|
167
148
|
onLoginSuccess === null || onLoginSuccess === void 0 ? void 0 : onLoginSuccess({
|
|
168
|
-
userPub:
|
|
169
|
-
username:
|
|
149
|
+
userPub: userPub,
|
|
150
|
+
username: displayName,
|
|
170
151
|
authMethod: authMethod,
|
|
171
152
|
});
|
|
172
153
|
}
|
|
@@ -182,6 +163,7 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
182
163
|
};
|
|
183
164
|
// Unified signup
|
|
184
165
|
const signUp = async (method, ...args) => {
|
|
166
|
+
var _a, _b;
|
|
185
167
|
try {
|
|
186
168
|
if (!sdk) {
|
|
187
169
|
throw new Error("SDK not initialized");
|
|
@@ -244,13 +226,14 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
244
226
|
throw new Error("Unsupported signup method");
|
|
245
227
|
}
|
|
246
228
|
if (result.success) {
|
|
229
|
+
const userPub = result.userPub || ((_b = (_a = sdk.gun.user()) === null || _a === void 0 ? void 0 : _a.is) === null || _b === void 0 ? void 0 : _b.pub) || "";
|
|
230
|
+
const displayName = result.alias || username || userPub.slice(0, 8) + '...';
|
|
247
231
|
setIsLoggedIn(true);
|
|
248
|
-
const userPub = result.userPub || "";
|
|
249
232
|
setUserPub(userPub);
|
|
250
|
-
setUsername(
|
|
233
|
+
setUsername(displayName);
|
|
251
234
|
onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess({
|
|
252
235
|
userPub: userPub,
|
|
253
|
-
username:
|
|
236
|
+
username: displayName,
|
|
254
237
|
authMethod: authMethod,
|
|
255
238
|
});
|
|
256
239
|
}
|
|
@@ -270,6 +253,9 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
270
253
|
setIsLoggedIn(false);
|
|
271
254
|
setUserPub(null);
|
|
272
255
|
setUsername(null);
|
|
256
|
+
sessionStorage.removeItem("gun/pair");
|
|
257
|
+
sessionStorage.removeItem("gun/session");
|
|
258
|
+
sessionStorage.removeItem("pair");
|
|
273
259
|
};
|
|
274
260
|
// Implementazione del metodo setProvider
|
|
275
261
|
const setProvider = (provider) => {
|
|
@@ -304,7 +290,6 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
304
290
|
};
|
|
305
291
|
// Export Gun pair functionality
|
|
306
292
|
const exportGunPair = async (password) => {
|
|
307
|
-
var _a;
|
|
308
293
|
if (!sdk) {
|
|
309
294
|
throw new Error("SDK not initialized");
|
|
310
295
|
}
|
|
@@ -312,8 +297,7 @@ export function ShogunButtonProvider({ children, sdk, options, onLoginSuccess, o
|
|
|
312
297
|
throw new Error("User not authenticated");
|
|
313
298
|
}
|
|
314
299
|
try {
|
|
315
|
-
const
|
|
316
|
-
const pair = (_a = user === null || user === void 0 ? void 0 : user._) === null || _a === void 0 ? void 0 : _a.sea;
|
|
300
|
+
const pair = sessionStorage.getItem("gun/pair") || sessionStorage.getItem("pair");
|
|
317
301
|
if (!pair) {
|
|
318
302
|
throw new Error("No Gun pair available for current user");
|
|
319
303
|
}
|
|
@@ -417,6 +401,16 @@ const LockIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2
|
|
|
417
401
|
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" },
|
|
418
402
|
React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
419
403
|
React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })));
|
|
404
|
+
const ImportIcon = () => (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" },
|
|
405
|
+
React.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
406
|
+
React.createElement("polyline", { points: "14,2 14,8 20,8" }),
|
|
407
|
+
React.createElement("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
|
|
408
|
+
React.createElement("line", { x1: "12", y1: "17", x2: "12", y2: "9" })));
|
|
409
|
+
const ExportIcon = () => (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" },
|
|
410
|
+
React.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
411
|
+
React.createElement("polyline", { points: "14,2 14,8 20,8" }),
|
|
412
|
+
React.createElement("line", { x1: "12", y1: "11", x2: "12", y2: "21" }),
|
|
413
|
+
React.createElement("polyline", { points: "16,15 12,11 8,15" })));
|
|
420
414
|
// Component for Shogun login button
|
|
421
415
|
export const ShogunButton = (() => {
|
|
422
416
|
const Button = () => {
|
|
@@ -439,6 +433,8 @@ export const ShogunButton = (() => {
|
|
|
439
433
|
const [importPassword, setImportPassword] = useState("");
|
|
440
434
|
const [importPairData, setImportPairData] = useState("");
|
|
441
435
|
const [exportedPair, setExportedPair] = useState("");
|
|
436
|
+
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
|
437
|
+
const [showImportSuccess, setShowImportSuccess] = useState(false);
|
|
442
438
|
const dropdownRef = useRef(null);
|
|
443
439
|
// Handle click outside to close dropdown
|
|
444
440
|
useEffect(() => {
|
|
@@ -455,7 +451,7 @@ export const ShogunButton = (() => {
|
|
|
455
451
|
}
|
|
456
452
|
}, [dropdownOpen]);
|
|
457
453
|
// If already logged in, show only logout button
|
|
458
|
-
if (isLoggedIn && username) {
|
|
454
|
+
if (isLoggedIn && username && !modalIsOpen) {
|
|
459
455
|
return (React.createElement("div", { className: "shogun-logged-in-container" },
|
|
460
456
|
React.createElement("div", { className: "shogun-dropdown", ref: dropdownRef },
|
|
461
457
|
React.createElement("button", { className: "shogun-button shogun-logged-in", onClick: () => setDropdownOpen(!dropdownOpen) },
|
|
@@ -470,16 +466,16 @@ export const ShogunButton = (() => {
|
|
|
470
466
|
React.createElement("span", { className: "shogun-username-full" }, username.length > 20
|
|
471
467
|
? `${username.substring(0, 10)}...${username.substring(username.length - 6)}`
|
|
472
468
|
: username))),
|
|
473
|
-
React.createElement("div", { className: "shogun-dropdown-item", onClick: logout },
|
|
474
|
-
React.createElement(LogoutIcon, null),
|
|
475
|
-
React.createElement("span", null, "Disconnect")),
|
|
476
469
|
React.createElement("div", { className: "shogun-dropdown-item", onClick: () => {
|
|
477
470
|
setDropdownOpen(false);
|
|
478
471
|
setAuthView("export");
|
|
479
472
|
setModalIsOpen(true);
|
|
480
473
|
} },
|
|
481
|
-
React.createElement(
|
|
482
|
-
React.createElement("span", null, "Export Pair"))
|
|
474
|
+
React.createElement(ExportIcon, null),
|
|
475
|
+
React.createElement("span", null, "Export Pair")),
|
|
476
|
+
React.createElement("div", { className: "shogun-dropdown-item", onClick: logout },
|
|
477
|
+
React.createElement(LogoutIcon, null),
|
|
478
|
+
React.createElement("span", null, "Disconnect")))))));
|
|
483
479
|
}
|
|
484
480
|
// Event handlers
|
|
485
481
|
const handleAuth = async (method, ...args) => {
|
|
@@ -540,11 +536,7 @@ export const ShogunButton = (() => {
|
|
|
540
536
|
setError("WebAuthn is not supported in your browser");
|
|
541
537
|
return;
|
|
542
538
|
}
|
|
543
|
-
|
|
544
|
-
setError("Username required for WebAuthn");
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
handleAuth("webauthn", formUsername);
|
|
539
|
+
setAuthView("webauthn-username");
|
|
548
540
|
};
|
|
549
541
|
const handleNostrAuth = () => handleAuth("nostr");
|
|
550
542
|
const handleOAuth = (provider) => handleAuth("oauth", provider);
|
|
@@ -582,10 +574,8 @@ export const ShogunButton = (() => {
|
|
|
582
574
|
// Copy to clipboard
|
|
583
575
|
if (navigator.clipboard) {
|
|
584
576
|
await navigator.clipboard.writeText(pairData);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
else {
|
|
588
|
-
alert("Pair exported! Please copy it manually from the text area.");
|
|
577
|
+
setShowCopySuccess(true);
|
|
578
|
+
setTimeout(() => setShowCopySuccess(false), 3000);
|
|
589
579
|
}
|
|
590
580
|
}
|
|
591
581
|
catch (e) {
|
|
@@ -604,8 +594,12 @@ export const ShogunButton = (() => {
|
|
|
604
594
|
}
|
|
605
595
|
const success = await importGunPair(importPairData, importPassword || undefined);
|
|
606
596
|
if (success) {
|
|
607
|
-
|
|
608
|
-
|
|
597
|
+
setShowImportSuccess(true);
|
|
598
|
+
// Chiudiamo il modal con un piccolo delay per permettere all'utente di vedere il successo
|
|
599
|
+
setTimeout(() => {
|
|
600
|
+
setModalIsOpen(false);
|
|
601
|
+
setShowImportSuccess(false);
|
|
602
|
+
}, 1500);
|
|
609
603
|
}
|
|
610
604
|
else {
|
|
611
605
|
throw new Error("Failed to import pair");
|
|
@@ -627,6 +621,13 @@ export const ShogunButton = (() => {
|
|
|
627
621
|
setError("");
|
|
628
622
|
setLoading(false);
|
|
629
623
|
setAuthView("options");
|
|
624
|
+
setExportPassword("");
|
|
625
|
+
setImportPassword("");
|
|
626
|
+
setImportPairData("");
|
|
627
|
+
setExportedPair("");
|
|
628
|
+
setShowCopySuccess(false);
|
|
629
|
+
setShowImportSuccess(false);
|
|
630
|
+
setRecoveredHint("");
|
|
630
631
|
};
|
|
631
632
|
const openModal = () => {
|
|
632
633
|
resetForm();
|
|
@@ -638,7 +639,7 @@ export const ShogunButton = (() => {
|
|
|
638
639
|
};
|
|
639
640
|
const toggleMode = () => {
|
|
640
641
|
resetForm();
|
|
641
|
-
setAuthView("
|
|
642
|
+
setAuthView("options"); // Porta alla selezione dei metodi invece che direttamente al form password
|
|
642
643
|
setFormMode((prev) => (prev === "login" ? "signup" : "login"));
|
|
643
644
|
};
|
|
644
645
|
// Add buttons for both login and signup for alternative auth methods
|
|
@@ -648,13 +649,7 @@ export const ShogunButton = (() => {
|
|
|
648
649
|
React.createElement(WalletIcon, null),
|
|
649
650
|
formMode === "login" ? "Login with MetaMask" : "Signup with MetaMask"))),
|
|
650
651
|
options.showWebauthn !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("webauthn")) && (React.createElement("div", { className: "shogun-auth-option-group" },
|
|
651
|
-
React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick:
|
|
652
|
-
if (!formUsername) {
|
|
653
|
-
setError("Username required for WebAuthn");
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
handleAuth("webauthn", formUsername);
|
|
657
|
-
}, disabled: loading },
|
|
652
|
+
React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: handleWebAuthnAuth, disabled: loading },
|
|
658
653
|
React.createElement(WebAuthnIcon, null),
|
|
659
654
|
formMode === "login" ? "Login with WebAuthn" : "Signup with WebAuthn"))),
|
|
660
655
|
options.showNostr !== false && (sdk === null || sdk === void 0 ? void 0 : sdk.hasPlugin("nostr")) && (React.createElement("div", { className: "shogun-auth-option-group" },
|
|
@@ -671,7 +666,7 @@ export const ShogunButton = (() => {
|
|
|
671
666
|
React.createElement(LockIcon, null),
|
|
672
667
|
formMode === "login" ? "Login with Password" : "Signup with Password"),
|
|
673
668
|
formMode === "login" && (React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => setAuthView("import"), disabled: loading },
|
|
674
|
-
React.createElement(
|
|
669
|
+
React.createElement(ImportIcon, null),
|
|
675
670
|
"Import Gun Pair"))));
|
|
676
671
|
const renderPasswordForm = () => (React.createElement("form", { onSubmit: handleSubmit, className: "shogun-auth-form" },
|
|
677
672
|
React.createElement("div", { className: "shogun-form-group" },
|
|
@@ -715,6 +710,22 @@ export const ShogunButton = (() => {
|
|
|
715
710
|
? "Don't have an account? Sign up"
|
|
716
711
|
: "Already have an account? Log in"),
|
|
717
712
|
formMode === "login" && (React.createElement("button", { type: "button", className: "shogun-toggle-mode", onClick: () => setAuthView("recover"), disabled: loading }, "Forgot password?")))));
|
|
713
|
+
const renderWebAuthnUsernameForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
714
|
+
React.createElement("h3", null, formMode === "login" ? "Login with WebAuthn" : "Sign Up with WebAuthn"),
|
|
715
|
+
React.createElement("div", { style: { backgroundColor: '#f0f9ff', padding: '12px', borderRadius: '8px', marginBottom: '16px', border: '1px solid #0ea5e9' } },
|
|
716
|
+
React.createElement("p", { style: { fontSize: '14px', color: '#0c4a6e', margin: '0', fontWeight: '500' } }, "\uD83D\uDD11 WebAuthn Authentication"),
|
|
717
|
+
React.createElement("p", { style: { fontSize: '13px', color: '#075985', margin: '4px 0 0 0' } },
|
|
718
|
+
"Please enter your username to continue with WebAuthn ",
|
|
719
|
+
formMode === "login" ? "login" : "registration",
|
|
720
|
+
".")),
|
|
721
|
+
React.createElement("div", { className: "shogun-form-group" },
|
|
722
|
+
React.createElement("label", { htmlFor: "username" },
|
|
723
|
+
React.createElement(UserIcon, null),
|
|
724
|
+
React.createElement("span", null, "Username")),
|
|
725
|
+
React.createElement("input", { type: "text", id: "username", value: formUsername, onChange: (e) => setFormUsername(e.target.value), disabled: loading, required: true, placeholder: "Enter your username", autoFocus: true })),
|
|
726
|
+
React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: () => handleAuth("webauthn", formUsername), disabled: loading || !formUsername.trim() }, loading ? "Processing..." : `Continue with WebAuthn`),
|
|
727
|
+
React.createElement("div", { className: "shogun-form-footer" },
|
|
728
|
+
React.createElement("button", { type: "button", className: "shogun-back-button", onClick: () => setAuthView("options"), disabled: loading }, "\u2190 Back to Options"))));
|
|
718
729
|
const renderRecoveryForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
719
730
|
React.createElement("div", { className: "shogun-form-group" },
|
|
720
731
|
React.createElement("label", { htmlFor: "username" },
|
|
@@ -742,7 +753,9 @@ export const ShogunButton = (() => {
|
|
|
742
753
|
} }, "Back to Login")));
|
|
743
754
|
const renderExportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
744
755
|
React.createElement("h3", null, "Export Gun Pair"),
|
|
745
|
-
React.createElement("
|
|
756
|
+
React.createElement("div", { style: { backgroundColor: '#f0f9ff', padding: '12px', borderRadius: '8px', marginBottom: '16px', border: '1px solid #0ea5e9' } },
|
|
757
|
+
React.createElement("p", { style: { fontSize: '14px', color: '#0c4a6e', margin: '0', fontWeight: '500' } }, "\uD83D\uDD12 Backup Your Account"),
|
|
758
|
+
React.createElement("p", { style: { fontSize: '13px', color: '#075985', margin: '4px 0 0 0' } }, "Export your Gun pair to backup your account. You can use this to login from another device or restore access if needed.")),
|
|
746
759
|
React.createElement("div", { className: "shogun-form-group" },
|
|
747
760
|
React.createElement("label", { htmlFor: "exportPassword" },
|
|
748
761
|
React.createElement(LockIcon, null),
|
|
@@ -750,6 +763,15 @@ export const ShogunButton = (() => {
|
|
|
750
763
|
React.createElement("input", { type: "password", id: "exportPassword", value: exportPassword, onChange: (e) => setExportPassword(e.target.value), disabled: loading, placeholder: "Leave empty to export unencrypted" })),
|
|
751
764
|
exportedPair && (React.createElement("div", { className: "shogun-form-group" },
|
|
752
765
|
React.createElement("label", null, "Your Gun Pair (copy this safely):"),
|
|
766
|
+
showCopySuccess && (React.createElement("div", { style: {
|
|
767
|
+
backgroundColor: '#dcfce7',
|
|
768
|
+
color: '#166534',
|
|
769
|
+
padding: '8px 12px',
|
|
770
|
+
borderRadius: '4px',
|
|
771
|
+
marginBottom: '8px',
|
|
772
|
+
fontSize: '14px',
|
|
773
|
+
border: '1px solid #22c55e'
|
|
774
|
+
} }, "\u2705 Copied to clipboard successfully!")),
|
|
753
775
|
React.createElement("textarea", { value: exportedPair, readOnly: true, rows: 6, style: {
|
|
754
776
|
fontFamily: 'monospace',
|
|
755
777
|
fontSize: '12px',
|
|
@@ -757,20 +779,31 @@ export const ShogunButton = (() => {
|
|
|
757
779
|
padding: '8px',
|
|
758
780
|
border: '1px solid #ccc',
|
|
759
781
|
borderRadius: '4px'
|
|
760
|
-
} })
|
|
782
|
+
} }),
|
|
783
|
+
!navigator.clipboard && (React.createElement("p", { style: { fontSize: '12px', color: '#666', marginTop: '8px' } }, "\u26A0\uFE0F Auto-copy not available. Please manually copy the text above.")))),
|
|
761
784
|
React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleExportPair, disabled: loading }, loading ? "Exporting..." : "Export Pair"),
|
|
762
785
|
React.createElement("div", { className: "shogun-form-footer" },
|
|
763
786
|
React.createElement("button", { className: "shogun-toggle-mode", onClick: () => {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
787
|
+
if (isLoggedIn) {
|
|
788
|
+
// If user is logged in, close the modal instead of going to options
|
|
789
|
+
setModalIsOpen(false);
|
|
790
|
+
setExportPassword("");
|
|
791
|
+
setExportedPair("");
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
setAuthView("options");
|
|
795
|
+
setExportPassword("");
|
|
796
|
+
setExportedPair("");
|
|
797
|
+
}
|
|
767
798
|
}, disabled: loading }, "Back"))));
|
|
768
799
|
const renderImportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
769
800
|
React.createElement("h3", null, "Import Gun Pair"),
|
|
770
|
-
React.createElement("
|
|
801
|
+
React.createElement("div", { style: { backgroundColor: '#fef3c7', padding: '12px', borderRadius: '8px', marginBottom: '16px', border: '1px solid #f59e0b' } },
|
|
802
|
+
React.createElement("p", { style: { fontSize: '14px', color: '#92400e', margin: '0', fontWeight: '500' } }, "\uD83D\uDD11 Restore Your Account"),
|
|
803
|
+
React.createElement("p", { style: { fontSize: '13px', color: '#a16207', margin: '4px 0 0 0' } }, "Import a Gun pair to login with your existing account from another device. Make sure you have your backup data ready.")),
|
|
771
804
|
React.createElement("div", { className: "shogun-form-group" },
|
|
772
805
|
React.createElement("label", { htmlFor: "importPairData" },
|
|
773
|
-
React.createElement(
|
|
806
|
+
React.createElement(ImportIcon, null),
|
|
774
807
|
React.createElement("span", null, "Gun Pair Data")),
|
|
775
808
|
React.createElement("textarea", { id: "importPairData", value: importPairData, onChange: (e) => setImportPairData(e.target.value), disabled: loading, placeholder: "Paste your Gun pair JSON here...", rows: 6, style: {
|
|
776
809
|
fontFamily: 'monospace',
|
|
@@ -785,7 +818,17 @@ export const ShogunButton = (() => {
|
|
|
785
818
|
React.createElement(LockIcon, null),
|
|
786
819
|
React.createElement("span", null, "Decryption Password (if encrypted)")),
|
|
787
820
|
React.createElement("input", { type: "password", id: "importPassword", value: importPassword, onChange: (e) => setImportPassword(e.target.value), disabled: loading, placeholder: "Enter password if pair was encrypted" })),
|
|
788
|
-
React.createElement("
|
|
821
|
+
showImportSuccess && (React.createElement("div", { style: {
|
|
822
|
+
backgroundColor: '#dcfce7',
|
|
823
|
+
color: '#166534',
|
|
824
|
+
padding: '12px',
|
|
825
|
+
borderRadius: '8px',
|
|
826
|
+
marginBottom: '16px',
|
|
827
|
+
fontSize: '14px',
|
|
828
|
+
border: '1px solid #22c55e',
|
|
829
|
+
textAlign: 'center'
|
|
830
|
+
} }, "\u2705 Pair imported successfully! Logging you in...")),
|
|
831
|
+
React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleImportPair, disabled: loading || showImportSuccess }, loading ? "Importing..." : showImportSuccess ? "Success!" : "Import and Login"),
|
|
789
832
|
React.createElement("div", { className: "shogun-form-footer" },
|
|
790
833
|
React.createElement("button", { className: "shogun-toggle-mode", onClick: () => {
|
|
791
834
|
setAuthView("options");
|
|
@@ -808,9 +851,11 @@ export const ShogunButton = (() => {
|
|
|
808
851
|
? "Export Gun Pair"
|
|
809
852
|
: authView === "import"
|
|
810
853
|
? "Import Gun Pair"
|
|
811
|
-
:
|
|
812
|
-
? "
|
|
813
|
-
:
|
|
854
|
+
: authView === "webauthn-username"
|
|
855
|
+
? "WebAuthn"
|
|
856
|
+
: formMode === "login"
|
|
857
|
+
? "Login"
|
|
858
|
+
: "Sign Up"),
|
|
814
859
|
React.createElement("button", { className: "shogun-close-button", onClick: closeModal, "aria-label": "Close" },
|
|
815
860
|
React.createElement(CloseIcon, null))),
|
|
816
861
|
React.createElement("div", { className: "shogun-modal-content" },
|
|
@@ -827,7 +872,8 @@ export const ShogunButton = (() => {
|
|
|
827
872
|
authView === "recover" && renderRecoveryForm(),
|
|
828
873
|
authView === "showHint" && renderHint(),
|
|
829
874
|
authView === "export" && renderExportForm(),
|
|
830
|
-
authView === "import" && renderImportForm()
|
|
875
|
+
authView === "import" && renderImportForm(),
|
|
876
|
+
authView === "webauthn-username" && renderWebAuthnUsernameForm()))))));
|
|
831
877
|
};
|
|
832
878
|
Button.displayName = "ShogunButton";
|
|
833
879
|
return Object.assign(Button, {
|