EventGroupView.swift (9562B)
1 // 2 // RepostGroupView.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-02-21. 6 // 7 8 import SwiftUI 9 10 11 enum EventGroupType { 12 case repost(EventGroup) 13 case reaction(EventGroup) 14 case zap(ZapGroup) 15 case profile_zap(ZapGroup) 16 17 var is_note_zap: Bool { 18 switch self { 19 case .repost: return false 20 case .reaction: return false 21 case .zap: return true 22 case .profile_zap: return false 23 } 24 } 25 26 var zap_group: ZapGroup? { 27 switch self { 28 case .profile_zap(let grp): 29 return grp 30 case .zap(let grp): 31 return grp 32 case .reaction: 33 return nil 34 case .repost: 35 return nil 36 } 37 } 38 39 var events: [NostrEvent] { 40 switch self { 41 case .repost(let grp): 42 return grp.events 43 case .reaction(let grp): 44 return grp.events 45 case .zap(let zapgrp): 46 return zapgrp.zap_requests() 47 case .profile_zap(let zapgrp): 48 return zapgrp.zap_requests() 49 } 50 } 51 } 52 53 enum ReactingTo { 54 case your_note 55 case tagged_in 56 case your_profile 57 } 58 59 func determine_reacting_to(our_pubkey: Pubkey, ev: NostrEvent?) -> ReactingTo { 60 guard let ev else { 61 return .your_profile 62 } 63 64 if ev.pubkey == our_pubkey { 65 return .your_note 66 } 67 68 return .tagged_in 69 } 70 71 func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [Pubkey] { 72 var seen = Set<Pubkey>() 73 var sorted = [Pubkey]() 74 75 if let zapgrp = group.zap_group { 76 let zaps = zapgrp.zaps 77 78 for i in 0..<zaps.count { 79 let zap = zapgrp.zaps[i] 80 let pubkey: Pubkey 81 82 if zap.is_anon { 83 pubkey = ANON_PUBKEY 84 } else { 85 pubkey = zap.request.ev.pubkey 86 } 87 88 if !seen.contains(pubkey) { 89 seen.insert(pubkey) 90 sorted.append(pubkey) 91 } 92 } 93 } else { 94 let events = group.events 95 96 for i in 0..<events.count { 97 let ev = events[i] 98 let pubkey = ev.pubkey 99 if !seen.contains(pubkey) { 100 seen.insert(pubkey) 101 sorted.append(pubkey) 102 } 103 } 104 } 105 106 return sorted 107 } 108 109 /** 110 Returns a notification string describing user actions in response to an event group type. 111 112 The localization keys read by this function are the following (although some keys may not actually be used in practice): 113 114 "??" - returned when there are no events associated with the specified event group type. 115 116 "reacted_tagged_in_1" - returned when 1 reaction occurred to a post that the current user was tagged in 117 "reacted_tagged_in_2" - returned when 2 reactions occurred to a post that the current user was tagged in 118 "reacted_tagged_in_3" - returned when 3 or more reactions occurred to a post that the current user was tagged in 119 "reacted_your_note_1" - returned when 1 reaction occurred to the current user's post 120 "reacted_your_note_2" - returned when 2 reactions occurred to the current user's post 121 "reacted_your_note_3" - returned when 3 or more reactions occurred to the current user's post 122 "reacted_your_profile_1" - returned when 1 reaction occurred to the current user's profile 123 "reacted_your_profile_2" - returned when 2 reactions occurred to the current user's profile 124 "reacted_your_profile_3" - returned when 3 or more reactions occurred to the current user's profile 125 126 "reposted_tagged_in_1" - returned when 1 repost occurred to a post that the current user was tagged in 127 "reposted_tagged_in_2" - returned when 2 reposts occurred to a post that the current user was tagged in 128 "reposted_tagged_in_3" - returned when 3 or more reposts occurred to a post that the current user was tagged in 129 "reposted_your_note_1" - returned when 1 repost occurred to the current user's post 130 "reposted_your_note_2" - returned when 2 reposts occurred to the current user's post 131 "reposted_your_note_3" - returned when 3 or more reposts occurred to the current user's post 132 "reposted_your_profile_1" - returned when 1 repost occurred to the current user's profile 133 "reposted_your_profile_2" - returned when 2 reposts occurred to the current user's profile 134 "reposted_your_profile_3" - returned when 3 or more reposts occurred to the current user's profile 135 136 "zapped_tagged_in_1" - returned when 1 zap occurred to a post that the current user was tagged in 137 "zapped_tagged_in_2" - returned when 2 zaps occurred to a post that the current user was tagged in 138 "zapped_tagged_in_3" - returned when 3 or more zaps occurred to a post that the current user was tagged in 139 "zapped_your_note_1" - returned when 1 zap occurred to the current user's post 140 "zapped_your_note_2" - returned when 2 zaps occurred to the current user's post 141 "zapped_your_note_3" - returned when 3 or more zaps occurred to the current user's post 142 "zapped_your_profile_1" - returned when 1 zap occurred to the current user's profile 143 "zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile 144 "zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile 145 */ 146 func reacting_to_text(profiles: Profiles, our_pubkey: Pubkey, group: EventGroupType, ev: NostrEvent?, pubkeys: [Pubkey], locale: Locale? = nil) -> String { 147 if group.events.count == 0 { 148 return "??" 149 } 150 151 let verb = reacting_to_verb(group: group) 152 let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev) 153 let localization_key = "\(verb)_\(reacting_to)_\(min(pubkeys.count, 3))" 154 let format = localizedStringFormat(key: localization_key, locale: locale) 155 156 switch pubkeys.count { 157 case 1: 158 let display_name = event_author_name(profiles: profiles, pubkey: pubkeys[0]) 159 160 return String(format: format, locale: locale, display_name) 161 case 2: 162 let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0]) 163 let bob_name = event_author_name(profiles: profiles, pubkey: pubkeys[1]) 164 165 return String(format: format, locale: locale, alice_name, bob_name) 166 default: 167 let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0]) 168 let count = pubkeys.count - 1 169 170 return String(format: format, locale: locale, count, alice_name) 171 } 172 } 173 174 func reacting_to_verb(group: EventGroupType) -> String { 175 switch group { 176 case .reaction: 177 return "reacted" 178 case .repost: 179 return "reposted" 180 case .zap, .profile_zap: 181 return "zapped" 182 } 183 } 184 185 struct EventGroupView: View { 186 let state: DamusState 187 let event: NostrEvent? 188 let group: EventGroupType 189 190 func GroupDescription(_ pubkeys: [Pubkey]) -> some View { 191 Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys))") 192 } 193 194 func ZapIcon(_ zapgrp: ZapGroup) -> some View { 195 let fmt = format_msats_abbrev(zapgrp.msat_total) 196 return VStack(alignment: .center) { 197 Image("zap.fill") 198 .foregroundColor(.orange) 199 Text(fmt) 200 .foregroundColor(Color.orange) 201 } 202 } 203 204 var GroupIcon: some View { 205 Group { 206 switch group { 207 case .repost: 208 Image("repost") 209 .foregroundColor(DamusColors.green) 210 case .reaction: 211 LINEAR_GRADIENT 212 .mask(Image("shaka.fill") 213 .resizable() 214 .aspectRatio(contentMode: .fit) 215 ) 216 .frame(width: 20, height: 20) 217 case .profile_zap(let zapgrp): 218 ZapIcon(zapgrp) 219 case .zap(let zapgrp): 220 ZapIcon(zapgrp) 221 } 222 } 223 } 224 225 var body: some View { 226 HStack(alignment: .top) { 227 GroupIcon 228 .frame(width: PFP_SIZE + 10) 229 230 VStack(alignment: .leading) { 231 let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group) 232 233 ProfilePicturesView(state: state, pubkeys: unique_pubkeys) 234 235 if let event { 236 let thread = ThreadModel(event: event, damus_state: state) 237 NavigationLink(value: Route.Thread(thread: thread)) { 238 VStack(alignment: .leading) { 239 GroupDescription(unique_pubkeys) 240 EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content]) 241 .padding([.top], 1) 242 .padding([.trailing]) 243 .foregroundColor(.gray) 244 } 245 } 246 .buttonStyle(.plain) 247 } else { 248 GroupDescription(unique_pubkeys) 249 } 250 } 251 } 252 .padding([.top], 6) 253 } 254 } 255 256 struct EventGroupView_Previews: PreviewProvider { 257 static var previews: some View { 258 VStack { 259 EventGroupView(state: test_damus_state, event: test_note, group: .repost(test_event_group)) 260 .frame(height: 200) 261 .padding() 262 263 EventGroupView(state: test_damus_state, event: test_note, group: .reaction(test_event_group)) 264 .frame(height: 200) 265 .padding() 266 } 267 } 268 269 } 270