feat: add real-time streaming LDK Node log viewer

This commit is contained in:
Evan Kaloudis
2026-03-26 20:01:30 -04:00
parent c7991a2bd5
commit be5e33a989
17 changed files with 440 additions and 7 deletions
+1
View File
@@ -4,6 +4,7 @@ android/local.properties
# OSX
#
.DS_Store
__MACOSX/
# Xcode
#
+5
View File
@@ -168,6 +168,7 @@ import RapidGossipSync from './views/Settings/EmbeddedNode/RapidGossipSync';
import EsploraServer from './views/Settings/EmbeddedNode/EsploraServer';
import VssServer from './views/Settings/EmbeddedNode/VssServer';
import LNDLogs from './views/Settings/EmbeddedNode/LNDLogs';
import LDKLogs from './views/Settings/EmbeddedNode/LDKLogs';
import Peers from './views/Settings/EmbeddedNode/Peers';
import NeutrinoPeers from './views/Settings/EmbeddedNode/Peers/NeutrinoPeers';
import ZeroConfPeers from './views/Settings/EmbeddedNode/Peers/ZeroConfPeers';
@@ -1058,6 +1059,10 @@ export default class App extends React.PureComponent {
name="LNDLogs" // @ts-ignore:next-line
component={LNDLogs}
/>
<Stack.Screen
name="LDKLogs" // @ts-ignore:next-line
component={LDKLogs}
/>
<Stack.Screen
name="Peers" // @ts-ignore:next-line
component={Peers}
@@ -13,6 +13,7 @@ class LdkNodeModule(reactContext: ReactApplicationContext) : ReactContextBaseJav
private var builder: Builder? = null
private var node: Node? = null
private var logFileObserver: LogFileObserver? = null
// Stored config values for building with custom Config
private var storedNetwork: Network = Network.BITCOIN
@@ -397,6 +398,12 @@ class LdkNodeModule(reactContext: ReactApplicationContext) : ReactContextBaseJav
builder.setLiquiditySourceLsps7(nodeId, address, this.storedLsps7Token)
}
}
// Enable filesystem logging
if (this.storedStorageDirPath.isNotEmpty()) {
Log.d("LdkNodeModule", "applyBuilderSettings: Enabling filesystem logger")
builder.setFilesystemLogger("${this.storedStorageDirPath}/ldk_node.log", LogLevel.DEBUG)
}
}
private fun createEsploraSyncConfig(): EsploraSyncConfig {
@@ -434,6 +441,8 @@ class LdkNodeModule(reactContext: ReactApplicationContext) : ReactContextBaseJav
try {
val node = this@LdkNodeModule.node ?: throw Exception("Node not initialized")
node.stop()
this@LdkNodeModule.logFileObserver?.stopObserving()
this@LdkNodeModule.logFileObserver = null
this@LdkNodeModule.node = null
withContext(Dispatchers.Main) {
promise.resolve(null)
@@ -1522,4 +1531,28 @@ class LdkNodeModule(reactContext: ReactApplicationContext) : ReactContextBaseJav
promise.reject("error", e.message, e)
}
}
// Log File Methods
@ReactMethod
fun tailLdkNodeLog(numLines: Int, promise: Promise) {
val logPath = "${this.storedStorageDirPath}/ldk_node.log"
promise.resolve(LogFileObserver.tailFile(logPath, numLines))
}
@ReactMethod
fun observeLdkNodeLogFile(promise: Promise) {
if (logFileObserver != null) {
promise.resolve(true)
return
}
val logPath = "${this.storedStorageDirPath}/ldk_node.log"
logFileObserver = LogFileObserver(logPath) { line ->
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("ldklog", line + "\n")
}
logFileObserver?.startObserving()
promise.resolve(true)
}
}
@@ -0,0 +1,73 @@
package org.lightningdevkit.ldknode
import android.os.FileObserver
import android.util.Log
import java.io.*
class LogFileObserver(
private val filePath: String,
private val onNewLine: (String) -> Unit
) {
private var fileObserver: FileObserver? = null
private var reader: BufferedReader? = null
fun startObserving() {
// Open file, create if needed
val file = File(filePath)
file.parentFile?.mkdirs()
if (!file.exists()) file.createNewFile()
val stream = FileInputStream(file)
reader = BufferedReader(InputStreamReader(stream))
// Read to EOF without emitting (skip historical)
readToEnd(emit = false)
// Watch for MODIFY events
fileObserver = object : FileObserver(filePath) {
override fun onEvent(event: Int, path: String?) {
if (event != MODIFY) return
readToEnd(emit = true)
}
}
fileObserver?.startWatching()
}
fun stopObserving() {
fileObserver?.stopWatching()
fileObserver = null
reader?.close()
reader = null
}
private fun readToEnd(emit: Boolean) {
try {
var line = reader?.readLine()
while (line != null) {
if (emit) onNewLine(line)
line = reader?.readLine()
}
} catch (e: IOException) {
Log.e("LogFileObserver", "Error reading from log file", e)
}
}
companion object {
fun tailFile(filePath: String, numLines: Int): String {
val file = File(filePath)
if (!file.exists() || file.length() == 0L) return ""
// Read last chunk (256KB should be plenty for typical numLines)
val chunkSize = minOf(262144L, file.length())
val raf = RandomAccessFile(file, "r")
raf.use {
it.seek(file.length() - chunkSize)
val bytes = ByteArray(chunkSize.toInt())
it.readFully(bytes)
val content = String(bytes, Charsets.UTF_8)
val lines = content.split("\n")
return lines.takeLast(numLines).joinToString("\n")
}
}
}
}
+2 -2
View File
@@ -12,9 +12,9 @@ ANDROID_LINK=$FILE_PATH$ANDROID_FILE
IOS_LINK=$FILE_PATH$IOS_FILE.zip
# LDK Node
LDK_NODE_VERSION=v0.7.0-zeus-lsps7-rgs-troubleshoot
LDK_NODE_VERSION=v0.7.0-zeus-lsps7-rgs-troubleshoot-logobserver
LDK_NODE_IOS_FILE=LDKNodeFFI.xcframework
LDK_NODE_IOS_SHA256='a99cf0cfd623737e588c48ae8f97bab4c71b18ce9593bb60a4a7cabcaf0d9026'
LDK_NODE_IOS_SHA256='75b031ebb05d186f0ffb09c336097a6f7b30205cb26596551b054bc07ea67907'
LDK_NODE_IOS_LINK=https://github.com/ZeusLN/ldk-node/releases/download/$LDK_NODE_VERSION/$LDK_NODE_IOS_FILE.zip
# test that curl and unzip are installed
+10
View File
@@ -78,6 +78,16 @@ RCT_EXTERN_METHOD(eventHandled:(RCTPromiseResolveBlock)resolve rejecter:(RCTProm
RCT_EXTERN_METHOD(lsps1RequestChannel:(double)lspBalanceSat clientBalanceSat:(double)clientBalanceSat channelExpiryBlocks:(double)channelExpiryBlocks announceChannel:(BOOL)announceChannel resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(lsps1CheckOrderStatus:(NSString *)orderId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
// LSPS7 Methods
RCT_EXTERN_METHOD(setLiquiditySourceLsps7:(NSString *)nodeId address:(NSString *)address token:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(lsps7GetExtendableChannels:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(lsps7CreateOrder:(NSString *)shortChannelId channelExtensionExpiryBlocks:(double)channelExtensionExpiryBlocks token:(NSString *)token refundOnchainAddress:(NSString *)refundOnchainAddress resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(lsps7CheckOrderStatus:(NSString *)orderId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
// Log File Methods
RCT_EXTERN_METHOD(tailLdkNodeLog:(nonnull NSNumber *)numLines resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(observeLdkNodeLogFile:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
// Message Signing Methods
RCT_EXTERN_METHOD(signMessage:(NSString *)message resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(verifySignature:(NSString *)message signature:(NSString *)signature publicKey:(NSString *)publicKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
+77 -1
View File
@@ -6,6 +6,7 @@ class LdkNodeModule: RCTEventEmitter {
private var node: Node?
private var builder: Builder?
private var logFileObserver: LogFileObserver?
// Stored config values for building with custom Config
private var storedNetwork: Network = .bitcoin
@@ -37,7 +38,7 @@ class LdkNodeModule: RCTEventEmitter {
}
override func supportedEvents() -> [String]! {
return ["LdkNodeEvent"]
return ["LdkNodeEvent", "ldklog"]
}
@objc
@@ -381,6 +382,11 @@ class LdkNodeModule: RCTEventEmitter {
if let lsps7NodeId = self.storedLsps7NodeId, let lsps7Address = self.storedLsps7Address {
builder.setLiquiditySourceLsps7(nodeId: lsps7NodeId, address: lsps7Address, token: self.storedLsps7Token)
}
if !self.storedStorageDirPath.isEmpty {
NSLog("LdkNodeModule: applyBuilderSettings: Enabling filesystem logger")
builder.setFilesystemLogger(logFilePath: "\(self.storedStorageDirPath)/ldk_node.log", maxLogLevel: .debug)
}
}
private func createEsploraSyncConfig() -> EsploraSyncConfig {
@@ -420,6 +426,8 @@ class LdkNodeModule: RCTEventEmitter {
do {
try node.stop()
self.logFileObserver?.stopObserving()
self.logFileObserver = nil
self.node = nil
resolve(["status": "ok"])
} catch {
@@ -1490,4 +1498,72 @@ class LdkNodeModule: RCTEventEmitter {
return "refunded"
}
}
// MARK: - LSPS7 Serialization Helpers
private func serializeLsps7OrderResponse(_ response: Lsps7OrderResponse) -> [String: Any] {
var result: [String: Any] = [
"orderId": response.orderId,
"orderState": serializeLsps7OrderState(response.orderState),
"channelExtensionExpiryBlocks": response.channelExtensionExpiryBlocks,
"newChannelExpiryBlock": response.newChannelExpiryBlock,
"paymentInfo": serializeLsps1PaymentInfo(response.payment),
"channel": serializeLsps7ExtendableChannel(response.channel)
]
return result
}
private func serializeLsps7ExtendableChannel(_ channel: Lsps7ExtendableChannel) -> [String: Any] {
var result: [String: Any] = [
"shortChannelId": channel.shortChannelId,
"maxChannelExtensionExpiryBlocks": channel.maxChannelExtensionExpiryBlocks,
"expirationBlock": channel.expirationBlock
]
if let originalOrder = channel.originalOrder {
result["originalOrder"] = [
"id": originalOrder.id,
"service": originalOrder.service
]
}
if let extensionOrderIds = channel.extensionOrderIds {
result["extensionOrderIds"] = extensionOrderIds
}
return result
}
// MARK: - Log File Methods
@objc(tailLdkNodeLog:resolver:rejecter:)
func tailLdkNodeLog(_ numLines: NSNumber,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
let logPath = "\(self.storedStorageDirPath)/ldk_node.log"
resolve(LogFileObserver.tailFile(path: logPath, numLines: numLines.intValue))
}
@objc(observeLdkNodeLogFile:rejecter:)
func observeLdkNodeLogFile(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
if logFileObserver != nil {
resolve(true)
return
}
let logPath = "\(self.storedStorageDirPath)/ldk_node.log"
logFileObserver = LogFileObserver(filePath: logPath) { [weak self] data in
self?.sendEvent(withName: "ldklog", body: data)
}
logFileObserver?.startObserving()
resolve(true)
}
private func serializeLsps7OrderState(_ state: Lsps7OrderState) -> String {
switch state {
case .created:
return "CREATED"
case .completed:
return "COMPLETED"
case .failed:
return "FAILED"
}
}
}
+71
View File
@@ -0,0 +1,71 @@
import Foundation
public class LogFileObserver {
private let filePath: String
private let onNewData: (String) -> Void
private var fileHandle: FileHandle?
private var source: DispatchSourceFileSystemObject?
public init(filePath: String, onNewData: @escaping (String) -> Void) {
self.filePath = filePath
self.onNewData = onNewData
}
public func startObserving() {
// Create file if needed
let fm = FileManager.default
if !fm.fileExists(atPath: filePath) {
let dir = (filePath as NSString).deletingLastPathComponent
try? fm.createDirectory(atPath: dir, withIntermediateDirectories: true)
fm.createFile(atPath: filePath, contents: nil)
}
guard let fh = FileHandle(forReadingAtPath: filePath) else { return }
self.fileHandle = fh
// Seek to end (skip historical)
fh.seekToEndOfFile()
// Use GCD dispatch source to watch for writes
let fd = fh.fileDescriptor
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fd,
eventMask: .write,
queue: DispatchQueue.global(qos: .utility)
)
source.setEventHandler { [weak self] in
let data = fh.availableData
if data.count > 0, let str = String(data: data, encoding: .utf8) {
self?.onNewData(str)
}
}
source.setCancelHandler {
fh.closeFile()
}
source.resume()
self.source = source
}
public func stopObserving() {
source?.cancel()
source = nil
fileHandle = nil
}
public static func tailFile(path: String, numLines: Int) -> String {
guard let fh = FileHandle(forReadingAtPath: path) else { return "" }
defer { fh.closeFile() }
let fileSize = fh.seekToEndOfFile()
if fileSize == 0 { return "" }
// Read last chunk (256KB should be plenty for typical numLines)
var chunkSize: UInt64 = 262144
if chunkSize > fileSize { chunkSize = fileSize }
fh.seek(toFileOffset: fileSize - chunkSize)
guard let data = String(data: fh.readDataToEndOfFile(), encoding: .utf8) else { return "" }
let lines = data.components(separatedBy: "\n")
return lines.suffix(numLines).joined(separator: "\n")
}
}
+4
View File
@@ -170,6 +170,7 @@
B1C2D3E62D36C99900000003 /* LDKNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E52D36C99900000002 /* LDKNode.swift */; };
B1C2D3E92D36C99900000006 /* LdkNodeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */; };
B1C2D3EA2D36C99900000007 /* LdkNodeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3E82D36C99900000005 /* LdkNodeModule.m */; };
B1C2D3EC2D36C99900000009 /* LogFileObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */; };
B1DB19555C1C4546922217C2 /* eye_closed.svg in Resources */ = {isa = PBXBuildFile; fileRef = 744BE042F00E4B16BDC79870 /* eye_closed.svg */; };
B20A92EA4E82493CB6D1A2F0 /* NFC.svg in Resources */ = {isa = PBXBuildFile; fileRef = 3C37D42473A54AD981B549B4 /* NFC.svg */; };
B39739CDBF39F16F2AD52938 /* libPods-zeus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC14DF216D694B9C5C997FE /* libPods-zeus.a */; };
@@ -676,6 +677,7 @@
B1C2D3E52D36C99900000002 /* LDKNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LDKNode.swift; sourceTree = "<group>"; };
B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LdkNodeModule.swift; sourceTree = "<group>"; };
B1C2D3E82D36C99900000005 /* LdkNodeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LdkNodeModule.m; sourceTree = "<group>"; };
B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogFileObserver.swift; sourceTree = "<group>"; };
B36D434AE9B14EE7AD5E6BCE /* Routing.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Routing.svg; path = ../assets/images/SVG/Routing.svg; sourceTree = "<group>"; };
B4C39762C9B748E39BFDA90F /* ExchangeBitcoin.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = ExchangeBitcoin.svg; path = ../assets/images/SVG/ExchangeBitcoin.svg; sourceTree = "<group>"; };
B4D00602022C4AE1BFA1A2C1 /* Hourglass.svg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Hourglass.svg; path = ../assets/images/SVG/Hourglass.svg; sourceTree = "<group>"; };
@@ -1097,6 +1099,7 @@
isa = PBXGroup;
children = (
B1C2D3E52D36C99900000002 /* LDKNode.swift */,
B1C2D3EB2D36C99900000008 /* LogFileObserver.swift */,
B1C2D3E72D36C99900000004 /* LdkNodeModule.swift */,
B1C2D3E82D36C99900000005 /* LdkNodeModule.m */,
);
@@ -2038,6 +2041,7 @@
B1C2D3E62D36C99900000003 /* LDKNode.swift in Sources */,
B1C2D3E92D36C99900000006 /* LdkNodeModule.swift in Sources */,
B1C2D3EA2D36C99900000007 /* LdkNodeModule.m in Sources */,
B1C2D3EC2D36C99900000009 /* LogFileObserver.swift in Sources */,
745388452A54C2B6000927CE /* LndMobileScheduledSync.swift in Sources */,
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
13B07FBD1A6810BG00A75B9A /* BackgroundTaskManager.m in Sources */,
+19
View File
@@ -518,6 +518,25 @@ export interface ILdkNodeModule {
): Promise<Lsps1OrderResponse>;
lsps1CheckOrderStatus(orderId: string): Promise<Lsps1OrderStatus>;
// LSPS7 Methods
setLiquiditySourceLsps7(
nodeId: string,
address: string,
token?: string | null
): Promise<void>;
lsps7GetExtendableChannels(): Promise<Lsps7ExtendableChannel[]>;
lsps7CreateOrder(
shortChannelId: string,
channelExtensionExpiryBlocks: number,
token?: string | null,
refundOnchainAddress?: string | null
): Promise<Lsps7OrderResponse>;
lsps7CheckOrderStatus(orderId: string): Promise<Lsps7OrderResponse>;
// Log File Methods
tailLdkNodeLog(numLines: number): Promise<string>;
observeLdkNodeLogFile(): Promise<boolean>;
// Message Signing Methods
signMessage(message: string): Promise<{ signature: string }>;
verifySignature(
+1
View File
@@ -1291,6 +1291,7 @@
"views.Settings.EmbeddedNode.AdvancedRescan.start": "Start rescan",
"views.Settings.EmbeddedNode.LNDLogs.title": "LND Logs",
"views.Settings.EmbeddedNode.LNDLogs.copyLogs": "Copy logs to clipboard",
"views.Settings.EmbeddedNode.LDKLogs.title": "LDK Node Logs",
"views.Settings.EmbeddedNode.feeEstimator": "Fee estimator",
"views.Settings.EmbeddedNode.customFeeEstimator": "Custom fee estimator",
"views.Settings.EmbeddedNode.speedloader": "Express Graph Sync server",
+6
View File
@@ -16,3 +16,9 @@ export const LndMobileToolsEventEmitter =
? DeviceEventEmitter
: // @ts-ignore:next-line
new NativeEventEmitter(NativeModules.LndMobileTools);
export const LdkNodeEventEmitter =
Platform.OS == 'android'
? DeviceEventEmitter
: // @ts-ignore:next-line
new NativeEventEmitter(NativeModules.LdkNodeModule);
+104
View File
@@ -0,0 +1,104 @@
import * as React from 'react';
import { NativeModules, Platform, View } from 'react-native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import CopyButton from '../../../components/CopyButton';
import Screen from '../../../components/Screen';
import Header from '../../../components/Header';
import LogBox from '../../../components/LogBox';
import { localeString } from '../../../utils/LocaleUtils';
import { themeColor } from '../../../utils/ThemeUtils';
import { LdkNodeEventEmitter } from '../../../utils/EventListenerUtils';
const MAX_LOG_LENGTH = 100000;
interface LDKLogsProps {
navigation: NativeStackNavigationProp<any, any>;
}
interface LDKLogsState {
log: string;
}
export default class LDKLogs extends React.Component<
LDKLogsProps,
LDKLogsState
> {
state = {
log: ''
};
logListener: any = null;
async componentDidMount(): Promise<void> {
(async () => {
const tailLog = await NativeModules.LdkNodeModule.tailLdkNodeLog(
100
);
let log = tailLog;
this.logListener = LdkNodeEventEmitter.addListener(
'ldklog',
(data: string) => {
log = log + data;
if (log.length > MAX_LOG_LENGTH) {
log = log.slice(-MAX_LOG_LENGTH);
}
this.setState({ log });
}
);
NativeModules.LdkNodeModule.observeLdkNodeLogFile().catch(
(e: any) => {
console.log('Could not observe LDK log file:', e);
}
);
this.setState({ log });
})();
}
componentWillUnmount(): void {
if (this.logListener) {
this.logListener.remove();
}
}
render() {
const { navigation } = this.props;
const { log } = this.state;
return (
<Screen>
<View style={{ flex: 1 }}>
<Header
leftComponent="Back"
centerComponent={{
text: localeString(
'views.Settings.EmbeddedNode.LDKLogs.title'
),
style: {
color: themeColor('text'),
fontFamily: 'PPNeueMontreal-Book'
}
}}
navigation={navigation}
/>
<LogBox text={log} scrollLock={false} />
<View
style={{
marginBottom: Platform.OS === 'android' ? 20 : 0
}}
>
<CopyButton
title={localeString(
'views.Settings.EmbeddedNode.LNDLogs.copyLogs'
)}
copyValue={log}
/>
</View>
</View>
</Screen>
);
}
}
+3
View File
@@ -54,6 +54,9 @@ export default class LNDLogs extends React.Component<
'lndlog',
(data: string) => {
log = log + data.slice(11);
if (log.length > 100000) {
log = log.slice(-100000);
}
this.setState({
log
});
@@ -1,7 +1,7 @@
import * as React from 'react';
import { ScrollView, Text, View } from 'react-native';
import { inject, observer } from 'mobx-react';
import { StackNavigationProp } from '@react-navigation/stack';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import Button from '../../../components/Button';
import Header from '../../../components/Header';
@@ -16,7 +16,7 @@ import { themeColor } from '../../../utils/ThemeUtils';
import { getDefaultRgsServer } from '../../../utils/EmbeddedLdkNodeUtils';
interface RapidGossipSyncProps {
navigation: StackNavigationProp<any, any>;
navigation: NativeStackNavigationProp<any, any>;
SettingsStore: SettingsStore;
}
+2 -2
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
import { ScrollView, Text, View } from 'react-native';
import { inject, observer } from 'mobx-react';
import { StackNavigationProp } from '@react-navigation/stack';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import Button from '../../../components/Button';
import Header from '../../../components/Header';
@@ -16,7 +16,7 @@ import { themeColor } from '../../../utils/ThemeUtils';
import { DEFAULT_VSS_SERVER } from '../../../utils/EmbeddedLdkNodeUtils';
interface VssServerProps {
navigation: StackNavigationProp<any, any>;
navigation: NativeStackNavigationProp<any, any>;
SettingsStore: SettingsStore;
}
+27
View File
@@ -273,6 +273,33 @@ export default class EmbeddedNode extends React.Component<
</ListItem>
)}
{/* Embedded LDK: LDK Logs */}
{isEmbeddedLdk && (
<ListItem
containerStyle={{
backgroundColor: 'transparent'
}}
onPress={() => navigation.navigate('LDKLogs')}
>
<ListItem.Content>
<ListItem.Title
style={{
color: themeColor('text'),
fontFamily: 'PPNeueMontreal-Book'
}}
>
{localeString(
'views.Settings.EmbeddedNode.LDKLogs.title'
)}
</ListItem.Title>
</ListItem.Content>
<Icon
name="keyboard-arrow-right"
color={themeColor('secondaryText')}
/>
</ListItem>
)}
{/* Embedded LND: Advanced */}
{isEmbeddedLnd && (
<ListItem