Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 77 additions & 84 deletions Bitkit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Bitkit.xcodeproj/xcshareddata/xcschemes/Bitkit.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
LastUpgradeVersion = "2610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
LastUpgradeVersion = "2610"
wasCreatedForAppExtension = "YES"
version = "1.7">
<BuildAction
Expand Down
9 changes: 3 additions & 6 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,13 @@ struct AppScene: View {
showRecoveryScreen = true
case "PaykitRequestPayment":
// Navigate to Paykit payment request creation
navigation.activeDrawerMenuItem = .paykit
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
NotificationCenter.default.post(name: .paykitRequestPayment, object: nil)
}
navigation.navigate(.paykitPaymentRequests)
case "PaykitPayContact":
// Navigate to Paykit contacts for payment
navigation.activeDrawerMenuItem = .contacts
navigation.navigate(.paykitContacts)
case "ScanQR":
// Open scanner with Paykit URI support
sheets.showSheet(.qrScanner)
sheets.showSheet(.scanner)
default:
break
}
Expand Down
4 changes: 0 additions & 4 deletions Bitkit/Bitkit.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@
<string>development</string>
<key>com.apple.developer.aps-environment</key>
<string>development</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.bitkit</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)to.bitkit</string>
Expand Down
11 changes: 11 additions & 0 deletions Bitkit/BitkitApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ class AppDelegate: NSObject, UIApplicationDelegate {
func applicationWillTerminate(_ application: UIApplication) {
try? StateLocker.unlock(.lightning)
}

// MARK: - URL Handling

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Route bitkit:// URLs to PubkyRingBridge for Paykit/Pubky-ring callbacks
if url.scheme == "bitkit" {
Logger.info("AppDelegate: Received bitkit:// URL: \(url.absoluteString)", context: "AppDelegate")
return PubkyRingBridge.shared.handleCallback(url: url)
}
return false
}
}

