@fiber-pay/react 0.2.3 → 0.2.4
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 +37 -1
- package/dist/index.d.ts +106 -19
- package/dist/index.js +886 -51
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -1,13 +1,39 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
ChannelState,
|
|
4
|
+
ConfigBuilder as ConfigBuilder2,
|
|
5
|
+
ckbHash,
|
|
6
|
+
ckbToShannons,
|
|
7
|
+
derivePublicKey,
|
|
8
|
+
FiberBrowserNode as FiberBrowserNode2,
|
|
9
|
+
FiberRpcError,
|
|
10
|
+
formatShannonsAsCkb as formatShannonsAsCkb2,
|
|
11
|
+
fromHex,
|
|
12
|
+
getLockBalanceShannons as getLockBalanceShannons2,
|
|
13
|
+
PasskeyCredentialProvider as PasskeyCredentialProvider2,
|
|
14
|
+
PasswordCredentialProvider as PasswordCredentialProvider2,
|
|
15
|
+
RawKeyCredentialProvider as RawKeyCredentialProvider2,
|
|
16
|
+
scriptToAddress as scriptToAddress2,
|
|
17
|
+
shannonsToCkb,
|
|
18
|
+
toHex
|
|
19
|
+
} from "@fiber-pay/sdk/browser";
|
|
20
|
+
|
|
21
|
+
// src/connect-button.tsx
|
|
22
|
+
import {
|
|
23
|
+
useCallback as useCallback2,
|
|
24
|
+
useEffect as useEffect2,
|
|
25
|
+
useRef as useRef2,
|
|
26
|
+
useState as useState2
|
|
27
|
+
} from "react";
|
|
3
28
|
|
|
4
29
|
// src/use-fiber-node.ts
|
|
5
30
|
import {
|
|
6
31
|
FiberBrowserNode,
|
|
7
32
|
PasskeyCredentialProvider,
|
|
8
|
-
PasswordCredentialProvider
|
|
33
|
+
PasswordCredentialProvider,
|
|
34
|
+
RawKeyCredentialProvider
|
|
9
35
|
} from "@fiber-pay/sdk/browser";
|
|
10
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
36
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
37
|
var PASSKEY_UNAVAILABLE_REASON_TEXT = {
|
|
12
38
|
"window-unavailable": "Passkey is not available because there is no browser window context.",
|
|
13
39
|
"insecure-context": "Passkey requires a secure context (HTTPS or localhost).",
|
|
@@ -66,6 +92,7 @@ function useFiberNode(options) {
|
|
|
66
92
|
[detachNodeListeners]
|
|
67
93
|
);
|
|
68
94
|
useEffect(() => {
|
|
95
|
+
if (options.enabled === false) return;
|
|
69
96
|
let cancelled = false;
|
|
70
97
|
PasskeyCredentialProvider.getSupportStatus().then((status) => {
|
|
71
98
|
if (!cancelled) {
|
|
@@ -89,7 +116,7 @@ function useFiberNode(options) {
|
|
|
89
116
|
return () => {
|
|
90
117
|
cancelled = true;
|
|
91
118
|
};
|
|
92
|
-
}, [walletId]);
|
|
119
|
+
}, [walletId, options.enabled]);
|
|
93
120
|
const initNode = useCallback(
|
|
94
121
|
(credential) => {
|
|
95
122
|
if (nodeRef.current) {
|
|
@@ -217,6 +244,26 @@ function useFiberNode(options) {
|
|
|
217
244
|
await cleanupFailedStart(node);
|
|
218
245
|
}
|
|
219
246
|
}, [cleanupFailedStart, initNode, walletId]);
|
|
247
|
+
const startWithRawKey = useCallback(
|
|
248
|
+
async (fiberKey, ckbSecretKey) => {
|
|
249
|
+
setError(null);
|
|
250
|
+
let node = null;
|
|
251
|
+
try {
|
|
252
|
+
const credential = new RawKeyCredentialProvider(fiberKey, ckbSecretKey, walletId);
|
|
253
|
+
node = initNode(credential);
|
|
254
|
+
const info = await node.start();
|
|
255
|
+
if (isMountedRef.current) {
|
|
256
|
+
setNodeInfo(info);
|
|
257
|
+
}
|
|
258
|
+
} catch (startError) {
|
|
259
|
+
if (isMountedRef.current) {
|
|
260
|
+
setError(asErrorMessage(startError));
|
|
261
|
+
}
|
|
262
|
+
await cleanupFailedStart(node);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
[cleanupFailedStart, initNode, walletId]
|
|
266
|
+
);
|
|
220
267
|
const stop = useCallback(async () => {
|
|
221
268
|
const node = nodeRef.current;
|
|
222
269
|
if (!node) {
|
|
@@ -238,11 +285,15 @@ function useFiberNode(options) {
|
|
|
238
285
|
}
|
|
239
286
|
}
|
|
240
287
|
}, [detachNodeListeners]);
|
|
288
|
+
const isStarting = useMemo(() => state === "unlocking" || state === "starting", [state]);
|
|
289
|
+
const isRunning = useMemo(() => state === "running", [state]);
|
|
241
290
|
return {
|
|
242
291
|
state,
|
|
243
292
|
node: nodeRef.current,
|
|
244
293
|
nodeInfo,
|
|
245
294
|
error,
|
|
295
|
+
isStarting,
|
|
296
|
+
isRunning,
|
|
246
297
|
isPasskeySupported,
|
|
247
298
|
passkeySupportReason,
|
|
248
299
|
passkeyUnavailableReason,
|
|
@@ -250,12 +301,390 @@ function useFiberNode(options) {
|
|
|
250
301
|
startWithPassword,
|
|
251
302
|
createPasskeyAndStart,
|
|
252
303
|
startWithPasskey,
|
|
304
|
+
startWithRawKey,
|
|
253
305
|
stop
|
|
254
306
|
};
|
|
255
307
|
}
|
|
256
308
|
|
|
309
|
+
// src/connect-button.tsx
|
|
310
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
311
|
+
function truncateNodeId(id) {
|
|
312
|
+
if (id.length <= 16) return id;
|
|
313
|
+
return `${id.slice(0, 8)}\u2026${id.slice(-4)}`;
|
|
314
|
+
}
|
|
315
|
+
var styles = {
|
|
316
|
+
root: {
|
|
317
|
+
position: "relative",
|
|
318
|
+
display: "inline-flex",
|
|
319
|
+
alignItems: "center",
|
|
320
|
+
gap: "0.5rem",
|
|
321
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
322
|
+
},
|
|
323
|
+
button: {
|
|
324
|
+
display: "inline-flex",
|
|
325
|
+
alignItems: "center",
|
|
326
|
+
gap: "0.5rem",
|
|
327
|
+
padding: "0.5rem 1.25rem",
|
|
328
|
+
fontSize: "0.875rem",
|
|
329
|
+
fontWeight: 600,
|
|
330
|
+
borderRadius: "9999px",
|
|
331
|
+
border: "none",
|
|
332
|
+
cursor: "pointer",
|
|
333
|
+
transition: "background-color 0.15s, opacity 0.15s",
|
|
334
|
+
lineHeight: 1.4
|
|
335
|
+
},
|
|
336
|
+
connectButton: {
|
|
337
|
+
backgroundColor: "var(--fpay-accent, #6366f1)",
|
|
338
|
+
color: "var(--fpay-accent-fg, #fff)"
|
|
339
|
+
},
|
|
340
|
+
connectedButton: {
|
|
341
|
+
backgroundColor: "var(--fpay-accent-subtle, rgba(99,102,241,0.12))",
|
|
342
|
+
color: "var(--fpay-accent, #6366f1)",
|
|
343
|
+
border: "1px solid var(--fpay-accent-border, rgba(99,102,241,0.35))"
|
|
344
|
+
},
|
|
345
|
+
disabledButton: {
|
|
346
|
+
opacity: 0.6,
|
|
347
|
+
cursor: "not-allowed"
|
|
348
|
+
},
|
|
349
|
+
statusDot: {
|
|
350
|
+
display: "inline-block",
|
|
351
|
+
width: "0.5rem",
|
|
352
|
+
height: "0.5rem",
|
|
353
|
+
borderRadius: "50%",
|
|
354
|
+
backgroundColor: "var(--fpay-accent, #6366f1)"
|
|
355
|
+
},
|
|
356
|
+
errorText: {
|
|
357
|
+
fontSize: "0.75rem",
|
|
358
|
+
color: "var(--fpay-error, #ef4444)",
|
|
359
|
+
maxWidth: "220px",
|
|
360
|
+
overflow: "hidden",
|
|
361
|
+
textOverflow: "ellipsis",
|
|
362
|
+
whiteSpace: "nowrap"
|
|
363
|
+
},
|
|
364
|
+
dropdown: {
|
|
365
|
+
position: "absolute",
|
|
366
|
+
right: 0,
|
|
367
|
+
top: "100%",
|
|
368
|
+
marginTop: "0.5rem",
|
|
369
|
+
width: "280px",
|
|
370
|
+
borderRadius: "0.75rem",
|
|
371
|
+
border: "1px solid var(--fpay-border, #e5e7eb)",
|
|
372
|
+
backgroundColor: "var(--fpay-bg-elevated, #fff)",
|
|
373
|
+
padding: "1rem",
|
|
374
|
+
boxShadow: "0 10px 25px rgba(0,0,0,0.1)",
|
|
375
|
+
zIndex: 100
|
|
376
|
+
},
|
|
377
|
+
infoRow: {
|
|
378
|
+
display: "flex",
|
|
379
|
+
justifyContent: "space-between",
|
|
380
|
+
alignItems: "center",
|
|
381
|
+
padding: "0.375rem 0",
|
|
382
|
+
fontSize: "0.75rem"
|
|
383
|
+
},
|
|
384
|
+
infoLabel: {
|
|
385
|
+
color: "var(--fpay-text-secondary, #6b7280)"
|
|
386
|
+
},
|
|
387
|
+
infoValue: {
|
|
388
|
+
fontFamily: "ui-monospace, monospace",
|
|
389
|
+
color: "var(--fpay-text-primary, #111827)"
|
|
390
|
+
},
|
|
391
|
+
separator: {
|
|
392
|
+
borderTop: "1px solid var(--fpay-border, #e5e7eb)",
|
|
393
|
+
margin: "0.75rem 0"
|
|
394
|
+
},
|
|
395
|
+
disconnectButton: {
|
|
396
|
+
display: "flex",
|
|
397
|
+
width: "100%",
|
|
398
|
+
alignItems: "center",
|
|
399
|
+
justifyContent: "space-between",
|
|
400
|
+
padding: "0.625rem 0.75rem",
|
|
401
|
+
fontSize: "0.875rem",
|
|
402
|
+
fontWeight: 600,
|
|
403
|
+
border: "1px solid var(--fpay-border, #e5e7eb)",
|
|
404
|
+
borderRadius: "0.5rem",
|
|
405
|
+
backgroundColor: "var(--fpay-bg-secondary, #f9fafb)",
|
|
406
|
+
color: "var(--fpay-text-primary, #111827)",
|
|
407
|
+
cursor: "pointer",
|
|
408
|
+
transition: "background-color 0.15s"
|
|
409
|
+
},
|
|
410
|
+
spinner: {
|
|
411
|
+
animation: "fpay-spin 1s linear infinite"
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
var KEYFRAMES_ID = "fpay-connect-button-keyframes";
|
|
415
|
+
function ensureKeyframes() {
|
|
416
|
+
if (typeof document === "undefined") return;
|
|
417
|
+
if (document.getElementById(KEYFRAMES_ID)) return;
|
|
418
|
+
const style = document.createElement("style");
|
|
419
|
+
style.id = KEYFRAMES_ID;
|
|
420
|
+
style.textContent = `@keyframes fpay-spin { to { transform: rotate(360deg); } }`;
|
|
421
|
+
document.head.appendChild(style);
|
|
422
|
+
}
|
|
423
|
+
function Spinner({ size = 16 }) {
|
|
424
|
+
return /* @__PURE__ */ jsx(
|
|
425
|
+
"svg",
|
|
426
|
+
{
|
|
427
|
+
width: size,
|
|
428
|
+
height: size,
|
|
429
|
+
viewBox: "0 0 24 24",
|
|
430
|
+
fill: "none",
|
|
431
|
+
stroke: "currentColor",
|
|
432
|
+
strokeWidth: "2",
|
|
433
|
+
strokeLinecap: "round",
|
|
434
|
+
strokeLinejoin: "round",
|
|
435
|
+
style: styles.spinner,
|
|
436
|
+
role: "img",
|
|
437
|
+
"aria-label": "Loading",
|
|
438
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
function Chevron({ open }) {
|
|
443
|
+
return /* @__PURE__ */ jsx(
|
|
444
|
+
"svg",
|
|
445
|
+
{
|
|
446
|
+
width: "12",
|
|
447
|
+
height: "12",
|
|
448
|
+
viewBox: "0 0 24 24",
|
|
449
|
+
fill: "none",
|
|
450
|
+
stroke: "currentColor",
|
|
451
|
+
strokeWidth: "2",
|
|
452
|
+
strokeLinecap: "round",
|
|
453
|
+
strokeLinejoin: "round",
|
|
454
|
+
style: { transition: "transform 0.15s", transform: open ? "rotate(180deg)" : "none" },
|
|
455
|
+
"aria-hidden": "true",
|
|
456
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
function ConnectButton(props) {
|
|
461
|
+
const {
|
|
462
|
+
network = "testnet",
|
|
463
|
+
fiber: externalFiber,
|
|
464
|
+
strategy = "auto",
|
|
465
|
+
password,
|
|
466
|
+
rawKey,
|
|
467
|
+
rawCkbKey,
|
|
468
|
+
walletId,
|
|
469
|
+
passkeyUsername = "User",
|
|
470
|
+
wasmFactory,
|
|
471
|
+
nodeConfig,
|
|
472
|
+
className,
|
|
473
|
+
style,
|
|
474
|
+
dropdownStyle,
|
|
475
|
+
renderConnectedDropdown,
|
|
476
|
+
onConnect,
|
|
477
|
+
onDisconnect,
|
|
478
|
+
onError
|
|
479
|
+
} = props;
|
|
480
|
+
const internalFiber = useFiberNode({
|
|
481
|
+
network,
|
|
482
|
+
walletId,
|
|
483
|
+
wasmFactory,
|
|
484
|
+
nodeConfig,
|
|
485
|
+
enabled: !externalFiber
|
|
486
|
+
});
|
|
487
|
+
const fiber = externalFiber ?? internalFiber;
|
|
488
|
+
const {
|
|
489
|
+
state,
|
|
490
|
+
node,
|
|
491
|
+
nodeInfo,
|
|
492
|
+
error,
|
|
493
|
+
isStarting,
|
|
494
|
+
isRunning,
|
|
495
|
+
isPasskeySupported,
|
|
496
|
+
passkeyUnavailableReason,
|
|
497
|
+
hasPasskeyConfigured,
|
|
498
|
+
createPasskeyAndStart,
|
|
499
|
+
startWithPasskey,
|
|
500
|
+
startWithPassword,
|
|
501
|
+
startWithRawKey,
|
|
502
|
+
stop
|
|
503
|
+
} = fiber;
|
|
504
|
+
const [isConnecting, setIsConnecting] = useState2(false);
|
|
505
|
+
const [showDropdown, setShowDropdown] = useState2(false);
|
|
506
|
+
const [localError, setLocalError] = useState2(null);
|
|
507
|
+
const dropdownRef = useRef2(null);
|
|
508
|
+
const effectiveIsStarting = isConnecting || isStarting;
|
|
509
|
+
useEffect2(() => ensureKeyframes(), []);
|
|
510
|
+
useEffect2(() => {
|
|
511
|
+
if (!showDropdown) return;
|
|
512
|
+
function handleClickOutside(e) {
|
|
513
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
514
|
+
setShowDropdown(false);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
518
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
519
|
+
}, [showDropdown]);
|
|
520
|
+
const prevRunningRef = useRef2(false);
|
|
521
|
+
useEffect2(() => {
|
|
522
|
+
if (isRunning && !prevRunningRef.current && node && nodeInfo) {
|
|
523
|
+
onConnect?.(node, nodeInfo);
|
|
524
|
+
}
|
|
525
|
+
if (!isRunning && prevRunningRef.current) {
|
|
526
|
+
onDisconnect?.();
|
|
527
|
+
}
|
|
528
|
+
prevRunningRef.current = isRunning;
|
|
529
|
+
}, [isRunning, node, nodeInfo, onConnect, onDisconnect]);
|
|
530
|
+
useEffect2(() => {
|
|
531
|
+
if (error) onError?.(error);
|
|
532
|
+
}, [error, onError]);
|
|
533
|
+
const resolvedStrategy = strategy === "auto" ? hasPasskeyConfigured && isPasskeySupported ? "passkey" : password ? "password" : rawKey ? "rawKey" : "passkey" : strategy;
|
|
534
|
+
const handleConnect = useCallback2(async () => {
|
|
535
|
+
setIsConnecting(true);
|
|
536
|
+
setLocalError(null);
|
|
537
|
+
try {
|
|
538
|
+
switch (resolvedStrategy) {
|
|
539
|
+
case "password":
|
|
540
|
+
if (!password) throw new Error('Password is required for "password" strategy');
|
|
541
|
+
await startWithPassword(password);
|
|
542
|
+
break;
|
|
543
|
+
case "rawKey":
|
|
544
|
+
if (!rawKey) throw new Error('rawKey is required for "rawKey" strategy');
|
|
545
|
+
await startWithRawKey(rawKey, rawCkbKey);
|
|
546
|
+
break;
|
|
547
|
+
case "passkey":
|
|
548
|
+
if (hasPasskeyConfigured) {
|
|
549
|
+
await startWithPasskey();
|
|
550
|
+
} else {
|
|
551
|
+
await createPasskeyAndStart(passkeyUsername);
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
} catch (err) {
|
|
556
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
557
|
+
setLocalError(msg);
|
|
558
|
+
onError?.(msg);
|
|
559
|
+
} finally {
|
|
560
|
+
setIsConnecting(false);
|
|
561
|
+
}
|
|
562
|
+
}, [
|
|
563
|
+
resolvedStrategy,
|
|
564
|
+
password,
|
|
565
|
+
rawKey,
|
|
566
|
+
rawCkbKey,
|
|
567
|
+
passkeyUsername,
|
|
568
|
+
hasPasskeyConfigured,
|
|
569
|
+
startWithPassword,
|
|
570
|
+
startWithPasskey,
|
|
571
|
+
startWithRawKey,
|
|
572
|
+
createPasskeyAndStart,
|
|
573
|
+
onError
|
|
574
|
+
]);
|
|
575
|
+
const handleDisconnect = useCallback2(async () => {
|
|
576
|
+
try {
|
|
577
|
+
await stop();
|
|
578
|
+
} catch (err) {
|
|
579
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
580
|
+
setLocalError(msg);
|
|
581
|
+
onError?.(msg);
|
|
582
|
+
} finally {
|
|
583
|
+
setShowDropdown(false);
|
|
584
|
+
}
|
|
585
|
+
}, [stop, onError]);
|
|
586
|
+
const closeDropdown = useCallback2(() => {
|
|
587
|
+
setShowDropdown(false);
|
|
588
|
+
}, []);
|
|
589
|
+
const hasError = !!(error || localError);
|
|
590
|
+
let buttonLabel;
|
|
591
|
+
let buttonOnClick;
|
|
592
|
+
let buttonDisabled = false;
|
|
593
|
+
let buttonStyle;
|
|
594
|
+
if (isRunning) {
|
|
595
|
+
buttonLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
596
|
+
/* @__PURE__ */ jsx("span", { style: styles.statusDot }),
|
|
597
|
+
/* @__PURE__ */ jsx("span", { style: { fontFamily: "ui-monospace, monospace" }, children: nodeInfo?.pubkey ? truncateNodeId(nodeInfo.pubkey) : "Connected" }),
|
|
598
|
+
/* @__PURE__ */ jsx(Chevron, { open: showDropdown })
|
|
599
|
+
] });
|
|
600
|
+
buttonOnClick = () => setShowDropdown((s) => !s);
|
|
601
|
+
buttonStyle = { ...styles.button, ...styles.connectedButton };
|
|
602
|
+
} else if (effectiveIsStarting) {
|
|
603
|
+
buttonLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
604
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
605
|
+
"Connecting\u2026"
|
|
606
|
+
] });
|
|
607
|
+
buttonDisabled = true;
|
|
608
|
+
buttonStyle = { ...styles.button, ...styles.connectButton, ...styles.disabledButton };
|
|
609
|
+
} else {
|
|
610
|
+
switch (resolvedStrategy) {
|
|
611
|
+
case "passkey":
|
|
612
|
+
buttonLabel = hasPasskeyConfigured ? "Connect with Passkey" : "Connect via Passkey";
|
|
613
|
+
if (!isPasskeySupported) {
|
|
614
|
+
buttonLabel = "Passkey unavailable";
|
|
615
|
+
buttonDisabled = true;
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
case "password":
|
|
619
|
+
buttonLabel = "Connect";
|
|
620
|
+
break;
|
|
621
|
+
case "rawKey":
|
|
622
|
+
buttonLabel = "Connect";
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
buttonOnClick = handleConnect;
|
|
626
|
+
buttonStyle = {
|
|
627
|
+
...styles.button,
|
|
628
|
+
...styles.connectButton,
|
|
629
|
+
...buttonDisabled ? styles.disabledButton : {}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return /* @__PURE__ */ jsxs("div", { className, style: { ...styles.root, ...style }, "data-fpay-connect-button": "", children: [
|
|
633
|
+
(hasError || !isPasskeySupported && passkeyUnavailableReason && resolvedStrategy === "passkey") && /* @__PURE__ */ jsx("span", { style: styles.errorText, children: error || localError || passkeyUnavailableReason }),
|
|
634
|
+
isRunning ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: dropdownRef, children: [
|
|
635
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, style: buttonStyle, children: buttonLabel }),
|
|
636
|
+
showDropdown && /* @__PURE__ */ jsx("div", { style: { ...styles.dropdown, ...dropdownStyle }, children: renderConnectedDropdown ? renderConnectedDropdown({
|
|
637
|
+
fiber,
|
|
638
|
+
closeDropdown,
|
|
639
|
+
disconnect: handleDisconnect
|
|
640
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
641
|
+
nodeInfo && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
642
|
+
/* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
|
|
643
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "Pubkey" }),
|
|
644
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoValue, children: truncateNodeId(nodeInfo.pubkey) })
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
|
|
647
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "State" }),
|
|
648
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoValue, children: state })
|
|
649
|
+
] })
|
|
650
|
+
] }),
|
|
651
|
+
/* @__PURE__ */ jsx("div", { style: styles.separator }),
|
|
652
|
+
/* @__PURE__ */ jsxs(
|
|
653
|
+
"button",
|
|
654
|
+
{
|
|
655
|
+
type: "button",
|
|
656
|
+
onClick: () => void handleDisconnect(),
|
|
657
|
+
style: styles.disconnectButton,
|
|
658
|
+
children: [
|
|
659
|
+
/* @__PURE__ */ jsx("span", { children: "Disconnect" }),
|
|
660
|
+
/* @__PURE__ */ jsx(
|
|
661
|
+
"svg",
|
|
662
|
+
{
|
|
663
|
+
width: "14",
|
|
664
|
+
height: "14",
|
|
665
|
+
viewBox: "0 0 24 24",
|
|
666
|
+
fill: "none",
|
|
667
|
+
stroke: "currentColor",
|
|
668
|
+
strokeWidth: "2",
|
|
669
|
+
strokeLinecap: "round",
|
|
670
|
+
strokeLinejoin: "round",
|
|
671
|
+
"aria-hidden": "true",
|
|
672
|
+
children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
|
|
673
|
+
}
|
|
674
|
+
)
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
] }) })
|
|
679
|
+
] }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, disabled: buttonDisabled, style: buttonStyle, children: buttonLabel })
|
|
680
|
+
] });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// src/fiber-pay-quick-card.tsx
|
|
684
|
+
import { useEffect as useEffect4, useId, useState as useState4 } from "react";
|
|
685
|
+
|
|
257
686
|
// src/use-fiber-payment.ts
|
|
258
|
-
import { useCallback as
|
|
687
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
259
688
|
function asErrorMessage2(error) {
|
|
260
689
|
if (error instanceof Error) {
|
|
261
690
|
return error.message;
|
|
@@ -263,17 +692,17 @@ function asErrorMessage2(error) {
|
|
|
263
692
|
return String(error);
|
|
264
693
|
}
|
|
265
694
|
function useFiberPayment(node) {
|
|
266
|
-
const [isPaying, setIsPaying] =
|
|
267
|
-
const [paymentResult, setPaymentResult] =
|
|
268
|
-
const [error, setError] =
|
|
269
|
-
const isMountedRef =
|
|
270
|
-
|
|
695
|
+
const [isPaying, setIsPaying] = useState3(false);
|
|
696
|
+
const [paymentResult, setPaymentResult] = useState3(null);
|
|
697
|
+
const [error, setError] = useState3(null);
|
|
698
|
+
const isMountedRef = useRef3(true);
|
|
699
|
+
useEffect3(
|
|
271
700
|
() => () => {
|
|
272
701
|
isMountedRef.current = false;
|
|
273
702
|
},
|
|
274
703
|
[]
|
|
275
704
|
);
|
|
276
|
-
const payInvoice =
|
|
705
|
+
const payInvoice = useCallback3(
|
|
277
706
|
async (invoice) => {
|
|
278
707
|
if (!node) {
|
|
279
708
|
if (isMountedRef.current) {
|
|
@@ -318,7 +747,7 @@ function useFiberPayment(node) {
|
|
|
318
747
|
}
|
|
319
748
|
|
|
320
749
|
// src/fiber-pay-quick-card.tsx
|
|
321
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
750
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
322
751
|
var ONE_CKB_SHANNONS = "0x5f5e100";
|
|
323
752
|
var cardStyle = {
|
|
324
753
|
border: "1px solid #ddd",
|
|
@@ -356,22 +785,22 @@ function FiberPayQuickCard(props) {
|
|
|
356
785
|
stop
|
|
357
786
|
} = useFiberNode({ network, walletId: props.walletId });
|
|
358
787
|
const { payInvoice, isPaying, error: payError, paymentResult } = useFiberPayment(node);
|
|
359
|
-
const [password, setPassword] =
|
|
360
|
-
const [invoiceInput, setInvoiceInput] =
|
|
361
|
-
const [createdInvoice, setCreatedInvoice] =
|
|
362
|
-
const [isCreatingInvoice, setIsCreatingInvoice] =
|
|
363
|
-
const [invoiceError, setInvoiceError] =
|
|
364
|
-
|
|
788
|
+
const [password, setPassword] = useState4("");
|
|
789
|
+
const [invoiceInput, setInvoiceInput] = useState4("");
|
|
790
|
+
const [createdInvoice, setCreatedInvoice] = useState4("");
|
|
791
|
+
const [isCreatingInvoice, setIsCreatingInvoice] = useState4(false);
|
|
792
|
+
const [invoiceError, setInvoiceError] = useState4(null);
|
|
793
|
+
useEffect4(() => {
|
|
365
794
|
if (nodeError) {
|
|
366
795
|
onError?.({ scope: "node", message: nodeError });
|
|
367
796
|
}
|
|
368
797
|
}, [nodeError, onError]);
|
|
369
|
-
|
|
798
|
+
useEffect4(() => {
|
|
370
799
|
if (payError) {
|
|
371
800
|
onError?.({ scope: "payment", message: payError });
|
|
372
801
|
}
|
|
373
802
|
}, [onError, payError]);
|
|
374
|
-
|
|
803
|
+
useEffect4(() => {
|
|
375
804
|
if (paymentResult) {
|
|
376
805
|
onPaymentResult?.(paymentResult);
|
|
377
806
|
}
|
|
@@ -398,18 +827,18 @@ function FiberPayQuickCard(props) {
|
|
|
398
827
|
setIsCreatingInvoice(false);
|
|
399
828
|
}
|
|
400
829
|
};
|
|
401
|
-
return /* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
830
|
+
return /* @__PURE__ */ jsxs2("div", { style: { ...cardStyle, ...props.style }, className: props.className, children: [
|
|
831
|
+
/* @__PURE__ */ jsxs2("h3", { children: [
|
|
403
832
|
title,
|
|
404
833
|
" (",
|
|
405
834
|
network,
|
|
406
835
|
")"
|
|
407
836
|
] }),
|
|
408
|
-
!nodeInfo ? /* @__PURE__ */
|
|
409
|
-
isPasskeySupported ? /* @__PURE__ */
|
|
410
|
-
/* @__PURE__ */
|
|
411
|
-
/* @__PURE__ */
|
|
412
|
-
/* @__PURE__ */
|
|
837
|
+
!nodeInfo ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
838
|
+
isPasskeySupported ? /* @__PURE__ */ jsx2("div", { style: rowWithMarginStyle, children: hasPasskeyConfigured ? /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void startWithPasskey(), children: "Login with Passkey" }) : /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void createPasskeyAndStart(passkeyUsername), children: "Register Passkey" }) }) : null,
|
|
839
|
+
/* @__PURE__ */ jsx2("label", { htmlFor: passwordInputId, children: "Password" }),
|
|
840
|
+
/* @__PURE__ */ jsxs2("div", { style: rowStyle, children: [
|
|
841
|
+
/* @__PURE__ */ jsx2(
|
|
413
842
|
"input",
|
|
414
843
|
{
|
|
415
844
|
id: passwordInputId,
|
|
@@ -421,31 +850,31 @@ function FiberPayQuickCard(props) {
|
|
|
421
850
|
placeholder: "Password"
|
|
422
851
|
}
|
|
423
852
|
),
|
|
424
|
-
/* @__PURE__ */
|
|
853
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void startWithPassword(password), children: "Start with Password" })
|
|
425
854
|
] })
|
|
426
|
-
] }) : /* @__PURE__ */
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
855
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
856
|
+
/* @__PURE__ */ jsxs2("p", { children: [
|
|
857
|
+
/* @__PURE__ */ jsx2("strong", { children: "State:" }),
|
|
429
858
|
" ",
|
|
430
859
|
state
|
|
431
860
|
] }),
|
|
432
|
-
/* @__PURE__ */
|
|
433
|
-
/* @__PURE__ */
|
|
861
|
+
/* @__PURE__ */ jsxs2("p", { children: [
|
|
862
|
+
/* @__PURE__ */ jsx2("strong", { children: "Pubkey:" }),
|
|
434
863
|
" ",
|
|
435
864
|
nodeInfo.pubkey
|
|
436
865
|
] }),
|
|
437
|
-
/* @__PURE__ */
|
|
438
|
-
/* @__PURE__ */
|
|
439
|
-
/* @__PURE__ */
|
|
866
|
+
/* @__PURE__ */ jsxs2("div", { style: rowWithMarginStyle, children: [
|
|
867
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void createInvoice(), disabled: isCreatingInvoice, children: isCreatingInvoice ? "Creating..." : "Create Invoice (1 CKB)" }),
|
|
868
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void stop(), children: "Stop Node" })
|
|
440
869
|
] }),
|
|
441
|
-
createdInvoice ? /* @__PURE__ */
|
|
442
|
-
/* @__PURE__ */
|
|
870
|
+
createdInvoice ? /* @__PURE__ */ jsxs2("p", { children: [
|
|
871
|
+
/* @__PURE__ */ jsx2("strong", { children: "Created invoice:" }),
|
|
443
872
|
" ",
|
|
444
873
|
createdInvoice
|
|
445
874
|
] }) : null,
|
|
446
|
-
/* @__PURE__ */
|
|
447
|
-
/* @__PURE__ */
|
|
448
|
-
/* @__PURE__ */
|
|
875
|
+
/* @__PURE__ */ jsx2("label", { htmlFor: invoiceInputId, children: "Invoice" }),
|
|
876
|
+
/* @__PURE__ */ jsxs2("div", { style: rowStyle, children: [
|
|
877
|
+
/* @__PURE__ */ jsx2(
|
|
449
878
|
"input",
|
|
450
879
|
{
|
|
451
880
|
id: invoiceInputId,
|
|
@@ -455,33 +884,439 @@ function FiberPayQuickCard(props) {
|
|
|
455
884
|
placeholder: "Paste invoice to pay"
|
|
456
885
|
}
|
|
457
886
|
),
|
|
458
|
-
/* @__PURE__ */
|
|
887
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void payInvoice(invoiceInput), disabled: isPaying, children: isPaying ? "Paying..." : "Pay" })
|
|
459
888
|
] }),
|
|
460
|
-
paymentResult ? /* @__PURE__ */
|
|
461
|
-
/* @__PURE__ */
|
|
889
|
+
paymentResult ? /* @__PURE__ */ jsxs2("p", { children: [
|
|
890
|
+
/* @__PURE__ */ jsx2("strong", { children: "Payment:" }),
|
|
462
891
|
" ",
|
|
463
892
|
paymentResult.status
|
|
464
893
|
] }) : null
|
|
465
894
|
] }),
|
|
466
|
-
nodeError ? /* @__PURE__ */
|
|
467
|
-
/* @__PURE__ */
|
|
895
|
+
nodeError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
|
|
896
|
+
/* @__PURE__ */ jsx2("strong", { children: "Node error:" }),
|
|
468
897
|
" ",
|
|
469
898
|
nodeError
|
|
470
899
|
] }) : null,
|
|
471
|
-
payError ? /* @__PURE__ */
|
|
472
|
-
/* @__PURE__ */
|
|
900
|
+
payError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
|
|
901
|
+
/* @__PURE__ */ jsx2("strong", { children: "Payment error:" }),
|
|
473
902
|
" ",
|
|
474
903
|
payError
|
|
475
904
|
] }) : null,
|
|
476
|
-
invoiceError ? /* @__PURE__ */
|
|
477
|
-
/* @__PURE__ */
|
|
905
|
+
invoiceError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
|
|
906
|
+
/* @__PURE__ */ jsx2("strong", { children: "Invoice error:" }),
|
|
478
907
|
" ",
|
|
479
908
|
invoiceError
|
|
480
909
|
] }) : null
|
|
481
910
|
] });
|
|
482
911
|
}
|
|
912
|
+
|
|
913
|
+
// src/node-info-panel.tsx
|
|
914
|
+
import {
|
|
915
|
+
ConfigBuilder,
|
|
916
|
+
formatShannonsAsCkb,
|
|
917
|
+
getLockBalanceShannons,
|
|
918
|
+
scriptToAddress
|
|
919
|
+
} from "@fiber-pay/sdk/browser";
|
|
920
|
+
import {
|
|
921
|
+
useCallback as useCallback4,
|
|
922
|
+
useEffect as useEffect5,
|
|
923
|
+
useRef as useRef4,
|
|
924
|
+
useState as useState5
|
|
925
|
+
} from "react";
|
|
926
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
927
|
+
function truncateMiddle(str, left = 8, right = 8) {
|
|
928
|
+
if (str.length <= left + right + 3) return str;
|
|
929
|
+
return `${str.slice(0, left)}\u2026${str.slice(-right)}`;
|
|
930
|
+
}
|
|
931
|
+
function copyToClipboard(text) {
|
|
932
|
+
void navigator.clipboard.writeText(text);
|
|
933
|
+
}
|
|
934
|
+
async function fetchNodeStats(node, network) {
|
|
935
|
+
const [nodeInfo, peers, channels] = await Promise.all([
|
|
936
|
+
node.nodeInfo(),
|
|
937
|
+
node.listPeers(),
|
|
938
|
+
node.listChannels()
|
|
939
|
+
]);
|
|
940
|
+
const lockScript = nodeInfo.default_funding_lock_script;
|
|
941
|
+
const ckbRpcUrl = ConfigBuilder.getDefaults(network).ckbRpcUrl;
|
|
942
|
+
if (!lockScript || lockScript.args === "0x") {
|
|
943
|
+
return {
|
|
944
|
+
pubkey: nodeInfo.pubkey,
|
|
945
|
+
peers: peers.peers.length,
|
|
946
|
+
channels: channels.channels.length,
|
|
947
|
+
ckbAddress: null,
|
|
948
|
+
balanceCkb: null,
|
|
949
|
+
externalFunding: true
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
const ckbAddress = scriptToAddress(lockScript, network);
|
|
953
|
+
const balanceShannons = await getLockBalanceShannons(ckbRpcUrl, lockScript);
|
|
954
|
+
const balanceCkb = formatShannonsAsCkb(balanceShannons, 4);
|
|
955
|
+
return {
|
|
956
|
+
pubkey: nodeInfo.pubkey,
|
|
957
|
+
peers: peers.peers.length,
|
|
958
|
+
channels: channels.channels.length,
|
|
959
|
+
ckbAddress,
|
|
960
|
+
balanceCkb,
|
|
961
|
+
externalFunding: false
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
var styles2 = {
|
|
965
|
+
root: {
|
|
966
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
967
|
+
fontSize: "0.875rem",
|
|
968
|
+
color: "var(--fpay-text-primary, #111827)"
|
|
969
|
+
},
|
|
970
|
+
idle: {
|
|
971
|
+
padding: "1rem",
|
|
972
|
+
textAlign: "center",
|
|
973
|
+
color: "var(--fpay-text-secondary, #6b7280)",
|
|
974
|
+
fontSize: "0.75rem"
|
|
975
|
+
},
|
|
976
|
+
loading: {
|
|
977
|
+
display: "flex",
|
|
978
|
+
alignItems: "center",
|
|
979
|
+
gap: "0.5rem",
|
|
980
|
+
padding: "0.5rem 0",
|
|
981
|
+
fontSize: "0.75rem",
|
|
982
|
+
color: "var(--fpay-text-secondary, #6b7280)"
|
|
983
|
+
},
|
|
984
|
+
errorBox: {
|
|
985
|
+
marginBottom: "0.5rem",
|
|
986
|
+
padding: "0.375rem 0.5rem",
|
|
987
|
+
fontSize: "0.75rem",
|
|
988
|
+
borderRadius: "0.5rem",
|
|
989
|
+
border: "1px solid var(--fpay-error-border, rgba(239,68,68,0.3))",
|
|
990
|
+
backgroundColor: "var(--fpay-error-bg, rgba(239,68,68,0.1))",
|
|
991
|
+
color: "var(--fpay-error, #ef4444)"
|
|
992
|
+
},
|
|
993
|
+
infoRow: {
|
|
994
|
+
display: "flex",
|
|
995
|
+
justifyContent: "space-between",
|
|
996
|
+
alignItems: "center",
|
|
997
|
+
gap: "0.75rem",
|
|
998
|
+
padding: "0.375rem 0"
|
|
999
|
+
},
|
|
1000
|
+
infoLabel: {
|
|
1001
|
+
fontSize: "0.75rem",
|
|
1002
|
+
color: "var(--fpay-text-secondary, #6b7280)"
|
|
1003
|
+
},
|
|
1004
|
+
infoValueWrapper: {
|
|
1005
|
+
display: "flex",
|
|
1006
|
+
alignItems: "center",
|
|
1007
|
+
gap: "0.375rem"
|
|
1008
|
+
},
|
|
1009
|
+
infoValue: {
|
|
1010
|
+
fontFamily: "ui-monospace, monospace",
|
|
1011
|
+
fontSize: "0.75rem",
|
|
1012
|
+
color: "var(--fpay-text-primary, #111827)"
|
|
1013
|
+
},
|
|
1014
|
+
copyButton: {
|
|
1015
|
+
display: "inline-flex",
|
|
1016
|
+
alignItems: "center",
|
|
1017
|
+
justifyContent: "center",
|
|
1018
|
+
padding: "0.25rem",
|
|
1019
|
+
borderRadius: "0.25rem",
|
|
1020
|
+
border: "none",
|
|
1021
|
+
background: "none",
|
|
1022
|
+
cursor: "pointer",
|
|
1023
|
+
color: "var(--fpay-text-secondary, #6b7280)",
|
|
1024
|
+
transition: "color 0.15s"
|
|
1025
|
+
},
|
|
1026
|
+
statsGrid: {
|
|
1027
|
+
display: "grid",
|
|
1028
|
+
gridTemplateColumns: "1fr 1fr",
|
|
1029
|
+
gap: "0.5rem",
|
|
1030
|
+
marginTop: "0.5rem"
|
|
1031
|
+
},
|
|
1032
|
+
statCard: {
|
|
1033
|
+
padding: "0.375rem 0.5rem",
|
|
1034
|
+
textAlign: "center",
|
|
1035
|
+
borderRadius: "0.5rem",
|
|
1036
|
+
border: "1px solid var(--fpay-border, #e5e7eb)",
|
|
1037
|
+
backgroundColor: "var(--fpay-bg-secondary, #f9fafb)"
|
|
1038
|
+
},
|
|
1039
|
+
statLabel: {
|
|
1040
|
+
fontSize: "0.625rem",
|
|
1041
|
+
color: "var(--fpay-text-secondary, #6b7280)"
|
|
1042
|
+
},
|
|
1043
|
+
statValue: {
|
|
1044
|
+
fontSize: "0.875rem",
|
|
1045
|
+
fontWeight: 600,
|
|
1046
|
+
color: "var(--fpay-text-primary, #111827)"
|
|
1047
|
+
},
|
|
1048
|
+
qrContainer: {
|
|
1049
|
+
display: "flex",
|
|
1050
|
+
flexDirection: "column",
|
|
1051
|
+
alignItems: "center",
|
|
1052
|
+
gap: "0.25rem",
|
|
1053
|
+
marginTop: "0.75rem",
|
|
1054
|
+
padding: "0.5rem",
|
|
1055
|
+
borderRadius: "0.5rem",
|
|
1056
|
+
border: "1px solid var(--fpay-border, #e5e7eb)",
|
|
1057
|
+
backgroundColor: "var(--fpay-bg-secondary, #f9fafb)"
|
|
1058
|
+
},
|
|
1059
|
+
qrCaption: {
|
|
1060
|
+
fontSize: "0.625rem",
|
|
1061
|
+
color: "var(--fpay-text-secondary, #6b7280)"
|
|
1062
|
+
},
|
|
1063
|
+
balanceRow: {
|
|
1064
|
+
display: "flex",
|
|
1065
|
+
justifyContent: "space-between",
|
|
1066
|
+
alignItems: "center",
|
|
1067
|
+
width: "100%",
|
|
1068
|
+
marginTop: "0.5rem",
|
|
1069
|
+
paddingTop: "0.5rem",
|
|
1070
|
+
borderTop: "1px solid var(--fpay-border, #e5e7eb)"
|
|
1071
|
+
},
|
|
1072
|
+
statusBadge: {
|
|
1073
|
+
display: "inline-flex",
|
|
1074
|
+
alignItems: "center",
|
|
1075
|
+
gap: "0.375rem",
|
|
1076
|
+
padding: "0.25rem 0.625rem",
|
|
1077
|
+
fontSize: "0.75rem",
|
|
1078
|
+
fontWeight: 500,
|
|
1079
|
+
borderRadius: "9999px",
|
|
1080
|
+
marginBottom: "0.5rem"
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
var STATUS_COLORS = {
|
|
1084
|
+
idle: { bg: "rgba(107,114,128,0.1)", fg: "#6b7280", dot: "#9ca3af" },
|
|
1085
|
+
unlocking: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
|
|
1086
|
+
starting: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
|
|
1087
|
+
running: { bg: "rgba(34,197,94,0.1)", fg: "#16a34a", dot: "#22c55e" },
|
|
1088
|
+
stopping: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
|
|
1089
|
+
stopped: { bg: "rgba(107,114,128,0.1)", fg: "#6b7280", dot: "#9ca3af" },
|
|
1090
|
+
error: { bg: "rgba(239,68,68,0.1)", fg: "#dc2626", dot: "#ef4444" }
|
|
1091
|
+
};
|
|
1092
|
+
function CopyIcon() {
|
|
1093
|
+
return /* @__PURE__ */ jsxs3(
|
|
1094
|
+
"svg",
|
|
1095
|
+
{
|
|
1096
|
+
width: "12",
|
|
1097
|
+
height: "12",
|
|
1098
|
+
viewBox: "0 0 24 24",
|
|
1099
|
+
fill: "none",
|
|
1100
|
+
stroke: "currentColor",
|
|
1101
|
+
strokeWidth: "2",
|
|
1102
|
+
strokeLinecap: "round",
|
|
1103
|
+
strokeLinejoin: "round",
|
|
1104
|
+
"aria-hidden": "true",
|
|
1105
|
+
children: [
|
|
1106
|
+
/* @__PURE__ */ jsx3("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
1107
|
+
/* @__PURE__ */ jsx3("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
1108
|
+
]
|
|
1109
|
+
}
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
function InfoRow({ label, value, copyable }) {
|
|
1113
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles2.infoRow, children: [
|
|
1114
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.infoLabel, children: label }),
|
|
1115
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.infoValueWrapper, children: [
|
|
1116
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.infoValue, children: truncateMiddle(value, 6, 6) }),
|
|
1117
|
+
copyable && /* @__PURE__ */ jsx3(
|
|
1118
|
+
"button",
|
|
1119
|
+
{
|
|
1120
|
+
type: "button",
|
|
1121
|
+
onClick: () => copyToClipboard(value),
|
|
1122
|
+
style: styles2.copyButton,
|
|
1123
|
+
title: "Copy to clipboard",
|
|
1124
|
+
"aria-label": `Copy ${label}`,
|
|
1125
|
+
children: /* @__PURE__ */ jsx3(CopyIcon, {})
|
|
1126
|
+
}
|
|
1127
|
+
)
|
|
1128
|
+
] })
|
|
1129
|
+
] });
|
|
1130
|
+
}
|
|
1131
|
+
function NodeInfoPanel(props) {
|
|
1132
|
+
const {
|
|
1133
|
+
node,
|
|
1134
|
+
network,
|
|
1135
|
+
pollInterval = 15e3,
|
|
1136
|
+
showQrCode = false,
|
|
1137
|
+
renderQrCode,
|
|
1138
|
+
className,
|
|
1139
|
+
style
|
|
1140
|
+
} = props;
|
|
1141
|
+
const [stats, setStats] = useState5(null);
|
|
1142
|
+
const [statsError, setStatsError] = useState5(null);
|
|
1143
|
+
const [statsLoading, setStatsLoading] = useState5(false);
|
|
1144
|
+
const cancelledRef = useRef4(false);
|
|
1145
|
+
const [QRComponent, setQRComponent] = useState5(null);
|
|
1146
|
+
useEffect5(() => {
|
|
1147
|
+
let cancelled = false;
|
|
1148
|
+
if (!showQrCode || renderQrCode) return;
|
|
1149
|
+
import("qrcode.react").then((mod) => {
|
|
1150
|
+
if (cancelled) return;
|
|
1151
|
+
const Comp = mod.QRCodeSVG ?? mod.default;
|
|
1152
|
+
if (Comp) setQRComponent(() => Comp);
|
|
1153
|
+
}).catch(() => {
|
|
1154
|
+
});
|
|
1155
|
+
return () => {
|
|
1156
|
+
cancelled = true;
|
|
1157
|
+
};
|
|
1158
|
+
}, [showQrCode, renderQrCode]);
|
|
1159
|
+
const loadingRef = useRef4(false);
|
|
1160
|
+
const loadStats = useCallback4(async () => {
|
|
1161
|
+
if (!node || node.state !== "running" || loadingRef.current) return;
|
|
1162
|
+
loadingRef.current = true;
|
|
1163
|
+
setStatsLoading(true);
|
|
1164
|
+
setStatsError(null);
|
|
1165
|
+
try {
|
|
1166
|
+
const data = await fetchNodeStats(node, network);
|
|
1167
|
+
if (!cancelledRef.current) setStats(data);
|
|
1168
|
+
} catch (e) {
|
|
1169
|
+
if (!cancelledRef.current) {
|
|
1170
|
+
setStatsError(e instanceof Error ? e.message : String(e));
|
|
1171
|
+
}
|
|
1172
|
+
} finally {
|
|
1173
|
+
loadingRef.current = false;
|
|
1174
|
+
if (!cancelledRef.current) setStatsLoading(false);
|
|
1175
|
+
}
|
|
1176
|
+
}, [node, network]);
|
|
1177
|
+
useEffect5(() => {
|
|
1178
|
+
cancelledRef.current = false;
|
|
1179
|
+
if (!node || node.state !== "running") {
|
|
1180
|
+
setStats(null);
|
|
1181
|
+
setStatsError(null);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
void loadStats();
|
|
1185
|
+
const interval = setInterval(() => void loadStats(), pollInterval);
|
|
1186
|
+
return () => {
|
|
1187
|
+
cancelledRef.current = true;
|
|
1188
|
+
clearInterval(interval);
|
|
1189
|
+
};
|
|
1190
|
+
}, [node, node?.state, pollInterval, loadStats]);
|
|
1191
|
+
if (!node) {
|
|
1192
|
+
return /* @__PURE__ */ jsx3("div", { className, style: { ...styles2.root, ...style }, "data-fpay-node-info": "", children: /* @__PURE__ */ jsx3("div", { style: styles2.idle, children: "No node connected" }) });
|
|
1193
|
+
}
|
|
1194
|
+
const nodeState = node.state;
|
|
1195
|
+
const statusColor = STATUS_COLORS[nodeState] ?? STATUS_COLORS.idle;
|
|
1196
|
+
return /* @__PURE__ */ jsxs3("div", { className, style: { ...styles2.root, ...style }, "data-fpay-node-info": "", children: [
|
|
1197
|
+
/* @__PURE__ */ jsxs3(
|
|
1198
|
+
"div",
|
|
1199
|
+
{
|
|
1200
|
+
style: {
|
|
1201
|
+
...styles2.statusBadge,
|
|
1202
|
+
backgroundColor: statusColor.bg,
|
|
1203
|
+
color: statusColor.fg
|
|
1204
|
+
},
|
|
1205
|
+
children: [
|
|
1206
|
+
/* @__PURE__ */ jsx3(
|
|
1207
|
+
"span",
|
|
1208
|
+
{
|
|
1209
|
+
style: {
|
|
1210
|
+
display: "inline-block",
|
|
1211
|
+
width: "0.5rem",
|
|
1212
|
+
height: "0.5rem",
|
|
1213
|
+
borderRadius: "50%",
|
|
1214
|
+
backgroundColor: statusColor.dot
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
),
|
|
1218
|
+
nodeState
|
|
1219
|
+
]
|
|
1220
|
+
}
|
|
1221
|
+
),
|
|
1222
|
+
statsLoading && !stats && /* @__PURE__ */ jsxs3("div", { style: styles2.loading, children: [
|
|
1223
|
+
/* @__PURE__ */ jsxs3(
|
|
1224
|
+
"svg",
|
|
1225
|
+
{
|
|
1226
|
+
width: "12",
|
|
1227
|
+
height: "12",
|
|
1228
|
+
viewBox: "0 0 24 24",
|
|
1229
|
+
fill: "none",
|
|
1230
|
+
stroke: "currentColor",
|
|
1231
|
+
strokeWidth: "2",
|
|
1232
|
+
strokeLinecap: "round",
|
|
1233
|
+
strokeLinejoin: "round",
|
|
1234
|
+
role: "img",
|
|
1235
|
+
"aria-label": "Loading",
|
|
1236
|
+
children: [
|
|
1237
|
+
/* @__PURE__ */ jsx3("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }),
|
|
1238
|
+
/* @__PURE__ */ jsx3(
|
|
1239
|
+
"animateTransform",
|
|
1240
|
+
{
|
|
1241
|
+
attributeName: "transform",
|
|
1242
|
+
type: "rotate",
|
|
1243
|
+
from: "0 12 12",
|
|
1244
|
+
to: "360 12 12",
|
|
1245
|
+
dur: "1s",
|
|
1246
|
+
repeatCount: "indefinite"
|
|
1247
|
+
}
|
|
1248
|
+
)
|
|
1249
|
+
]
|
|
1250
|
+
}
|
|
1251
|
+
),
|
|
1252
|
+
"Loading\u2026"
|
|
1253
|
+
] }),
|
|
1254
|
+
statsError && /* @__PURE__ */ jsx3("div", { style: styles2.errorBox, children: statsError }),
|
|
1255
|
+
stats && /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1256
|
+
/* @__PURE__ */ jsx3(InfoRow, { label: "Pubkey", value: stats.pubkey, copyable: true }),
|
|
1257
|
+
stats.externalFunding ? /* @__PURE__ */ jsx3("div", { style: { padding: "0.25rem 0", fontSize: "0.75rem", color: "#6b7280" }, children: "External funding mode" }) : stats.ckbAddress ? /* @__PURE__ */ jsx3(InfoRow, { label: "CKB Address", value: stats.ckbAddress, copyable: true }) : null,
|
|
1258
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.statsGrid, children: [
|
|
1259
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.statCard, children: [
|
|
1260
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.statLabel, children: "Peers" }),
|
|
1261
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.statValue, children: stats.peers })
|
|
1262
|
+
] }),
|
|
1263
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.statCard, children: [
|
|
1264
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.statLabel, children: "Channels" }),
|
|
1265
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.statValue, children: stats.channels })
|
|
1266
|
+
] })
|
|
1267
|
+
] }),
|
|
1268
|
+
showQrCode && stats.ckbAddress && /* @__PURE__ */ jsxs3("div", { style: styles2.qrContainer, children: [
|
|
1269
|
+
renderQrCode ? renderQrCode(stats.ckbAddress) : QRComponent ? /* @__PURE__ */ jsx3(
|
|
1270
|
+
QRComponent,
|
|
1271
|
+
{
|
|
1272
|
+
value: stats.ckbAddress,
|
|
1273
|
+
size: 120,
|
|
1274
|
+
bgColor: "transparent",
|
|
1275
|
+
fgColor: "currentColor"
|
|
1276
|
+
}
|
|
1277
|
+
) : /* @__PURE__ */ jsx3("div", { style: { fontSize: "0.625rem", color: "#9ca3af" }, children: "Install qrcode.react for QR code" }),
|
|
1278
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.qrCaption, children: "Scan to deposit CKB" }),
|
|
1279
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.balanceRow, children: [
|
|
1280
|
+
/* @__PURE__ */ jsx3("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: "Balance" }),
|
|
1281
|
+
/* @__PURE__ */ jsxs3(
|
|
1282
|
+
"span",
|
|
1283
|
+
{
|
|
1284
|
+
style: {
|
|
1285
|
+
fontFamily: "ui-monospace, monospace",
|
|
1286
|
+
fontSize: "0.75rem",
|
|
1287
|
+
fontWeight: 500
|
|
1288
|
+
},
|
|
1289
|
+
children: [
|
|
1290
|
+
stats.balanceCkb ?? "\u2014",
|
|
1291
|
+
" CKB"
|
|
1292
|
+
]
|
|
1293
|
+
}
|
|
1294
|
+
)
|
|
1295
|
+
] })
|
|
1296
|
+
] })
|
|
1297
|
+
] })
|
|
1298
|
+
] });
|
|
1299
|
+
}
|
|
483
1300
|
export {
|
|
1301
|
+
ChannelState,
|
|
1302
|
+
ConfigBuilder2 as ConfigBuilder,
|
|
1303
|
+
ConnectButton,
|
|
1304
|
+
FiberBrowserNode2 as FiberBrowserNode,
|
|
484
1305
|
FiberPayQuickCard,
|
|
1306
|
+
FiberRpcError,
|
|
1307
|
+
NodeInfoPanel,
|
|
1308
|
+
PasskeyCredentialProvider2 as PasskeyCredentialProvider,
|
|
1309
|
+
PasswordCredentialProvider2 as PasswordCredentialProvider,
|
|
1310
|
+
RawKeyCredentialProvider2 as RawKeyCredentialProvider,
|
|
1311
|
+
ckbHash,
|
|
1312
|
+
ckbToShannons,
|
|
1313
|
+
derivePublicKey,
|
|
1314
|
+
formatShannonsAsCkb2 as formatShannonsAsCkb,
|
|
1315
|
+
fromHex,
|
|
1316
|
+
getLockBalanceShannons2 as getLockBalanceShannons,
|
|
1317
|
+
scriptToAddress2 as scriptToAddress,
|
|
1318
|
+
shannonsToCkb,
|
|
1319
|
+
toHex,
|
|
485
1320
|
useFiberNode,
|
|
486
1321
|
useFiberPayment
|
|
487
1322
|
};
|