diff --git a/release-notes/Release-notes-0.15.9.md b/release-notes/Release-notes-0.15.9.md
index 6a72fbb4..d1cf7c8a 100644
--- a/release-notes/Release-notes-0.15.9.md
+++ b/release-notes/Release-notes-0.15.9.md
@@ -16,6 +16,17 @@ this release should add its entry under the appropriate section below.
sync, and the list columns read `peer_connected` directly. Regression tests were added for
both channel tables.
+- **Core Lightning: fix the channel View Info modal rendering blank for disconnected channels**
+ ([#1625](https://github.com/Ride-The-Lightning/RTL/pull/1625), fixes
+ [#1606](https://github.com/Ride-The-Lightning/RTL/issues/1606)).
+ The channel information modal renders a block-explorer link from `selNode.settings.blockExplorerUrl`,
+ but the pending/inactive channels table opened the modal without passing `selNode`. With it
+ undefined, that binding threw during change detection and blanked every field below it — State,
+ Connected, Private and the balances all showed no value. Because a disconnected channel moves to
+ the pending/inactive table, this is exactly what was seen on "View Info" for a disconnected
+ channel. The pending table now passes `selNode` (matching the open table), and the modal guards
+ the explorer link so a missing `selNode` can no longer blank the dialog.
+
## Developer Tooling
- **Added a Core Lightning node to the regtest docker fixture**
diff --git a/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.html b/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.html
index a4f938ce..0fb69f9d 100644
--- a/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.html
+++ b/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.html
@@ -43,7 +43,7 @@
Funding Transaction ID
{{channel.funding_txid}}
-
+
diff --git a/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.ts b/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.ts
index a2053d0a..25c6cd37 100644
--- a/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.ts
+++ b/src/app/cln/peers-channels/channels/channel-information-modal/channel-information.component.ts
@@ -57,6 +57,7 @@ export class CLNChannelInformationComponent implements OnInit {
}
onExplorerClicked() {
+ if (!this.selNode?.settings?.blockExplorerUrl) { return; }
window.open(this.selNode.settings.blockExplorerUrl + '/tx/' + this.channel.funding_txid, '_blank');
}
diff --git a/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.spec.ts b/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.spec.ts
index 9d5448ce..dd9acf83 100644
--- a/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.spec.ts
+++ b/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.spec.ts
@@ -1,5 +1,5 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
-import { StoreModule } from '@ngrx/store';
+import { Store, StoreModule } from '@ngrx/store';
import { RootReducer } from '../../../../../store/rtl.reducers';
import { LNDReducer } from '../../../../../lnd/store/lnd.reducers';
@@ -73,6 +73,20 @@ describe('CLNChannelPendingTableComponent', () => {
expect(connectedCellText()).toBe('Disconnected');
});
+ // Issue #1606: the channel information modal reads selNode.settings.blockExplorerUrl, so the
+ // pending table must pass selNode when opening it. Without it the modal throws mid-render and
+ // blanks State/Connected/balances for disconnected channels (which live in this table).
+ it('should pass selNode when opening the channel information modal', () => {
+ const store = TestBed.inject(Store);
+ const dispatchSpy = spyOn(store, 'dispatch');
+ const selNode: any = { settings: { blockExplorerUrl: 'https://mempool.space' } };
+ component.selNode = selNode;
+ component.onChannelClick({ short_channel_id: '120x1x0', peer_connected: false } as any, {} as any);
+ expect(dispatchSpy).toHaveBeenCalled();
+ const action: any = dispatchSpy.calls.mostRecent().args[0];
+ expect(action.payload.data.selNode).toBe(selNode);
+ });
+
afterEach(() => {
TestBed.resetTestingModule();
});
diff --git a/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.ts b/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.ts
index 4b6d8fb0..9781befb 100644
--- a/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.ts
+++ b/src/app/cln/peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component.ts
@@ -20,6 +20,7 @@ import { openAlert, openConfirmation } from '../../../../../store/rtl.actions';
import { RTLState } from '../../../../../store/rtl.state';
import { closeChannel } from '../../../../store/cln.actions';
import { channels, clnPageSettings, nodeInfoAndBalanceAndNumPeers } from '../../../../store/cln.selector';
+import { rootSelectedNode } from '../../../../../store/rtl.selector';
import { ColumnDefinition, PageSettings, TableSetting } from '../../../../../shared/models/pageSettings';
import { CamelCaseWithReplacePipe } from '../../../../../shared/pipes/app.pipe';
import { MAT_SELECT_CONFIG } from '@angular/material/select';
@@ -62,6 +63,7 @@ export class CLNChannelPendingTableComponent implements OnInit, AfterViewInit, O
public errorMessage = '';
public apiCallStatus: ApiCallStatusPayload | null = null;
public apiCallStatusEnum = APICallStatusEnum;
+ public selNode: Node | null = null;
private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store, private rtlEffects: RTLEffects, private commonService: CommonService, private camelCaseWithReplace: CamelCaseWithReplacePipe) {
@@ -109,6 +111,10 @@ export class CLNChannelPendingTableComponent implements OnInit, AfterViewInit, O
}
this.logger.info(channelsSeletor);
});
+ this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[4])).
+ subscribe((nodeSettings) => {
+ this.selNode = nodeSettings;
+ });
}
ngAfterViewInit() {
@@ -133,6 +139,7 @@ export class CLNChannelPendingTableComponent implements OnInit, AfterViewInit, O
payload: {
data: {
channel: selChannel,
+ selNode: this.selNode,
showCopy: true,
component: CLNChannelInformationComponent
}