// MARK: - Push Notifications
Expand Down
1 change: 1 addition & 0 deletions Bitkit/MainNavView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ struct MainNavView: View {
case .paykitNoisePayment: NoisePaymentView()
case .paykitPrivateEndpoints: PrivateEndpointsView()
case .paykitRotationSettings: RotationSettingsView()
case .paykitSessionManagement: SessionManagementView()
case .node: NodeStateView()
case .electrumSettings: ElectrumSettingsScreen()
case .rgsSettings: RgsSettingsScreen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
</dict>
<dict>
<key>BinaryPath</key>
<string>universal-sim-libpubky_noise.a</string>
<string>libpubky_noise.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>universal-sim-libpubky_noise.a</string>
<string>libpubky_noise.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion Bitkit/PaykitIntegration/Models/PaymentRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public struct BitkitPaymentRequest: Identifiable, Codable {
}

/// Status of a payment request
public enum PaymentRequestStatus: String, Codable {
public enum PaymentRequestStatus: String, Codable, CaseIterable {
case pending = "Pending"
case accepted = "Accepted"
case declined = "Declined"
Expand Down
5 changes: 5 additions & 0 deletions Bitkit/PaykitIntegration/PaykitManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public final class PaykitManager {
lightningNetwork: lightningNetwork.toFfi()
)

// Restore persisted sessions and configure SDK
PubkyRingBridge.shared.restoreSessions()
PubkySDKService.shared.restoreSessions()
PubkySDKService.shared.configure()

isInitialized = true
Logger.info("PaykitManager initialized successfully", context: "PaykitManager")
}
Expand Down
63 changes: 62 additions & 1 deletion Bitkit/PaykitIntegration/Services/DirectoryService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,40 @@ public final class DirectoryService {
// MARK: - Profile Operations

/// Fetch profile for a pubkey from Pubky directory
/// Uses PubkySDKService first, falls back to direct FFI if unavailable
public func fetchProfile(for pubkey: String) async throws -> PubkyProfile? {
// Try PubkySDKService first (preferred, direct homeserver access)
do {
let sdkProfile = try await PubkySDKService.shared.fetchProfile(pubkey: pubkey)
// Convert to local PubkyProfile type
return PubkyProfile(
name: sdkProfile.name,
bio: sdkProfile.bio,
avatar: sdkProfile.image,
links: sdkProfile.links?.map { PubkyProfileLink(title: $0.title, url: $0.url) }
)
} catch {
Logger.debug("PubkySDKService profile fetch failed: \(error)", context: "DirectoryService")
}

// Try PubkyRingBridge if Pubky-ring is installed (user interaction required)
if PubkyRingBridge.shared.isPubkyRingInstalled {
do {
if let profile = try await PubkyRingBridge.shared.requestProfile(pubkey: pubkey) {
Logger.debug("Got profile from Pubky-ring", context: "DirectoryService")
return profile
}
} catch {
Logger.debug("PubkyRingBridge profile fetch failed: \(error)", context: "DirectoryService")
}
}

// Fallback to direct FFI
return try await fetchProfileViaFFI(for: pubkey)
}

/// Fetch profile using direct FFI (fallback)
private func fetchProfileViaFFI(for pubkey: String) async throws -> PubkyProfile? {
let adapter = unauthenticatedTransport ?? {
let adapter = PubkyUnauthenticatedStorageAdapter(homeserverBaseURL: homeserverBaseURL)
let transport = UnauthenticatedTransportFfi.fromCallback(callback: adapter)
Expand Down Expand Up @@ -261,11 +294,38 @@ public final class DirectoryService {
// MARK: - Follows Operations

/// Fetch list of pubkeys user follows
/// Uses PubkySDKService first, falls back to direct FFI if unavailable
public func fetchFollows() async throws -> [String] {
guard let ownerPubkey = PaykitKeyManager.shared.getCurrentPublicKeyZ32() else {
return []
}

// Try PubkySDKService first (preferred, direct homeserver access)
do {
return try await PubkySDKService.shared.fetchFollows(pubkey: ownerPubkey)
} catch {
Logger.debug("PubkySDKService follows fetch failed: \(error)", context: "DirectoryService")
}

// Try PubkyRingBridge if Pubky-ring is installed (user interaction required)
if PubkyRingBridge.shared.isPubkyRingInstalled {
do {
let follows = try await PubkyRingBridge.shared.requestFollows()
if !follows.isEmpty {
Logger.debug("Got \(follows.count) follows from Pubky-ring", context: "DirectoryService")
return follows
}
} catch {
Logger.debug("PubkyRingBridge follows fetch failed: \(error)", context: "DirectoryService")
}
}

// Fallback to direct FFI
return try await fetchFollowsViaFFI(ownerPubkey: ownerPubkey)
}

/// Fetch follows using direct FFI (fallback)
private func fetchFollowsViaFFI(ownerPubkey: String) async throws -> [String] {
let adapter = unauthenticatedTransport ?? {
let adapter = PubkyUnauthenticatedStorageAdapter(homeserverBaseURL: homeserverBaseURL)
let transport = UnauthenticatedTransportFfi.fromCallback(callback: adapter)
Expand All @@ -275,8 +335,9 @@ public final class DirectoryService {

let pubkyStorage = PubkyStorageAdapter.shared
let followsPath = "/pub/pubky.app/follows/"
let unauthenticatedAdapter = PubkyUnauthenticatedStorageAdapter(homeserverBaseURL: homeserverBaseURL)

return try await pubkyStorage.listDirectory(path: followsPath, adapter: adapter, ownerPubkey: ownerPubkey)
return try await pubkyStorage.listDirectory(path: followsPath, adapter: unauthenticatedAdapter, ownerPubkey: ownerPubkey)
}

/// Add a follow to the Pubky directory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ public class PaymentRequestService {

case .needsApproval:
completion(.success(.needsApproval(request: request)))

case .needsBiometric:
completion(.success(.needsApproval(request: request)))
}
} catch {
Logger.error("PaymentRequestService: Failed to handle request: \(error)", context: "PaymentRequestService")
Expand Down Expand Up @@ -196,6 +199,8 @@ enum PaymentRequestError: LocalizedError {

// Make AutoPayViewModel conform to AutopayEvaluator
extension AutoPayViewModel: AutopayEvaluator {
// Already implements evaluate() method above
func evaluate(peerPubkey: String, amount: Int64, methodId: String) -> AutopayEvaluationResult {
return evaluate(peerPubkey: peerPubkey, peerName: "", amount: amount, methodId: methodId, isSubscription: false)
}
}

Loading