damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

URLHandler.swift (4370B)


      1 //
      2 //  URLHandler.swift
      3 //  damus
      4 //
      5 //  Created by Daniel D’Aquino on 2024-09-06.
      6 //
      7 
      8 import Foundation
      9 
     10 /// Parses URLs into actions within the app.
     11 ///
     12 /// ## Implementation notes
     13 ///
     14 /// - This exists so that we can separate the logic of parsing the URL and the actual action within the app. That makes the code more readable, testable, and extensible
     15 struct DamusURLHandler {
     16     /// Parses a URL, handles any needed actions within damus state, and returns the view to be opened in the app
     17     ///
     18     /// Side effects: May mutate `damus_state` in some circumstances
     19     ///
     20     /// - Parameters:
     21     ///   - damus_state: The Damus state. May be mutated as part of this function
     22     ///   - url: The URL to be opened
     23     /// - Returns: A view to be shown to the user
     24     static func handle_opening_url_and_compute_view_action(damus_state: DamusState, url: URL) async -> ContentView.ViewOpenAction {
     25         let parsed_url_info = parse_url(url: url)
     26         
     27         switch parsed_url_info {
     28         case .profile(let pubkey):
     29             return .route(.ProfileByKey(pubkey: pubkey))
     30         case .filter(let nostrFilter):
     31             let search = SearchModel(state: damus_state, search: nostrFilter)
     32             return .route(.Search(search: search))
     33         case .event(let nostrEvent):
     34             let thread = await ThreadModel(event: nostrEvent, damus_state: damus_state)
     35             return .route(.Thread(thread: thread))
     36         case .event_reference(let event_reference):
     37             return .route(.LoadableNostrEvent(note_reference: event_reference))
     38         case .wallet_connect(let walletConnectURL):
     39             damus_state.wallet.new(walletConnectURL)
     40             return .route(.Wallet(wallet: damus_state.wallet))
     41         case .script(let data):
     42             let model = ScriptModel(data: data, state: .not_loaded)
     43             return .route(.Script(script: model))
     44         case .purple(let purple_url):
     45             return await damus_state.purple.handle(purple_url: purple_url)
     46         case nil:
     47             break
     48         }
     49         return .sheet(.error(ErrorView.UserPresentableError(
     50             user_visible_description: NSLocalizedString("Could not parse the URL you are trying to open.", comment: "User visible error description"),
     51             tip: NSLocalizedString("Please try again, check the URL for typos, or contact support for further help.", comment: "User visible error tips"),
     52             technical_info: "Could not find a suitable open action. User tried to open this URL: \(url.absoluteString)"
     53         )))
     54     }
     55     
     56     /// Parses a URL into a structured information object.
     57     ///
     58     /// This function does not cause any mutations on the app, or any side-effects.
     59     ///
     60     /// - Parameter url: The URL to be parsed
     61     /// - Returns: Structured information about the contents inside the URL. Returns `nil` if URL is not compatible, invalid, or could not be parsed for some reason.
     62     static func parse_url(url: URL) -> ParsedURLInfo? {
     63         if let purple_url = DamusPurpleURL(url: url) {
     64             return .purple(purple_url)
     65         }
     66         
     67         if let nwc = WalletConnectURL(str: url.absoluteString) {
     68             return .wallet_connect(nwc)
     69         }
     70         
     71         guard let link = decode_nostr_uri(url.absoluteString) else {
     72             return nil
     73         }
     74         
     75         switch link {
     76         case .ref(let ref):
     77             switch ref {
     78             case .pubkey(let pk):
     79                 return .profile(pk)
     80             case .event(let noteid):
     81                 return .event_reference(.note_id(noteid))
     82             case .hashtag(let ht):
     83                 return .filter(.filter_hashtag([ht.hashtag]))
     84             case .param, .quote, .reference:
     85                 // doesn't really make sense here
     86                 break
     87             case .naddr(let naddr):
     88                 return .event_reference(.naddr(naddr))
     89             }
     90         case .filter(let filt):
     91             return .filter(filt)
     92         case .script(let script):
     93             return .script(script)
     94         }
     95         return nil
     96     }
     97     
     98     enum ParsedURLInfo {
     99         case profile(Pubkey)
    100         case filter(NostrFilter)
    101         case event(NostrEvent)
    102         case event_reference(LoadableNostrEventViewModel.NoteReference)
    103         case wallet_connect(WalletConnectURL)
    104         case script([UInt8])
    105         case purple(DamusPurpleURL)
    106     }
    107 }