damus

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

Mentions.swift (7307B)


      1 //
      2 //  Mentions.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2022-05-04.
      6 //
      7 
      8 import Foundation
      9 
     10 enum MentionType: AsciiCharacter, TagKey {
     11     case p
     12     case e
     13     case a
     14     case r
     15 
     16     var keychar: AsciiCharacter {
     17         self.rawValue
     18     }
     19 }
     20 
     21 enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
     22     case pubkey(Pubkey)
     23     case note(NoteId)
     24     case nevent(NEvent)
     25     case nprofile(NProfile)
     26     case nrelay(String)
     27     case naddr(NAddr)
     28 
     29     var key: MentionType {
     30         switch self {
     31         case .pubkey: return .p
     32         case .note: return .e
     33         case .nevent: return .e
     34         case .nprofile: return .p
     35         case .nrelay: return .r
     36         case .naddr: return .a
     37         }
     38     }
     39 
     40     var bech32: String {
     41         return Bech32Object.encode(toBech32Object())
     42     }
     43 
     44     static func from_bech32(str: String) -> MentionRef? {
     45         switch Bech32Object.parse(str) {
     46         case .note(let noteid): return .note(noteid)
     47         case .npub(let pubkey): return .pubkey(pubkey)
     48         default: return nil
     49         }
     50     }
     51 
     52     var pubkey: Pubkey? {
     53         switch self {
     54         case .pubkey(let pubkey): return pubkey
     55         case .note:              return nil
     56         case .nevent(let nevent): return nevent.author
     57         case .nprofile(let nprofile): return nprofile.author
     58         case .nrelay: return nil
     59         case .naddr: return nil
     60         }
     61     }
     62 
     63     var tag: [String] {
     64         switch self {
     65         case .pubkey(let pubkey): return ["p", pubkey.hex()]
     66         case .note(let noteId):   return ["e", noteId.hex()]
     67         case .nevent(let nevent): return ["e", nevent.noteid.hex()]
     68         case .nprofile(let nprofile): return ["p", nprofile.author.hex()]
     69         case .nrelay(let url): return ["r", url]
     70         case .naddr(let naddr): return ["a", naddr.kind.description + ":" + naddr.author.hex() + ":" + naddr.identifier.string()]
     71         }
     72     }
     73 
     74     static func from_tag(tag: TagSequence) -> MentionRef? {
     75         guard tag.count >= 2 else { return nil }
     76 
     77         var i = tag.makeIterator()
     78 
     79         guard let t0 = i.next(),
     80               let chr = t0.single_char,
     81               let mention_type = MentionType(rawValue: chr),
     82               let element = i.next()
     83         else {
     84             return nil
     85         }
     86 
     87         switch mention_type {
     88         case .p:
     89             guard let data = element.id() else { return nil }
     90             return .pubkey(Pubkey(data))
     91         case .e:
     92             guard let data = element.id() else { return nil }
     93             return .note(NoteId(data))
     94         case .a:
     95             let str = element.string()
     96             let data = str.split(separator: ":")
     97             if(data.count != 3) { return nil }
     98             
     99             guard let pubkey = Pubkey(hex: String(data[1])) else { return nil }
    100             guard let kind = UInt32(data[0]) else { return nil }
    101             
    102             return .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind))
    103         case .r: return .nrelay(element.string())
    104         }
    105     }
    106     
    107     func toBech32Object() -> Bech32Object {
    108         switch self {
    109         case .pubkey(let pk):
    110             return .npub(pk)
    111         case .note(let noteid):
    112             return .note(noteid)
    113         case .naddr(let naddr):
    114             return .naddr(naddr)
    115         case .nevent(let nevent):
    116             return .nevent(nevent)
    117         case .nprofile(let nprofile):
    118             return .nprofile(nprofile)
    119         case .nrelay(let url):
    120             return .nrelay(url)
    121         }
    122     }
    123 }
    124 
    125 protocol URLEncodable {
    126     func url() -> URL?
    127 }
    128 
    129 struct Mention<T: Equatable>: Equatable {
    130     let index: Int?
    131     let ref: T
    132 
    133     static func any(_ mention_id: MentionRef, index: Int? = nil) -> Mention<MentionRef> {
    134         return Mention<MentionRef>(index: index, ref: mention_id)
    135     }
    136 
    137     static func noteref(_ id: NoteRef, index: Int? = nil) -> Mention<NoteRef> {
    138         return Mention<NoteRef>(index: index, ref: id)
    139     }
    140 
    141     static func note(_ id: NoteId, index: Int? = nil) -> Mention<NoteId> {
    142         return Mention<NoteId>(index: index, ref: id)
    143     }
    144 
    145     static func pubkey(_ pubkey: Pubkey, index: Int? = nil) -> Mention<Pubkey> {
    146         return Mention<Pubkey>(index: index, ref: pubkey)
    147     }
    148 }
    149 
    150 typealias Invoice = LightningInvoice<Amount>
    151 typealias ZapInvoice = LightningInvoice<Int64>
    152 
    153 enum InvoiceDescription {
    154     case description(String)
    155     case description_hash(Data)
    156 }
    157 
    158 struct LightningInvoice<T> {
    159     let description: InvoiceDescription
    160     let amount: T
    161     let string: String
    162     let expiry: UInt64
    163     let payment_hash: Data
    164     let created_at: UInt64
    165     
    166     var description_string: String {
    167         switch description {
    168         case .description(let string):
    169             return string
    170         case .description_hash:
    171             return ""
    172         }
    173     }
    174 }
    175 
    176 func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>!) -> T? {
    177     guard p != nil else {
    178         return nil
    179     }
    180     return p.pointee
    181 }
    182 
    183 enum Amount: Equatable {
    184     case any
    185     case specific(Int64)
    186     
    187     func amount_sats_str() -> String {
    188         switch self {
    189         case .any:
    190             return NSLocalizedString("Any", comment: "Any amount of sats")
    191         case .specific(let amt):
    192             return format_msats(amt)
    193         }
    194     }
    195 }
    196 
    197 func format_msats_abbrev(_ msats: Int64) -> String {
    198     let formatter = NumberFormatter()
    199     formatter.numberStyle = .decimal
    200     formatter.positiveSuffix = "m"
    201     formatter.positivePrefix = ""
    202     formatter.minimumFractionDigits = 0
    203     formatter.maximumFractionDigits = 3
    204     formatter.roundingMode = .down
    205     formatter.roundingIncrement = 0.1
    206     formatter.multiplier = 1
    207     
    208     let sats = NSNumber(value: (Double(msats) / 1000.0))
    209     
    210     if msats >= 1_000_000*1000 {
    211         formatter.positiveSuffix = "m"
    212         formatter.multiplier = 0.000001
    213     } else if msats >= 1000*1000 {
    214         formatter.positiveSuffix = "k"
    215         formatter.multiplier = 0.001
    216     } else {
    217         return sats.stringValue
    218     }
    219     
    220     return formatter.string(from: sats) ?? sats.stringValue
    221 }
    222 
    223 func format_msats(_ msat: Int64, locale: Locale = Locale.current) -> String {
    224     let numberFormatter = NumberFormatter()
    225     numberFormatter.numberStyle = .decimal
    226     numberFormatter.minimumFractionDigits = 0
    227     numberFormatter.maximumFractionDigits = 3
    228     numberFormatter.roundingMode = .down
    229     numberFormatter.locale = locale
    230 
    231     let sats = NSNumber(value: (Double(msat) / 1000.0))
    232     let formattedSats = numberFormatter.string(from: sats) ?? sats.stringValue
    233 
    234     let format = localizedStringFormat(key: "sats_count", locale: locale)
    235     return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats)
    236 }
    237 
    238 func convert_invoice_description(b11: bolt11) -> InvoiceDescription? {
    239     if let desc = b11.description {
    240         return .description(String(cString: desc))
    241     }
    242     
    243     if var deschash = maybe_pointee(b11.description_hash) {
    244         return .description_hash(Data(bytes: &deschash, count: 32))
    245     }
    246     
    247     return nil
    248 }
    249 
    250 func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
    251     var i: Int = 0
    252     for tag in tags {
    253         if tag.count >= 2 {
    254             if tag[0] == type && tag[1] == id {
    255                 return i
    256             }
    257         }
    258         i += 1
    259     }
    260     
    261     return nil
    262 }