Skip to content

Commit

Permalink
ui: alias
Browse files Browse the repository at this point in the history
  • Loading branch information
reez committed Jan 31, 2024
1 parent adfb1b6 commit dfb0ea6
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 19 deletions.
20 changes: 20 additions & 0 deletions LDKNodeMonday.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
AE186B8E2A1540B700338463 /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE186B8D2A1540B700338463 /* StartView.swift */; };
AE1D9BEC2B2A1FFD00620748 /* BitcoinUI in Frameworks */ = {isa = PBXBuildFile; productRef = AE1D9BEB2B2A1FFD00620748 /* BitcoinUI */; };
AE1D9C0B2B2A251500620748 /* ChannelDetails+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1D9C0A2B2A251500620748 /* ChannelDetails+Extensions.swift */; };
AE3815362B6A9705006B2952 /* LightningNodesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE3815352B6A9705006B2952 /* LightningNodesService.swift */; };
AE3815392B6A978A006B2952 /* LightningNodeServiceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE3815382B6A978A006B2952 /* LightningNodeServiceError.swift */; };
AE38153B2B6A979E006B2952 /* LightningNodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38153A2B6A979E006B2952 /* LightningNodeInfo.swift */; };
AE49E84C2A24F96F002623E8 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E84B2A24F96F002623E8 /* Constants.swift */; };
AE49E8502A2535E3002623E8 /* TabHomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E84F2A2535E3002623E8 /* TabHomeViewModel.swift */; };
AE49E8522A253618002623E8 /* StartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE49E8512A253618002623E8 /* StartViewModel.swift */; };
Expand Down Expand Up @@ -109,6 +112,9 @@
AE17E90C29A42D430058C9C9 /* LightningNodeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodeService.swift; sourceTree = "<group>"; };
AE186B8D2A1540B700338463 /* StartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartView.swift; sourceTree = "<group>"; };
AE1D9C0A2B2A251500620748 /* ChannelDetails+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelDetails+Extensions.swift"; sourceTree = "<group>"; };
AE3815352B6A9705006B2952 /* LightningNodesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodesService.swift; sourceTree = "<group>"; };
AE3815382B6A978A006B2952 /* LightningNodeServiceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodeServiceError.swift; sourceTree = "<group>"; };
AE38153A2B6A979E006B2952 /* LightningNodeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightningNodeInfo.swift; sourceTree = "<group>"; };
AE49E84B2A24F96F002623E8 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
AE49E84F2A2535E3002623E8 /* TabHomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabHomeViewModel.swift; sourceTree = "<group>"; };
AE49E8512A253618002623E8 /* StartViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -231,6 +237,15 @@
name = Frameworks;
sourceTree = "<group>";
};
AE3815372B6A970D006B2952 /* LightningNodesService */ = {
isa = PBXGroup;
children = (
AE3815352B6A9705006B2952 /* LightningNodesService.swift */,
AE3815382B6A978A006B2952 /* LightningNodeServiceError.swift */,
);
path = LightningNodesService;
sourceTree = "<group>";
};
AE49E86D2A253A9A002623E8 /* Channel */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -372,6 +387,7 @@
AE89C7652AC5D49800EF8217 /* KeyService */,
AE49E8792A253D43002623E8 /* Lightning Service */,
AE7096362B5C206F0038BE56 /* PriceService */,
AE3815372B6A970D006B2952 /* LightningNodesService */,
);
path = Service;
sourceTree = "<group>";
Expand All @@ -386,6 +402,7 @@
AE70963B2B5C22270038BE56 /* CurrencyCode.swift */,
AEDF76032B5C687F002DDEE1 /* AnyOptional.swift */,
AEDF76052B5C68A0002DDEE1 /* ChannelDetailsFormatted.swift */,
AE38153A2B6A979E006B2952 /* LightningNodeInfo.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -609,9 +626,11 @@
AE49E8542A253647002623E8 /* BitcoinViewModel.swift in Sources */,
AE17E90D29A42D430058C9C9 /* LightningNodeService.swift in Sources */,
AEBAA4922A01C34A0042EA82 /* ChannelDetailView.swift in Sources */,
AE38153B2B6A979E006B2952 /* LightningNodeInfo.swift in Sources */,
AE70963A2B5C22060038BE56 /* Double+Extensions.swift in Sources */,
AE0A02242A0AD71900F2479E /* LogView.swift in Sources */,
AE49E8502A2535E3002623E8 /* TabHomeViewModel.swift in Sources */,
AE3815392B6A978A006B2952 /* LightningNodeServiceError.swift in Sources */,
AE83142829AEDDAD00088A70 /* SendView.swift in Sources */,
AE49E8662A2537D5002623E8 /* PeerViewModel.swift in Sources */,
AEBAA4942A01C6BD0042EA82 /* DisconnectView.swift in Sources */,
Expand Down Expand Up @@ -640,6 +659,7 @@
AE0055122B47A65700100797 /* Color+Extensions.swift in Sources */,
AED09EF42A312ACE00BBCFB1 /* SendBitcoinViewModel.swift in Sources */,
AE7C4A0C2B40785F0061189D /* SeedView.swift in Sources */,
AE3815362B6A9705006B2952 /* LightningNodesService.swift in Sources */,
AEE8FDDF29F8579600406DD9 /* Date+Extensions.swift in Sources */,
AEDF76042B5C687F002DDEE1 /* AnyOptional.swift in Sources */,
AE01C5B62AB3C0A200F28C7E /* BackupInfo.swift in Sources */,
Expand Down
60 changes: 60 additions & 0 deletions LDKNodeMonday/Model/LightningNodeInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// LightningNodeInfo.swift
// LDKNodeMonday
//
// Created by Matthew Ramsden on 1/31/24.
//

