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