EventGroupView.swift (9588B)
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 = Locale.current) -> 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 let text = reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys) 192 return Text(text) 193 } 194 195 func ZapIcon(_ zapgrp: ZapGroup) -> some View { 196 let fmt = format_msats_abbrev(zapgrp.msat_total) 197 return VStack(alignment: .center) { 198 Image("zap.fill") 199 .foregroundColor(.orange) 200 Text(fmt) 201 .foregroundColor(Color.orange) 202 } 203 } 204 205 var GroupIcon: some View { 206 Group { 207 switch group { 208 case .repost: 209 Image("repost") 210 .foregroundColor(DamusColors.green) 211 case .reaction: 212 LINEAR_GRADIENT 213 .mask(Image("shaka.fill") 214 .resizable() 215 .aspectRatio(contentMode: .fit) 216 ) 217 .frame(width: 20, height: 20) 218 case .profile_zap(let zapgrp): 219 ZapIcon(zapgrp) 220 case .zap(let zapgrp): 221 ZapIcon(zapgrp) 222 } 223 } 224 } 225 226 var body: some View { 227 HStack(alignment: .top) { 228 GroupIcon 229 .frame(width: PFP_SIZE + 10) 230 231 VStack(alignment: .leading) { 232 let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group) 233 234 ProfilePicturesView(state: state, pubkeys: unique_pubkeys) 235 236 if let event { 237 let thread = ThreadModel(event: event, damus_state: state) 238 NavigationLink(value: Route.Thread(thread: thread)) { 239 VStack(alignment: .leading) { 240 GroupDescription(unique_pubkeys) 241 EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content]) 242 .padding([.top], 1) 243 .padding([.trailing]) 244 .foregroundColor(.gray) 245 } 246 } 247 .buttonStyle(.plain) 248 } else { 249 GroupDescription(unique_pubkeys) 250 } 251 } 252 } 253 .padding([.top], 6) 254 } 255 } 256 257 struct EventGroupView_Previews: PreviewProvider { 258 static var previews: some View { 259 VStack { 260 EventGroupView(state: test_damus_state, event: test_note, group: .repost(test_event_group)) 261 .frame(height: 200) 262 .padding() 263 264 EventGroupView(state: test_damus_state, event: test_note, group: .reaction(test_event_group)) 265 .frame(height: 200) 266 .padding() 267 } 268 } 269 270 } 271