import Foundation

struct LightningNodeInfo: Codable {
let nodes: [Node]
let channels: [Channel]

struct Node: Codable {
let publicKey: String
let alias: String
let capacity: Int?
let channels: Int?

enum CodingKeys: String, CodingKey {
case publicKey = "public_key"
case alias
case capacity
case channels
}
}

struct Channel: Codable {
let channelId: String
let node1Pub: String
let node2Pub: String
let capacity: Int

enum CodingKeys: String, CodingKey {
case channelId = "channel_id"
case node1Pub = "node1_pub"
case node2Pub = "node2_pub"
case capacity
}
}
}

//#if DEBUG
let nodeMock = LightningNodeInfo.Node(
publicKey: "publicKey",
alias: "alias",
capacity: 1,
channels: 1
)
let channelMock = LightningNodeInfo.Channel(
channelId: "channelId",
node1Pub: "node1Pub",
node2Pub: "node2Pub",
capacity: 100
)
let nodeInfoMock = LightningNodeInfo(
nodes: [nodeMock],
channels: [channelMock]
)
//#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// LightningNodeServiceError.swift
// LDKNodeMonday
//
// Created by Matthew Ramsden on 1/31/24.
//

import Foundation

enum NodeInfoServiceError: Error {
case invalidURL
case invalidServerResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// LightningNodesService.swift
// LDKNodeMonday
//
// Created by Matthew Ramsden on 1/31/24.
//

import Foundation

private struct NodeInfoService {
func fetchNodeInfo(searchText: String) async throws -> LightningNodeInfo {
guard
let encodedSearchText = searchText.addingPercentEncoding(
withAllowedCharacters: .urlQueryAllowed
),
let url = URL(
string:
"https://mempool.space/api/v1/lightning/search?searchText=\(encodedSearchText)"
)
else {
throw NodeInfoServiceError.invalidURL
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode
else {
throw NodeInfoServiceError.invalidServerResponse
}
let jsonDecoder = JSONDecoder()
let jsonObject = try jsonDecoder.decode(LightningNodeInfo.self, from: data)
return jsonObject
}
}

struct NodeInfoClient {
let fetchNodeInfo: (_ searchText: String) async throws -> LightningNodeInfo
private init(fetchNodeInfo: @escaping (_ searchText: String) async throws -> LightningNodeInfo)
{
self.fetchNodeInfo = fetchNodeInfo
}
}

extension NodeInfoClient {
static let live = Self(fetchNodeInfo: { searchText in
try await NodeInfoService().fetchNodeInfo(searchText: searchText)
})
}

//#if DEBUG
extension NodeInfoClient {
static let mock = Self(fetchNodeInfo: { searchText in nodeInfoMock })
}
//#endif
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,46 @@
import LDKNode
import SwiftUI

class ChannelsListViewModel: ObservableObject {
@Published var channels: [ChannelDetails] = []
@Published var networkColor = Color.gray
@Observable
@MainActor
class ChannelsListViewModel {
let nodeInfoClient: NodeInfoClient
var channelsListViewError: MondayError?
var aliases = [String: String]()
var channels: [ChannelDetails] = []
var networkColor = Color.gray

func listChannels() {
init(nodeInfoClient: NodeInfoClient) {
self.nodeInfoClient = nodeInfoClient
}

func listChannels() async {
self.channels = LightningNodeService.shared.listChannels()

// Open Issue https://github.com/lightningdevkit/ldk-node/issues/234
// Temporary: if mainnet then get alias, ignore if other networks
if LightningNodeService.shared.network == Network.bitcoin {
for channel in channels {
await fetchAlias(for: channel.counterpartyNodeId)
}
}
}

func fetchAlias(for nodeId: String) async {
do {
let info = try await nodeInfoClient.fetchNodeInfo(nodeId)
if let alias = info.nodes.first?.alias {
self.aliases[nodeId] = alias
}
} catch let error as NodeError {
let errorString = handleNodeError(error)
self.channelsListViewError = .init(title: errorString.title, detail: errorString.detail)
} catch {
self.channelsListViewError = .init(
title: "Unexpected error",
detail: error.localizedDescription
)
}
}

func getColor() {
Expand Down
2 changes: 1 addition & 1 deletion LDKNodeMonday/View/Home/TabHomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct TabHomeView: View {
)
}

ChannelsListView(viewModel: .init())
ChannelsListView(viewModel: .init(nodeInfoClient: .live))
.tabItem {
Label(
"",
Expand Down
53 changes: 39 additions & 14 deletions LDKNodeMonday/View/Lightning/Channel/ChannelsListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SimpleToast
import SwiftUI

struct ChannelsListView: View {
@ObservedObject var viewModel: ChannelsListViewModel
@Bindable var viewModel: ChannelsListViewModel
@State private var isSendPresented = false
@State private var isReceivePresented = false
@State private var isViewPeersPresented = false
Expand Down Expand Up @@ -92,6 +92,19 @@ struct ChannelsListView: View {
Text("\(channel.channelValueSats) sats ")
.font(.caption)
.bold()
if let alias = viewModel.aliases[
channel.counterpartyNodeId
] {
Text(alias)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
} else {
Text("None")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
}
Text(channel.counterpartyNodeId)
.font(.caption)
.truncationMode(.middle)
Expand All @@ -108,7 +121,7 @@ struct ChannelsListView: View {
}
.listStyle(.plain)
.refreshable {
viewModel.listChannels()
await viewModel.listChannels()
}
}

Expand Down Expand Up @@ -162,11 +175,13 @@ struct ChannelsListView: View {
"\(viewModel.channels.count) \(viewModel.channels.count == 1 ? "Channel" : "Channels")"
)
.onAppear {
viewModel.listChannels()
viewModel.getColor()
if refreshFlag {
viewModel.listChannels()
refreshFlag = false
Task {
await viewModel.listChannels()
viewModel.getColor()
if refreshFlag {
await viewModel.listChannels()
refreshFlag = false
}
}
}
.simpleToast(
Expand Down Expand Up @@ -201,7 +216,9 @@ struct ChannelsListView: View {
.sheet(
isPresented: $isSendPresented,
onDismiss: {
viewModel.listChannels()
Task {
await viewModel.listChannels()
}
}
) {
SendView(viewModel: .init())
Expand All @@ -210,7 +227,9 @@ struct ChannelsListView: View {
.sheet(
isPresented: $isReceivePresented,
onDismiss: {
viewModel.listChannels()
Task {
await viewModel.listChannels()
}
}
) {
ReceiveView(viewModel: .init())
Expand All @@ -220,7 +239,9 @@ struct ChannelsListView: View {
.sheet(
isPresented: $isViewPeersPresented,
onDismiss: {
viewModel.listChannels()
Task {
await viewModel.listChannels()
}
}
) {
PeersListView(viewModel: .init())
Expand All @@ -229,7 +250,9 @@ struct ChannelsListView: View {
.sheet(
isPresented: $isAddChannelPresented,
onDismiss: {
viewModel.listChannels()
Task {
await viewModel.listChannels()
}
}
) {
ChannelAddView(viewModel: .init())
Expand All @@ -238,7 +261,9 @@ struct ChannelsListView: View {
.sheet(
isPresented: $isPaymentsPresented,
onDismiss: {
viewModel.listChannels()
Task {
await viewModel.listChannels()
}
}
) {
PaymentsView(viewModel: .init())
Expand All @@ -255,8 +280,8 @@ struct ChannelsListView: View {

struct ChannelsListView_Previews: PreviewProvider {
static var previews: some View {
ChannelsListView(viewModel: .init())
ChannelsListView(viewModel: .init())
ChannelsListView(viewModel: .init(nodeInfoClient: .mock))
ChannelsListView(viewModel: .init(nodeInfoClient: .mock))
.environment(\.colorScheme, .dark)
}
}

0 comments on commit dfb0ea6

Please sign in to comment.