damus

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

Nostr.swift (9371B)


      1 //
      2 //  Nostr.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2022-04-07.
      6 //
      7 
      8 import Foundation
      9 
     10 typealias Profile = NdbProfile
     11 typealias ProfileKey = UInt64
     12 //typealias ProfileRecord = NdbProfileRecord
     13 
     14 class ProfileRecord {
     15     let data: NdbProfileRecord
     16 
     17     init(data: NdbProfileRecord, key: ProfileKey) {
     18         self.data = data
     19         self.profileKey = key
     20     }
     21 
     22     let profileKey: ProfileKey
     23     var profile: Profile? { return data.profile }
     24     var receivedAt: UInt64 { data.receivedAt }
     25     var noteKey: UInt64 { data.noteKey }
     26 
     27     private var _lnurl: String? = nil
     28     var lnurl: String? {
     29         if let _lnurl {
     30             return _lnurl
     31         }
     32         
     33         guard let profile = data.profile,
     34               let addr = profile.lud16 ?? profile.lud06 else {
     35             return nil;
     36         }
     37         
     38         if addr.contains("@") {
     39             // this is a heavy op and is used a lot in views, cache it!
     40             let addr = lnaddress_to_lnurl(addr);
     41             self._lnurl = addr
     42             return addr
     43         }
     44         
     45         if !addr.lowercased().hasPrefix("lnurl") {
     46             return nil
     47         }
     48         
     49         return addr;
     50     }
     51     
     52 }
     53 
     54 extension NdbProfile {
     55     var display_name: String? {
     56         return displayName
     57     }
     58 
     59     static func displayName(profile: Profile?, pubkey: Pubkey) -> DisplayName {
     60         return parse_display_name(profile: profile, pubkey: pubkey)
     61     }
     62 
     63     var damus_donation: Int? {
     64         return Int(damusDonation)
     65     }
     66 
     67     var damus_donation_v2: Int {
     68         return Int(damusDonationV2)
     69     }
     70 
     71     var website_url: URL? {
     72         if self.website?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
     73             return nil
     74         }
     75         return self.website.flatMap { url in
     76             let trim = url.trimmingCharacters(in: .whitespacesAndNewlines)
     77             if !(trim.hasPrefix("http://") || trim.hasPrefix("https://")) {
     78                 return URL(string: "https://" + trim)
     79             }
     80             return URL(string: trim)
     81         }
     82     }
     83 
     84     init(name: String? = nil, display_name: String? = nil, about: String? = nil, picture: String? = nil, banner: String? = nil, website: String? = nil, lud06: String? = nil, lud16: String? = nil, nip05: String? = nil, damus_donation: Int? = nil, reactions: Bool = true) {
     85 
     86         var fbb = FlatBufferBuilder()
     87 
     88         let name_off = fbb.create(string: name)
     89         let display_name_off = fbb.create(string: display_name)
     90         let about_off = fbb.create(string: about)
     91         let picture_off = fbb.create(string: picture)
     92         let banner_off = fbb.create(string: banner)
     93         let website_off = fbb.create(string: website)
     94         let lud06_off = fbb.create(string: lud06)
     95         let lud16_off = fbb.create(string: lud16)
     96         let nip05_off = fbb.create(string: nip05)
     97 
     98         let profile_data = NdbProfile.createNdbProfile(&fbb,
     99                                     nameOffset: name_off,
    100                                     websiteOffset: website_off,
    101                                     aboutOffset: about_off,
    102                                     lud16Offset: lud16_off,
    103                                     bannerOffset: banner_off,
    104                                     displayNameOffset: display_name_off,
    105                                     reactions: reactions,
    106                                     pictureOffset: picture_off,
    107                                     nip05Offset: nip05_off,
    108                                     damusDonation: 0,
    109                                     damusDonationV2: damus_donation.map({ Int32($0) }) ?? 0,
    110                                     lud06Offset: lud06_off)
    111 
    112         fbb.finish(offset: profile_data)
    113 
    114         var buf = ByteBuffer(bytes: fbb.sizedByteArray)
    115         let profile: Profile = try! getCheckedRoot(byteBuffer: &buf)
    116         self = profile
    117     }
    118 }
    119 
    120 /*
    121 class Profile: Codable {
    122     var value: [String: AnyCodable]
    123     
    124     init(name: String? = nil, display_name: String? = nil, about: String? = nil, picture: String? = nil, banner: String? = nil, website: String? = nil, lud06: String? = nil, lud16: String? = nil, nip05: String? = nil, damus_donation: Int? = nil) {
    125         self.value = [:]
    126         self.name = name
    127         self.display_name = display_name
    128         self.about = about
    129         self.picture = picture
    130         self.banner = banner
    131         self.website = website
    132         self.lud06 = lud06
    133         self.lud16 = lud16
    134         self.nip05 = nip05
    135         self.damus_donation = damus_donation
    136     }
    137     
    138     private func str(_ str: String) -> String? {
    139         return get_val(str)
    140     }
    141     
    142     private func int(_ key: String) -> Int? {
    143         return get_val(key)
    144     }
    145     
    146     private func get_val<T>(_ v: String) -> T? {
    147         guard let val = self.value[v] else{
    148             return nil
    149         }
    150         
    151         guard let s = val.value as? T else {
    152             return nil
    153         }
    154         
    155         return s
    156     }
    157     
    158     private func set_val<T>(_ key: String, _ val: T?) {
    159         if val == nil {
    160             self.value.removeValue(forKey: key)
    161             return
    162         }
    163         
    164         self.value[key] = AnyCodable.init(val)
    165     }
    166     
    167     private func set_str(_ key: String, _ val: String?) {
    168         set_val(key, val)
    169     }
    170     
    171     private func set_int(_ key: String, _ val: Int?) {
    172         set_val(key, val)
    173     }
    174     
    175     var reactions: Bool? {
    176         get { return get_val("reactions"); }
    177         set(s) { set_val("reactions", s) }
    178     }
    179     
    180     var display_name: String? {
    181         get { return str("display_name"); }
    182         set(s) { set_str("display_name", s) }
    183     }
    184     
    185     var name: String? {
    186         get { return str("name"); }
    187         set(s) { set_str("name", s) }
    188     }
    189     
    190     var about: String? {
    191         get { return str("about"); }
    192         set(s) { set_str("about", s) }
    193     }
    194     
    195     var damus_donation: Int? {
    196         get { return int("damus_donation_v2"); }
    197         set(s) { set_int("damus_donation_v2", s) }
    198     }
    199     
    200     var picture: String? {
    201         get { return str("picture"); }
    202         set(s) { set_str("picture", s) }
    203     }
    204     
    205     var banner: String? {
    206         get { return str("banner"); }
    207         set(s) { set_str("banner", s) }
    208     }
    209     
    210     var website: String? {
    211         get { return str("website"); }
    212         set(s) { set_str("website", s) }
    213     }
    214     
    215     var lud06: String? {
    216         get { return str("lud06"); }
    217         set(s) { set_str("lud06", s) }
    218     }
    219     
    220     var lud16: String? {
    221         get { return str("lud16"); }
    222         set(s) { set_str("lud16", s) }
    223     }
    224     
    225     var website_url: URL? {
    226         if self.website?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
    227             return nil
    228         }
    229         return self.website.flatMap { url in
    230             let trim = url.trimmingCharacters(in: .whitespacesAndNewlines)
    231             if !(trim.hasPrefix("http://") || trim.hasPrefix("https://")) {
    232                 return URL(string: "https://" + trim)
    233             }
    234             return URL(string: trim)
    235         }
    236     }
    237     
    238     func cache_lnurl() {
    239         guard self._lnurl == nil else {
    240             return
    241         }
    242         
    243         guard let addr = lud16 ?? lud06 else {
    244             return
    245         }
    246         
    247         self._lnurl = lnaddress_to_lnurl(addr)
    248     }
    249     
    250     private var _lnurl: String? = nil
    251     var lnurl: String? {
    252         if let _lnurl {
    253             return _lnurl
    254         }
    255         
    256         guard let addr = lud16 ?? lud06 else {
    257             return nil;
    258         }
    259         
    260         if addr.contains("@") {
    261             // this is a heavy op and is used a lot in views, cache it!
    262             let addr = lnaddress_to_lnurl(addr);
    263             self._lnurl = addr
    264             return addr
    265         }
    266         
    267         if !addr.lowercased().hasPrefix("lnurl") {
    268             return nil
    269         }
    270         
    271         return addr;
    272     }
    273     
    274     var nip05: String? {
    275         get { return str("nip05"); }
    276         set(s) { set_str("nip05", s) }
    277     }
    278     
    279     var lightning_uri: URL? {
    280         return make_ln_url(self.lnurl)
    281     }
    282     
    283     init() {
    284         self.value = [:]
    285     }
    286     
    287     required init(from decoder: Decoder) throws {
    288         let container = try decoder.singleValueContainer()
    289         self.value = try container.decode([String: AnyCodable].self)
    290     }
    291     
    292     func encode(to encoder: Encoder) throws {
    293         var container = encoder.singleValueContainer()
    294         try container.encode(value)
    295     }
    296     
    297     static func displayName(profile: Profile?, pubkey: Pubkey) -> DisplayName {
    298         return parse_display_name(profile: profile, pubkey: pubkey)
    299     }
    300 }
    301 */
    302 
    303 func make_test_profile() -> Profile {
    304     return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200",  website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com", damus_donation: 1)
    305 }
    306 
    307 func make_ln_url(_ str: String?) -> URL? {
    308     return str.flatMap { URL(string: "lightning:" + $0) }
    309 }
    310 
    311 func lnaddress_to_lnurl(_ lnaddr: String) -> String? {
    312     let parts = lnaddr.split(separator: "@")
    313     guard parts.count == 2 else {
    314         return nil
    315     }
    316     
    317     let url = "https://\(parts[1])/.well-known/lnurlp/\(parts[0])";
    318     guard let dat = url.data(using: .utf8) else {
    319         return nil
    320     }
    321     
    322     return bech32_encode(hrp: "lnurl", Array(dat))
    323 }
    324