From 90c8097fcc538e057c8add7a6e972d127b9428cf Mon Sep 17 00:00:00 2001 From: mplorentz Date: Wed, 22 May 2024 10:48:29 -0400 Subject: [PATCH 1/3] Handle nos:nevent and similar links --- Nos/Service/DeepLinkService.swift | 44 ++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/Nos/Service/DeepLinkService.swift b/Nos/Service/DeepLinkService.swift index 8b9815cb7..5dc95fd10 100644 --- a/Nos/Service/DeepLinkService.swift +++ b/Nos/Service/DeepLinkService.swift @@ -1,26 +1,64 @@ import Foundation import Logger +import Dependencies enum DeepLinkService { - static let nosURLScheme = "nos" + /// Returns the URL scheme for Nos, which varies by build (dev, staging, production). + static var nosURLScheme: String? = { + if let urlTypes = Bundle.main.infoDictionary?["CFBundleURLTypes"] as? [[String: Any]] { + for urlTypeDictionary in urlTypes { + guard let urlSchemes = urlTypeDictionary["CFBundleURLSchemes"] as? [String] else { continue } + guard let externalURLScheme = urlSchemes.first else { continue } + return externalURLScheme + } + } + + return nil + }() @MainActor static func handle(_ url: URL, router: Router) { + @Dependency(\.persistenceController) var persistenceController + @Dependency(\.router) var router Log.info("handling link \(url.absoluteString)") let components = URLComponents(url: url, resolvingAgainstBaseURL: true) - guard let components else { + guard let components, let nosURLScheme, components.scheme == nosURLScheme else { return } - if components.scheme == nosURLScheme, components.host == "note", components.path == "/new" { + if components.host == "note", components.path == "/new" { let noteContents = components .queryItems? .first(where: { $0.name == "contents" })? .value router.showNewNoteView(contents: noteContents) + } else { + /// Check for links like nos:nevent123174 + let firstPathComponent = components.path + let unformattedRegex = /(?:nostr:)?(?((npub1|note1|nprofile1|nevent1)[a-zA-Z0-9]{58,255}))/ + do { + if let match = try unformattedRegex.firstMatch(in: firstPathComponent) { + let entity = match.1 + let string = String(entity) + + let (humanReadablePart, checksum) = try Bech32.decode(string) + + if humanReadablePart == Nostr.publicKeyPrefix, let hex = SHA256Key.decode(base5: checksum) { + router.push(try Author.findOrCreate(by: hex, context: persistenceController.viewContext)) + } else if humanReadablePart == Nostr.notePrefix, let hex = SHA256Key.decode(base5: checksum) { + router.push(try Event.findOrCreateStubBy(id: hex, context: persistenceController.viewContext)) + } else if humanReadablePart == Nostr.profilePrefix, let hex = TLV.decode(checksum: checksum) { + router.push(try Author.findOrCreate(by: hex, context: persistenceController.viewContext)) + } else if humanReadablePart == Nostr.eventPrefix, let hex = TLV.decode(checksum: checksum) { + router.push(try Event.findOrCreateStubBy(id: hex, context: persistenceController.viewContext)) + } + } + } catch { + Log.optional(error) + } } } } From b1d51fc3816fb5f6b47f7375e5b730ddafc311cd Mon Sep 17 00:00:00 2001 From: mplorentz Date: Thu, 30 May 2024 13:06:56 -0400 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e679f5874..dbb524c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Added support for opening njump.me content in Nos. - Fixed a crash on logout ## [0.1.15] - 2024-05-29Z From 2cdc8a10594987881bcab0bf486c5ba5fb7978e0 Mon Sep 17 00:00:00 2001 From: mplorentz Date: Wed, 5 Jun 2024 14:18:22 -0400 Subject: [PATCH 3/3] fix swiftlint error --- Nos/Service/DeepLinkService.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Nos/Service/DeepLinkService.swift b/Nos/Service/DeepLinkService.swift index 5dc95fd10..e0145d78d 100644 --- a/Nos/Service/DeepLinkService.swift +++ b/Nos/Service/DeepLinkService.swift @@ -38,6 +38,7 @@ enum DeepLinkService { } else { /// Check for links like nos:nevent123174 let firstPathComponent = components.path + // swiftlint:disable:next opening_brace let unformattedRegex = /(?:nostr:)?(?((npub1|note1|nprofile1|nevent1)[a-zA-Z0-9]{58,255}))/ do { if let match = try unformattedRegex.firstMatch(in: firstPathComponent) {