@fiber-pay/react 0.2.5 → 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 +232 -1
- package/dist/index.d.ts +225 -5
- package/dist/index.js +2604 -269
- 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);
|
|
@@ -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,7 +473,8 @@ 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
479
|
walletId,
|
|
467
480
|
passkeyUsername = "User",
|
|
@@ -480,6 +493,7 @@ function ConnectButton(props) {
|
|
|
480
493
|
walletId,
|
|
481
494
|
wasmFactory,
|
|
482
495
|
nodeConfig,
|
|
496
|
+
externalWallet,
|
|
483
497
|
enabled: !externalFiber
|
|
484
498
|
});
|
|
485
499
|
const fiber = externalFiber ?? internalFiber;
|
|
@@ -502,6 +516,8 @@ function ConnectButton(props) {
|
|
|
502
516
|
const [showDropdown, setShowDropdown] = useState2(false);
|
|
503
517
|
const [localError, setLocalError] = useState2(null);
|
|
504
518
|
const dropdownRef = useRef2(null);
|
|
519
|
+
const dropdownId = useId();
|
|
520
|
+
const lastReportedErrorRef = useRef2(null);
|
|
505
521
|
const effectiveIsStarting = isConnecting || isStarting;
|
|
506
522
|
useEffect2(() => ensureKeyframes(), []);
|
|
507
523
|
useEffect2(() => {
|
|
@@ -524,9 +540,18 @@ function ConnectButton(props) {
|
|
|
524
540
|
}
|
|
525
541
|
prevRunningRef.current = isRunning;
|
|
526
542
|
}, [isRunning, node, nodeInfo, onConnect, onDisconnect]);
|
|
543
|
+
const effectiveError = error ?? localError;
|
|
527
544
|
useEffect2(() => {
|
|
528
|
-
if (
|
|
529
|
-
|
|
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]);
|
|
530
555
|
const handleConnect = useCallback2(async () => {
|
|
531
556
|
setIsConnecting(true);
|
|
532
557
|
setLocalError(null);
|
|
@@ -547,7 +572,6 @@ function ConnectButton(props) {
|
|
|
547
572
|
} catch (err) {
|
|
548
573
|
const msg = err instanceof Error ? err.message : String(err);
|
|
549
574
|
setLocalError(msg);
|
|
550
|
-
onError?.(msg);
|
|
551
575
|
} finally {
|
|
552
576
|
setIsConnecting(false);
|
|
553
577
|
}
|
|
@@ -558,8 +582,7 @@ function ConnectButton(props) {
|
|
|
558
582
|
hasPasskeyConfigured,
|
|
559
583
|
startWithPassword,
|
|
560
584
|
startWithPasskey,
|
|
561
|
-
createPasskeyAndStart
|
|
562
|
-
onError
|
|
585
|
+
createPasskeyAndStart
|
|
563
586
|
]);
|
|
564
587
|
const handleDisconnect = useCallback2(async () => {
|
|
565
588
|
try {
|
|
@@ -567,15 +590,14 @@ function ConnectButton(props) {
|
|
|
567
590
|
} catch (err) {
|
|
568
591
|
const msg = err instanceof Error ? err.message : String(err);
|
|
569
592
|
setLocalError(msg);
|
|
570
|
-
onError?.(msg);
|
|
571
593
|
} finally {
|
|
572
594
|
setShowDropdown(false);
|
|
573
595
|
}
|
|
574
|
-
}, [stop
|
|
596
|
+
}, [stop]);
|
|
575
597
|
const closeDropdown = useCallback2(() => {
|
|
576
598
|
setShowDropdown(false);
|
|
577
599
|
}, []);
|
|
578
|
-
const hasError = !!
|
|
600
|
+
const hasError = !!effectiveError;
|
|
579
601
|
let buttonLabel;
|
|
580
602
|
let buttonOnClick;
|
|
581
603
|
let buttonDisabled = false;
|
|
@@ -618,188 +640,2496 @@ function ConnectButton(props) {
|
|
|
618
640
|
return /* @__PURE__ */ jsxs("div", { className, style: { ...styles.root, ...style }, "data-fpay-connect-button": "", children: [
|
|
619
641
|
(hasError || !isPasskeySupported && passkeyUnavailableReason && strategy === "passkey") && /* @__PURE__ */ jsx("span", { style: styles.errorText, children: error || localError || passkeyUnavailableReason }),
|
|
620
642
|
isRunning ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: dropdownRef, children: [
|
|
621
|
-
/* @__PURE__ */ jsx(
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
+
)
|
|
635
704
|
] })
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
/* @__PURE__ */ jsxs(
|
|
639
|
-
"button",
|
|
640
|
-
{
|
|
641
|
-
type: "button",
|
|
642
|
-
onClick: () => void handleDisconnect(),
|
|
643
|
-
style: styles.disconnectButton,
|
|
644
|
-
children: [
|
|
645
|
-
/* @__PURE__ */ jsx("span", { children: "Disconnect" }),
|
|
646
|
-
/* @__PURE__ */ jsx(
|
|
647
|
-
"svg",
|
|
648
|
-
{
|
|
649
|
-
width: "14",
|
|
650
|
-
height: "14",
|
|
651
|
-
viewBox: "0 0 24 24",
|
|
652
|
-
fill: "none",
|
|
653
|
-
stroke: "currentColor",
|
|
654
|
-
strokeWidth: "2",
|
|
655
|
-
strokeLinecap: "round",
|
|
656
|
-
strokeLinejoin: "round",
|
|
657
|
-
"aria-hidden": "true",
|
|
658
|
-
children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
|
|
659
|
-
}
|
|
660
|
-
)
|
|
661
|
-
]
|
|
662
|
-
}
|
|
663
|
-
)
|
|
664
|
-
] }) })
|
|
705
|
+
}
|
|
706
|
+
)
|
|
665
707
|
] }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, disabled: buttonDisabled, style: buttonStyle, children: buttonLabel })
|
|
666
708
|
] });
|
|
667
709
|
}
|
|
668
710
|
|
|
669
|
-
// src/fiber-
|
|
670
|
-
import {
|
|
711
|
+
// src/fiber-node-button/index.tsx
|
|
712
|
+
import { useCallback as useCallback6 } from "react";
|
|
671
713
|
|
|
672
|
-
// src/
|
|
673
|
-
import {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
|
677
1083
|
}
|
|
678
|
-
|
|
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)}`;
|
|
679
1093
|
}
|
|
680
|
-
function
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
} catch (payError) {
|
|
716
|
-
if (isMountedRef.current) {
|
|
717
|
-
setError(asErrorMessage2(payError));
|
|
718
|
-
}
|
|
719
|
-
} finally {
|
|
720
|
-
if (isMountedRef.current) {
|
|
721
|
-
setIsPaying(false);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
},
|
|
725
|
-
[node]
|
|
726
|
-
);
|
|
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
|
+
}
|
|
727
1128
|
return {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
error
|
|
1129
|
+
...style,
|
|
1130
|
+
opacity: 0.55,
|
|
1131
|
+
cursor: "not-allowed"
|
|
732
1132
|
};
|
|
733
1133
|
}
|
|
734
1134
|
|
|
735
|
-
// src/fiber-
|
|
736
|
-
import {
|
|
737
|
-
|
|
738
|
-
var cardStyle = {
|
|
739
|
-
border: "1px solid #ddd",
|
|
740
|
-
borderRadius: 8,
|
|
741
|
-
padding: 16,
|
|
742
|
-
maxWidth: 520
|
|
743
|
-
};
|
|
744
|
-
var rowStyle = {
|
|
745
|
-
display: "flex",
|
|
746
|
-
gap: 8
|
|
747
|
-
};
|
|
748
|
-
var rowWithMarginStyle = {
|
|
749
|
-
...rowStyle,
|
|
750
|
-
marginBottom: 8
|
|
751
|
-
};
|
|
752
|
-
function FiberPayQuickCard(props) {
|
|
753
|
-
const network = props.network ?? "testnet";
|
|
754
|
-
const passkeyUsername = props.passkeyUsername ?? "User";
|
|
755
|
-
const title = props.title ?? "FiberPay Quick Card";
|
|
756
|
-
const onError = props.onError;
|
|
757
|
-
const onInvoiceCreated = props.onInvoiceCreated;
|
|
758
|
-
const onPaymentResult = props.onPaymentResult;
|
|
759
|
-
const passwordInputId = useId();
|
|
760
|
-
const invoiceInputId = useId();
|
|
1135
|
+
// src/fiber-node-button/render-action.tsx
|
|
1136
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1137
|
+
function renderPanelAction(options) {
|
|
761
1138
|
const {
|
|
762
|
-
|
|
763
|
-
|
|
1139
|
+
id,
|
|
1140
|
+
defaultProps,
|
|
1141
|
+
fiber,
|
|
764
1142
|
state,
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
if (paymentResult) {
|
|
791
|
-
onPaymentResult?.(paymentResult);
|
|
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
|
|
792
1168
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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(() => {
|
|
3115
|
+
if (payError) {
|
|
3116
|
+
onError?.({ scope: "payment", message: payError });
|
|
3117
|
+
}
|
|
3118
|
+
}, [onError, payError]);
|
|
3119
|
+
useEffect6(() => {
|
|
3120
|
+
if (paymentResult) {
|
|
3121
|
+
onPaymentResult?.(paymentResult);
|
|
3122
|
+
}
|
|
3123
|
+
}, [onPaymentResult, paymentResult]);
|
|
3124
|
+
const createInvoice = async () => {
|
|
3125
|
+
if (!node) {
|
|
3126
|
+
return;
|
|
797
3127
|
}
|
|
798
3128
|
setIsCreatingInvoice(true);
|
|
799
3129
|
setInvoiceError(null);
|
|
800
3130
|
try {
|
|
801
3131
|
const created = await node.newInvoice({
|
|
802
|
-
amount:
|
|
3132
|
+
amount: ONE_CKB_SHANNONS2,
|
|
803
3133
|
currency: network === "mainnet" ? "Fibb" : "Fibt",
|
|
804
3134
|
description: "FiberPay QuickCard invoice"
|
|
805
3135
|
});
|
|
@@ -813,18 +3143,21 @@ function FiberPayQuickCard(props) {
|
|
|
813
3143
|
setIsCreatingInvoice(false);
|
|
814
3144
|
}
|
|
815
3145
|
};
|
|
816
|
-
return /* @__PURE__ */
|
|
817
|
-
/* @__PURE__ */
|
|
3146
|
+
return /* @__PURE__ */ jsxs6("div", { style: { ...cardStyle, ...props.style }, className: props.className, children: [
|
|
3147
|
+
/* @__PURE__ */ jsxs6("h3", { children: [
|
|
818
3148
|
title,
|
|
819
3149
|
" (",
|
|
820
3150
|
network,
|
|
821
3151
|
")"
|
|
822
3152
|
] }),
|
|
823
|
-
!nodeInfo ? /* @__PURE__ */
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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(
|
|
828
3161
|
"input",
|
|
829
3162
|
{
|
|
830
3163
|
id: passwordInputId,
|
|
@@ -836,31 +3169,31 @@ function FiberPayQuickCard(props) {
|
|
|
836
3169
|
placeholder: "Password"
|
|
837
3170
|
}
|
|
838
3171
|
),
|
|
839
|
-
/* @__PURE__ */
|
|
3172
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void startWithPassword(password), children: "Start with Password" })
|
|
840
3173
|
] })
|
|
841
|
-
] }) : /* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
843
|
-
/* @__PURE__ */
|
|
3174
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment6, { children: [
|
|
3175
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
3176
|
+
/* @__PURE__ */ jsx8("strong", { children: "State:" }),
|
|
844
3177
|
" ",
|
|
845
3178
|
state
|
|
846
3179
|
] }),
|
|
847
|
-
/* @__PURE__ */
|
|
848
|
-
/* @__PURE__ */
|
|
3180
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
3181
|
+
/* @__PURE__ */ jsx8("strong", { children: "Pubkey:" }),
|
|
849
3182
|
" ",
|
|
850
3183
|
nodeInfo.pubkey
|
|
851
3184
|
] }),
|
|
852
|
-
/* @__PURE__ */
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
/* @__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" })
|
|
855
3188
|
] }),
|
|
856
|
-
createdInvoice ? /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
3189
|
+
createdInvoice ? /* @__PURE__ */ jsxs6("p", { children: [
|
|
3190
|
+
/* @__PURE__ */ jsx8("strong", { children: "Created invoice:" }),
|
|
858
3191
|
" ",
|
|
859
3192
|
createdInvoice
|
|
860
3193
|
] }) : null,
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
863
|
-
/* @__PURE__ */
|
|
3194
|
+
/* @__PURE__ */ jsx8("label", { htmlFor: invoiceInputId, children: "Invoice" }),
|
|
3195
|
+
/* @__PURE__ */ jsxs6("div", { style: rowStyle, children: [
|
|
3196
|
+
/* @__PURE__ */ jsx8(
|
|
864
3197
|
"input",
|
|
865
3198
|
{
|
|
866
3199
|
id: invoiceInputId,
|
|
@@ -870,26 +3203,26 @@ function FiberPayQuickCard(props) {
|
|
|
870
3203
|
placeholder: "Paste invoice to pay"
|
|
871
3204
|
}
|
|
872
3205
|
),
|
|
873
|
-
/* @__PURE__ */
|
|
3206
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void payInvoice(invoiceInput), disabled: isPaying, children: isPaying ? "Paying..." : "Pay" })
|
|
874
3207
|
] }),
|
|
875
|
-
paymentResult ? /* @__PURE__ */
|
|
876
|
-
/* @__PURE__ */
|
|
3208
|
+
paymentResult ? /* @__PURE__ */ jsxs6("p", { children: [
|
|
3209
|
+
/* @__PURE__ */ jsx8("strong", { children: "Payment:" }),
|
|
877
3210
|
" ",
|
|
878
3211
|
paymentResult.status
|
|
879
3212
|
] }) : null
|
|
880
3213
|
] }),
|
|
881
|
-
nodeError ? /* @__PURE__ */
|
|
882
|
-
/* @__PURE__ */
|
|
3214
|
+
nodeError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3215
|
+
/* @__PURE__ */ jsx8("strong", { children: "Node error:" }),
|
|
883
3216
|
" ",
|
|
884
3217
|
nodeError
|
|
885
3218
|
] }) : null,
|
|
886
|
-
payError ? /* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
3219
|
+
payError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3220
|
+
/* @__PURE__ */ jsx8("strong", { children: "Payment error:" }),
|
|
888
3221
|
" ",
|
|
889
3222
|
payError
|
|
890
3223
|
] }) : null,
|
|
891
|
-
invoiceError ? /* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
3224
|
+
invoiceError ? /* @__PURE__ */ jsxs6("p", { style: { color: "#b91c1c" }, children: [
|
|
3225
|
+
/* @__PURE__ */ jsx8("strong", { children: "Invoice error:" }),
|
|
893
3226
|
" ",
|
|
894
3227
|
invoiceError
|
|
895
3228
|
] }) : null
|
|
@@ -904,12 +3237,12 @@ import {
|
|
|
904
3237
|
scriptToAddress
|
|
905
3238
|
} from "@fiber-pay/sdk/browser";
|
|
906
3239
|
import {
|
|
907
|
-
useCallback as
|
|
908
|
-
useEffect as
|
|
909
|
-
useRef as
|
|
910
|
-
useState as
|
|
3240
|
+
useCallback as useCallback7,
|
|
3241
|
+
useEffect as useEffect7,
|
|
3242
|
+
useRef as useRef6,
|
|
3243
|
+
useState as useState7
|
|
911
3244
|
} from "react";
|
|
912
|
-
import { Fragment as
|
|
3245
|
+
import { Fragment as Fragment7, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
913
3246
|
function truncateMiddle(str, left = 8, right = 8) {
|
|
914
3247
|
if (str.length <= left + right + 3) return str;
|
|
915
3248
|
return `${str.slice(0, left)}\u2026${str.slice(-right)}`;
|
|
@@ -947,7 +3280,7 @@ async function fetchNodeStats(node, network) {
|
|
|
947
3280
|
externalFunding: false
|
|
948
3281
|
};
|
|
949
3282
|
}
|
|
950
|
-
var
|
|
3283
|
+
var styles3 = {
|
|
951
3284
|
root: {
|
|
952
3285
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
953
3286
|
fontSize: "0.875rem",
|
|
@@ -1076,7 +3409,7 @@ var STATUS_COLORS = {
|
|
|
1076
3409
|
error: { bg: "rgba(239,68,68,0.1)", fg: "#dc2626", dot: "#ef4444" }
|
|
1077
3410
|
};
|
|
1078
3411
|
function CopyIcon() {
|
|
1079
|
-
return /* @__PURE__ */
|
|
3412
|
+
return /* @__PURE__ */ jsxs7(
|
|
1080
3413
|
"svg",
|
|
1081
3414
|
{
|
|
1082
3415
|
width: "12",
|
|
@@ -1089,26 +3422,26 @@ function CopyIcon() {
|
|
|
1089
3422
|
strokeLinejoin: "round",
|
|
1090
3423
|
"aria-hidden": "true",
|
|
1091
3424
|
children: [
|
|
1092
|
-
/* @__PURE__ */
|
|
1093
|
-
/* @__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" })
|
|
1094
3427
|
]
|
|
1095
3428
|
}
|
|
1096
3429
|
);
|
|
1097
3430
|
}
|
|
1098
3431
|
function InfoRow({ label, value, copyable }) {
|
|
1099
|
-
return /* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
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(
|
|
1104
3437
|
"button",
|
|
1105
3438
|
{
|
|
1106
3439
|
type: "button",
|
|
1107
3440
|
onClick: () => copyToClipboard(value),
|
|
1108
|
-
style:
|
|
3441
|
+
style: styles3.copyButton,
|
|
1109
3442
|
title: "Copy to clipboard",
|
|
1110
3443
|
"aria-label": `Copy ${label}`,
|
|
1111
|
-
children: /* @__PURE__ */
|
|
3444
|
+
children: /* @__PURE__ */ jsx9(CopyIcon, {})
|
|
1112
3445
|
}
|
|
1113
3446
|
)
|
|
1114
3447
|
] })
|
|
@@ -1124,12 +3457,12 @@ function NodeInfoPanel(props) {
|
|
|
1124
3457
|
className,
|
|
1125
3458
|
style
|
|
1126
3459
|
} = props;
|
|
1127
|
-
const [stats, setStats] =
|
|
1128
|
-
const [statsError, setStatsError] =
|
|
1129
|
-
const [statsLoading, setStatsLoading] =
|
|
1130
|
-
const cancelledRef =
|
|
1131
|
-
const [QRComponent, setQRComponent] =
|
|
1132
|
-
|
|
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(() => {
|
|
1133
3466
|
let cancelled = false;
|
|
1134
3467
|
if (!showQrCode || renderQrCode) return;
|
|
1135
3468
|
import("qrcode.react").then((mod) => {
|
|
@@ -1142,8 +3475,8 @@ function NodeInfoPanel(props) {
|
|
|
1142
3475
|
cancelled = true;
|
|
1143
3476
|
};
|
|
1144
3477
|
}, [showQrCode, renderQrCode]);
|
|
1145
|
-
const loadingRef =
|
|
1146
|
-
const loadStats =
|
|
3478
|
+
const loadingRef = useRef6(false);
|
|
3479
|
+
const loadStats = useCallback7(async () => {
|
|
1147
3480
|
if (!node || node.state !== "running" || loadingRef.current) return;
|
|
1148
3481
|
loadingRef.current = true;
|
|
1149
3482
|
setStatsLoading(true);
|
|
@@ -1160,7 +3493,7 @@ function NodeInfoPanel(props) {
|
|
|
1160
3493
|
if (!cancelledRef.current) setStatsLoading(false);
|
|
1161
3494
|
}
|
|
1162
3495
|
}, [node, network]);
|
|
1163
|
-
|
|
3496
|
+
useEffect7(() => {
|
|
1164
3497
|
cancelledRef.current = false;
|
|
1165
3498
|
if (!node || node.state !== "running") {
|
|
1166
3499
|
setStats(null);
|
|
@@ -1175,21 +3508,21 @@ function NodeInfoPanel(props) {
|
|
|
1175
3508
|
};
|
|
1176
3509
|
}, [node, node?.state, pollInterval, loadStats]);
|
|
1177
3510
|
if (!node) {
|
|
1178
|
-
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" }) });
|
|
1179
3512
|
}
|
|
1180
3513
|
const nodeState = node.state;
|
|
1181
3514
|
const statusColor = STATUS_COLORS[nodeState] ?? STATUS_COLORS.idle;
|
|
1182
|
-
return /* @__PURE__ */
|
|
1183
|
-
/* @__PURE__ */
|
|
3515
|
+
return /* @__PURE__ */ jsxs7("div", { className, style: { ...styles3.root, ...style }, "data-fpay-node-info": "", children: [
|
|
3516
|
+
/* @__PURE__ */ jsxs7(
|
|
1184
3517
|
"div",
|
|
1185
3518
|
{
|
|
1186
3519
|
style: {
|
|
1187
|
-
...
|
|
3520
|
+
...styles3.statusBadge,
|
|
1188
3521
|
backgroundColor: statusColor.bg,
|
|
1189
3522
|
color: statusColor.fg
|
|
1190
3523
|
},
|
|
1191
3524
|
children: [
|
|
1192
|
-
/* @__PURE__ */
|
|
3525
|
+
/* @__PURE__ */ jsx9(
|
|
1193
3526
|
"span",
|
|
1194
3527
|
{
|
|
1195
3528
|
style: {
|
|
@@ -1205,8 +3538,8 @@ function NodeInfoPanel(props) {
|
|
|
1205
3538
|
]
|
|
1206
3539
|
}
|
|
1207
3540
|
),
|
|
1208
|
-
statsLoading && !stats && /* @__PURE__ */
|
|
1209
|
-
/* @__PURE__ */
|
|
3541
|
+
statsLoading && !stats && /* @__PURE__ */ jsxs7("div", { style: styles3.loading, children: [
|
|
3542
|
+
/* @__PURE__ */ jsxs7(
|
|
1210
3543
|
"svg",
|
|
1211
3544
|
{
|
|
1212
3545
|
width: "12",
|
|
@@ -1220,8 +3553,8 @@ function NodeInfoPanel(props) {
|
|
|
1220
3553
|
role: "img",
|
|
1221
3554
|
"aria-label": "Loading",
|
|
1222
3555
|
children: [
|
|
1223
|
-
/* @__PURE__ */
|
|
1224
|
-
/* @__PURE__ */
|
|
3556
|
+
/* @__PURE__ */ jsx9("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }),
|
|
3557
|
+
/* @__PURE__ */ jsx9(
|
|
1225
3558
|
"animateTransform",
|
|
1226
3559
|
{
|
|
1227
3560
|
attributeName: "transform",
|
|
@@ -1237,22 +3570,22 @@ function NodeInfoPanel(props) {
|
|
|
1237
3570
|
),
|
|
1238
3571
|
"Loading\u2026"
|
|
1239
3572
|
] }),
|
|
1240
|
-
statsError && /* @__PURE__ */
|
|
1241
|
-
stats && /* @__PURE__ */
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
stats.externalFunding ? /* @__PURE__ */
|
|
1244
|
-
/* @__PURE__ */
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__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 })
|
|
1248
3581
|
] }),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__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 })
|
|
1252
3585
|
] })
|
|
1253
3586
|
] }),
|
|
1254
|
-
showQrCode && stats.ckbAddress && /* @__PURE__ */
|
|
1255
|
-
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(
|
|
1256
3589
|
QRComponent,
|
|
1257
3590
|
{
|
|
1258
3591
|
value: stats.ckbAddress,
|
|
@@ -1260,11 +3593,11 @@ function NodeInfoPanel(props) {
|
|
|
1260
3593
|
bgColor: "transparent",
|
|
1261
3594
|
fgColor: "currentColor"
|
|
1262
3595
|
}
|
|
1263
|
-
) : /* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1265
|
-
/* @__PURE__ */
|
|
1266
|
-
/* @__PURE__ */
|
|
1267
|
-
/* @__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(
|
|
1268
3601
|
"span",
|
|
1269
3602
|
{
|
|
1270
3603
|
style: {
|
|
@@ -1284,10 +3617,11 @@ function NodeInfoPanel(props) {
|
|
|
1284
3617
|
] });
|
|
1285
3618
|
}
|
|
1286
3619
|
export {
|
|
1287
|
-
ChannelState,
|
|
3620
|
+
ChannelState3 as ChannelState,
|
|
1288
3621
|
ConfigBuilder2 as ConfigBuilder,
|
|
1289
3622
|
ConnectButton,
|
|
1290
3623
|
FiberBrowserNode2 as FiberBrowserNode,
|
|
3624
|
+
FiberNodeButton,
|
|
1291
3625
|
FiberPayQuickCard,
|
|
1292
3626
|
FiberRpcError,
|
|
1293
3627
|
NodeInfoPanel,
|
|
@@ -1295,14 +3629,15 @@ export {
|
|
|
1295
3629
|
PasswordCredentialProvider2 as PasswordCredentialProvider,
|
|
1296
3630
|
RawKeyCredentialProvider2 as RawKeyCredentialProvider,
|
|
1297
3631
|
ckbHash,
|
|
1298
|
-
ckbToShannons,
|
|
3632
|
+
ckbToShannons2 as ckbToShannons,
|
|
1299
3633
|
derivePublicKey,
|
|
1300
3634
|
formatShannonsAsCkb2 as formatShannonsAsCkb,
|
|
1301
3635
|
fromHex,
|
|
1302
3636
|
getLockBalanceShannons2 as getLockBalanceShannons,
|
|
1303
3637
|
scriptToAddress2 as scriptToAddress,
|
|
1304
|
-
shannonsToCkb,
|
|
3638
|
+
shannonsToCkb2 as shannonsToCkb,
|
|
1305
3639
|
toHex,
|
|
3640
|
+
useChannelOpenFlow,
|
|
1306
3641
|
useFiberNode,
|
|
1307
3642
|
useFiberPayment
|
|
1308
3643
|
};
|