damus

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

UserStatus.swift (4679B)


      1 //
      2 //  UserStatus.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2023-08-22.
      6 //
      7 
      8 import Foundation
      9 import MediaPlayer
     10 
     11 struct Song {
     12     let started_playing: Date
     13     let content: String
     14 }
     15 
     16 struct UserStatus {
     17     let type: UserStatusType
     18     let expires_at: Date?
     19     var content: String
     20     let created_at: UInt32
     21     var url: URL?
     22 
     23     func to_note(keypair: FullKeypair) -> NostrEvent? {
     24         return make_user_status_note(status: self, keypair: keypair)
     25     }
     26 
     27     init(type: UserStatusType, expires_at: Date?, content: String, created_at: UInt32, url: URL? = nil) {
     28         self.type = type
     29         self.expires_at = expires_at
     30         self.content = content
     31         self.created_at = created_at
     32         self.url = url
     33     }
     34 
     35     func expired() -> Bool {
     36         guard let expires_at else { return false }
     37         return Date.now >= expires_at
     38     }
     39 
     40     init?(ev: NostrEvent) {
     41         guard let tag = ev.referenced_params.just_one() else {
     42             return nil
     43         }
     44 
     45         let str = tag.param.string()
     46         if str == "general" {
     47             self.type = .general
     48         } else if str == "music" {
     49             self.type = .music
     50         } else {
     51             return nil
     52         }
     53 
     54         if let tag = ev.tags.first(where: { t in t.count >= 2 && t[0].matches_char("r") }),
     55            tag.count >= 2,
     56            let url = URL(string: tag[1].string())
     57         {
     58             self.url = url
     59         } else {
     60             self.url = nil
     61         }
     62 
     63         if let tag = ev.tags.first(where: { t in t.count >= 2 && t[0].matches_str("expiration") }),
     64            tag.count == 2,
     65            let expires = UInt32(tag[1].string())
     66         {
     67             self.expires_at = Date(timeIntervalSince1970: TimeInterval(expires))
     68         } else {
     69             self.expires_at = nil
     70         }
     71 
     72         self.content = ev.content
     73         self.created_at = ev.created_at
     74     }
     75 
     76 }
     77 
     78 enum UserStatusType: String {
     79     case music
     80     case general
     81 
     82 }
     83 
     84 class UserStatusModel: ObservableObject {
     85     @Published var general: UserStatus?
     86     @Published var music: UserStatus?
     87 
     88     func update_status(_ s: UserStatus) {
     89         // whitespace = delete
     90         let del = s.content.allSatisfy({ c in c.isWhitespace })
     91 
     92         switch s.type {
     93         case .music:
     94             if del {
     95                 self.music = nil
     96             } else {
     97                 self.music = s
     98             }
     99         case .general:
    100             if del {
    101                 self.general = nil
    102             } else {
    103                 self.general = s
    104             }
    105         }
    106     }
    107 
    108     func try_expire() {
    109         if let general, general.expired() {
    110             self.general = nil
    111         }
    112 
    113         if let music, music.expired() {
    114             self.music = nil
    115         }
    116     }
    117 
    118     var _playing_enabled: Bool
    119     var playing_enabled: Bool {
    120         set {
    121             var new_val = newValue
    122 
    123             if newValue {
    124                 MPMediaLibrary.requestAuthorization { astatus in
    125                     switch astatus {
    126                     case .notDetermined: new_val = false
    127                     case .denied:        new_val = false
    128                     case .restricted:    new_val = false
    129                     case .authorized:    new_val = true
    130                     @unknown default:
    131                         new_val = false
    132                     }
    133 
    134                 }
    135             }
    136 
    137             if new_val != playing_enabled {
    138                 _playing_enabled = new_val
    139                 self.objectWillChange.send()
    140             }
    141         }
    142 
    143         get {
    144             return _playing_enabled
    145         }
    146     }
    147 
    148     init(playing: UserStatus? = nil, status: UserStatus? = nil) {
    149         self.general = status
    150         self.music = playing
    151         self._playing_enabled = false
    152         self.playing_enabled = false
    153     }
    154 
    155     static var current_track: String? {
    156         let player = MPMusicPlayerController.systemMusicPlayer
    157         guard let nowPlayingItem = player.nowPlayingItem else { return nil }
    158         return nowPlayingItem.title
    159     }
    160 }
    161 
    162 func make_user_status_note(status: UserStatus, keypair: FullKeypair, expiry: Date? = nil) -> NostrEvent?
    163 {
    164     var tags: [[String]] = [ ["d", status.type.rawValue] ]
    165 
    166     if let expiry {
    167         tags.append(["expiration", String(UInt32(expiry.timeIntervalSince1970))])
    168     } else if let expiry = status.expires_at  {
    169         tags.append(["expiration", String(UInt32(expiry.timeIntervalSince1970))])
    170     }
    171 
    172     if let url = status.url {
    173         tags.append(["r", url.absoluteString])
    174     }
    175 
    176     let kind = NostrKind.status.rawValue
    177     guard let ev = NostrEvent(content: status.content, keypair: keypair.to_keypair(), kind: kind, tags: tags) else {
    178         return nil
    179     }
    180 
    181     return ev
    182 }
    183