@fiber-pay/react 0.2.4 → 0.2.6
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 +236 -2
- package/dist/index.d.ts +225 -9
- package/dist/index.js +2602 -281
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
-
ChannelState,
|
|
3
|
+
ChannelState as ChannelState3,
|
|
4
4
|
ConfigBuilder as ConfigBuilder2,
|
|
5
5
|
ckbHash,
|
|
6
|
-
ckbToShannons,
|
|
6
|
+
ckbToShannons as ckbToShannons2,
|
|
7
7
|
derivePublicKey,
|
|
8
8
|
FiberBrowserNode as FiberBrowserNode2,
|
|
9
9
|
FiberRpcError,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
PasswordCredentialProvider as PasswordCredentialProvider2,
|
|
15
15
|
RawKeyCredentialProvider as RawKeyCredentialProvider2,
|
|
16
16
|
scriptToAddress as scriptToAddress2,
|
|
17
|
-
shannonsToCkb,
|
|
17
|
+
shannonsToCkb as shannonsToCkb2,
|
|
18
18
|
toHex
|
|
19
19
|
} from "@fiber-pay/sdk/browser";
|
|
20
20
|
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
useCallback as useCallback2,
|
|
24
24
|
useEffect as useEffect2,
|
|
25
|
+
useId,
|
|
25
26
|
useRef as useRef2,
|
|
26
27
|
useState as useState2
|
|
27
28
|
} from "react";
|
|
@@ -58,6 +59,7 @@ function asErrorMessage(error) {
|
|
|
58
59
|
}
|
|
59
60
|
function useFiberNode(options) {
|
|
60
61
|
const walletId = options.walletId ?? `wallet-${options.network}`;
|
|
62
|
+
const externalWallet = options.externalWallet ?? false;
|
|
61
63
|
const [state, setState] = useState("idle");
|
|
62
64
|
const [nodeInfo, setNodeInfo] = useState(null);
|
|
63
65
|
const [error, setError] = useState(null);
|
|
@@ -78,8 +80,9 @@ function useFiberNode(options) {
|
|
|
78
80
|
node.off("error", nodeListenersRef.current.error);
|
|
79
81
|
nodeListenersRef.current = null;
|
|
80
82
|
}, []);
|
|
81
|
-
useEffect(
|
|
82
|
-
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
isMountedRef.current = true;
|
|
85
|
+
return () => {
|
|
83
86
|
isMountedRef.current = false;
|
|
84
87
|
const node = nodeRef.current;
|
|
85
88
|
nodeRef.current = null;
|
|
@@ -88,9 +91,8 @@ function useFiberNode(options) {
|
|
|
88
91
|
void node.stop().catch(() => {
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
+
};
|
|
95
|
+
}, [detachNodeListeners]);
|
|
94
96
|
useEffect(() => {
|
|
95
97
|
if (options.enabled === false) return;
|
|
96
98
|
let cancelled = false;
|
|
@@ -111,12 +113,12 @@ function useFiberNode(options) {
|
|
|
111
113
|
console.warn("[fiber-pay/react] Failed to detect passkey support:", supportError.message);
|
|
112
114
|
}
|
|
113
115
|
});
|
|
114
|
-
const provider = new PasskeyCredentialProvider(walletId);
|
|
116
|
+
const provider = new PasskeyCredentialProvider(walletId, { skipCkbKey: externalWallet });
|
|
115
117
|
setHasPasskeyConfigured(provider.isConfigured());
|
|
116
118
|
return () => {
|
|
117
119
|
cancelled = true;
|
|
118
120
|
};
|
|
119
|
-
}, [walletId, options.enabled]);
|
|
121
|
+
}, [walletId, options.enabled, externalWallet]);
|
|
120
122
|
const initNode = useCallback(
|
|
121
123
|
(credential) => {
|
|
122
124
|
if (nodeRef.current) {
|
|
@@ -188,7 +190,9 @@ function useFiberNode(options) {
|
|
|
188
190
|
setError(null);
|
|
189
191
|
let node = null;
|
|
190
192
|
try {
|
|
191
|
-
const credential = new PasswordCredentialProvider(walletId
|
|
193
|
+
const credential = new PasswordCredentialProvider(walletId, {
|
|
194
|
+
skipCkbKey: externalWallet
|
|
195
|
+
});
|
|
192
196
|
node = initNode(credential);
|
|
193
197
|
const info = await node.start({ unlockParams: { password } });
|
|
194
198
|
if (isMountedRef.current) {
|
|
@@ -201,14 +205,16 @@ function useFiberNode(options) {
|
|
|
201
205
|
await cleanupFailedStart(node);
|
|
202
206
|
}
|
|
203
207
|
},
|
|
204
|
-
[cleanupFailedStart, initNode, walletId]
|
|
208
|
+
[cleanupFailedStart, initNode, walletId, externalWallet]
|
|
205
209
|
);
|
|
206
210
|
const createPasskeyAndStart = useCallback(
|
|
207
211
|
async (username = "User") => {
|
|
208
212
|
setError(null);
|
|
209
213
|
let node = null;
|
|
210
214
|
try {
|
|
211
|
-
const credential = new PasskeyCredentialProvider(walletId
|
|
215
|
+
const credential = new PasskeyCredentialProvider(walletId, {
|
|
216
|
+
skipCkbKey: externalWallet
|
|
217
|
+
});
|
|
212
218
|
await credential.register(username);
|
|
213
219
|
if (isMountedRef.current) {
|
|
214
220
|
setHasPasskeyConfigured(true);
|
|
@@ -225,13 +231,15 @@ function useFiberNode(options) {
|
|
|
225
231
|
await cleanupFailedStart(node);
|
|
226
232
|
}
|
|
227
233
|
},
|
|
228
|
-
[cleanupFailedStart, initNode, walletId]
|
|
234
|
+
[cleanupFailedStart, initNode, walletId, externalWallet]
|
|
229
235
|
);
|
|
230
236
|
const startWithPasskey = useCallback(async () => {
|
|
231
237
|
setError(null);
|
|
232
238
|
let node = null;
|
|
233
239
|
try {
|
|
234
|
-
const credential = new PasskeyCredentialProvider(walletId
|
|
240
|
+
const credential = new PasskeyCredentialProvider(walletId, {
|
|
241
|
+
skipCkbKey: externalWallet
|
|
242
|
+
});
|
|
235
243
|
node = initNode(credential);
|
|
236
244
|
const info = await node.start();
|
|
237
245
|
if (isMountedRef.current) {
|
|
@@ -243,13 +251,17 @@ function useFiberNode(options) {
|
|
|
243
251
|
}
|
|
244
252
|
await cleanupFailedStart(node);
|
|
245
253
|
}
|
|
246
|
-
}, [cleanupFailedStart, initNode, walletId]);
|
|
254
|
+
}, [cleanupFailedStart, initNode, walletId, externalWallet]);
|
|
247
255
|
const startWithRawKey = useCallback(
|
|
248
256
|
async (fiberKey, ckbSecretKey) => {
|
|
249
257
|
setError(null);
|
|
250
258
|
let node = null;
|
|
251
259
|
try {
|
|
252
|
-
const credential = new RawKeyCredentialProvider(
|
|
260
|
+
const credential = new RawKeyCredentialProvider(
|
|
261
|
+
fiberKey,
|
|
262
|
+
externalWallet ? void 0 : ckbSecretKey,
|
|
263
|
+
walletId
|
|
264
|
+
);
|
|
253
265
|
node = initNode(credential);
|
|
254
266
|
const info = await node.start();
|
|
255
267
|
if (isMountedRef.current) {
|
|
@@ -262,7 +274,7 @@ function useFiberNode(options) {
|
|
|
262
274
|
await cleanupFailedStart(node);
|
|
263
275
|
}
|
|
264
276
|
},
|
|
265
|
-
[cleanupFailedStart, initNode, walletId]
|
|
277
|
+
[cleanupFailedStart, initNode, walletId, externalWallet]
|
|
266
278
|
);
|
|
267
279
|
const stop = useCallback(async () => {
|
|
268
280
|
const node = nodeRef.current;
|
|
@@ -461,10 +473,9 @@ function ConnectButton(props) {
|
|
|
461
473
|
const {
|
|
462
474
|
network = "testnet",
|
|
463
475
|
fiber: externalFiber,
|
|
464
|
-
strategy = "
|
|
476
|
+
strategy = "passkey",
|
|
477
|
+
externalWallet = false,
|
|
465
478
|
password,
|
|
466
|
-
rawKey,
|
|
467
|
-
rawCkbKey,
|
|
468
479
|
walletId,
|
|
469
480
|
passkeyUsername = "User",
|
|
470
481
|
wasmFactory,
|
|
@@ -482,6 +493,7 @@ function ConnectButton(props) {
|
|
|
482
493
|
walletId,
|
|
483
494
|
wasmFactory,
|
|
484
495
|
nodeConfig,
|
|
496
|
+
externalWallet,
|
|
485
497
|
enabled: !externalFiber
|
|
486
498
|
});
|
|
487
499
|
const fiber = externalFiber ?? internalFiber;
|
|
@@ -498,13 +510,14 @@ function ConnectButton(props) {
|
|
|
498
510
|
createPasskeyAndStart,
|
|
499
511
|
startWithPasskey,
|
|
500
512
|
startWithPassword,
|
|
501
|
-
startWithRawKey,
|
|
502
513
|
stop
|
|
503
514
|
} = fiber;
|
|
504
515
|
const [isConnecting, setIsConnecting] = useState2(false);
|
|
505
516
|
const [showDropdown, setShowDropdown] = useState2(false);
|
|
506
517
|
const [localError, setLocalError] = useState2(null);
|
|
507
518
|
const dropdownRef = useRef2(null);
|
|
519
|
+
const dropdownId = useId();
|
|
520
|
+
const lastReportedErrorRef = useRef2(null);
|
|
508
521
|
const effectiveIsStarting = isConnecting || isStarting;
|
|
509
522
|
useEffect2(() => ensureKeyframes(), []);
|
|
510
523
|
useEffect2(() => {
|
|
@@ -527,23 +540,27 @@ function ConnectButton(props) {
|
|
|
527
540
|
}
|
|
528
541
|
prevRunningRef.current = isRunning;
|
|
529
542
|
}, [isRunning, node, nodeInfo, onConnect, onDisconnect]);
|
|
543
|
+
const effectiveError = error ?? localError;
|
|
530
544
|
useEffect2(() => {
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
545
|
+
if (!effectiveError) {
|
|
546
|
+
lastReportedErrorRef.current = null;
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (lastReportedErrorRef.current === effectiveError) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
lastReportedErrorRef.current = effectiveError;
|
|
553
|
+
onError?.(effectiveError);
|
|
554
|
+
}, [effectiveError, onError]);
|
|
534
555
|
const handleConnect = useCallback2(async () => {
|
|
535
556
|
setIsConnecting(true);
|
|
536
557
|
setLocalError(null);
|
|
537
558
|
try {
|
|
538
|
-
switch (
|
|
559
|
+
switch (strategy) {
|
|
539
560
|
case "password":
|
|
540
561
|
if (!password) throw new Error('Password is required for "password" strategy');
|
|
541
562
|
await startWithPassword(password);
|
|
542
563
|
break;
|
|
543
|
-
case "rawKey":
|
|
544
|
-
if (!rawKey) throw new Error('rawKey is required for "rawKey" strategy');
|
|
545
|
-
await startWithRawKey(rawKey, rawCkbKey);
|
|
546
|
-
break;
|
|
547
564
|
case "passkey":
|
|
548
565
|
if (hasPasskeyConfigured) {
|
|
549
566
|
await startWithPasskey();
|
|
@@ -555,22 +572,17 @@ function ConnectButton(props) {
|
|
|
555
572
|
} catch (err) {
|
|
556
573
|
const msg = err instanceof Error ? err.message : String(err);
|
|
557
574
|
setLocalError(msg);
|
|
558
|
-
onError?.(msg);
|
|
559
575
|
} finally {
|
|
560
576
|
setIsConnecting(false);
|
|
561
577
|
}
|
|
562
578
|
}, [
|
|
563
|
-
|
|
579
|
+
strategy,
|
|
564
580
|
password,
|
|
565
|
-
rawKey,
|
|
566
|
-
rawCkbKey,
|
|
567
581
|
passkeyUsername,
|
|
568
582
|
hasPasskeyConfigured,
|
|
569
583
|
startWithPassword,
|
|
570
584
|
startWithPasskey,
|
|
571
|
-
|
|
572
|
-
createPasskeyAndStart,
|
|
573
|
-
onError
|
|
585
|
+
createPasskeyAndStart
|
|
574
586
|
]);
|
|
575
587
|
const handleDisconnect = useCallback2(async () => {
|
|
576
588
|
try {
|
|
@@ -578,15 +590,14 @@ function ConnectButton(props) {
|
|
|
578
590
|
} catch (err) {
|
|
579
591
|
const msg = err instanceof Error ? err.message : String(err);
|
|
580
592
|
setLocalError(msg);
|
|
581
|
-
onError?.(msg);
|
|
582
593
|
} finally {
|
|
583
594
|
setShowDropdown(false);
|
|
584
595
|
}
|
|
585
|
-
}, [stop
|
|
596
|
+
}, [stop]);
|
|
586
597
|
const closeDropdown = useCallback2(() => {
|
|
587
598
|
setShowDropdown(false);
|
|
588
599
|
}, []);
|
|
589
|
-
const hasError = !!
|
|
600
|
+
const hasError = !!effectiveError;
|
|
590
601
|
let buttonLabel;
|
|
591
602
|
let buttonOnClick;
|
|
592
603
|
let buttonDisabled = false;
|
|
@@ -607,7 +618,7 @@ function ConnectButton(props) {
|
|
|
607
618
|
buttonDisabled = true;
|
|
608
619
|
buttonStyle = { ...styles.button, ...styles.connectButton, ...styles.disabledButton };
|
|
609
620
|
} else {
|
|
610
|
-
switch (
|
|
621
|
+
switch (strategy) {
|
|
611
622
|
case "passkey":
|
|
612
623
|
buttonLabel = hasPasskeyConfigured ? "Connect with Passkey" : "Connect via Passkey";
|
|
613
624
|
if (!isPasskeySupported) {
|
|
@@ -618,9 +629,6 @@ function ConnectButton(props) {
|
|
|
618
629
|
case "password":
|
|
619
630
|
buttonLabel = "Connect";
|
|
620
631
|
break;
|
|
621
|
-
case "rawKey":
|
|
622
|
-
buttonLabel = "Connect";
|
|
623
|
-
break;
|
|
624
632
|
}
|
|
625
633
|
buttonOnClick = handleConnect;
|
|
626
634
|
buttonStyle = {
|
|
@@ -630,177 +638,2485 @@ function ConnectButton(props) {
|
|
|
630
638
|
};
|
|
631
639
|
}
|
|
632
640
|
return /* @__PURE__ */ jsxs("div", { className, style: { ...styles.root, ...style }, "data-fpay-connect-button": "", children: [
|
|
633
|
-
(hasError || !isPasskeySupported && passkeyUnavailableReason &&
|
|
641
|
+
(hasError || !isPasskeySupported && passkeyUnavailableReason && strategy === "passkey") && /* @__PURE__ */ jsx("span", { style: styles.errorText, children: error || localError || passkeyUnavailableReason }),
|
|
634
642
|
isRunning ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: dropdownRef, children: [
|
|
635
|
-
/* @__PURE__ */ jsx(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
643
|
+
/* @__PURE__ */ jsx(
|
|
644
|
+
"button",
|
|
645
|
+
{
|
|
646
|
+
type: "button",
|
|
647
|
+
onClick: buttonOnClick,
|
|
648
|
+
style: buttonStyle,
|
|
649
|
+
"aria-haspopup": "dialog",
|
|
650
|
+
"aria-expanded": showDropdown,
|
|
651
|
+
"aria-controls": showDropdown ? dropdownId : void 0,
|
|
652
|
+
children: buttonLabel
|
|
653
|
+
}
|
|
654
|
+
),
|
|
655
|
+
showDropdown && /* @__PURE__ */ jsx(
|
|
656
|
+
"div",
|
|
657
|
+
{
|
|
658
|
+
id: dropdownId,
|
|
659
|
+
role: "dialog",
|
|
660
|
+
"aria-label": "Connection panel",
|
|
661
|
+
style: { ...styles.dropdown, ...dropdownStyle },
|
|
662
|
+
children: renderConnectedDropdown ? renderConnectedDropdown({
|
|
663
|
+
fiber,
|
|
664
|
+
closeDropdown,
|
|
665
|
+
disconnect: handleDisconnect
|
|
666
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
667
|
+
nodeInfo && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
668
|
+
/* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
|
|
669
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "Pubkey" }),
|
|
670
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoValue, children: truncateNodeId(nodeInfo.pubkey) })
|
|
671
|
+
] }),
|
|
672
|
+
/* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
|
|
673
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "State" }),
|
|
674
|
+
/* @__PURE__ */ jsx("span", { style: styles.infoValue, children: state })
|
|
675
|
+
] })
|
|
676
|
+
] }),
|
|
677
|
+
/* @__PURE__ */ jsx("div", { style: styles.separator }),
|
|
678
|
+
/* @__PURE__ */ jsxs(
|
|
679
|
+
"button",
|
|
680
|
+
{
|
|
681
|
+
type: "button",
|
|
682
|
+
onClick: () => void handleDisconnect(),
|
|
683
|
+
style: styles.disconnectButton,
|
|
684
|
+
children: [
|
|
685
|
+
/* @__PURE__ */ jsx("span", { children: "Disconnect" }),
|
|
686
|
+
/* @__PURE__ */ jsx(
|
|
687
|
+
"svg",
|
|
688
|
+
{
|
|
689
|
+
width: "14",
|
|
690
|
+
height: "14",
|
|
691
|
+
viewBox: "0 0 24 24",
|
|
692
|
+
fill: "none",
|
|
693
|
+
stroke: "currentColor",
|
|
694
|
+
strokeWidth: "2",
|
|
695
|
+
strokeLinecap: "round",
|
|
696
|
+
strokeLinejoin: "round",
|
|
697
|
+
"aria-hidden": "true",
|
|
698
|
+
children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
|
|
699
|
+
}
|
|
700
|
+
)
|
|
701
|
+
]
|
|
702
|
+
}
|
|
703
|
+
)
|
|
649
704
|
] })
|
|
650
|
-
|
|
651
|
-
|
|
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
|
-
] }) })
|
|
705
|
+
}
|
|
706
|
+
)
|
|
679
707
|
] }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, disabled: buttonDisabled, style: buttonStyle, children: buttonLabel })
|
|
680
708
|
] });
|
|
681
709
|
}
|
|
682
710
|
|
|
683
|
-
// src/fiber-
|
|
684
|
-
import {
|
|
711
|
+
// src/fiber-node-button/index.tsx
|
|
712
|
+
import { useCallback as useCallback6 } from "react";
|
|
685
713
|
|
|
686
|
-
// src/
|
|
687
|
-
import {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
714
|
+
// src/fiber-node-button/panel.tsx
|
|
715
|
+
import { useEffect as useEffect5, useMemo as useMemo4, useRef as useRef5 } from "react";
|
|
716
|
+
|
|
717
|
+
// src/fiber-node-button/styles.ts
|
|
718
|
+
var styles2 = {
|
|
719
|
+
shell: {
|
|
720
|
+
position: "relative",
|
|
721
|
+
display: "grid",
|
|
722
|
+
gridTemplateRows: "auto auto minmax(0, 1fr)",
|
|
723
|
+
gap: "0.7rem",
|
|
724
|
+
minWidth: "280px",
|
|
725
|
+
width: "min(460px, calc(100vw - 1rem))",
|
|
726
|
+
maxHeight: "72vh"
|
|
727
|
+
},
|
|
728
|
+
globalBar: {
|
|
729
|
+
border: "1px solid var(--fpay-border, #d8dee8)",
|
|
730
|
+
borderRadius: "0.72rem",
|
|
731
|
+
background: "linear-gradient(180deg, #ffffff 0%, #f8fafc 100%)",
|
|
732
|
+
padding: "0.52rem 0.56rem",
|
|
733
|
+
display: "grid",
|
|
734
|
+
gap: "0.45rem"
|
|
735
|
+
},
|
|
736
|
+
globalRow: {
|
|
737
|
+
display: "flex",
|
|
738
|
+
alignItems: "center",
|
|
739
|
+
justifyContent: "space-between",
|
|
740
|
+
gap: "0.35rem",
|
|
741
|
+
flexWrap: "wrap"
|
|
742
|
+
},
|
|
743
|
+
globalMetrics: {
|
|
744
|
+
display: "flex",
|
|
745
|
+
alignItems: "center",
|
|
746
|
+
gap: "0.5rem",
|
|
747
|
+
flexWrap: "wrap",
|
|
748
|
+
minWidth: 0
|
|
749
|
+
},
|
|
750
|
+
metricInline: {
|
|
751
|
+
display: "inline-flex",
|
|
752
|
+
alignItems: "center",
|
|
753
|
+
gap: "0.24rem",
|
|
754
|
+
whiteSpace: "nowrap"
|
|
755
|
+
},
|
|
756
|
+
metricDot: {
|
|
757
|
+
display: "inline-flex",
|
|
758
|
+
width: "0.34rem",
|
|
759
|
+
height: "0.34rem",
|
|
760
|
+
borderRadius: "999px",
|
|
761
|
+
flexShrink: 0,
|
|
762
|
+
background: "#94a3b8"
|
|
763
|
+
},
|
|
764
|
+
metricMain: {
|
|
765
|
+
fontSize: "0.82rem",
|
|
766
|
+
fontWeight: 750,
|
|
767
|
+
color: "var(--fpay-text-primary, #0f172a)",
|
|
768
|
+
lineHeight: 1.1
|
|
769
|
+
},
|
|
770
|
+
metricSub: {
|
|
771
|
+
fontSize: "0.72rem",
|
|
772
|
+
color: "var(--fpay-text-secondary, #64748b)",
|
|
773
|
+
lineHeight: 1.1
|
|
774
|
+
},
|
|
775
|
+
metricDivider: {
|
|
776
|
+
fontSize: "0.7rem",
|
|
777
|
+
color: "#94a3b8",
|
|
778
|
+
lineHeight: 1
|
|
779
|
+
},
|
|
780
|
+
globalMeta: {
|
|
781
|
+
display: "flex",
|
|
782
|
+
alignItems: "center",
|
|
783
|
+
justifyContent: "space-between",
|
|
784
|
+
gap: "0.5rem",
|
|
785
|
+
flexWrap: "wrap"
|
|
786
|
+
},
|
|
787
|
+
globalErrorInline: {
|
|
788
|
+
margin: 0,
|
|
789
|
+
fontSize: "0.71rem",
|
|
790
|
+
color: "#9f1239",
|
|
791
|
+
lineHeight: 1.25
|
|
792
|
+
},
|
|
793
|
+
statusDot: {
|
|
794
|
+
display: "inline-flex",
|
|
795
|
+
width: "0.45rem",
|
|
796
|
+
height: "0.45rem",
|
|
797
|
+
borderRadius: "999px",
|
|
798
|
+
flexShrink: 0
|
|
799
|
+
},
|
|
800
|
+
globalActions: {
|
|
801
|
+
display: "flex",
|
|
802
|
+
gap: "0.36rem",
|
|
803
|
+
justifyContent: "flex-end",
|
|
804
|
+
flexWrap: "wrap"
|
|
805
|
+
},
|
|
806
|
+
globalActionButton: {
|
|
807
|
+
border: "1px solid var(--fpay-border, #cbd5e1)",
|
|
808
|
+
borderRadius: "0.45rem",
|
|
809
|
+
padding: "0.28rem 0.48rem",
|
|
810
|
+
fontSize: "0.72rem",
|
|
811
|
+
fontWeight: 650,
|
|
812
|
+
background: "#fff",
|
|
813
|
+
color: "var(--fpay-text-primary, #111827)",
|
|
814
|
+
cursor: "pointer"
|
|
815
|
+
},
|
|
816
|
+
tabList: {
|
|
817
|
+
borderRadius: "0.68rem",
|
|
818
|
+
border: "none",
|
|
819
|
+
background: "#e9eef6",
|
|
820
|
+
padding: "0.14rem",
|
|
821
|
+
display: "grid",
|
|
822
|
+
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
|
|
823
|
+
gap: "0.14rem",
|
|
824
|
+
boxShadow: "inset 0 0 0 1px var(--fpay-border, #d8dee8)"
|
|
825
|
+
},
|
|
826
|
+
tabButton: {
|
|
827
|
+
border: "none",
|
|
828
|
+
borderRadius: "0.5rem",
|
|
829
|
+
background: "transparent",
|
|
830
|
+
color: "#334155",
|
|
831
|
+
fontSize: "0.74rem",
|
|
832
|
+
fontWeight: 700,
|
|
833
|
+
padding: "0.44rem 0.45rem",
|
|
834
|
+
cursor: "pointer"
|
|
835
|
+
},
|
|
836
|
+
tabButtonActive: {
|
|
837
|
+
border: "none",
|
|
838
|
+
borderRadius: "0.5rem",
|
|
839
|
+
background: "var(--fpay-accent, #1d4ed8)",
|
|
840
|
+
color: "#fff",
|
|
841
|
+
fontSize: "0.74rem",
|
|
842
|
+
fontWeight: 700,
|
|
843
|
+
padding: "0.44rem 0.45rem",
|
|
844
|
+
cursor: "pointer",
|
|
845
|
+
boxShadow: "none"
|
|
846
|
+
},
|
|
847
|
+
content: {
|
|
848
|
+
overflowY: "auto",
|
|
849
|
+
paddingRight: "0.15rem",
|
|
850
|
+
display: "grid",
|
|
851
|
+
gap: "0.7rem",
|
|
852
|
+
minHeight: 0
|
|
853
|
+
},
|
|
854
|
+
section: {
|
|
855
|
+
border: "none",
|
|
856
|
+
borderBottom: "1px solid var(--fpay-border, #e2e8f0)",
|
|
857
|
+
borderRadius: 0,
|
|
858
|
+
padding: "0.15rem 0 0.55rem",
|
|
859
|
+
background: "transparent",
|
|
860
|
+
display: "grid",
|
|
861
|
+
gap: "0.44rem"
|
|
862
|
+
},
|
|
863
|
+
sectionTitle: {
|
|
864
|
+
margin: 0,
|
|
865
|
+
fontSize: "0.8rem",
|
|
866
|
+
fontWeight: 750,
|
|
867
|
+
color: "var(--fpay-text-primary, #0f172a)",
|
|
868
|
+
letterSpacing: "0.01em"
|
|
869
|
+
},
|
|
870
|
+
row: {
|
|
871
|
+
display: "flex",
|
|
872
|
+
alignItems: "center",
|
|
873
|
+
gap: "0.45rem",
|
|
874
|
+
flexWrap: "wrap"
|
|
875
|
+
},
|
|
876
|
+
rowBetween: {
|
|
877
|
+
display: "flex",
|
|
878
|
+
alignItems: "center",
|
|
879
|
+
justifyContent: "space-between",
|
|
880
|
+
gap: "0.45rem",
|
|
881
|
+
flexWrap: "wrap"
|
|
882
|
+
},
|
|
883
|
+
compactText: {
|
|
884
|
+
margin: 0,
|
|
885
|
+
fontSize: "0.74rem",
|
|
886
|
+
color: "var(--fpay-text-secondary, #64748b)",
|
|
887
|
+
lineHeight: 1.4
|
|
888
|
+
},
|
|
889
|
+
inlineCode: {
|
|
890
|
+
margin: 0,
|
|
891
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
892
|
+
fontSize: "0.72rem",
|
|
893
|
+
color: "var(--fpay-text-primary, #111827)",
|
|
894
|
+
wordBreak: "break-all",
|
|
895
|
+
lineHeight: 1.4
|
|
896
|
+
},
|
|
897
|
+
fieldLabel: {
|
|
898
|
+
display: "grid",
|
|
899
|
+
gap: "0.25rem",
|
|
900
|
+
fontSize: "0.68rem",
|
|
901
|
+
fontWeight: 700,
|
|
902
|
+
color: "var(--fpay-text-secondary, #64748b)",
|
|
903
|
+
textTransform: "uppercase"
|
|
904
|
+
},
|
|
905
|
+
input: {
|
|
906
|
+
width: "100%",
|
|
907
|
+
border: "1px solid var(--fpay-border, #cbd5e1)",
|
|
908
|
+
borderRadius: "0.45rem",
|
|
909
|
+
padding: "0.38rem 0.48rem",
|
|
910
|
+
fontSize: "0.8rem",
|
|
911
|
+
background: "#fff",
|
|
912
|
+
color: "var(--fpay-text-primary, #0f172a)"
|
|
913
|
+
},
|
|
914
|
+
actionButton: {
|
|
915
|
+
border: "1px solid var(--fpay-border, #cbd5e1)",
|
|
916
|
+
borderRadius: "0.45rem",
|
|
917
|
+
padding: "0.35rem 0.55rem",
|
|
918
|
+
fontSize: "0.74rem",
|
|
919
|
+
fontWeight: 650,
|
|
920
|
+
background: "#fff",
|
|
921
|
+
color: "var(--fpay-text-primary, #111827)",
|
|
922
|
+
cursor: "pointer"
|
|
923
|
+
},
|
|
924
|
+
primaryButton: {
|
|
925
|
+
border: "1px solid var(--fpay-accent, #1d4ed8)",
|
|
926
|
+
borderRadius: "0.45rem",
|
|
927
|
+
padding: "0.35rem 0.55rem",
|
|
928
|
+
fontSize: "0.74rem",
|
|
929
|
+
fontWeight: 750,
|
|
930
|
+
background: "var(--fpay-accent, #1d4ed8)",
|
|
931
|
+
color: "#fff",
|
|
932
|
+
cursor: "pointer"
|
|
933
|
+
},
|
|
934
|
+
ghostButton: {
|
|
935
|
+
border: "1px solid transparent",
|
|
936
|
+
borderRadius: "0.45rem",
|
|
937
|
+
padding: "0.32rem 0.5rem",
|
|
938
|
+
fontSize: "0.74rem",
|
|
939
|
+
fontWeight: 650,
|
|
940
|
+
background: "transparent",
|
|
941
|
+
color: "var(--fpay-text-secondary, #475569)",
|
|
942
|
+
cursor: "pointer"
|
|
943
|
+
},
|
|
944
|
+
dangerButton: {
|
|
945
|
+
border: "1px solid #fecaca",
|
|
946
|
+
borderRadius: "0.45rem",
|
|
947
|
+
padding: "0.35rem 0.55rem",
|
|
948
|
+
fontSize: "0.74rem",
|
|
949
|
+
fontWeight: 750,
|
|
950
|
+
background: "#fff1f2",
|
|
951
|
+
color: "#9f1239",
|
|
952
|
+
cursor: "pointer"
|
|
953
|
+
},
|
|
954
|
+
badge: {
|
|
955
|
+
display: "inline-flex",
|
|
956
|
+
alignItems: "center",
|
|
957
|
+
width: "fit-content",
|
|
958
|
+
borderRadius: "999px",
|
|
959
|
+
border: "1px solid var(--fpay-border, #cbd5e1)",
|
|
960
|
+
background: "#f8fafc",
|
|
961
|
+
color: "var(--fpay-text-secondary, #475569)",
|
|
962
|
+
padding: "0.12rem 0.4rem",
|
|
963
|
+
fontSize: "0.66rem",
|
|
964
|
+
fontWeight: 700,
|
|
965
|
+
lineHeight: 1.1
|
|
966
|
+
},
|
|
967
|
+
notice: {
|
|
968
|
+
border: "1px solid #bfdbfe",
|
|
969
|
+
background: "#eff6ff",
|
|
970
|
+
color: "#1d4ed8",
|
|
971
|
+
borderRadius: "0.52rem",
|
|
972
|
+
padding: "0.46rem 0.52rem",
|
|
973
|
+
fontSize: "0.74rem",
|
|
974
|
+
lineHeight: 1.35
|
|
975
|
+
},
|
|
976
|
+
successNotice: {
|
|
977
|
+
borderColor: "#86efac",
|
|
978
|
+
background: "#f0fdf4",
|
|
979
|
+
color: "#166534"
|
|
980
|
+
},
|
|
981
|
+
errorNotice: {
|
|
982
|
+
border: "1px solid #fecaca",
|
|
983
|
+
background: "#fff1f2",
|
|
984
|
+
color: "#9f1239",
|
|
985
|
+
borderRadius: "0.52rem",
|
|
986
|
+
padding: "0.46rem 0.52rem",
|
|
987
|
+
fontSize: "0.74rem",
|
|
988
|
+
lineHeight: 1.35
|
|
989
|
+
},
|
|
990
|
+
summaryGrid: {
|
|
991
|
+
display: "none"
|
|
992
|
+
},
|
|
993
|
+
summaryTile: {
|
|
994
|
+
display: "none"
|
|
995
|
+
},
|
|
996
|
+
summaryValue: {
|
|
997
|
+
display: "block",
|
|
998
|
+
fontSize: "0.92rem",
|
|
999
|
+
fontWeight: 750,
|
|
1000
|
+
color: "var(--fpay-text-primary, #0f172a)",
|
|
1001
|
+
lineHeight: 1.1
|
|
1002
|
+
},
|
|
1003
|
+
summaryLabel: {
|
|
1004
|
+
display: "block",
|
|
1005
|
+
marginTop: "0.12rem",
|
|
1006
|
+
fontSize: "0.64rem",
|
|
1007
|
+
color: "var(--fpay-text-secondary, #64748b)"
|
|
1008
|
+
},
|
|
1009
|
+
summaryInline: {
|
|
1010
|
+
margin: 0,
|
|
1011
|
+
fontSize: "0.76rem",
|
|
1012
|
+
color: "var(--fpay-text-secondary, #475569)",
|
|
1013
|
+
lineHeight: 1.35
|
|
1014
|
+
},
|
|
1015
|
+
filterBar: {
|
|
1016
|
+
display: "flex",
|
|
1017
|
+
flexWrap: "wrap",
|
|
1018
|
+
gap: "0.28rem"
|
|
1019
|
+
},
|
|
1020
|
+
list: {
|
|
1021
|
+
display: "grid",
|
|
1022
|
+
gap: "0.34rem",
|
|
1023
|
+
maxHeight: "240px",
|
|
1024
|
+
overflowY: "auto",
|
|
1025
|
+
paddingRight: "0.1rem"
|
|
1026
|
+
},
|
|
1027
|
+
compactChannelRow: {
|
|
1028
|
+
border: "1px solid var(--fpay-border, #d8dee8)",
|
|
1029
|
+
borderRadius: "0.5rem",
|
|
1030
|
+
background: "#fff",
|
|
1031
|
+
padding: "0.42rem 0.46rem",
|
|
1032
|
+
cursor: "pointer",
|
|
1033
|
+
display: "grid",
|
|
1034
|
+
gap: "0.22rem",
|
|
1035
|
+
textAlign: "left"
|
|
1036
|
+
},
|
|
1037
|
+
compactChannelRowActive: {
|
|
1038
|
+
borderColor: "var(--fpay-accent, #1d4ed8)",
|
|
1039
|
+
boxShadow: "0 0 0 1px rgba(29, 78, 216, 0.12) inset",
|
|
1040
|
+
background: "#f8fbff"
|
|
1041
|
+
},
|
|
1042
|
+
compactChannelTop: {
|
|
1043
|
+
display: "grid",
|
|
1044
|
+
gridTemplateColumns: "1fr auto",
|
|
1045
|
+
alignItems: "center",
|
|
1046
|
+
gap: "0.35rem"
|
|
1047
|
+
},
|
|
1048
|
+
detailPanel: {
|
|
1049
|
+
border: "1px solid var(--fpay-border, #d8dee8)",
|
|
1050
|
+
borderRadius: "0.6rem",
|
|
1051
|
+
background: "#f8fafc",
|
|
1052
|
+
padding: "0.6rem",
|
|
1053
|
+
display: "grid",
|
|
1054
|
+
gap: "0.45rem"
|
|
1055
|
+
},
|
|
1056
|
+
dialogBackdrop: {
|
|
1057
|
+
position: "absolute",
|
|
1058
|
+
inset: 0,
|
|
1059
|
+
background: "rgba(15, 23, 42, 0.36)",
|
|
1060
|
+
display: "grid",
|
|
1061
|
+
placeItems: "center",
|
|
1062
|
+
padding: "0.75rem",
|
|
1063
|
+
zIndex: 3
|
|
1064
|
+
},
|
|
1065
|
+
dialogCard: {
|
|
1066
|
+
width: "min(100%, 360px)",
|
|
1067
|
+
borderRadius: "0.68rem",
|
|
1068
|
+
border: "1px solid #fecaca",
|
|
1069
|
+
background: "#fff",
|
|
1070
|
+
padding: "0.72rem",
|
|
1071
|
+
display: "grid",
|
|
1072
|
+
gap: "0.55rem"
|
|
1073
|
+
},
|
|
1074
|
+
srOnly: {
|
|
1075
|
+
position: "absolute",
|
|
1076
|
+
width: 1,
|
|
1077
|
+
height: 1,
|
|
1078
|
+
padding: 0,
|
|
1079
|
+
margin: -1,
|
|
1080
|
+
overflow: "hidden",
|
|
1081
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1082
|
+
border: 0
|
|
691
1083
|
}
|
|
692
|
-
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// src/fiber-node-button/utils.ts
|
|
1087
|
+
import { ChannelState, shannonsToCkb } from "@fiber-pay/sdk/browser";
|
|
1088
|
+
function shorten(value, head = 10, tail = 8) {
|
|
1089
|
+
if (!value || value.length <= head + tail + 3) {
|
|
1090
|
+
return value;
|
|
1091
|
+
}
|
|
1092
|
+
return `${value.slice(0, head)}...${value.slice(-tail)}`;
|
|
693
1093
|
}
|
|
694
|
-
function
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
729
|
-
} catch (payError) {
|
|
730
|
-
if (isMountedRef.current) {
|
|
731
|
-
setError(asErrorMessage2(payError));
|
|
732
|
-
}
|
|
733
|
-
} finally {
|
|
734
|
-
if (isMountedRef.current) {
|
|
735
|
-
setIsPaying(false);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
},
|
|
739
|
-
[node]
|
|
740
|
-
);
|
|
1094
|
+
function summarizeError(message, max = 72) {
|
|
1095
|
+
const trimmed = message.trim();
|
|
1096
|
+
if (trimmed.length <= max) {
|
|
1097
|
+
return trimmed;
|
|
1098
|
+
}
|
|
1099
|
+
return `${trimmed.slice(0, max - 3)}...`;
|
|
1100
|
+
}
|
|
1101
|
+
function toHexPrefixed(value) {
|
|
1102
|
+
const trimmed = value.trim();
|
|
1103
|
+
if (!trimmed) {
|
|
1104
|
+
throw new Error("Hex value is empty.");
|
|
1105
|
+
}
|
|
1106
|
+
return /^0x/i.test(trimmed) ? trimmed : `0x${trimmed}`;
|
|
1107
|
+
}
|
|
1108
|
+
function isPendingChannelState(state) {
|
|
1109
|
+
return state === ChannelState.NegotiatingFunding || state === ChannelState.CollaboratingFundingTx || state === ChannelState.SigningCommitment || state === ChannelState.AwaitingTxSignatures || state === ChannelState.AwaitingChannelReady;
|
|
1110
|
+
}
|
|
1111
|
+
function formatChannelBalance(shannonsHex) {
|
|
1112
|
+
const ckb = shannonsToCkb(shannonsHex);
|
|
1113
|
+
return Number.isFinite(ckb) ? ckb.toFixed(4) : "0.0000";
|
|
1114
|
+
}
|
|
1115
|
+
function isClosedChannelState(state) {
|
|
1116
|
+
return state === ChannelState.Closed || state === ChannelState.ShuttingDown;
|
|
1117
|
+
}
|
|
1118
|
+
function getChannelFilterState(channel) {
|
|
1119
|
+
const state = channel.state.state_name;
|
|
1120
|
+
if (isPendingChannelState(state)) return "pending";
|
|
1121
|
+
if (isClosedChannelState(state)) return "closed";
|
|
1122
|
+
return "active";
|
|
1123
|
+
}
|
|
1124
|
+
function withDisabledStyle(style, disabled) {
|
|
1125
|
+
if (!disabled) {
|
|
1126
|
+
return style;
|
|
1127
|
+
}
|
|
741
1128
|
return {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
error
|
|
1129
|
+
...style,
|
|
1130
|
+
opacity: 0.55,
|
|
1131
|
+
cursor: "not-allowed"
|
|
746
1132
|
};
|
|
747
1133
|
}
|
|
748
1134
|
|
|
749
|
-
// src/fiber-
|
|
750
|
-
import {
|
|
751
|
-
|
|
752
|
-
var cardStyle = {
|
|
753
|
-
border: "1px solid #ddd",
|
|
754
|
-
borderRadius: 8,
|
|
755
|
-
padding: 16,
|
|
756
|
-
maxWidth: 520
|
|
757
|
-
};
|
|
758
|
-
var rowStyle = {
|
|
759
|
-
display: "flex",
|
|
760
|
-
gap: 8
|
|
761
|
-
};
|
|
762
|
-
var rowWithMarginStyle = {
|
|
763
|
-
...rowStyle,
|
|
764
|
-
marginBottom: 8
|
|
765
|
-
};
|
|
766
|
-
function FiberPayQuickCard(props) {
|
|
767
|
-
const network = props.network ?? "testnet";
|
|
768
|
-
const passkeyUsername = props.passkeyUsername ?? "User";
|
|
769
|
-
const title = props.title ?? "FiberPay Quick Card";
|
|
770
|
-
const onError = props.onError;
|
|
771
|
-
const onInvoiceCreated = props.onInvoiceCreated;
|
|
772
|
-
const onPaymentResult = props.onPaymentResult;
|
|
773
|
-
const passwordInputId = useId();
|
|
774
|
-
const invoiceInputId = useId();
|
|
1135
|
+
// src/fiber-node-button/render-action.tsx
|
|
1136
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1137
|
+
function renderPanelAction(options) {
|
|
775
1138
|
const {
|
|
776
|
-
|
|
777
|
-
|
|
1139
|
+
id,
|
|
1140
|
+
defaultProps,
|
|
1141
|
+
fiber,
|
|
778
1142
|
state,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1143
|
+
renderAction,
|
|
1144
|
+
t,
|
|
1145
|
+
buttonStyle = styles2.actionButton
|
|
1146
|
+
} = options;
|
|
1147
|
+
const customAction = renderAction?.({
|
|
1148
|
+
id,
|
|
1149
|
+
defaultProps,
|
|
1150
|
+
fiber,
|
|
1151
|
+
state,
|
|
1152
|
+
t
|
|
1153
|
+
});
|
|
1154
|
+
if (customAction !== void 0) {
|
|
1155
|
+
return customAction;
|
|
1156
|
+
}
|
|
1157
|
+
const loadingText = defaultProps.loadingLabel ?? t("actions.loading.default", "Processing...");
|
|
1158
|
+
return /* @__PURE__ */ jsx2(
|
|
1159
|
+
"button",
|
|
1160
|
+
{
|
|
1161
|
+
type: "button",
|
|
1162
|
+
style: withDisabledStyle(buttonStyle, defaultProps.disabled),
|
|
1163
|
+
disabled: defaultProps.disabled,
|
|
1164
|
+
onClick: () => {
|
|
1165
|
+
void defaultProps.onTrigger();
|
|
1166
|
+
},
|
|
1167
|
+
children: defaultProps.loading ? loadingText : defaultProps.label
|
|
796
1168
|
}
|
|
797
|
-
|
|
798
|
-
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// src/fiber-node-button/types.ts
|
|
1173
|
+
var ONE_CKB_SHANNONS = "0x5f5e100";
|
|
1174
|
+
var TAB_ITEMS = [
|
|
1175
|
+
{ id: "workbench", label: "Workbench" },
|
|
1176
|
+
{ id: "channels", label: "Channels" },
|
|
1177
|
+
{ id: "diagnostics", label: "Diagnostics" }
|
|
1178
|
+
];
|
|
1179
|
+
var FILTER_ITEMS = ["active", "pending", "closed", "all"];
|
|
1180
|
+
|
|
1181
|
+
// src/fiber-node-button/channels-tab.tsx
|
|
1182
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1183
|
+
function ChannelsTab({ state, fiber, onLog, renderAction, t }) {
|
|
1184
|
+
const {
|
|
1185
|
+
isRefreshingChannels,
|
|
1186
|
+
refreshChannels,
|
|
1187
|
+
channelCounts,
|
|
1188
|
+
channelFilter,
|
|
1189
|
+
setChannelFilter,
|
|
1190
|
+
visibleChannels,
|
|
1191
|
+
selectedChannelId,
|
|
1192
|
+
setSelectedChannelId,
|
|
1193
|
+
setForceCloseConfirmOpen,
|
|
1194
|
+
selectedChannel,
|
|
1195
|
+
selectedCanClose,
|
|
1196
|
+
selectedIsClosing,
|
|
1197
|
+
selectedPending,
|
|
1198
|
+
closeChannel
|
|
1199
|
+
} = state;
|
|
1200
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1201
|
+
/* @__PURE__ */ jsxs2("section", { style: styles2.section, children: [
|
|
1202
|
+
/* @__PURE__ */ jsxs2("div", { style: styles2.rowBetween, children: [
|
|
1203
|
+
/* @__PURE__ */ jsx3("h4", { style: styles2.sectionTitle, children: t("channels.summary.title", "Channel Summary") }),
|
|
1204
|
+
/* @__PURE__ */ jsx3(
|
|
1205
|
+
"button",
|
|
1206
|
+
{
|
|
1207
|
+
type: "button",
|
|
1208
|
+
style: withDisabledStyle(styles2.actionButton, isRefreshingChannels),
|
|
1209
|
+
disabled: isRefreshingChannels,
|
|
1210
|
+
onClick: () => {
|
|
1211
|
+
void refreshChannels();
|
|
1212
|
+
},
|
|
1213
|
+
children: isRefreshingChannels ? t("channels.summary.refresh.loading", "Refreshing...") : t("channels.summary.refresh", "Refresh")
|
|
1214
|
+
}
|
|
1215
|
+
)
|
|
1216
|
+
] }),
|
|
1217
|
+
/* @__PURE__ */ jsxs2("p", { style: styles2.summaryInline, children: [
|
|
1218
|
+
t("channels.summary.active", "Active"),
|
|
1219
|
+
" ",
|
|
1220
|
+
channelCounts.active,
|
|
1221
|
+
" |",
|
|
1222
|
+
" ",
|
|
1223
|
+
t("channels.summary.pending", "Pending"),
|
|
1224
|
+
" ",
|
|
1225
|
+
channelCounts.pending,
|
|
1226
|
+
" |",
|
|
1227
|
+
" ",
|
|
1228
|
+
t("channels.summary.closed", "Closed"),
|
|
1229
|
+
" ",
|
|
1230
|
+
channelCounts.closed,
|
|
1231
|
+
" |",
|
|
1232
|
+
" ",
|
|
1233
|
+
t("channels.summary.total", "Total"),
|
|
1234
|
+
" ",
|
|
1235
|
+
channelCounts.all
|
|
1236
|
+
] }),
|
|
1237
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.filterBar, children: FILTER_ITEMS.map((filter) => /* @__PURE__ */ jsx3(
|
|
1238
|
+
"button",
|
|
1239
|
+
{
|
|
1240
|
+
type: "button",
|
|
1241
|
+
style: channelFilter === filter ? styles2.primaryButton : styles2.actionButton,
|
|
1242
|
+
onClick: () => setChannelFilter(filter),
|
|
1243
|
+
children: filter === "all" ? `${t("channels.filter.all", "All")} (${channelCounts.all})` : `${filter} (${channelCounts[filter]})`
|
|
1244
|
+
},
|
|
1245
|
+
filter
|
|
1246
|
+
)) }),
|
|
1247
|
+
/* @__PURE__ */ jsx3("div", { style: styles2.list, children: visibleChannels.length === 0 ? /* @__PURE__ */ jsx3("p", { style: styles2.compactText, children: t("channels.list.empty", "No channels found for this filter.") }) : visibleChannels.map((channel) => {
|
|
1248
|
+
const selected = channel.channel_id === selectedChannelId;
|
|
1249
|
+
return /* @__PURE__ */ jsxs2(
|
|
1250
|
+
"button",
|
|
1251
|
+
{
|
|
1252
|
+
type: "button",
|
|
1253
|
+
style: {
|
|
1254
|
+
...styles2.compactChannelRow,
|
|
1255
|
+
...selected ? styles2.compactChannelRowActive : {}
|
|
1256
|
+
},
|
|
1257
|
+
onClick: () => {
|
|
1258
|
+
setSelectedChannelId(channel.channel_id);
|
|
1259
|
+
setForceCloseConfirmOpen(false);
|
|
1260
|
+
},
|
|
1261
|
+
children: [
|
|
1262
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.srOnly, children: selected ? t("channels.list.selected", "Selected channel") : t("channels.list.select", "Select channel") }),
|
|
1263
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.compactChannelTop, children: [
|
|
1264
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.inlineCode, children: [
|
|
1265
|
+
t("channels.list.id", "ID"),
|
|
1266
|
+
": ",
|
|
1267
|
+
shorten(channel.channel_id, 12, 8)
|
|
1268
|
+
] }),
|
|
1269
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.badge, children: channel.state.state_name })
|
|
1270
|
+
] }),
|
|
1271
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.compactText, children: [
|
|
1272
|
+
t("channels.list.peer", "Peer"),
|
|
1273
|
+
": ",
|
|
1274
|
+
shorten(channel.pubkey, 16, 10)
|
|
1275
|
+
] }),
|
|
1276
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.compactText, children: [
|
|
1277
|
+
"L ",
|
|
1278
|
+
formatChannelBalance(channel.local_balance),
|
|
1279
|
+
" / R",
|
|
1280
|
+
" ",
|
|
1281
|
+
formatChannelBalance(channel.remote_balance),
|
|
1282
|
+
" CKB"
|
|
1283
|
+
] })
|
|
1284
|
+
]
|
|
1285
|
+
},
|
|
1286
|
+
channel.channel_id
|
|
1287
|
+
);
|
|
1288
|
+
}) })
|
|
1289
|
+
] }),
|
|
1290
|
+
selectedChannel ? /* @__PURE__ */ jsxs2("section", { style: styles2.detailPanel, children: [
|
|
1291
|
+
/* @__PURE__ */ jsxs2("div", { style: styles2.rowBetween, children: [
|
|
1292
|
+
/* @__PURE__ */ jsx3("h4", { style: styles2.sectionTitle, children: t("channels.details.title", "Channel Details") }),
|
|
1293
|
+
/* @__PURE__ */ jsx3("span", { style: styles2.badge, children: selectedChannel.state.state_name })
|
|
1294
|
+
] }),
|
|
1295
|
+
/* @__PURE__ */ jsxs2("p", { style: styles2.inlineCode, children: [
|
|
1296
|
+
t("channels.details.channelId", "Channel ID"),
|
|
1297
|
+
": ",
|
|
1298
|
+
selectedChannel.channel_id
|
|
1299
|
+
] }),
|
|
1300
|
+
/* @__PURE__ */ jsxs2("p", { style: styles2.inlineCode, children: [
|
|
1301
|
+
t("channels.details.peer", "Peer"),
|
|
1302
|
+
": ",
|
|
1303
|
+
selectedChannel.pubkey
|
|
1304
|
+
] }),
|
|
1305
|
+
/* @__PURE__ */ jsxs2("div", { style: styles2.row, children: [
|
|
1306
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.badge, children: [
|
|
1307
|
+
t("channels.details.local", "Local"),
|
|
1308
|
+
" ",
|
|
1309
|
+
formatChannelBalance(selectedChannel.local_balance),
|
|
1310
|
+
" CKB"
|
|
1311
|
+
] }),
|
|
1312
|
+
/* @__PURE__ */ jsxs2("span", { style: styles2.badge, children: [
|
|
1313
|
+
t("channels.details.remote", "Remote"),
|
|
1314
|
+
" ",
|
|
1315
|
+
formatChannelBalance(selectedChannel.remote_balance),
|
|
1316
|
+
" CKB"
|
|
1317
|
+
] }),
|
|
1318
|
+
/* @__PURE__ */ jsxs2(
|
|
1319
|
+
"span",
|
|
1320
|
+
{
|
|
1321
|
+
style: styles2.badge,
|
|
1322
|
+
title: "Pending TLCs are in-flight payment locks associated with this channel.",
|
|
1323
|
+
children: [
|
|
1324
|
+
t("channels.details.tlcs", "TLCs"),
|
|
1325
|
+
" ",
|
|
1326
|
+
selectedChannel.pending_tlcs.length
|
|
1327
|
+
]
|
|
1328
|
+
}
|
|
1329
|
+
)
|
|
1330
|
+
] }),
|
|
1331
|
+
selectedChannel.failure_detail ? /* @__PURE__ */ jsxs2("p", { style: styles2.compactText, children: [
|
|
1332
|
+
t("channels.details.failure", "Failure"),
|
|
1333
|
+
": ",
|
|
1334
|
+
selectedChannel.failure_detail
|
|
1335
|
+
] }) : null,
|
|
1336
|
+
selectedChannel.shutdown_transaction_hash ? /* @__PURE__ */ jsxs2("p", { style: styles2.inlineCode, children: [
|
|
1337
|
+
t("channels.details.shutdownTx", "Shutdown TX"),
|
|
1338
|
+
":",
|
|
1339
|
+
" ",
|
|
1340
|
+
selectedChannel.shutdown_transaction_hash
|
|
1341
|
+
] }) : null,
|
|
1342
|
+
/* @__PURE__ */ jsxs2("div", { style: { ...styles2.row, justifyContent: "flex-end" }, children: [
|
|
1343
|
+
renderPanelAction({
|
|
1344
|
+
id: "close-channel",
|
|
1345
|
+
fiber,
|
|
1346
|
+
state,
|
|
1347
|
+
renderAction,
|
|
1348
|
+
t,
|
|
1349
|
+
defaultProps: {
|
|
1350
|
+
id: "close-channel",
|
|
1351
|
+
channelId: selectedChannel.channel_id,
|
|
1352
|
+
label: selectedPending ? t("actions.abandonPending", "Abandon Pending") : t("actions.closeChannel", "Close Channel"),
|
|
1353
|
+
loadingLabel: t("actions.closeChannel.loading", "Closing..."),
|
|
1354
|
+
disabled: !selectedCanClose || selectedIsClosing,
|
|
1355
|
+
loading: selectedIsClosing,
|
|
1356
|
+
onTrigger: async () => {
|
|
1357
|
+
await closeChannel(selectedChannel.channel_id, false);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}),
|
|
1361
|
+
renderPanelAction({
|
|
1362
|
+
id: "force-close-channel",
|
|
1363
|
+
fiber,
|
|
1364
|
+
state,
|
|
1365
|
+
renderAction,
|
|
1366
|
+
t,
|
|
1367
|
+
buttonStyle: styles2.dangerButton,
|
|
1368
|
+
defaultProps: {
|
|
1369
|
+
id: "force-close-channel",
|
|
1370
|
+
channelId: selectedChannel.channel_id,
|
|
1371
|
+
label: t("actions.forceClose", "Force Close"),
|
|
1372
|
+
disabled: !selectedCanClose || selectedPending || selectedIsClosing,
|
|
1373
|
+
onTrigger: () => {
|
|
1374
|
+
setForceCloseConfirmOpen(true);
|
|
1375
|
+
onLog?.("fiber_channel_force_close_confirm_opened");
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
})
|
|
1379
|
+
] })
|
|
1380
|
+
] }) : null
|
|
1381
|
+
] });
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// src/fiber-node-button/diagnostics-tab.tsx
|
|
1385
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1386
|
+
function DiagnosticsTab({ state, t }) {
|
|
1387
|
+
const {
|
|
1388
|
+
isRefreshingPeers,
|
|
1389
|
+
refreshConnectedPeers,
|
|
1390
|
+
connectedPeers,
|
|
1391
|
+
setPeerPubkey,
|
|
1392
|
+
switchTab,
|
|
1393
|
+
peerAddress,
|
|
1394
|
+
setPeerAddress,
|
|
1395
|
+
isConnectingPeer,
|
|
1396
|
+
connectPeerByAddress,
|
|
1397
|
+
isRefreshingGraph,
|
|
1398
|
+
refreshDiagnostics,
|
|
1399
|
+
refreshGraph,
|
|
1400
|
+
graphNodes,
|
|
1401
|
+
graphChannels,
|
|
1402
|
+
channelOpenFlow
|
|
1403
|
+
} = state;
|
|
1404
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1405
|
+
/* @__PURE__ */ jsxs3("section", { style: styles2.section, children: [
|
|
1406
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.rowBetween, children: [
|
|
1407
|
+
/* @__PURE__ */ jsx4("h4", { style: styles2.sectionTitle, children: t("diagnostics.peers.title", "Connected Peers") }),
|
|
1408
|
+
/* @__PURE__ */ jsx4(
|
|
1409
|
+
"button",
|
|
1410
|
+
{
|
|
1411
|
+
type: "button",
|
|
1412
|
+
style: withDisabledStyle(styles2.actionButton, isRefreshingPeers),
|
|
1413
|
+
disabled: isRefreshingPeers,
|
|
1414
|
+
onClick: () => {
|
|
1415
|
+
void refreshConnectedPeers();
|
|
1416
|
+
},
|
|
1417
|
+
children: isRefreshingPeers ? t("diagnostics.peers.refresh.loading", "Refreshing...") : t("diagnostics.peers.refresh", "Refresh Peers")
|
|
1418
|
+
}
|
|
1419
|
+
)
|
|
1420
|
+
] }),
|
|
1421
|
+
/* @__PURE__ */ jsxs3("p", { style: styles2.compactText, children: [
|
|
1422
|
+
t("diagnostics.peers.count", "Peers"),
|
|
1423
|
+
": ",
|
|
1424
|
+
connectedPeers.length
|
|
1425
|
+
] }),
|
|
1426
|
+
/* @__PURE__ */ jsx4("div", { style: { ...styles2.list, maxHeight: "190px" }, children: connectedPeers.length === 0 ? /* @__PURE__ */ jsx4("p", { style: styles2.compactText, children: t("diagnostics.peers.empty", "No connected peers.") }) : connectedPeers.map((peer) => /* @__PURE__ */ jsxs3("article", { style: styles2.compactChannelRow, children: [
|
|
1427
|
+
/* @__PURE__ */ jsx4("p", { style: styles2.inlineCode, children: shorten(peer.pubkey, 18, 12) }),
|
|
1428
|
+
/* @__PURE__ */ jsxs3("details", { children: [
|
|
1429
|
+
/* @__PURE__ */ jsx4("summary", { style: { ...styles2.compactText, cursor: "pointer" }, children: t("diagnostics.peers.address", "Address") }),
|
|
1430
|
+
/* @__PURE__ */ jsx4("p", { style: styles2.inlineCode, children: peer.address })
|
|
1431
|
+
] }),
|
|
1432
|
+
/* @__PURE__ */ jsx4("div", { style: styles2.row, children: /* @__PURE__ */ jsx4(
|
|
1433
|
+
"button",
|
|
1434
|
+
{
|
|
1435
|
+
type: "button",
|
|
1436
|
+
style: styles2.ghostButton,
|
|
1437
|
+
onClick: () => {
|
|
1438
|
+
setPeerPubkey(peer.pubkey);
|
|
1439
|
+
switchTab("workbench");
|
|
1440
|
+
},
|
|
1441
|
+
children: t("diagnostics.peers.useForOpenChannel", "Use for Open Channel")
|
|
1442
|
+
}
|
|
1443
|
+
) })
|
|
1444
|
+
] }, peer.pubkey)) }),
|
|
1445
|
+
/* @__PURE__ */ jsxs3("label", { style: styles2.fieldLabel, children: [
|
|
1446
|
+
t("diagnostics.peers.connectPeerAddress", "Connect Peer Address"),
|
|
1447
|
+
/* @__PURE__ */ jsx4(
|
|
1448
|
+
"input",
|
|
1449
|
+
{
|
|
1450
|
+
style: styles2.input,
|
|
1451
|
+
value: peerAddress,
|
|
1452
|
+
onChange: (event) => setPeerAddress(event.target.value),
|
|
1453
|
+
placeholder: "/dns4/.../wss/p2p/..."
|
|
1454
|
+
}
|
|
1455
|
+
)
|
|
1456
|
+
] }),
|
|
1457
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.rowBetween, children: [
|
|
1458
|
+
/* @__PURE__ */ jsx4(
|
|
1459
|
+
"button",
|
|
1460
|
+
{
|
|
1461
|
+
type: "button",
|
|
1462
|
+
style: withDisabledStyle(styles2.primaryButton, isConnectingPeer || !peerAddress.trim()),
|
|
1463
|
+
disabled: isConnectingPeer || !peerAddress.trim(),
|
|
1464
|
+
onClick: () => {
|
|
1465
|
+
void connectPeerByAddress();
|
|
1466
|
+
},
|
|
1467
|
+
children: isConnectingPeer ? t("diagnostics.peers.connect.loading", "Connecting...") : t("diagnostics.peers.connect", "Connect Peer")
|
|
1468
|
+
}
|
|
1469
|
+
),
|
|
1470
|
+
/* @__PURE__ */ jsx4(
|
|
1471
|
+
"button",
|
|
1472
|
+
{
|
|
1473
|
+
type: "button",
|
|
1474
|
+
style: withDisabledStyle(styles2.actionButton, isRefreshingPeers || isRefreshingGraph),
|
|
1475
|
+
disabled: isRefreshingPeers || isRefreshingGraph,
|
|
1476
|
+
onClick: () => {
|
|
1477
|
+
void refreshDiagnostics();
|
|
1478
|
+
},
|
|
1479
|
+
children: isRefreshingPeers || isRefreshingGraph ? t("diagnostics.peers.refreshAll.loading", "Refreshing...") : t("diagnostics.peers.refreshAll", "Refresh All")
|
|
1480
|
+
}
|
|
1481
|
+
)
|
|
1482
|
+
] })
|
|
1483
|
+
] }),
|
|
1484
|
+
/* @__PURE__ */ jsxs3("section", { style: styles2.section, children: [
|
|
1485
|
+
/* @__PURE__ */ jsxs3("div", { style: styles2.rowBetween, children: [
|
|
1486
|
+
/* @__PURE__ */ jsx4("h4", { style: styles2.sectionTitle, children: t("diagnostics.graph.title", "Graph Snapshot") }),
|
|
1487
|
+
/* @__PURE__ */ jsx4(
|
|
1488
|
+
"button",
|
|
1489
|
+
{
|
|
1490
|
+
type: "button",
|
|
1491
|
+
style: withDisabledStyle(styles2.actionButton, isRefreshingGraph),
|
|
1492
|
+
disabled: isRefreshingGraph,
|
|
1493
|
+
onClick: () => {
|
|
1494
|
+
void refreshGraph();
|
|
1495
|
+
},
|
|
1496
|
+
children: isRefreshingGraph ? t("diagnostics.graph.refresh.loading", "Refreshing...") : t("diagnostics.graph.refresh", "Refresh Graph")
|
|
1497
|
+
}
|
|
1498
|
+
)
|
|
1499
|
+
] }),
|
|
1500
|
+
/* @__PURE__ */ jsxs3("p", { style: styles2.compactText, children: [
|
|
1501
|
+
t("diagnostics.graph.showing", "showing"),
|
|
1502
|
+
" ",
|
|
1503
|
+
Math.min(graphNodes.length, 3),
|
|
1504
|
+
" ",
|
|
1505
|
+
t("diagnostics.graph.of", "of"),
|
|
1506
|
+
" ",
|
|
1507
|
+
graphNodes.length,
|
|
1508
|
+
" ",
|
|
1509
|
+
t("diagnostics.graph.nodes", "nodes"),
|
|
1510
|
+
", ",
|
|
1511
|
+
Math.min(graphChannels.length, 2),
|
|
1512
|
+
" ",
|
|
1513
|
+
t("diagnostics.graph.of", "of"),
|
|
1514
|
+
" ",
|
|
1515
|
+
graphChannels.length,
|
|
1516
|
+
" ",
|
|
1517
|
+
t("diagnostics.graph.channels", "channels"),
|
|
1518
|
+
"."
|
|
1519
|
+
] }),
|
|
1520
|
+
graphNodes.slice(0, 3).map((node) => /* @__PURE__ */ jsxs3("p", { style: styles2.inlineCode, children: [
|
|
1521
|
+
"Node: ",
|
|
1522
|
+
node.node_name || shorten(node.pubkey, 18, 10)
|
|
1523
|
+
] }, node.pubkey)),
|
|
1524
|
+
graphChannels.slice(0, 2).map((channel) => /* @__PURE__ */ jsxs3(
|
|
1525
|
+
"p",
|
|
1526
|
+
{
|
|
1527
|
+
style: styles2.inlineCode,
|
|
1528
|
+
children: [
|
|
1529
|
+
shorten(channel.node1, 10, 6),
|
|
1530
|
+
" to ",
|
|
1531
|
+
shorten(channel.node2, 10, 6),
|
|
1532
|
+
";",
|
|
1533
|
+
" ",
|
|
1534
|
+
formatChannelBalance(channel.capacity),
|
|
1535
|
+
" CKB"
|
|
1536
|
+
]
|
|
1537
|
+
},
|
|
1538
|
+
`${channel.node1}-${channel.node2}-${channel.channel_outpoint.tx_hash}`
|
|
1539
|
+
)),
|
|
1540
|
+
/* @__PURE__ */ jsxs3("details", { children: [
|
|
1541
|
+
/* @__PURE__ */ jsx4("summary", { style: { ...styles2.compactText, cursor: "pointer" }, children: t("diagnostics.graph.rawSnapshot", "Raw graph snapshot") }),
|
|
1542
|
+
/* @__PURE__ */ jsx4(
|
|
1543
|
+
"pre",
|
|
1544
|
+
{
|
|
1545
|
+
style: {
|
|
1546
|
+
...styles2.inlineCode,
|
|
1547
|
+
marginTop: "0.45rem",
|
|
1548
|
+
maxHeight: "160px",
|
|
1549
|
+
overflow: "auto",
|
|
1550
|
+
background: "#f1f5f9",
|
|
1551
|
+
borderRadius: "0.45rem",
|
|
1552
|
+
padding: "0.45rem"
|
|
1553
|
+
},
|
|
1554
|
+
children: JSON.stringify(
|
|
1555
|
+
{
|
|
1556
|
+
nodes: graphNodes,
|
|
1557
|
+
channels: graphChannels
|
|
1558
|
+
},
|
|
1559
|
+
null,
|
|
1560
|
+
2
|
|
1561
|
+
)
|
|
1562
|
+
}
|
|
1563
|
+
)
|
|
1564
|
+
] })
|
|
1565
|
+
] }),
|
|
1566
|
+
channelOpenFlow.diagnostic ? /* @__PURE__ */ jsx4("div", { style: styles2.notice, children: channelOpenFlow.diagnostic }) : null
|
|
1567
|
+
] });
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/fiber-node-button/i18n.ts
|
|
1571
|
+
function applyVariables(template, vars) {
|
|
1572
|
+
if (!vars) {
|
|
1573
|
+
return template;
|
|
1574
|
+
}
|
|
1575
|
+
return template.replace(/\{(\w+)\}/g, (full, key) => {
|
|
1576
|
+
const value = vars[key];
|
|
1577
|
+
return value === void 0 ? full : String(value);
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
var defaultFiberNodeButtonI18n = (_key, fallback, vars) => applyVariables(fallback, vars);
|
|
1581
|
+
|
|
1582
|
+
// src/fiber-node-button/use-panel-state.ts
|
|
1583
|
+
import { ChannelState as ChannelState2 } from "@fiber-pay/sdk/browser";
|
|
1584
|
+
import {
|
|
1585
|
+
useCallback as useCallback5,
|
|
1586
|
+
useEffect as useEffect4,
|
|
1587
|
+
useId as useId2,
|
|
1588
|
+
useMemo as useMemo3,
|
|
1589
|
+
useRef as useRef4,
|
|
1590
|
+
useState as useState5
|
|
1591
|
+
} from "react";
|
|
1592
|
+
|
|
1593
|
+
// src/use-channel-open-flow.ts
|
|
1594
|
+
import {
|
|
1595
|
+
computeSuggestedFundingAmountCkb,
|
|
1596
|
+
diagnoseExternalFundingFailure,
|
|
1597
|
+
extractRequiredCapacityCkbFromFundingError,
|
|
1598
|
+
openChannelWithExternalFundingFlow,
|
|
1599
|
+
shouldDiagnoseFundingAbortError
|
|
1600
|
+
} from "@fiber-pay/sdk/browser";
|
|
1601
|
+
import { useCallback as useCallback3, useMemo as useMemo2, useState as useState3 } from "react";
|
|
1602
|
+
var SHANNONS_PER_CKB = 100000000n;
|
|
1603
|
+
function toHexPrefixed2(value) {
|
|
1604
|
+
const trimmed = value.trim();
|
|
1605
|
+
if (!trimmed) {
|
|
1606
|
+
throw new Error("Hex value is empty.");
|
|
1607
|
+
}
|
|
1608
|
+
return trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
1609
|
+
}
|
|
1610
|
+
function ckbToShannons(amountCkb) {
|
|
1611
|
+
const normalized = amountCkb.trim();
|
|
1612
|
+
if (!/^\d+(\.\d+)?$/.test(normalized)) {
|
|
1613
|
+
throw new Error("Funding amount must be a valid CKB number.");
|
|
1614
|
+
}
|
|
1615
|
+
const [wholePart, fracPart = ""] = normalized.split(".");
|
|
1616
|
+
if (fracPart.length > 8 && /[1-9]/.test(fracPart.slice(8))) {
|
|
1617
|
+
throw new Error("Funding amount supports up to 8 decimal places.");
|
|
1618
|
+
}
|
|
1619
|
+
const fracPadded = `${fracPart}00000000`.slice(0, 8);
|
|
1620
|
+
const shannons = BigInt(wholePart) * SHANNONS_PER_CKB + BigInt(fracPadded || "0");
|
|
1621
|
+
if (shannons <= 0n) {
|
|
1622
|
+
throw new Error("Funding amount must be greater than 0.");
|
|
1623
|
+
}
|
|
1624
|
+
return shannons;
|
|
1625
|
+
}
|
|
1626
|
+
function toHookResultFromExternal(result) {
|
|
1627
|
+
return {
|
|
1628
|
+
mode: "external",
|
|
1629
|
+
channelId: result.channelId,
|
|
1630
|
+
fundingTxHash: result.fundingTxHash,
|
|
1631
|
+
unsignedFundingTx: result.unsignedFundingTx,
|
|
1632
|
+
signedFundingTx: result.signedFundingTx
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function useChannelOpenFlow(options) {
|
|
1636
|
+
const { node, onLog } = options;
|
|
1637
|
+
const [isOpening, setIsOpening] = useState3(false);
|
|
1638
|
+
const [error, setError] = useState3(null);
|
|
1639
|
+
const [diagnostic, setDiagnostic] = useState3(null);
|
|
1640
|
+
const [suggestedFundingAmountCkb, setSuggestedFundingAmountCkb] = useState3(null);
|
|
1641
|
+
const [lastResult, setLastResult] = useState3(null);
|
|
1642
|
+
const reset = useCallback3(() => {
|
|
1643
|
+
setError(null);
|
|
1644
|
+
setDiagnostic(null);
|
|
1645
|
+
setSuggestedFundingAmountCkb(null);
|
|
1646
|
+
setLastResult(null);
|
|
1647
|
+
}, []);
|
|
1648
|
+
const openChannel = useCallback3(
|
|
1649
|
+
async (params) => {
|
|
1650
|
+
if (!node) {
|
|
1651
|
+
setError("Node is not connected.");
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
setIsOpening(true);
|
|
1655
|
+
setError(null);
|
|
1656
|
+
setDiagnostic(null);
|
|
1657
|
+
setSuggestedFundingAmountCkb(null);
|
|
1658
|
+
let requestedFundingShannons;
|
|
1659
|
+
let effectiveFundingLockScript = params.fundingLockScript;
|
|
1660
|
+
try {
|
|
1661
|
+
requestedFundingShannons = ckbToShannons(params.fundingAmountCkb);
|
|
1662
|
+
const pubkey = toHexPrefixed2(params.pubkey);
|
|
1663
|
+
if (!params.externalWallet) {
|
|
1664
|
+
const openResult = await node.openChannel({
|
|
1665
|
+
pubkey,
|
|
1666
|
+
funding_amount: `0x${requestedFundingShannons.toString(16)}`
|
|
1667
|
+
});
|
|
1668
|
+
const result2 = {
|
|
1669
|
+
mode: "internal",
|
|
1670
|
+
channelId: openResult.temporary_channel_id
|
|
1671
|
+
};
|
|
1672
|
+
setLastResult(result2);
|
|
1673
|
+
onLog?.(`Internal channel open requested: ${openResult.temporary_channel_id}`);
|
|
1674
|
+
return result2;
|
|
1675
|
+
}
|
|
1676
|
+
if (!params.signFundingTx) {
|
|
1677
|
+
throw new Error("Missing signFundingTx callback for external wallet funding flow.");
|
|
1678
|
+
}
|
|
1679
|
+
const nodeInfo = await node.nodeInfo();
|
|
1680
|
+
const defaultScript = nodeInfo.default_funding_lock_script;
|
|
1681
|
+
const shutdownScript = params.shutdownScript ?? defaultScript;
|
|
1682
|
+
effectiveFundingLockScript = params.fundingLockScript ?? defaultScript;
|
|
1683
|
+
const flowResult = await openChannelWithExternalFundingFlow({
|
|
1684
|
+
node,
|
|
1685
|
+
params: {
|
|
1686
|
+
pubkey,
|
|
1687
|
+
funding_amount: `0x${requestedFundingShannons.toString(16)}`,
|
|
1688
|
+
shutdown_script: shutdownScript,
|
|
1689
|
+
funding_lock_script: effectiveFundingLockScript,
|
|
1690
|
+
funding_lock_script_cell_deps: params.fundingLockScriptCellDeps
|
|
1691
|
+
},
|
|
1692
|
+
signFundingTx: params.signFundingTx
|
|
1693
|
+
});
|
|
1694
|
+
const result = toHookResultFromExternal(flowResult);
|
|
1695
|
+
setLastResult(result);
|
|
1696
|
+
onLog?.(
|
|
1697
|
+
`External funding completed: channel=${flowResult.channelId}, tx=${flowResult.fundingTxHash}`
|
|
1698
|
+
);
|
|
1699
|
+
return result;
|
|
1700
|
+
} catch (unknownError) {
|
|
1701
|
+
const message = unknownError instanceof Error ? unknownError.message : String(unknownError);
|
|
1702
|
+
let displayMessage = message;
|
|
1703
|
+
const requiredCapacity = extractRequiredCapacityCkbFromFundingError(message);
|
|
1704
|
+
if (requiredCapacity) {
|
|
1705
|
+
try {
|
|
1706
|
+
const suggested = computeSuggestedFundingAmountCkb(
|
|
1707
|
+
params.fundingAmountCkb,
|
|
1708
|
+
requiredCapacity
|
|
1709
|
+
);
|
|
1710
|
+
if (suggested) {
|
|
1711
|
+
setSuggestedFundingAmountCkb(suggested);
|
|
1712
|
+
displayMessage = `Insufficient capacity: current amount ${params.fundingAmountCkb} CKB may not cover fee. Suggested amount: ${suggested} CKB. Original error: ${message}`;
|
|
1713
|
+
}
|
|
1714
|
+
} catch {
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
setError(displayMessage);
|
|
1718
|
+
if (params.externalWallet) {
|
|
1719
|
+
const hasKnownAbortPattern = shouldDiagnoseFundingAbortError(message);
|
|
1720
|
+
if (!hasKnownAbortPattern) {
|
|
1721
|
+
onLog?.(
|
|
1722
|
+
"External funding error did not match known abort patterns; running best-effort diagnostics."
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
const targetPubkey = (() => {
|
|
1726
|
+
try {
|
|
1727
|
+
return toHexPrefixed2(params.pubkey);
|
|
1728
|
+
} catch {
|
|
1729
|
+
return void 0;
|
|
1730
|
+
}
|
|
1731
|
+
})();
|
|
1732
|
+
try {
|
|
1733
|
+
const diagnoseResult = await diagnoseExternalFundingFailure({
|
|
1734
|
+
node,
|
|
1735
|
+
rawError: message,
|
|
1736
|
+
targetPubkey,
|
|
1737
|
+
fundingLockScript: effectiveFundingLockScript,
|
|
1738
|
+
requestedFundingShannons,
|
|
1739
|
+
ckbRpcUrl: params.ckbRpcUrl
|
|
1740
|
+
});
|
|
1741
|
+
if (diagnoseResult.summary) {
|
|
1742
|
+
setDiagnostic(diagnoseResult.summary);
|
|
1743
|
+
onLog?.(`External funding diagnostic: ${diagnoseResult.summary}`);
|
|
1744
|
+
} else {
|
|
1745
|
+
const fallbackDiagnostic = "No additional channel diagnostics were available for this external funding error.";
|
|
1746
|
+
setDiagnostic(fallbackDiagnostic);
|
|
1747
|
+
onLog?.(fallbackDiagnostic);
|
|
1748
|
+
}
|
|
1749
|
+
} catch (diagnosticError) {
|
|
1750
|
+
const diagnosticMessage = diagnosticError instanceof Error ? diagnosticError.message : String(diagnosticError);
|
|
1751
|
+
const fallbackDiagnostic = "External funding diagnostics failed; no additional diagnostic details are available.";
|
|
1752
|
+
setDiagnostic(fallbackDiagnostic);
|
|
1753
|
+
onLog?.(`External funding diagnostics failed: ${diagnosticMessage}`);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
onLog?.(`Channel open flow failed: ${message}`);
|
|
1757
|
+
return null;
|
|
1758
|
+
} finally {
|
|
1759
|
+
setIsOpening(false);
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1762
|
+
[node, onLog]
|
|
1763
|
+
);
|
|
1764
|
+
return useMemo2(
|
|
1765
|
+
() => ({
|
|
1766
|
+
openChannel,
|
|
1767
|
+
reset,
|
|
1768
|
+
isOpening,
|
|
1769
|
+
error,
|
|
1770
|
+
diagnostic,
|
|
1771
|
+
suggestedFundingAmountCkb,
|
|
1772
|
+
lastResult
|
|
1773
|
+
}),
|
|
1774
|
+
[openChannel, reset, isOpening, error, diagnostic, suggestedFundingAmountCkb, lastResult]
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/use-fiber-payment.ts
|
|
1779
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
|
|
1780
|
+
function asErrorMessage2(error) {
|
|
1781
|
+
if (error instanceof Error) {
|
|
1782
|
+
return error.message;
|
|
1783
|
+
}
|
|
1784
|
+
return String(error);
|
|
1785
|
+
}
|
|
1786
|
+
function useFiberPayment(node) {
|
|
1787
|
+
const [isPaying, setIsPaying] = useState4(false);
|
|
1788
|
+
const [paymentResult, setPaymentResult] = useState4(null);
|
|
1789
|
+
const [error, setError] = useState4(null);
|
|
1790
|
+
const isMountedRef = useRef3(true);
|
|
1791
|
+
useEffect3(
|
|
1792
|
+
() => () => {
|
|
1793
|
+
isMountedRef.current = false;
|
|
1794
|
+
},
|
|
1795
|
+
[]
|
|
1796
|
+
);
|
|
1797
|
+
const ensureNode = useCallback4(() => {
|
|
1798
|
+
if (!node) {
|
|
1799
|
+
throw new Error("Node is not initialized");
|
|
1800
|
+
}
|
|
1801
|
+
return node;
|
|
1802
|
+
}, [node]);
|
|
1803
|
+
const parseInvoiceInternal = useCallback4(
|
|
1804
|
+
async (invoice) => {
|
|
1805
|
+
const activeNode = ensureNode();
|
|
1806
|
+
return activeNode.parseInvoice({ invoice });
|
|
1807
|
+
},
|
|
1808
|
+
[ensureNode]
|
|
1809
|
+
);
|
|
1810
|
+
const sendPaymentInternal = useCallback4(
|
|
1811
|
+
async (invoice) => {
|
|
1812
|
+
const activeNode = ensureNode();
|
|
1813
|
+
return activeNode.sendPayment({ invoice });
|
|
1814
|
+
},
|
|
1815
|
+
[ensureNode]
|
|
1816
|
+
);
|
|
1817
|
+
const waitForPaymentInternal = useCallback4(
|
|
1818
|
+
async (paymentHash) => {
|
|
1819
|
+
const activeNode = ensureNode();
|
|
1820
|
+
return activeNode.waitForPayment(paymentHash);
|
|
1821
|
+
},
|
|
1822
|
+
[ensureNode]
|
|
1823
|
+
);
|
|
1824
|
+
const parseInvoice = useCallback4(
|
|
1825
|
+
async (invoice) => {
|
|
1826
|
+
if (isMountedRef.current) {
|
|
1827
|
+
setError(null);
|
|
1828
|
+
}
|
|
1829
|
+
try {
|
|
1830
|
+
return await parseInvoiceInternal(invoice);
|
|
1831
|
+
} catch (parseError) {
|
|
1832
|
+
if (isMountedRef.current) {
|
|
1833
|
+
setError(asErrorMessage2(parseError));
|
|
1834
|
+
}
|
|
1835
|
+
throw parseError;
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
[parseInvoiceInternal]
|
|
1839
|
+
);
|
|
1840
|
+
const sendPayment = useCallback4(
|
|
1841
|
+
async (invoice) => {
|
|
1842
|
+
if (isMountedRef.current) {
|
|
1843
|
+
setIsPaying(true);
|
|
1844
|
+
setError(null);
|
|
1845
|
+
setPaymentResult(null);
|
|
1846
|
+
}
|
|
1847
|
+
try {
|
|
1848
|
+
return await sendPaymentInternal(invoice);
|
|
1849
|
+
} catch (sendError) {
|
|
1850
|
+
if (isMountedRef.current) {
|
|
1851
|
+
setError(asErrorMessage2(sendError));
|
|
1852
|
+
}
|
|
1853
|
+
throw sendError;
|
|
1854
|
+
} finally {
|
|
1855
|
+
if (isMountedRef.current) {
|
|
1856
|
+
setIsPaying(false);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
[sendPaymentInternal]
|
|
1861
|
+
);
|
|
1862
|
+
const waitForPayment = useCallback4(
|
|
1863
|
+
async (paymentHash) => {
|
|
1864
|
+
if (isMountedRef.current) {
|
|
1865
|
+
setIsPaying(true);
|
|
1866
|
+
setError(null);
|
|
1867
|
+
setPaymentResult(null);
|
|
1868
|
+
}
|
|
1869
|
+
try {
|
|
1870
|
+
const result = await waitForPaymentInternal(paymentHash);
|
|
1871
|
+
if (isMountedRef.current) {
|
|
1872
|
+
setPaymentResult(result);
|
|
1873
|
+
}
|
|
1874
|
+
return result;
|
|
1875
|
+
} catch (waitError) {
|
|
1876
|
+
if (isMountedRef.current) {
|
|
1877
|
+
setError(asErrorMessage2(waitError));
|
|
1878
|
+
}
|
|
1879
|
+
throw waitError;
|
|
1880
|
+
} finally {
|
|
1881
|
+
if (isMountedRef.current) {
|
|
1882
|
+
setIsPaying(false);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
},
|
|
1886
|
+
[waitForPaymentInternal]
|
|
1887
|
+
);
|
|
1888
|
+
const payInvoice = useCallback4(
|
|
1889
|
+
async (invoice) => {
|
|
1890
|
+
if (isMountedRef.current) {
|
|
1891
|
+
setIsPaying(true);
|
|
1892
|
+
setError(null);
|
|
1893
|
+
setPaymentResult(null);
|
|
1894
|
+
}
|
|
1895
|
+
try {
|
|
1896
|
+
const parsed = await parseInvoiceInternal(invoice);
|
|
1897
|
+
await sendPaymentInternal(invoice);
|
|
1898
|
+
const paymentHash = parsed.invoice.data.payment_hash;
|
|
1899
|
+
const result = await waitForPaymentInternal(paymentHash);
|
|
1900
|
+
if (result.status === "Failed") {
|
|
1901
|
+
throw new Error(result.failed_error ?? "Payment failed during routing/execution");
|
|
1902
|
+
}
|
|
1903
|
+
if (isMountedRef.current) {
|
|
1904
|
+
setPaymentResult(result);
|
|
1905
|
+
}
|
|
1906
|
+
} catch (payError) {
|
|
1907
|
+
if (isMountedRef.current) {
|
|
1908
|
+
setError(asErrorMessage2(payError));
|
|
1909
|
+
}
|
|
1910
|
+
} finally {
|
|
1911
|
+
if (isMountedRef.current) {
|
|
1912
|
+
setIsPaying(false);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
},
|
|
1916
|
+
[parseInvoiceInternal, sendPaymentInternal, waitForPaymentInternal]
|
|
1917
|
+
);
|
|
1918
|
+
return {
|
|
1919
|
+
parseInvoice,
|
|
1920
|
+
sendPayment,
|
|
1921
|
+
waitForPayment,
|
|
1922
|
+
payInvoice,
|
|
1923
|
+
isPaying,
|
|
1924
|
+
paymentResult,
|
|
1925
|
+
error
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// src/fiber-node-button/use-panel-state.ts
|
|
1930
|
+
function useFiberNodeButtonPanelState(props) {
|
|
1931
|
+
const {
|
|
1932
|
+
network,
|
|
1933
|
+
fiber,
|
|
1934
|
+
onLog,
|
|
1935
|
+
onError,
|
|
1936
|
+
initialPeerPubkey,
|
|
1937
|
+
initialPeerAddress,
|
|
1938
|
+
initialFundingAmountCkb,
|
|
1939
|
+
externalFunding
|
|
1940
|
+
} = props;
|
|
1941
|
+
const [activeTab, setActiveTab] = useState5("workbench");
|
|
1942
|
+
const [peerPubkey, setPeerPubkey] = useState5(initialPeerPubkey);
|
|
1943
|
+
const [peerAddress, setPeerAddress] = useState5(initialPeerAddress);
|
|
1944
|
+
const [fundingAmountCkb, setFundingAmountCkb] = useState5(initialFundingAmountCkb);
|
|
1945
|
+
const [connectedPeers, setConnectedPeers] = useState5([]);
|
|
1946
|
+
const [isRefreshingPeers, setIsRefreshingPeers] = useState5(false);
|
|
1947
|
+
const [isConnectingPeer, setIsConnectingPeer] = useState5(false);
|
|
1948
|
+
const [graphNodes, setGraphNodes] = useState5([]);
|
|
1949
|
+
const [graphChannels, setGraphChannels] = useState5([]);
|
|
1950
|
+
const [isRefreshingGraph, setIsRefreshingGraph] = useState5(false);
|
|
1951
|
+
const [channels, setChannels] = useState5([]);
|
|
1952
|
+
const [channelFilter, setChannelFilter] = useState5("active");
|
|
1953
|
+
const [isRefreshingChannels, setIsRefreshingChannels] = useState5(false);
|
|
1954
|
+
const [closingChannelId, setClosingChannelId] = useState5(null);
|
|
1955
|
+
const [selectedChannelId, setSelectedChannelId] = useState5(null);
|
|
1956
|
+
const [forceCloseConfirmOpen, setForceCloseConfirmOpen] = useState5(false);
|
|
1957
|
+
const [invoiceInput, setInvoiceInput] = useState5("");
|
|
1958
|
+
const [createdInvoice, setCreatedInvoice] = useState5("");
|
|
1959
|
+
const [isCreatingInvoice, setIsCreatingInvoice] = useState5(false);
|
|
1960
|
+
const [latestError, setLatestError] = useState5(null);
|
|
1961
|
+
const [statusNotice, setStatusNotice] = useState5(null);
|
|
1962
|
+
const statusTimerRef = useRef4(null);
|
|
1963
|
+
const mountedRef = useRef4(true);
|
|
1964
|
+
const peerListId = useId2();
|
|
1965
|
+
const tabPanelId = useId2();
|
|
1966
|
+
const channelOpenFlow = useChannelOpenFlow({
|
|
1967
|
+
node: fiber.node,
|
|
1968
|
+
onLog
|
|
1969
|
+
});
|
|
1970
|
+
const { payInvoice, isPaying, paymentResult, error: paymentError } = useFiberPayment(fiber.node);
|
|
1971
|
+
const isNodeReady = fiber.isRunning && !!fiber.node;
|
|
1972
|
+
useEffect4(() => {
|
|
1973
|
+
return () => {
|
|
1974
|
+
mountedRef.current = false;
|
|
1975
|
+
if (statusTimerRef.current !== null) {
|
|
1976
|
+
window.clearTimeout(statusTimerRef.current);
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
}, []);
|
|
1980
|
+
const flashStatus = useCallback5((text, tone = "info") => {
|
|
1981
|
+
setStatusNotice({ tone, text });
|
|
1982
|
+
if (statusTimerRef.current !== null) {
|
|
1983
|
+
window.clearTimeout(statusTimerRef.current);
|
|
1984
|
+
}
|
|
1985
|
+
statusTimerRef.current = window.setTimeout(() => {
|
|
1986
|
+
setStatusNotice(null);
|
|
1987
|
+
statusTimerRef.current = null;
|
|
1988
|
+
}, 3200);
|
|
1989
|
+
}, []);
|
|
1990
|
+
const reportError = useCallback5(
|
|
1991
|
+
(message) => {
|
|
1992
|
+
setLatestError(message);
|
|
1993
|
+
onError?.(message);
|
|
1994
|
+
onLog?.(`fiber_panel_error_shown: ${summarizeError(message, 120)}`);
|
|
1995
|
+
},
|
|
1996
|
+
[onError, onLog]
|
|
1997
|
+
);
|
|
1998
|
+
const refreshConnectedPeers = useCallback5(async () => {
|
|
1999
|
+
if (!fiber.node) {
|
|
2000
|
+
if (mountedRef.current) {
|
|
2001
|
+
setConnectedPeers([]);
|
|
2002
|
+
}
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
if (!mountedRef.current) {
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
2008
|
+
setIsRefreshingPeers(true);
|
|
2009
|
+
try {
|
|
2010
|
+
const peers = await fiber.node.listPeers();
|
|
2011
|
+
if (!mountedRef.current) {
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
setConnectedPeers(peers.peers);
|
|
2015
|
+
setPeerPubkey((prev) => prev.trim() ? prev : peers.peers[0]?.pubkey ?? prev);
|
|
2016
|
+
onLog?.(`Loaded connected peers: ${peers.peers.length}.`);
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
if (!mountedRef.current) {
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2022
|
+
reportError(message);
|
|
2023
|
+
onLog?.(`Refresh peers failed: ${message}`);
|
|
2024
|
+
} finally {
|
|
2025
|
+
if (mountedRef.current) {
|
|
2026
|
+
setIsRefreshingPeers(false);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}, [fiber.node, onLog, reportError]);
|
|
2030
|
+
const refreshGraph = useCallback5(async () => {
|
|
2031
|
+
if (!fiber.node) {
|
|
2032
|
+
if (mountedRef.current) {
|
|
2033
|
+
setGraphNodes([]);
|
|
2034
|
+
setGraphChannels([]);
|
|
2035
|
+
}
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
if (!mountedRef.current) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
setIsRefreshingGraph(true);
|
|
2042
|
+
try {
|
|
2043
|
+
const [nodesResult, channelsResult] = await Promise.all([
|
|
2044
|
+
fiber.node.graphNodes({ limit: "0x8" }),
|
|
2045
|
+
fiber.node.graphChannels({ limit: "0x8" })
|
|
2046
|
+
]);
|
|
2047
|
+
if (!mountedRef.current) {
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
setGraphNodes(nodesResult.nodes);
|
|
2051
|
+
setGraphChannels(channelsResult.channels);
|
|
2052
|
+
onLog?.(
|
|
2053
|
+
`Loaded graph: ${nodesResult.nodes.length} nodes, ${channelsResult.channels.length} channels.`
|
|
2054
|
+
);
|
|
2055
|
+
} catch (error) {
|
|
2056
|
+
if (!mountedRef.current) {
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2060
|
+
reportError(message);
|
|
2061
|
+
onLog?.(`Refresh graph failed: ${message}`);
|
|
2062
|
+
} finally {
|
|
2063
|
+
if (mountedRef.current) {
|
|
2064
|
+
setIsRefreshingGraph(false);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}, [fiber.node, onLog, reportError]);
|
|
2068
|
+
const refreshChannels = useCallback5(async () => {
|
|
2069
|
+
if (!fiber.node) {
|
|
2070
|
+
if (mountedRef.current) {
|
|
2071
|
+
setChannels([]);
|
|
2072
|
+
}
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
if (!mountedRef.current) {
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
setIsRefreshingChannels(true);
|
|
2079
|
+
try {
|
|
2080
|
+
const result = await fiber.node.listChannels({ include_closed: true });
|
|
2081
|
+
if (!mountedRef.current) {
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
setChannels(result.channels);
|
|
2085
|
+
onLog?.(`Loaded channels: ${result.channels.length}.`);
|
|
2086
|
+
} catch (error) {
|
|
2087
|
+
if (!mountedRef.current) {
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2091
|
+
reportError(message);
|
|
2092
|
+
onLog?.(`Refresh channels failed: ${message}`);
|
|
2093
|
+
} finally {
|
|
2094
|
+
if (mountedRef.current) {
|
|
2095
|
+
setIsRefreshingChannels(false);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}, [fiber.node, onLog, reportError]);
|
|
2099
|
+
const closeChannel = useCallback5(
|
|
2100
|
+
async (channelId, force = false) => {
|
|
2101
|
+
if (!fiber.node) {
|
|
2102
|
+
reportError("Node is not connected.");
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
setClosingChannelId(channelId);
|
|
2106
|
+
try {
|
|
2107
|
+
const latest = await fiber.node.listChannels({ include_closed: true });
|
|
2108
|
+
const target = latest.channels.find((item) => item.channel_id === channelId);
|
|
2109
|
+
if (!target) {
|
|
2110
|
+
throw new Error(`Channel not found in latest snapshot: ${channelId}.`);
|
|
2111
|
+
}
|
|
2112
|
+
if (target.state.state_name === ChannelState2.Closed) {
|
|
2113
|
+
setChannels(latest.channels);
|
|
2114
|
+
flashStatus("Channel is already closed.", "info");
|
|
2115
|
+
onLog?.(`Channel already closed: ${channelId}`);
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (target.state.state_name === ChannelState2.ShuttingDown) {
|
|
2119
|
+
setChannels(latest.channels);
|
|
2120
|
+
flashStatus("Channel is already shutting down.", "info");
|
|
2121
|
+
onLog?.(`Channel is already shutting down: ${channelId}`);
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
if (isPendingChannelState(target.state.state_name)) {
|
|
2125
|
+
const params = {
|
|
2126
|
+
channel_id: channelId
|
|
2127
|
+
};
|
|
2128
|
+
await fiber.node.abandonChannel(params);
|
|
2129
|
+
flashStatus("Pending channel abandoned.", "success");
|
|
2130
|
+
onLog?.(`Pending channel abandoned: ${channelId}`);
|
|
2131
|
+
} else {
|
|
2132
|
+
const params = {
|
|
2133
|
+
channel_id: channelId,
|
|
2134
|
+
force
|
|
2135
|
+
};
|
|
2136
|
+
await fiber.node.shutdownChannel(params);
|
|
2137
|
+
flashStatus(force ? "Force close requested." : "Close requested.", "success");
|
|
2138
|
+
onLog?.(`${force ? "Force close" : "Close"} channel requested: ${channelId}`);
|
|
2139
|
+
}
|
|
2140
|
+
await refreshChannels();
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2143
|
+
reportError(message);
|
|
2144
|
+
} finally {
|
|
2145
|
+
setClosingChannelId(null);
|
|
2146
|
+
}
|
|
2147
|
+
},
|
|
2148
|
+
[fiber.node, flashStatus, onLog, refreshChannels, reportError]
|
|
2149
|
+
);
|
|
2150
|
+
const connectPeerByAddress = useCallback5(async () => {
|
|
2151
|
+
if (!fiber.node) {
|
|
2152
|
+
reportError("Node is not connected.");
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (!peerAddress.trim()) {
|
|
2156
|
+
reportError("Peer address is empty.");
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
setIsConnectingPeer(true);
|
|
2160
|
+
try {
|
|
2161
|
+
await fiber.node.connectPeer({
|
|
2162
|
+
address: peerAddress.trim(),
|
|
2163
|
+
save: true
|
|
2164
|
+
});
|
|
2165
|
+
flashStatus("Peer connected.", "success");
|
|
2166
|
+
onLog?.("Peer connected from address input.");
|
|
2167
|
+
await refreshConnectedPeers();
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2170
|
+
reportError(message);
|
|
2171
|
+
onLog?.(`Connect peer failed: ${message}`);
|
|
2172
|
+
} finally {
|
|
2173
|
+
setIsConnectingPeer(false);
|
|
2174
|
+
}
|
|
2175
|
+
}, [fiber.node, flashStatus, onLog, peerAddress, refreshConnectedPeers, reportError]);
|
|
2176
|
+
const openChannel = useCallback5(async () => {
|
|
2177
|
+
if (!fiber.node) {
|
|
2178
|
+
reportError("Node is not connected.");
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
if (!peerPubkey.trim()) {
|
|
2182
|
+
reportError("Target peer pubkey is empty.");
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
channelOpenFlow.reset();
|
|
2186
|
+
try {
|
|
2187
|
+
const pubkey = toHexPrefixed(peerPubkey);
|
|
2188
|
+
if (!externalFunding?.enabled) {
|
|
2189
|
+
const openResult2 = await channelOpenFlow.openChannel({
|
|
2190
|
+
pubkey,
|
|
2191
|
+
fundingAmountCkb,
|
|
2192
|
+
externalWallet: false
|
|
2193
|
+
});
|
|
2194
|
+
if (!openResult2) {
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
flashStatus("Open channel submitted.", "success");
|
|
2198
|
+
onLog?.("fiber_panel_primary_action_clicked: open_channel");
|
|
2199
|
+
await refreshChannels();
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
const resolved = await externalFunding.resolve({
|
|
2203
|
+
node: fiber.node,
|
|
2204
|
+
pubkey,
|
|
2205
|
+
fundingAmountCkb
|
|
2206
|
+
});
|
|
2207
|
+
const openResult = await channelOpenFlow.openChannel({
|
|
2208
|
+
pubkey,
|
|
2209
|
+
fundingAmountCkb,
|
|
2210
|
+
externalWallet: true,
|
|
2211
|
+
shutdownScript: resolved.shutdownScript,
|
|
2212
|
+
fundingLockScript: resolved.fundingLockScript,
|
|
2213
|
+
fundingLockScriptCellDeps: resolved.fundingLockScriptCellDeps,
|
|
2214
|
+
signFundingTx: resolved.signFundingTx,
|
|
2215
|
+
ckbRpcUrl: resolved.ckbRpcUrl
|
|
2216
|
+
});
|
|
2217
|
+
if (!openResult) {
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
flashStatus("Open channel submitted.", "success");
|
|
2221
|
+
onLog?.("fiber_panel_primary_action_clicked: open_channel");
|
|
2222
|
+
await refreshChannels();
|
|
2223
|
+
} catch (error) {
|
|
2224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2225
|
+
reportError(message);
|
|
2226
|
+
onLog?.(`Open channel failed: ${message}`);
|
|
2227
|
+
}
|
|
2228
|
+
}, [
|
|
2229
|
+
channelOpenFlow,
|
|
2230
|
+
externalFunding,
|
|
2231
|
+
fiber.node,
|
|
2232
|
+
flashStatus,
|
|
2233
|
+
fundingAmountCkb,
|
|
2234
|
+
onLog,
|
|
2235
|
+
peerPubkey,
|
|
2236
|
+
refreshChannels,
|
|
2237
|
+
reportError
|
|
2238
|
+
]);
|
|
2239
|
+
const createInvoice = useCallback5(async () => {
|
|
2240
|
+
if (!fiber.node) {
|
|
2241
|
+
reportError("Node is not connected.");
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
setIsCreatingInvoice(true);
|
|
2245
|
+
try {
|
|
2246
|
+
const created = await fiber.node.newInvoice({
|
|
2247
|
+
amount: ONE_CKB_SHANNONS,
|
|
2248
|
+
currency: network === "mainnet" ? "Fibb" : "Fibt",
|
|
2249
|
+
description: "FiberNodeButton invoice"
|
|
2250
|
+
});
|
|
2251
|
+
setCreatedInvoice(created.invoice_address);
|
|
2252
|
+
flashStatus("Invoice created.", "success");
|
|
2253
|
+
onLog?.("fiber_panel_primary_action_clicked: create_invoice");
|
|
2254
|
+
onLog?.(`Invoice created: ${shorten(created.invoice_address, 20, 8)}`);
|
|
2255
|
+
} catch (error) {
|
|
2256
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2257
|
+
reportError(message);
|
|
2258
|
+
} finally {
|
|
2259
|
+
setIsCreatingInvoice(false);
|
|
2260
|
+
}
|
|
2261
|
+
}, [fiber.node, flashStatus, network, onLog, reportError]);
|
|
2262
|
+
const submitPayment = useCallback5(async () => {
|
|
2263
|
+
if (!invoiceInput.trim()) {
|
|
2264
|
+
reportError("Invoice is empty.");
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
onLog?.("fiber_panel_primary_action_clicked: pay_invoice");
|
|
2268
|
+
await payInvoice(invoiceInput);
|
|
2269
|
+
}, [invoiceInput, onLog, payInvoice, reportError]);
|
|
2270
|
+
useEffect4(() => {
|
|
2271
|
+
if (!fiber.isRunning || !fiber.node) {
|
|
2272
|
+
setConnectedPeers([]);
|
|
2273
|
+
setGraphNodes([]);
|
|
2274
|
+
setGraphChannels([]);
|
|
2275
|
+
setChannels([]);
|
|
2276
|
+
setSelectedChannelId(null);
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
void refreshConnectedPeers();
|
|
2280
|
+
void refreshGraph();
|
|
2281
|
+
void refreshChannels();
|
|
2282
|
+
}, [fiber.isRunning, fiber.node, refreshChannels, refreshConnectedPeers, refreshGraph]);
|
|
2283
|
+
useEffect4(() => {
|
|
2284
|
+
if (paymentError) {
|
|
2285
|
+
reportError(paymentError);
|
|
2286
|
+
}
|
|
2287
|
+
}, [paymentError, reportError]);
|
|
2288
|
+
useEffect4(() => {
|
|
2289
|
+
if (channelOpenFlow.error) {
|
|
2290
|
+
reportError(channelOpenFlow.error);
|
|
2291
|
+
}
|
|
2292
|
+
}, [channelOpenFlow.error, reportError]);
|
|
2293
|
+
useEffect4(() => {
|
|
2294
|
+
if (!paymentResult) {
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
flashStatus(`Payment ${paymentResult.status}.`, "success");
|
|
2298
|
+
onLog?.(`Payment status: ${paymentResult.status}`);
|
|
2299
|
+
}, [flashStatus, onLog, paymentResult]);
|
|
2300
|
+
const channelCounts = useMemo3(() => {
|
|
2301
|
+
const counts = { active: 0, pending: 0, closed: 0, all: channels.length };
|
|
2302
|
+
for (const channel of channels) {
|
|
2303
|
+
counts[getChannelFilterState(channel)] += 1;
|
|
2304
|
+
}
|
|
2305
|
+
return counts;
|
|
2306
|
+
}, [channels]);
|
|
2307
|
+
const visibleChannels = useMemo3(
|
|
2308
|
+
() => channelFilter === "all" ? channels : channels.filter((channel) => getChannelFilterState(channel) === channelFilter),
|
|
2309
|
+
[channelFilter, channels]
|
|
2310
|
+
);
|
|
2311
|
+
const activeChannelCount = channelCounts.active;
|
|
2312
|
+
const selectedChannel = useMemo3(
|
|
2313
|
+
() => channels.find((channel) => channel.channel_id === selectedChannelId) ?? null,
|
|
2314
|
+
[channels, selectedChannelId]
|
|
2315
|
+
);
|
|
2316
|
+
useEffect4(() => {
|
|
2317
|
+
if (!selectedChannelId) {
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
if (!channels.some((channel) => channel.channel_id === selectedChannelId)) {
|
|
2321
|
+
setSelectedChannelId(null);
|
|
2322
|
+
setForceCloseConfirmOpen(false);
|
|
2323
|
+
}
|
|
2324
|
+
}, [channels, selectedChannelId]);
|
|
2325
|
+
useEffect4(() => {
|
|
2326
|
+
const isSelectedVisible = selectedChannelId ? visibleChannels.some((channel) => channel.channel_id === selectedChannelId) : false;
|
|
2327
|
+
if (!isSelectedVisible) {
|
|
2328
|
+
setSelectedChannelId(visibleChannels[0]?.channel_id ?? null);
|
|
2329
|
+
setForceCloseConfirmOpen(false);
|
|
2330
|
+
}
|
|
2331
|
+
}, [selectedChannelId, visibleChannels]);
|
|
2332
|
+
const selectedState = selectedChannel?.state.state_name;
|
|
2333
|
+
const selectedPending = selectedChannel ? isPendingChannelState(selectedState) : false;
|
|
2334
|
+
const selectedCanClose = !!selectedChannel && selectedState !== ChannelState2.Closed && selectedState !== ChannelState2.ShuttingDown;
|
|
2335
|
+
const selectedIsClosing = !!selectedChannel && closingChannelId === selectedChannel.channel_id;
|
|
2336
|
+
const connectorContext = useMemo3(
|
|
2337
|
+
() => ({
|
|
2338
|
+
fiber,
|
|
2339
|
+
externalFundingEnabled: !!externalFunding?.enabled,
|
|
2340
|
+
isOpeningChannel: channelOpenFlow.isOpening
|
|
2341
|
+
}),
|
|
2342
|
+
[channelOpenFlow.isOpening, externalFunding?.enabled, fiber]
|
|
2343
|
+
);
|
|
2344
|
+
const switchTab = useCallback5(
|
|
2345
|
+
(next) => {
|
|
2346
|
+
setActiveTab(next);
|
|
2347
|
+
onLog?.(`fiber_panel_tab_switched: ${next}`);
|
|
2348
|
+
},
|
|
2349
|
+
[onLog]
|
|
2350
|
+
);
|
|
2351
|
+
const refreshDiagnostics = useCallback5(async () => {
|
|
2352
|
+
await Promise.all([refreshConnectedPeers(), refreshGraph()]);
|
|
2353
|
+
flashStatus("Diagnostics refreshed.", "info");
|
|
2354
|
+
}, [flashStatus, refreshConnectedPeers, refreshGraph]);
|
|
2355
|
+
return {
|
|
2356
|
+
// tab
|
|
2357
|
+
activeTab,
|
|
2358
|
+
switchTab,
|
|
2359
|
+
tabPanelId,
|
|
2360
|
+
// inputs
|
|
2361
|
+
peerPubkey,
|
|
2362
|
+
setPeerPubkey,
|
|
2363
|
+
peerAddress,
|
|
2364
|
+
setPeerAddress,
|
|
2365
|
+
fundingAmountCkb,
|
|
2366
|
+
setFundingAmountCkb,
|
|
2367
|
+
peerListId,
|
|
2368
|
+
// peers
|
|
2369
|
+
connectedPeers,
|
|
2370
|
+
isRefreshingPeers,
|
|
2371
|
+
isConnectingPeer,
|
|
2372
|
+
refreshConnectedPeers,
|
|
2373
|
+
connectPeerByAddress,
|
|
2374
|
+
// graph
|
|
2375
|
+
graphNodes,
|
|
2376
|
+
graphChannels,
|
|
2377
|
+
isRefreshingGraph,
|
|
2378
|
+
refreshGraph,
|
|
2379
|
+
// channels
|
|
2380
|
+
channels,
|
|
2381
|
+
channelFilter,
|
|
2382
|
+
setChannelFilter,
|
|
2383
|
+
isRefreshingChannels,
|
|
2384
|
+
refreshChannels,
|
|
2385
|
+
closeChannel,
|
|
2386
|
+
selectedChannelId,
|
|
2387
|
+
setSelectedChannelId,
|
|
2388
|
+
selectedChannel,
|
|
2389
|
+
selectedPending,
|
|
2390
|
+
selectedCanClose,
|
|
2391
|
+
selectedIsClosing,
|
|
2392
|
+
forceCloseConfirmOpen,
|
|
2393
|
+
setForceCloseConfirmOpen,
|
|
2394
|
+
channelCounts,
|
|
2395
|
+
visibleChannels,
|
|
2396
|
+
activeChannelCount,
|
|
2397
|
+
// payments
|
|
2398
|
+
invoiceInput,
|
|
2399
|
+
setInvoiceInput,
|
|
2400
|
+
createdInvoice,
|
|
2401
|
+
isCreatingInvoice,
|
|
2402
|
+
createInvoice,
|
|
2403
|
+
submitPayment,
|
|
2404
|
+
isPaying,
|
|
2405
|
+
paymentResult,
|
|
2406
|
+
// flow
|
|
2407
|
+
channelOpenFlow,
|
|
2408
|
+
openChannel,
|
|
2409
|
+
// notices
|
|
2410
|
+
latestError,
|
|
2411
|
+
statusNotice,
|
|
2412
|
+
// diagnostics aggregate
|
|
2413
|
+
refreshDiagnostics,
|
|
2414
|
+
// misc
|
|
2415
|
+
isNodeReady,
|
|
2416
|
+
connectorContext
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
// src/fiber-node-button/workbench-tab.tsx
|
|
2421
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2422
|
+
function WorkbenchTab({
|
|
2423
|
+
state,
|
|
2424
|
+
fiber,
|
|
2425
|
+
externalFunding,
|
|
2426
|
+
renderConnectorSection,
|
|
2427
|
+
renderAction,
|
|
2428
|
+
t
|
|
2429
|
+
}) {
|
|
2430
|
+
const {
|
|
2431
|
+
isNodeReady,
|
|
2432
|
+
connectorContext,
|
|
2433
|
+
channelOpenFlow,
|
|
2434
|
+
peerListId,
|
|
2435
|
+
peerPubkey,
|
|
2436
|
+
setPeerPubkey,
|
|
2437
|
+
connectedPeers,
|
|
2438
|
+
fundingAmountCkb,
|
|
2439
|
+
setFundingAmountCkb,
|
|
2440
|
+
openChannel,
|
|
2441
|
+
isCreatingInvoice,
|
|
2442
|
+
createInvoice,
|
|
2443
|
+
createdInvoice,
|
|
2444
|
+
invoiceInput,
|
|
2445
|
+
setInvoiceInput,
|
|
2446
|
+
isPaying,
|
|
2447
|
+
submitPayment,
|
|
2448
|
+
paymentResult
|
|
2449
|
+
} = state;
|
|
2450
|
+
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
2451
|
+
/* @__PURE__ */ jsxs4("section", { style: styles2.section, children: [
|
|
2452
|
+
/* @__PURE__ */ jsxs4("div", { style: styles2.rowBetween, children: [
|
|
2453
|
+
/* @__PURE__ */ jsx5("h4", { style: styles2.sectionTitle, children: t("workbench.connectionPrep.title", "Connection Prep") }),
|
|
2454
|
+
/* @__PURE__ */ jsx5("span", { style: styles2.badge, children: isNodeReady ? t("workbench.connectionPrep.connected", "Connected") : t("workbench.connectionPrep.disconnected", "Disconnected") })
|
|
2455
|
+
] }),
|
|
2456
|
+
/* @__PURE__ */ jsxs4("p", { style: styles2.compactText, children: [
|
|
2457
|
+
t("workbench.connectionPrep.node", "Node"),
|
|
2458
|
+
":",
|
|
2459
|
+
" ",
|
|
2460
|
+
fiber.nodeInfo?.pubkey ? shorten(fiber.nodeInfo.pubkey, 18, 12) : t("meta.na", "N/A")
|
|
2461
|
+
] }),
|
|
2462
|
+
/* @__PURE__ */ jsxs4("p", { style: styles2.compactText, children: [
|
|
2463
|
+
t("workbench.connectionPrep.externalWallet", "External wallet"),
|
|
2464
|
+
":",
|
|
2465
|
+
" ",
|
|
2466
|
+
externalFunding?.enabled ? t("workbench.connectionPrep.enabled", "Enabled") : t("workbench.connectionPrep.disabled", "Disabled")
|
|
2467
|
+
] }),
|
|
2468
|
+
renderConnectorSection ? /* @__PURE__ */ jsx5("div", { style: { borderTop: "1px solid #e2e8f0", paddingTop: "0.55rem" }, children: renderConnectorSection(connectorContext) }) : null
|
|
2469
|
+
] }),
|
|
2470
|
+
/* @__PURE__ */ jsxs4("section", { style: styles2.section, children: [
|
|
2471
|
+
/* @__PURE__ */ jsxs4("div", { style: styles2.rowBetween, children: [
|
|
2472
|
+
/* @__PURE__ */ jsx5("h4", { style: styles2.sectionTitle, children: t("workbench.openChannel.title", "Open Channel") }),
|
|
2473
|
+
channelOpenFlow.lastResult ? /* @__PURE__ */ jsx5("span", { style: styles2.badge, children: t("workbench.openChannel.recentSuccess", "Recent Success") }) : null
|
|
2474
|
+
] }),
|
|
2475
|
+
/* @__PURE__ */ jsxs4("label", { style: styles2.fieldLabel, children: [
|
|
2476
|
+
t("workbench.openChannel.targetPeerPubkey", "Target Peer Pubkey"),
|
|
2477
|
+
/* @__PURE__ */ jsx5(
|
|
2478
|
+
"input",
|
|
2479
|
+
{
|
|
2480
|
+
style: styles2.input,
|
|
2481
|
+
list: peerListId,
|
|
2482
|
+
value: peerPubkey,
|
|
2483
|
+
onChange: (event) => setPeerPubkey(event.target.value),
|
|
2484
|
+
placeholder: connectedPeers[0]?.pubkey ?? "0x..."
|
|
2485
|
+
}
|
|
2486
|
+
),
|
|
2487
|
+
/* @__PURE__ */ jsx5("datalist", { id: peerListId, children: connectedPeers.map((peer) => /* @__PURE__ */ jsx5("option", { value: peer.pubkey }, peer.pubkey)) })
|
|
2488
|
+
] }),
|
|
2489
|
+
/* @__PURE__ */ jsxs4("label", { style: styles2.fieldLabel, children: [
|
|
2490
|
+
t("workbench.openChannel.fundingAmount", "Funding Amount (CKB)"),
|
|
2491
|
+
/* @__PURE__ */ jsx5(
|
|
2492
|
+
"input",
|
|
2493
|
+
{
|
|
2494
|
+
style: styles2.input,
|
|
2495
|
+
value: fundingAmountCkb,
|
|
2496
|
+
onChange: (event) => setFundingAmountCkb(event.target.value),
|
|
2497
|
+
placeholder: "1000"
|
|
2498
|
+
}
|
|
2499
|
+
)
|
|
2500
|
+
] }),
|
|
2501
|
+
/* @__PURE__ */ jsx5("div", { style: styles2.row, children: renderPanelAction({
|
|
2502
|
+
id: "open-channel",
|
|
2503
|
+
fiber,
|
|
2504
|
+
state,
|
|
2505
|
+
renderAction,
|
|
2506
|
+
t,
|
|
2507
|
+
buttonStyle: styles2.primaryButton,
|
|
2508
|
+
defaultProps: {
|
|
2509
|
+
id: "open-channel",
|
|
2510
|
+
label: t("actions.openChannel", "Open Channel"),
|
|
2511
|
+
loadingLabel: t("actions.openChannel.loading", "Opening..."),
|
|
2512
|
+
disabled: !isNodeReady || channelOpenFlow.isOpening || !peerPubkey.trim(),
|
|
2513
|
+
loading: channelOpenFlow.isOpening,
|
|
2514
|
+
onTrigger: openChannel
|
|
2515
|
+
}
|
|
2516
|
+
}) }),
|
|
2517
|
+
channelOpenFlow.lastResult ? /* @__PURE__ */ jsxs4("p", { style: styles2.compactText, children: [
|
|
2518
|
+
t("workbench.openChannel.lastChannel", "Last channel"),
|
|
2519
|
+
":",
|
|
2520
|
+
" ",
|
|
2521
|
+
shorten(channelOpenFlow.lastResult.channelId, 14, 8)
|
|
2522
|
+
] }) : null,
|
|
2523
|
+
channelOpenFlow.suggestedFundingAmountCkb ? /* @__PURE__ */ jsxs4("p", { style: styles2.compactText, children: [
|
|
2524
|
+
t("workbench.openChannel.suggestedAmount", "Suggested amount"),
|
|
2525
|
+
":",
|
|
2526
|
+
" ",
|
|
2527
|
+
channelOpenFlow.suggestedFundingAmountCkb,
|
|
2528
|
+
" CKB"
|
|
2529
|
+
] }) : null
|
|
2530
|
+
] }),
|
|
2531
|
+
/* @__PURE__ */ jsxs4("section", { style: styles2.section, children: [
|
|
2532
|
+
/* @__PURE__ */ jsx5("h4", { style: styles2.sectionTitle, children: t("workbench.payments.title", "Payments") }),
|
|
2533
|
+
/* @__PURE__ */ jsxs4("div", { style: styles2.row, children: [
|
|
2534
|
+
renderPanelAction({
|
|
2535
|
+
id: "create-invoice",
|
|
2536
|
+
fiber,
|
|
2537
|
+
state,
|
|
2538
|
+
renderAction,
|
|
2539
|
+
t,
|
|
2540
|
+
defaultProps: {
|
|
2541
|
+
id: "create-invoice",
|
|
2542
|
+
label: t("actions.createInvoice", "Create Invoice (1 CKB)"),
|
|
2543
|
+
loadingLabel: t("actions.createInvoice.loading", "Creating..."),
|
|
2544
|
+
disabled: isCreatingInvoice || !isNodeReady,
|
|
2545
|
+
loading: isCreatingInvoice,
|
|
2546
|
+
onTrigger: createInvoice
|
|
2547
|
+
}
|
|
2548
|
+
}),
|
|
2549
|
+
createdInvoice ? /* @__PURE__ */ jsx5("span", { style: styles2.compactText, children: shorten(createdInvoice, 20, 10) }) : null
|
|
2550
|
+
] }),
|
|
2551
|
+
/* @__PURE__ */ jsxs4("label", { style: styles2.fieldLabel, children: [
|
|
2552
|
+
t("workbench.payments.invoice", "Invoice"),
|
|
2553
|
+
/* @__PURE__ */ jsx5(
|
|
2554
|
+
"input",
|
|
2555
|
+
{
|
|
2556
|
+
style: styles2.input,
|
|
2557
|
+
value: invoiceInput,
|
|
2558
|
+
onChange: (event) => setInvoiceInput(event.target.value),
|
|
2559
|
+
placeholder: t("workbench.payments.invoicePlaceholder", "Paste invoice to pay")
|
|
2560
|
+
}
|
|
2561
|
+
)
|
|
2562
|
+
] }),
|
|
2563
|
+
/* @__PURE__ */ jsxs4("div", { style: styles2.rowBetween, children: [
|
|
2564
|
+
renderPanelAction({
|
|
2565
|
+
id: "pay-invoice",
|
|
2566
|
+
fiber,
|
|
2567
|
+
state,
|
|
2568
|
+
renderAction,
|
|
2569
|
+
t,
|
|
2570
|
+
buttonStyle: styles2.primaryButton,
|
|
2571
|
+
defaultProps: {
|
|
2572
|
+
id: "pay-invoice",
|
|
2573
|
+
label: t("actions.payInvoice", "Pay Invoice"),
|
|
2574
|
+
loadingLabel: t("actions.payInvoice.loading", "Paying..."),
|
|
2575
|
+
disabled: isPaying || !isNodeReady || !invoiceInput.trim(),
|
|
2576
|
+
loading: isPaying,
|
|
2577
|
+
onTrigger: submitPayment
|
|
2578
|
+
}
|
|
2579
|
+
}),
|
|
2580
|
+
/* @__PURE__ */ jsxs4("span", { style: styles2.compactText, children: [
|
|
2581
|
+
t("workbench.payments.status", "Status"),
|
|
2582
|
+
":",
|
|
2583
|
+
" ",
|
|
2584
|
+
paymentResult?.status ?? t("workbench.payments.idle", "Idle")
|
|
2585
|
+
] })
|
|
2586
|
+
] })
|
|
2587
|
+
] })
|
|
2588
|
+
] });
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// src/fiber-node-button/panel.tsx
|
|
2592
|
+
import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2593
|
+
function toDomSafeTabId(tabId, index) {
|
|
2594
|
+
const normalized = tabId.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2595
|
+
const safeBase = normalized.length > 0 ? normalized : "tab";
|
|
2596
|
+
return `${safeBase}-${index + 1}`;
|
|
2597
|
+
}
|
|
2598
|
+
function resolveTabLabel(tabId, t) {
|
|
2599
|
+
const builtIn = TAB_ITEMS.find((item) => item.id === tabId);
|
|
2600
|
+
if (!builtIn) {
|
|
2601
|
+
return tabId;
|
|
2602
|
+
}
|
|
2603
|
+
if (builtIn.id === "workbench") {
|
|
2604
|
+
return t("tabs.workbench", builtIn.label);
|
|
2605
|
+
}
|
|
2606
|
+
if (builtIn.id === "channels") {
|
|
2607
|
+
return t("tabs.channels", builtIn.label);
|
|
2608
|
+
}
|
|
2609
|
+
return t("tabs.diagnostics", builtIn.label);
|
|
2610
|
+
}
|
|
2611
|
+
function resolveTabs(tabs, t) {
|
|
2612
|
+
const defaultIds = TAB_ITEMS.map((item) => item.id);
|
|
2613
|
+
if (!tabs || tabs.length === 0) {
|
|
2614
|
+
return defaultIds.map((id, index) => ({
|
|
2615
|
+
id,
|
|
2616
|
+
domId: toDomSafeTabId(id, index),
|
|
2617
|
+
label: resolveTabLabel(id, t)
|
|
2618
|
+
}));
|
|
2619
|
+
}
|
|
2620
|
+
const tabConfigById = /* @__PURE__ */ new Map();
|
|
2621
|
+
const orderedIds = [];
|
|
2622
|
+
for (const tabConfig of tabs) {
|
|
2623
|
+
tabConfigById.set(tabConfig.id, tabConfig);
|
|
2624
|
+
if (!orderedIds.includes(tabConfig.id)) {
|
|
2625
|
+
orderedIds.push(tabConfig.id);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
for (const id of defaultIds) {
|
|
2629
|
+
if (!orderedIds.includes(id)) {
|
|
2630
|
+
orderedIds.push(id);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
const resolvedTabs = [];
|
|
2634
|
+
for (const [index, id] of orderedIds.entries()) {
|
|
2635
|
+
const config = tabConfigById.get(id);
|
|
2636
|
+
if (config?.hidden) {
|
|
2637
|
+
continue;
|
|
2638
|
+
}
|
|
2639
|
+
const label = typeof config?.label === "function" ? config.label(t) : config?.label ?? resolveTabLabel(id, t);
|
|
2640
|
+
const tabItem = {
|
|
2641
|
+
id,
|
|
2642
|
+
domId: toDomSafeTabId(id, index),
|
|
2643
|
+
label
|
|
2644
|
+
};
|
|
2645
|
+
if (config?.render) {
|
|
2646
|
+
tabItem.render = config.render;
|
|
2647
|
+
}
|
|
2648
|
+
resolvedTabs.push(tabItem);
|
|
2649
|
+
}
|
|
2650
|
+
return resolvedTabs;
|
|
2651
|
+
}
|
|
2652
|
+
function FiberNodeButtonPanel(props) {
|
|
2653
|
+
const {
|
|
2654
|
+
dropdownContext,
|
|
2655
|
+
fiber,
|
|
2656
|
+
onLog,
|
|
2657
|
+
externalFunding,
|
|
2658
|
+
renderConnectorSection,
|
|
2659
|
+
tabs,
|
|
2660
|
+
renderTabContent,
|
|
2661
|
+
renderAction
|
|
2662
|
+
} = props;
|
|
2663
|
+
const t = props.t ?? defaultFiberNodeButtonI18n;
|
|
2664
|
+
const state = useFiberNodeButtonPanelState(props);
|
|
2665
|
+
const forceCloseDialogRef = useRef5(null);
|
|
2666
|
+
const {
|
|
2667
|
+
activeTab,
|
|
2668
|
+
switchTab,
|
|
2669
|
+
tabPanelId,
|
|
2670
|
+
statusNotice,
|
|
2671
|
+
latestError,
|
|
2672
|
+
activeChannelCount,
|
|
2673
|
+
connectedPeers,
|
|
2674
|
+
forceCloseConfirmOpen,
|
|
2675
|
+
setForceCloseConfirmOpen,
|
|
2676
|
+
selectedChannel,
|
|
2677
|
+
closeChannel,
|
|
2678
|
+
openChannel,
|
|
2679
|
+
createInvoice,
|
|
2680
|
+
submitPayment
|
|
2681
|
+
} = state;
|
|
2682
|
+
const tabActions = useMemo4(
|
|
2683
|
+
() => ({
|
|
2684
|
+
openChannel: async () => {
|
|
2685
|
+
await openChannel();
|
|
2686
|
+
},
|
|
2687
|
+
createInvoice: async () => {
|
|
2688
|
+
await createInvoice();
|
|
2689
|
+
},
|
|
2690
|
+
payInvoice: async () => {
|
|
2691
|
+
await submitPayment();
|
|
2692
|
+
},
|
|
2693
|
+
closeChannel: async (channelId) => {
|
|
2694
|
+
await closeChannel(channelId, false);
|
|
2695
|
+
},
|
|
2696
|
+
forceCloseChannel: async (channelId) => {
|
|
2697
|
+
await closeChannel(channelId, true);
|
|
2698
|
+
}
|
|
2699
|
+
}),
|
|
2700
|
+
[closeChannel, createInvoice, openChannel, submitPayment]
|
|
2701
|
+
);
|
|
2702
|
+
const tabContext = useMemo4(
|
|
2703
|
+
() => ({
|
|
2704
|
+
fiber,
|
|
2705
|
+
state,
|
|
2706
|
+
externalFundingEnabled: !!externalFunding?.enabled,
|
|
2707
|
+
t,
|
|
2708
|
+
actions: tabActions
|
|
2709
|
+
}),
|
|
2710
|
+
[externalFunding?.enabled, fiber, state, t, tabActions]
|
|
2711
|
+
);
|
|
2712
|
+
const resolvedTabs = useMemo4(() => resolveTabs(tabs, t), [t, tabs]);
|
|
2713
|
+
const effectiveActiveTab = useMemo4(
|
|
2714
|
+
() => resolvedTabs.some((tab) => tab.id === activeTab) ? activeTab : resolvedTabs[0]?.id ?? null,
|
|
2715
|
+
[activeTab, resolvedTabs]
|
|
2716
|
+
);
|
|
2717
|
+
const selectedResolvedTab = useMemo4(
|
|
2718
|
+
() => resolvedTabs.find((tab) => tab.id === effectiveActiveTab) ?? null,
|
|
2719
|
+
[effectiveActiveTab, resolvedTabs]
|
|
2720
|
+
);
|
|
2721
|
+
const tabListStyle = useMemo4(
|
|
2722
|
+
() => ({
|
|
2723
|
+
...styles2.tabList,
|
|
2724
|
+
gridTemplateColumns: `repeat(${Math.max(1, resolvedTabs.length)}, minmax(112px, 1fr))`,
|
|
2725
|
+
overflowX: "auto"
|
|
2726
|
+
}),
|
|
2727
|
+
[resolvedTabs.length]
|
|
2728
|
+
);
|
|
2729
|
+
const overriddenTabContent = effectiveActiveTab ? renderTabContent?.(effectiveActiveTab, tabContext) : void 0;
|
|
2730
|
+
let tabContent = overriddenTabContent;
|
|
2731
|
+
if (tabContent === void 0) {
|
|
2732
|
+
if (selectedResolvedTab?.render) {
|
|
2733
|
+
tabContent = selectedResolvedTab.render(tabContext);
|
|
2734
|
+
} else if (effectiveActiveTab === "workbench") {
|
|
2735
|
+
tabContent = /* @__PURE__ */ jsx6(
|
|
2736
|
+
WorkbenchTab,
|
|
2737
|
+
{
|
|
2738
|
+
state,
|
|
2739
|
+
fiber,
|
|
2740
|
+
externalFunding,
|
|
2741
|
+
renderConnectorSection,
|
|
2742
|
+
renderAction,
|
|
2743
|
+
t
|
|
2744
|
+
}
|
|
2745
|
+
);
|
|
2746
|
+
} else if (effectiveActiveTab === "channels") {
|
|
2747
|
+
tabContent = /* @__PURE__ */ jsx6(ChannelsTab, { state, onLog, renderAction, t, fiber });
|
|
2748
|
+
} else if (effectiveActiveTab === "diagnostics") {
|
|
2749
|
+
tabContent = /* @__PURE__ */ jsx6(DiagnosticsTab, { state, t });
|
|
2750
|
+
} else if (effectiveActiveTab === null) {
|
|
2751
|
+
tabContent = /* @__PURE__ */ jsx6("div", { style: styles2.notice, children: t("tabs.empty", "No visible tabs are configured.") });
|
|
2752
|
+
} else {
|
|
2753
|
+
tabContent = /* @__PURE__ */ jsx6("div", { style: styles2.notice, children: t("tabs.unimplemented", "Tab content is not implemented.") });
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
useEffect5(() => {
|
|
2757
|
+
if (forceCloseConfirmOpen) {
|
|
2758
|
+
forceCloseDialogRef.current?.focus();
|
|
2759
|
+
}
|
|
2760
|
+
}, [forceCloseConfirmOpen]);
|
|
2761
|
+
useEffect5(() => {
|
|
2762
|
+
if (resolvedTabs.length === 0) {
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
if (!resolvedTabs.some((tab) => tab.id === activeTab)) {
|
|
2766
|
+
switchTab(resolvedTabs[0].id);
|
|
2767
|
+
}
|
|
2768
|
+
}, [activeTab, resolvedTabs, switchTab]);
|
|
2769
|
+
return /* @__PURE__ */ jsxs5("div", { style: styles2.shell, children: [
|
|
2770
|
+
/* @__PURE__ */ jsxs5("header", { style: styles2.globalBar, children: [
|
|
2771
|
+
/* @__PURE__ */ jsxs5("div", { style: styles2.globalRow, children: [
|
|
2772
|
+
/* @__PURE__ */ jsxs5("div", { style: styles2.globalMetrics, children: [
|
|
2773
|
+
/* @__PURE__ */ jsxs5("span", { style: styles2.metricInline, children: [
|
|
2774
|
+
/* @__PURE__ */ jsx6(
|
|
2775
|
+
"span",
|
|
2776
|
+
{
|
|
2777
|
+
style: {
|
|
2778
|
+
...styles2.metricDot,
|
|
2779
|
+
background: fiber.state === "running" ? "#16a34a" : fiber.state === "error" ? "#dc2626" : "#64748b"
|
|
2780
|
+
},
|
|
2781
|
+
"aria-hidden": "true"
|
|
2782
|
+
}
|
|
2783
|
+
),
|
|
2784
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricMain, children: fiber.state }),
|
|
2785
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricSub, children: t("metrics.node", "Node") })
|
|
2786
|
+
] }),
|
|
2787
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricDivider, "aria-hidden": "true", children: "|" }),
|
|
2788
|
+
/* @__PURE__ */ jsxs5("span", { style: styles2.metricInline, children: [
|
|
2789
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricMain, children: externalFunding?.enabled ? t("metrics.funding.external", "External") : t("metrics.funding.internal", "Internal") }),
|
|
2790
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricSub, children: t("metrics.funding", "Funding") })
|
|
2791
|
+
] }),
|
|
2792
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricDivider, "aria-hidden": "true", children: "|" }),
|
|
2793
|
+
/* @__PURE__ */ jsxs5("span", { style: styles2.metricInline, children: [
|
|
2794
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricMain, children: activeChannelCount }),
|
|
2795
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricSub, children: t("metrics.active", "Active") })
|
|
2796
|
+
] }),
|
|
2797
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricDivider, "aria-hidden": "true", children: "|" }),
|
|
2798
|
+
/* @__PURE__ */ jsxs5("span", { style: styles2.metricInline, children: [
|
|
2799
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricMain, children: connectedPeers.length }),
|
|
2800
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricSub, children: t("metrics.peers", "Peers") })
|
|
2801
|
+
] }),
|
|
2802
|
+
latestError ? /* @__PURE__ */ jsxs5(Fragment5, { children: [
|
|
2803
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricDivider, "aria-hidden": "true", children: "|" }),
|
|
2804
|
+
/* @__PURE__ */ jsxs5("span", { style: styles2.metricInline, children: [
|
|
2805
|
+
/* @__PURE__ */ jsx6(
|
|
2806
|
+
"span",
|
|
2807
|
+
{
|
|
2808
|
+
style: {
|
|
2809
|
+
...styles2.metricDot,
|
|
2810
|
+
background: "#dc2626"
|
|
2811
|
+
},
|
|
2812
|
+
"aria-hidden": "true"
|
|
2813
|
+
}
|
|
2814
|
+
),
|
|
2815
|
+
/* @__PURE__ */ jsx6("span", { style: styles2.metricMain, children: t("metrics.error", "Error") })
|
|
2816
|
+
] })
|
|
2817
|
+
] }) : null
|
|
2818
|
+
] }),
|
|
2819
|
+
/* @__PURE__ */ jsxs5("div", { style: styles2.globalActions, children: [
|
|
2820
|
+
/* @__PURE__ */ jsx6(
|
|
2821
|
+
"button",
|
|
2822
|
+
{
|
|
2823
|
+
type: "button",
|
|
2824
|
+
style: styles2.globalActionButton,
|
|
2825
|
+
onClick: () => {
|
|
2826
|
+
void dropdownContext.disconnect();
|
|
2827
|
+
},
|
|
2828
|
+
"aria-label": t("actions.disconnect.aria", "Disconnect node"),
|
|
2829
|
+
children: t("actions.disconnect", "Disconnect")
|
|
2830
|
+
}
|
|
2831
|
+
),
|
|
2832
|
+
/* @__PURE__ */ jsx6(
|
|
2833
|
+
"button",
|
|
2834
|
+
{
|
|
2835
|
+
type: "button",
|
|
2836
|
+
style: styles2.globalActionButton,
|
|
2837
|
+
onClick: () => {
|
|
2838
|
+
dropdownContext.closeDropdown();
|
|
2839
|
+
},
|
|
2840
|
+
"aria-label": t("actions.closePanel.aria", "Close panel"),
|
|
2841
|
+
children: t("actions.closePanel", "Close Panel")
|
|
2842
|
+
}
|
|
2843
|
+
)
|
|
2844
|
+
] })
|
|
2845
|
+
] }),
|
|
2846
|
+
/* @__PURE__ */ jsxs5("div", { style: styles2.globalMeta, children: [
|
|
2847
|
+
/* @__PURE__ */ jsxs5("p", { style: styles2.inlineCode, children: [
|
|
2848
|
+
t("meta.node", "Node"),
|
|
2849
|
+
":",
|
|
2850
|
+
" ",
|
|
2851
|
+
fiber.nodeInfo?.pubkey ? shorten(fiber.nodeInfo.pubkey, 18, 12) : t("meta.na", "N/A")
|
|
2852
|
+
] }),
|
|
2853
|
+
latestError ? /* @__PURE__ */ jsxs5("p", { style: styles2.globalErrorInline, children: [
|
|
2854
|
+
t("meta.recentError", "Recent error"),
|
|
2855
|
+
": ",
|
|
2856
|
+
summarizeError(latestError, 92)
|
|
2857
|
+
] }) : null
|
|
2858
|
+
] })
|
|
2859
|
+
] }),
|
|
2860
|
+
/* @__PURE__ */ jsx6("div", { role: "tablist", "aria-label": t("tabs.aria", "Fiber panel tabs"), style: tabListStyle, children: resolvedTabs.map((tab) => {
|
|
2861
|
+
const selected = effectiveActiveTab === tab.id;
|
|
2862
|
+
return /* @__PURE__ */ jsx6(
|
|
2863
|
+
"button",
|
|
2864
|
+
{
|
|
2865
|
+
id: `${tabPanelId}-tab-${tab.domId}`,
|
|
2866
|
+
type: "button",
|
|
2867
|
+
role: "tab",
|
|
2868
|
+
"aria-selected": selected,
|
|
2869
|
+
"aria-controls": selected ? `${tabPanelId}-panel-${tab.domId}` : void 0,
|
|
2870
|
+
style: selected ? styles2.tabButtonActive : styles2.tabButton,
|
|
2871
|
+
onClick: () => switchTab(tab.id),
|
|
2872
|
+
children: tab.label
|
|
2873
|
+
},
|
|
2874
|
+
tab.id
|
|
2875
|
+
);
|
|
2876
|
+
}) }),
|
|
2877
|
+
/* @__PURE__ */ jsxs5(
|
|
2878
|
+
"div",
|
|
2879
|
+
{
|
|
2880
|
+
id: `${tabPanelId}-panel-${selectedResolvedTab?.domId ?? "empty"}`,
|
|
2881
|
+
role: "tabpanel",
|
|
2882
|
+
"aria-labelledby": selectedResolvedTab ? `${tabPanelId}-tab-${selectedResolvedTab.domId}` : void 0,
|
|
2883
|
+
style: styles2.content,
|
|
2884
|
+
children: [
|
|
2885
|
+
statusNotice ? /* @__PURE__ */ jsx6(
|
|
2886
|
+
"div",
|
|
2887
|
+
{
|
|
2888
|
+
style: {
|
|
2889
|
+
...styles2.notice,
|
|
2890
|
+
...statusNotice.tone === "success" ? styles2.successNotice : {}
|
|
2891
|
+
},
|
|
2892
|
+
children: statusNotice.text
|
|
2893
|
+
}
|
|
2894
|
+
) : null,
|
|
2895
|
+
tabContent,
|
|
2896
|
+
latestError ? /* @__PURE__ */ jsx6("div", { style: styles2.errorNotice, children: latestError }) : null
|
|
2897
|
+
]
|
|
2898
|
+
}
|
|
2899
|
+
),
|
|
2900
|
+
forceCloseConfirmOpen && selectedChannel ? /* @__PURE__ */ jsx6("div", { style: styles2.dialogBackdrop, children: /* @__PURE__ */ jsxs5(
|
|
2901
|
+
"div",
|
|
2902
|
+
{
|
|
2903
|
+
ref: forceCloseDialogRef,
|
|
2904
|
+
role: "dialog",
|
|
2905
|
+
"aria-modal": "true",
|
|
2906
|
+
"aria-label": t("dialog.forceClose.aria", "Force close confirmation"),
|
|
2907
|
+
tabIndex: -1,
|
|
2908
|
+
style: styles2.dialogCard,
|
|
2909
|
+
onKeyDown: (event) => {
|
|
2910
|
+
if (event.key === "Escape") {
|
|
2911
|
+
setForceCloseConfirmOpen(false);
|
|
2912
|
+
}
|
|
2913
|
+
},
|
|
2914
|
+
children: [
|
|
2915
|
+
/* @__PURE__ */ jsx6("h4", { style: styles2.sectionTitle, children: t("dialog.forceClose.title", "Force close this channel?") }),
|
|
2916
|
+
/* @__PURE__ */ jsx6("p", { style: styles2.compactText, children: t(
|
|
2917
|
+
"dialog.forceClose.description",
|
|
2918
|
+
"This action may immediately broadcast a unilateral close transaction, can lock liquidity until settlement, and may produce additional fees. Continue only if normal close cannot proceed."
|
|
2919
|
+
) }),
|
|
2920
|
+
/* @__PURE__ */ jsxs5("p", { style: styles2.inlineCode, children: [
|
|
2921
|
+
t("dialog.forceClose.channel", "Channel"),
|
|
2922
|
+
":",
|
|
2923
|
+
" ",
|
|
2924
|
+
shorten(selectedChannel.channel_id, 20, 12)
|
|
2925
|
+
] }),
|
|
2926
|
+
/* @__PURE__ */ jsxs5("div", { style: { ...styles2.row, justifyContent: "flex-end" }, children: [
|
|
2927
|
+
/* @__PURE__ */ jsx6(
|
|
2928
|
+
"button",
|
|
2929
|
+
{
|
|
2930
|
+
type: "button",
|
|
2931
|
+
style: styles2.actionButton,
|
|
2932
|
+
onClick: () => {
|
|
2933
|
+
setForceCloseConfirmOpen(false);
|
|
2934
|
+
},
|
|
2935
|
+
children: t("dialog.forceClose.cancel", "Cancel")
|
|
2936
|
+
}
|
|
2937
|
+
),
|
|
2938
|
+
/* @__PURE__ */ jsx6(
|
|
2939
|
+
"button",
|
|
2940
|
+
{
|
|
2941
|
+
type: "button",
|
|
2942
|
+
style: styles2.dangerButton,
|
|
2943
|
+
onClick: () => {
|
|
2944
|
+
setForceCloseConfirmOpen(false);
|
|
2945
|
+
onLog?.("fiber_channel_force_close_confirmed");
|
|
2946
|
+
void closeChannel(selectedChannel.channel_id, true);
|
|
2947
|
+
},
|
|
2948
|
+
children: t("dialog.forceClose.confirm", "Confirm Force Close")
|
|
2949
|
+
}
|
|
2950
|
+
)
|
|
2951
|
+
] })
|
|
2952
|
+
]
|
|
2953
|
+
}
|
|
2954
|
+
) }) : null
|
|
2955
|
+
] });
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// src/fiber-node-button/index.tsx
|
|
2959
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2960
|
+
function FiberNodeButton(props) {
|
|
2961
|
+
const {
|
|
2962
|
+
network = "testnet",
|
|
2963
|
+
fiber: externalFiber,
|
|
2964
|
+
strategy = "passkey",
|
|
2965
|
+
externalWallet = false,
|
|
2966
|
+
password,
|
|
2967
|
+
walletId,
|
|
2968
|
+
passkeyUsername = "User",
|
|
2969
|
+
wasmFactory,
|
|
2970
|
+
nodeConfig,
|
|
2971
|
+
className,
|
|
2972
|
+
style,
|
|
2973
|
+
dropdownStyle,
|
|
2974
|
+
onConnect,
|
|
2975
|
+
onDisconnect,
|
|
2976
|
+
onError,
|
|
2977
|
+
onLog,
|
|
2978
|
+
initialPeerPubkey = "",
|
|
2979
|
+
initialPeerAddress = "",
|
|
2980
|
+
initialFundingAmountCkb = "1000",
|
|
2981
|
+
externalFunding,
|
|
2982
|
+
renderConnectorSection,
|
|
2983
|
+
tabs,
|
|
2984
|
+
renderTabContent,
|
|
2985
|
+
renderAction,
|
|
2986
|
+
t
|
|
2987
|
+
} = props;
|
|
2988
|
+
const managedFiber = useFiberNode({
|
|
2989
|
+
network,
|
|
2990
|
+
walletId,
|
|
2991
|
+
wasmFactory,
|
|
2992
|
+
nodeConfig,
|
|
2993
|
+
externalWallet,
|
|
2994
|
+
enabled: !externalFiber
|
|
2995
|
+
});
|
|
2996
|
+
const fiber = externalFiber ?? managedFiber;
|
|
2997
|
+
const handleConnectButtonError = useCallback6(
|
|
2998
|
+
(error) => {
|
|
2999
|
+
onError?.(error);
|
|
3000
|
+
},
|
|
3001
|
+
[onError]
|
|
3002
|
+
);
|
|
3003
|
+
const renderDropdown = useCallback6(
|
|
3004
|
+
(dropdownContext) => /* @__PURE__ */ jsx7(
|
|
3005
|
+
FiberNodeButtonPanel,
|
|
3006
|
+
{
|
|
3007
|
+
dropdownContext,
|
|
3008
|
+
network,
|
|
3009
|
+
fiber,
|
|
3010
|
+
onLog,
|
|
3011
|
+
onError,
|
|
3012
|
+
initialPeerPubkey,
|
|
3013
|
+
initialPeerAddress,
|
|
3014
|
+
initialFundingAmountCkb,
|
|
3015
|
+
externalFunding,
|
|
3016
|
+
renderConnectorSection,
|
|
3017
|
+
tabs,
|
|
3018
|
+
renderTabContent,
|
|
3019
|
+
renderAction,
|
|
3020
|
+
t
|
|
3021
|
+
}
|
|
3022
|
+
),
|
|
3023
|
+
[
|
|
3024
|
+
externalFunding,
|
|
3025
|
+
fiber,
|
|
3026
|
+
initialFundingAmountCkb,
|
|
3027
|
+
initialPeerAddress,
|
|
3028
|
+
initialPeerPubkey,
|
|
3029
|
+
network,
|
|
3030
|
+
onError,
|
|
3031
|
+
onLog,
|
|
3032
|
+
renderAction,
|
|
3033
|
+
renderConnectorSection,
|
|
3034
|
+
renderTabContent,
|
|
3035
|
+
t,
|
|
3036
|
+
tabs
|
|
3037
|
+
]
|
|
3038
|
+
);
|
|
3039
|
+
return /* @__PURE__ */ jsx7(
|
|
3040
|
+
ConnectButton,
|
|
3041
|
+
{
|
|
3042
|
+
fiber,
|
|
3043
|
+
strategy,
|
|
3044
|
+
password,
|
|
3045
|
+
passkeyUsername,
|
|
3046
|
+
onConnect,
|
|
3047
|
+
onDisconnect,
|
|
3048
|
+
onError: handleConnectButtonError,
|
|
3049
|
+
className,
|
|
3050
|
+
style,
|
|
3051
|
+
dropdownStyle: { maxWidth: 460, width: "calc(100vw - 1rem)", ...dropdownStyle },
|
|
3052
|
+
renderConnectedDropdown: renderDropdown
|
|
3053
|
+
}
|
|
3054
|
+
);
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
// src/fiber-pay-quick-card.tsx
|
|
3058
|
+
import { useEffect as useEffect6, useId as useId3, useState as useState6 } from "react";
|
|
3059
|
+
import { Fragment as Fragment6, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3060
|
+
var ONE_CKB_SHANNONS2 = "0x5f5e100";
|
|
3061
|
+
var cardStyle = {
|
|
3062
|
+
border: "1px solid #ddd",
|
|
3063
|
+
borderRadius: 8,
|
|
3064
|
+
padding: 16,
|
|
3065
|
+
maxWidth: 520
|
|
3066
|
+
};
|
|
3067
|
+
var rowStyle = {
|
|
3068
|
+
display: "flex",
|
|
3069
|
+
gap: 8
|
|
3070
|
+
};
|
|
3071
|
+
var rowWithMarginStyle = {
|
|
3072
|
+
...rowStyle,
|
|
3073
|
+
marginBottom: 8
|
|
3074
|
+
};
|
|
3075
|
+
function FiberPayQuickCard(props) {
|
|
3076
|
+
const network = props.network ?? "testnet";
|
|
3077
|
+
const passkeyUsername = props.passkeyUsername ?? "User";
|
|
3078
|
+
const title = props.title ?? "FiberPay Quick Card";
|
|
3079
|
+
const usesExternalFiber = !!props.fiber;
|
|
3080
|
+
const onError = props.onError;
|
|
3081
|
+
const onInvoiceCreated = props.onInvoiceCreated;
|
|
3082
|
+
const onPaymentResult = props.onPaymentResult;
|
|
3083
|
+
const passwordInputId = useId3();
|
|
3084
|
+
const invoiceInputId = useId3();
|
|
3085
|
+
const managedFiber = useFiberNode({
|
|
3086
|
+
network,
|
|
3087
|
+
walletId: props.walletId,
|
|
3088
|
+
enabled: !usesExternalFiber
|
|
3089
|
+
});
|
|
3090
|
+
const fiber = props.fiber ?? managedFiber;
|
|
3091
|
+
const {
|
|
3092
|
+
node,
|
|
3093
|
+
nodeInfo,
|
|
3094
|
+
state,
|
|
3095
|
+
error: nodeError,
|
|
3096
|
+
isPasskeySupported,
|
|
3097
|
+
hasPasskeyConfigured,
|
|
3098
|
+
startWithPassword,
|
|
3099
|
+
startWithPasskey,
|
|
3100
|
+
createPasskeyAndStart,
|
|
3101
|
+
stop
|
|
3102
|
+
} = fiber;
|
|
3103
|
+
const { payInvoice, isPaying, error: payError, paymentResult } = useFiberPayment(node);
|
|
3104
|
+
const [password, setPassword] = useState6("");
|
|
3105
|
+
const [invoiceInput, setInvoiceInput] = useState6("");
|
|
3106
|
+
const [createdInvoice, setCreatedInvoice] = useState6("");
|
|
3107
|
+
const [isCreatingInvoice, setIsCreatingInvoice] = useState6(false);
|
|
3108
|
+
const [invoiceError, setInvoiceError] = useState6(null);
|
|
3109
|
+
useEffect6(() => {
|
|
3110
|
+
if (nodeError) {
|
|
3111
|
+
onError?.({ scope: "node", message: nodeError });
|
|
3112
|
+
}
|
|
3113
|
+
}, [nodeError, onError]);
|
|
3114
|
+
useEffect6(() => {
|
|
799
3115
|
if (payError) {
|
|
800
3116
|
onError?.({ scope: "payment", message: payError });
|
|
801
3117
|
}
|
|
802
3118
|
}, [onError, payError]);
|
|
803
|
-
|
|
3119
|
+
useEffect6(() => {
|
|
804
3120
|
if (paymentResult) {
|
|
805
3121
|
onPaymentResult?.(paymentResult);
|
|
806
3122
|
}
|
|
@@ -813,7 +3129,7 @@ function FiberPayQuickCard(props) {
|
|
|
813
3129
|
setInvoiceError(null);
|
|
814
3130
|
try {
|
|
815
3131
|
const created = await node.newInvoice({
|
|
816
|
-
amount:
|
|
3132
|
+
amount: ONE_CKB_SHANNONS2,
|
|
817
3133
|
currency: network === "mainnet" ? "Fibb" : "Fibt",
|
|
818
3134
|
description: "FiberPay QuickCard invoice"
|
|
819
3135
|
});
|
|
@@ -827,18 +3143,21 @@ function FiberPayQuickCard(props) {
|
|
|
827
3143
|
setIsCreatingInvoice(false);
|
|
828
3144
|
}
|
|
829
3145
|
};
|
|
830
|
-
return /* @__PURE__ */
|
|
831
|
-
/* @__PURE__ */
|
|
3146
|
+
return /* @__PURE__ */ jsxs6("div", { style: { ...cardStyle, ...props.style }, className: props.className, children: [
|
|
3147
|
+
/* @__PURE__ */ jsxs6("h3", { children: [
|
|
832
3148
|
title,
|
|
833
3149
|
" (",
|
|
834
3150
|
network,
|
|
835
3151
|
")"
|
|
836
3152
|
] }),
|
|
837
|
-
!nodeInfo ? /* @__PURE__ */
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
3153
|
+
!nodeInfo ? usesExternalFiber ? /* @__PURE__ */ jsxs6("p", { children: [
|
|
3154
|
+
/* @__PURE__ */ jsx8("strong", { children: "Connection required:" }),
|
|
3155
|
+
" connect the shared node first, then return here to create or pay invoices."
|
|
3156
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment6, { children: [
|
|
3157
|
+
isPasskeySupported ? /* @__PURE__ */ jsx8("div", { style: rowWithMarginStyle, children: hasPasskeyConfigured ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void startWithPasskey(), children: "Login with Passkey" }) : /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void createPasskeyAndStart(passkeyUsername), children: "Register Passkey" }) }) : null,
|
|
3158
|
+
/* @__PURE__ */ jsx8("label", { htmlFor: passwordInputId, children: "Password" }),
|
|
3159
|
+
/* @__PURE__ */ jsxs6("div", { style: rowStyle, children: [
|
|
3160
|
+
/* @__PURE__ */ jsx8(
|
|
842
3161
|
"input",
|
|
843
3162
|
{
|
|
844
3163
|
id: passwordInputId,
|
|
@@ -850,31 +3169,31 @@ function FiberPayQuickCard(props) {
|
|
|
850
3169
|
placeholder: "Password"
|
|
851
3170
|
}
|
|
852
3171
|
),
|
|
853
|
-
/* @__PURE__ */
|
|
3172
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void startWithPassword(password), children: "Start with Password" })
|
|
854
3173
|
] })
|
|
855
|
-
] }) : /* @__PURE__ */
|
|
856
|
-
/* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
3174
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment6, { children: [
|
|
3175
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
3176
|
+
/* @__PURE__ */ jsx8("strong", { children: "State:" }),
|
|
858
3177
|
" ",
|
|
859
3178
|
state
|
|
860
3179
|
] }),
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
3180
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
3181
|
+
/* @__PURE__ */ jsx8("strong", { children: "Pubkey:" }),
|
|
863
3182
|
" ",
|
|
864
3183
|
nodeInfo.pubkey
|
|
865
3184
|
] }),
|
|
866
|
-
/* @__PURE__ */
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
3185
|
+
/* @__PURE__ */ jsxs6("div", { style: rowWithMarginStyle, children: [
|
|
3186
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void createInvoice(), disabled: isCreatingInvoice, children: isCreatingInvoice ? "Creating..." : "Create Invoice (1 CKB)" }),
|
|
3187
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void stop(), children: "Stop Node" })
|
|
869
3188
|
] }),
|
|
870
|
-
createdInvoice ? /* @__PURE__ */
|
|
871
|
-
/* @__PURE__ */
|
|
3189
|
+
createdInvoice ? /* @__PURE__ */ jsxs6("p", { children: [
|
|
3190
|
+
/* @__PURE__ */ jsx8("strong", { children: "Created invoice:" }),
|
|
872
3191
|
" ",
|
|
873
3192
|
createdInvoice
|
|
874
3193
|
] }) : null,
|
|
875
|
-
/* @__PURE__ */
|
|
876
|
-
/* @__PURE__ */
|
|
877
|
-
/* @__PURE__ */
|
|
3194
|
+
/* @__PURE__ */ jsx8("label", { htmlFor: invoiceInputId, children: "Invoice" }),
|
|
3195
|
+
/* @__PURE__ */ jsxs6("div", { style: rowStyle, children: [
|
|
3196
|
+
/* @__PURE__ */ jsx8(
|
|
878
3197
|
"input",
|
|
879
3198
|
{
|
|
880
3199
|
id: invoiceInputId,
|
|
@@ -884,26 +3203,26 @@ function FiberPayQuickCard(props) {
|
|
|
884
3203
|
placeholder: "Paste invoice to pay"
|
|
885
3204
|
}
|
|
886
3205
|
),
|
|
887
|
-
/* @__PURE__ */
|
|
3206
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void payInvoice(invoiceInput), disabled: isPaying, children: isPaying ? "Paying..." : "Pay" })
|
|
888
3207
|
] }),
|
|
889
|
-
paymentResult ? /* @__PURE__ */
|
|
890
|
-
/* @__PURE__ */
|
|
3208
|
+
paymentResult ? /* @__PURE__ */ jsxs6("p", { children: [
|
|
3209
|
+
/* @__PURE__ */ jsx8("strong", { children: "Payment:" }),
|
|
891
3210
|
" ",
|
|
892
3211
|
paymentResult.status
|
|
893
3212
|
] }) : null
|
|
894
3213
|
] }),
|
|
895
|
-
nodeError ? /* @__PURE__ */
|
|
896
|
-
/* @__PURE__ */
|
|
3214
|
+
nodeError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3215
|
+
/* @__PURE__ */ jsx8("strong", { children: "Node error:" }),
|
|
897
3216
|
" ",
|
|
898
3217
|
nodeError
|
|
899
3218
|
] }) : null,
|
|
900
|
-
payError ? /* @__PURE__ */
|
|
901
|
-
/* @__PURE__ */
|
|
3219
|
+
payError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3220
|
+
/* @__PURE__ */ jsx8("strong", { children: "Payment error:" }),
|
|
902
3221
|
" ",
|
|
903
3222
|
payError
|
|
904
3223
|
] }) : null,
|
|
905
|
-
invoiceError ? /* @__PURE__ */
|
|
906
|
-
/* @__PURE__ */
|
|
3224
|
+
invoiceError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3225
|
+
/* @__PURE__ */ jsx8("strong", { children: "Invoice error:" }),
|
|
907
3226
|
" ",
|
|
908
3227
|
invoiceError
|
|
909
3228
|
] }) : null
|
|
@@ -918,12 +3237,12 @@ import {
|
|
|
918
3237
|
scriptToAddress
|
|
919
3238
|
} from "@fiber-pay/sdk/browser";
|
|
920
3239
|
import {
|
|
921
|
-
useCallback as
|
|
922
|
-
useEffect as
|
|
923
|
-
useRef as
|
|
924
|
-
useState as
|
|
3240
|
+
useCallback as useCallback7,
|
|
3241
|
+
useEffect as useEffect7,
|
|
3242
|
+
useRef as useRef6,
|
|
3243
|
+
useState as useState7
|
|
925
3244
|
} from "react";
|
|
926
|
-
import { Fragment as
|
|
3245
|
+
import { Fragment as Fragment7, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
927
3246
|
function truncateMiddle(str, left = 8, right = 8) {
|
|
928
3247
|
if (str.length <= left + right + 3) return str;
|
|
929
3248
|
return `${str.slice(0, left)}\u2026${str.slice(-right)}`;
|
|
@@ -961,7 +3280,7 @@ async function fetchNodeStats(node, network) {
|
|
|
961
3280
|
externalFunding: false
|
|
962
3281
|
};
|
|
963
3282
|
}
|
|
964
|
-
var
|
|
3283
|
+
var styles3 = {
|
|
965
3284
|
root: {
|
|
966
3285
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
967
3286
|
fontSize: "0.875rem",
|
|
@@ -1090,7 +3409,7 @@ var STATUS_COLORS = {
|
|
|
1090
3409
|
error: { bg: "rgba(239,68,68,0.1)", fg: "#dc2626", dot: "#ef4444" }
|
|
1091
3410
|
};
|
|
1092
3411
|
function CopyIcon() {
|
|
1093
|
-
return /* @__PURE__ */
|
|
3412
|
+
return /* @__PURE__ */ jsxs7(
|
|
1094
3413
|
"svg",
|
|
1095
3414
|
{
|
|
1096
3415
|
width: "12",
|
|
@@ -1103,26 +3422,26 @@ function CopyIcon() {
|
|
|
1103
3422
|
strokeLinejoin: "round",
|
|
1104
3423
|
"aria-hidden": "true",
|
|
1105
3424
|
children: [
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
/* @__PURE__ */
|
|
3425
|
+
/* @__PURE__ */ jsx9("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
3426
|
+
/* @__PURE__ */ jsx9("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
1108
3427
|
]
|
|
1109
3428
|
}
|
|
1110
3429
|
);
|
|
1111
3430
|
}
|
|
1112
3431
|
function InfoRow({ label, value, copyable }) {
|
|
1113
|
-
return /* @__PURE__ */
|
|
1114
|
-
/* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1117
|
-
copyable && /* @__PURE__ */
|
|
3432
|
+
return /* @__PURE__ */ jsxs7("div", { style: styles3.infoRow, children: [
|
|
3433
|
+
/* @__PURE__ */ jsx9("span", { style: styles3.infoLabel, children: label }),
|
|
3434
|
+
/* @__PURE__ */ jsxs7("div", { style: styles3.infoValueWrapper, children: [
|
|
3435
|
+
/* @__PURE__ */ jsx9("span", { style: styles3.infoValue, children: truncateMiddle(value, 6, 6) }),
|
|
3436
|
+
copyable && /* @__PURE__ */ jsx9(
|
|
1118
3437
|
"button",
|
|
1119
3438
|
{
|
|
1120
3439
|
type: "button",
|
|
1121
3440
|
onClick: () => copyToClipboard(value),
|
|
1122
|
-
style:
|
|
3441
|
+
style: styles3.copyButton,
|
|
1123
3442
|
title: "Copy to clipboard",
|
|
1124
3443
|
"aria-label": `Copy ${label}`,
|
|
1125
|
-
children: /* @__PURE__ */
|
|
3444
|
+
children: /* @__PURE__ */ jsx9(CopyIcon, {})
|
|
1126
3445
|
}
|
|
1127
3446
|
)
|
|
1128
3447
|
] })
|
|
@@ -1138,12 +3457,12 @@ function NodeInfoPanel(props) {
|
|
|
1138
3457
|
className,
|
|
1139
3458
|
style
|
|
1140
3459
|
} = props;
|
|
1141
|
-
const [stats, setStats] =
|
|
1142
|
-
const [statsError, setStatsError] =
|
|
1143
|
-
const [statsLoading, setStatsLoading] =
|
|
1144
|
-
const cancelledRef =
|
|
1145
|
-
const [QRComponent, setQRComponent] =
|
|
1146
|
-
|
|
3460
|
+
const [stats, setStats] = useState7(null);
|
|
3461
|
+
const [statsError, setStatsError] = useState7(null);
|
|
3462
|
+
const [statsLoading, setStatsLoading] = useState7(false);
|
|
3463
|
+
const cancelledRef = useRef6(false);
|
|
3464
|
+
const [QRComponent, setQRComponent] = useState7(null);
|
|
3465
|
+
useEffect7(() => {
|
|
1147
3466
|
let cancelled = false;
|
|
1148
3467
|
if (!showQrCode || renderQrCode) return;
|
|
1149
3468
|
import("qrcode.react").then((mod) => {
|
|
@@ -1156,8 +3475,8 @@ function NodeInfoPanel(props) {
|
|
|
1156
3475
|
cancelled = true;
|
|
1157
3476
|
};
|
|
1158
3477
|
}, [showQrCode, renderQrCode]);
|
|
1159
|
-
const loadingRef =
|
|
1160
|
-
const loadStats =
|
|
3478
|
+
const loadingRef = useRef6(false);
|
|
3479
|
+
const loadStats = useCallback7(async () => {
|
|
1161
3480
|
if (!node || node.state !== "running" || loadingRef.current) return;
|
|
1162
3481
|
loadingRef.current = true;
|
|
1163
3482
|
setStatsLoading(true);
|
|
@@ -1174,7 +3493,7 @@ function NodeInfoPanel(props) {
|
|
|
1174
3493
|
if (!cancelledRef.current) setStatsLoading(false);
|
|
1175
3494
|
}
|
|
1176
3495
|
}, [node, network]);
|
|
1177
|
-
|
|
3496
|
+
useEffect7(() => {
|
|
1178
3497
|
cancelledRef.current = false;
|
|
1179
3498
|
if (!node || node.state !== "running") {
|
|
1180
3499
|
setStats(null);
|
|
@@ -1189,21 +3508,21 @@ function NodeInfoPanel(props) {
|
|
|
1189
3508
|
};
|
|
1190
3509
|
}, [node, node?.state, pollInterval, loadStats]);
|
|
1191
3510
|
if (!node) {
|
|
1192
|
-
return /* @__PURE__ */
|
|
3511
|
+
return /* @__PURE__ */ jsx9("div", { className, style: { ...styles3.root, ...style }, "data-fpay-node-info": "", children: /* @__PURE__ */ jsx9("div", { style: styles3.idle, children: "No node connected" }) });
|
|
1193
3512
|
}
|
|
1194
3513
|
const nodeState = node.state;
|
|
1195
3514
|
const statusColor = STATUS_COLORS[nodeState] ?? STATUS_COLORS.idle;
|
|
1196
|
-
return /* @__PURE__ */
|
|
1197
|
-
/* @__PURE__ */
|
|
3515
|
+
return /* @__PURE__ */ jsxs7("div", { className, style: { ...styles3.root, ...style }, "data-fpay-node-info": "", children: [
|
|
3516
|
+
/* @__PURE__ */ jsxs7(
|
|
1198
3517
|
"div",
|
|
1199
3518
|
{
|
|
1200
3519
|
style: {
|
|
1201
|
-
...
|
|
3520
|
+
...styles3.statusBadge,
|
|
1202
3521
|
backgroundColor: statusColor.bg,
|
|
1203
3522
|
color: statusColor.fg
|
|
1204
3523
|
},
|
|
1205
3524
|
children: [
|
|
1206
|
-
/* @__PURE__ */
|
|
3525
|
+
/* @__PURE__ */ jsx9(
|
|
1207
3526
|
"span",
|
|
1208
3527
|
{
|
|
1209
3528
|
style: {
|
|
@@ -1219,8 +3538,8 @@ function NodeInfoPanel(props) {
|
|
|
1219
3538
|
]
|
|
1220
3539
|
}
|
|
1221
3540
|
),
|
|
1222
|
-
statsLoading && !stats && /* @__PURE__ */
|
|
1223
|
-
/* @__PURE__ */
|
|
3541
|
+
statsLoading && !stats && /* @__PURE__ */ jsxs7("div", { style: styles3.loading, children: [
|
|
3542
|
+
/* @__PURE__ */ jsxs7(
|
|
1224
3543
|
"svg",
|
|
1225
3544
|
{
|
|
1226
3545
|
width: "12",
|
|
@@ -1234,8 +3553,8 @@ function NodeInfoPanel(props) {
|
|
|
1234
3553
|
role: "img",
|
|
1235
3554
|
"aria-label": "Loading",
|
|
1236
3555
|
children: [
|
|
1237
|
-
/* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
3556
|
+
/* @__PURE__ */ jsx9("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }),
|
|
3557
|
+
/* @__PURE__ */ jsx9(
|
|
1239
3558
|
"animateTransform",
|
|
1240
3559
|
{
|
|
1241
3560
|
attributeName: "transform",
|
|
@@ -1251,22 +3570,22 @@ function NodeInfoPanel(props) {
|
|
|
1251
3570
|
),
|
|
1252
3571
|
"Loading\u2026"
|
|
1253
3572
|
] }),
|
|
1254
|
-
statsError && /* @__PURE__ */
|
|
1255
|
-
stats && /* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
stats.externalFunding ? /* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1260
|
-
/* @__PURE__ */
|
|
1261
|
-
/* @__PURE__ */
|
|
3573
|
+
statsError && /* @__PURE__ */ jsx9("div", { style: styles3.errorBox, children: statsError }),
|
|
3574
|
+
stats && /* @__PURE__ */ jsxs7(Fragment7, { children: [
|
|
3575
|
+
/* @__PURE__ */ jsx9(InfoRow, { label: "Pubkey", value: stats.pubkey, copyable: true }),
|
|
3576
|
+
stats.externalFunding ? /* @__PURE__ */ jsx9("div", { style: { padding: "0.25rem 0", fontSize: "0.75rem", color: "#6b7280" }, children: "External funding mode" }) : stats.ckbAddress ? /* @__PURE__ */ jsx9(InfoRow, { label: "CKB Address", value: stats.ckbAddress, copyable: true }) : null,
|
|
3577
|
+
/* @__PURE__ */ jsxs7("div", { style: styles3.statsGrid, children: [
|
|
3578
|
+
/* @__PURE__ */ jsxs7("div", { style: styles3.statCard, children: [
|
|
3579
|
+
/* @__PURE__ */ jsx9("div", { style: styles3.statLabel, children: "Peers" }),
|
|
3580
|
+
/* @__PURE__ */ jsx9("div", { style: styles3.statValue, children: stats.peers })
|
|
1262
3581
|
] }),
|
|
1263
|
-
/* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1265
|
-
/* @__PURE__ */
|
|
3582
|
+
/* @__PURE__ */ jsxs7("div", { style: styles3.statCard, children: [
|
|
3583
|
+
/* @__PURE__ */ jsx9("div", { style: styles3.statLabel, children: "Channels" }),
|
|
3584
|
+
/* @__PURE__ */ jsx9("div", { style: styles3.statValue, children: stats.channels })
|
|
1266
3585
|
] })
|
|
1267
3586
|
] }),
|
|
1268
|
-
showQrCode && stats.ckbAddress && /* @__PURE__ */
|
|
1269
|
-
renderQrCode ? renderQrCode(stats.ckbAddress) : QRComponent ? /* @__PURE__ */
|
|
3587
|
+
showQrCode && stats.ckbAddress && /* @__PURE__ */ jsxs7("div", { style: styles3.qrContainer, children: [
|
|
3588
|
+
renderQrCode ? renderQrCode(stats.ckbAddress) : QRComponent ? /* @__PURE__ */ jsx9(
|
|
1270
3589
|
QRComponent,
|
|
1271
3590
|
{
|
|
1272
3591
|
value: stats.ckbAddress,
|
|
@@ -1274,11 +3593,11 @@ function NodeInfoPanel(props) {
|
|
|
1274
3593
|
bgColor: "transparent",
|
|
1275
3594
|
fgColor: "currentColor"
|
|
1276
3595
|
}
|
|
1277
|
-
) : /* @__PURE__ */
|
|
1278
|
-
/* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
3596
|
+
) : /* @__PURE__ */ jsx9("div", { style: { fontSize: "0.625rem", color: "#9ca3af" }, children: "Install qrcode.react for QR code" }),
|
|
3597
|
+
/* @__PURE__ */ jsx9("span", { style: styles3.qrCaption, children: "Scan to deposit CKB" }),
|
|
3598
|
+
/* @__PURE__ */ jsxs7("div", { style: styles3.balanceRow, children: [
|
|
3599
|
+
/* @__PURE__ */ jsx9("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: "Balance" }),
|
|
3600
|
+
/* @__PURE__ */ jsxs7(
|
|
1282
3601
|
"span",
|
|
1283
3602
|
{
|
|
1284
3603
|
style: {
|
|
@@ -1298,10 +3617,11 @@ function NodeInfoPanel(props) {
|
|
|
1298
3617
|
] });
|
|
1299
3618
|
}
|
|
1300
3619
|
export {
|
|
1301
|
-
ChannelState,
|
|
3620
|
+
ChannelState3 as ChannelState,
|
|
1302
3621
|
ConfigBuilder2 as ConfigBuilder,
|
|
1303
3622
|
ConnectButton,
|
|
1304
3623
|
FiberBrowserNode2 as FiberBrowserNode,
|
|
3624
|
+
FiberNodeButton,
|
|
1305
3625
|
FiberPayQuickCard,
|
|
1306
3626
|
FiberRpcError,
|
|
1307
3627
|
NodeInfoPanel,
|
|
@@ -1309,14 +3629,15 @@ export {
|
|
|
1309
3629
|
PasswordCredentialProvider2 as PasswordCredentialProvider,
|
|
1310
3630
|
RawKeyCredentialProvider2 as RawKeyCredentialProvider,
|
|
1311
3631
|
ckbHash,
|
|
1312
|
-
ckbToShannons,
|
|
3632
|
+
ckbToShannons2 as ckbToShannons,
|
|
1313
3633
|
derivePublicKey,
|
|
1314
3634
|
formatShannonsAsCkb2 as formatShannonsAsCkb,
|
|
1315
3635
|
fromHex,
|
|
1316
3636
|
getLockBalanceShannons2 as getLockBalanceShannons,
|
|
1317
3637
|
scriptToAddress2 as scriptToAddress,
|
|
1318
|
-
shannonsToCkb,
|
|
3638
|
+
shannonsToCkb2 as shannonsToCkb,
|
|
1319
3639
|
toHex,
|
|
3640
|
+
useChannelOpenFlow,
|
|
1320
3641
|
useFiberNode,
|
|
1321
3642
|
useFiberPayment
|
|
1322
3643
|
};
|