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 }