mirror of
git://jb55.com/damus
synced 2024-09-18 19:23:49 +00:00
Deduplicate users in group notifications
Changelog-Fixed: Deduplicate users in notifications Closes: #1326
This commit is contained in:
parent
2046fe5502
commit
cfe14fac23
@ -1076,7 +1076,7 @@ func zap_notification_title(_ zap: Zap) -> String {
|
|||||||
|
|
||||||
func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
|
func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
|
||||||
let src = zap.request.ev
|
let src = zap.request.ev
|
||||||
let pk = zap.is_anon ? "anon" : src.pubkey
|
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
|
||||||
let profile = profiles.lookup(id: pk)
|
let profile = profiles.lookup(id: pk)
|
||||||
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
|
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
|
||||||
let formattedSats = format_msats_abbrev(zap.invoice.amount)
|
let formattedSats = format_msats_abbrev(zap.invoice.amount)
|
||||||
|
@ -38,7 +38,7 @@ enum DisplayName {
|
|||||||
|
|
||||||
|
|
||||||
func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName {
|
func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName {
|
||||||
if pubkey == "anon" {
|
if pubkey == ANON_PUBKEY {
|
||||||
return .one(NSLocalizedString("Anonymous", comment: "Placeholder display name of anonymous user."))
|
return .one(NSLocalizedString("Anonymous", comment: "Placeholder display name of anonymous user."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import Foundation
|
|||||||
import secp256k1
|
import secp256k1
|
||||||
|
|
||||||
let PUBKEY_HRP = "npub"
|
let PUBKEY_HRP = "npub"
|
||||||
|
let ANON_PUBKEY = "anon"
|
||||||
|
|
||||||
struct FullKeypair: Equatable {
|
struct FullKeypair: Equatable {
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
|
@ -132,7 +132,7 @@ struct TextEvent: View {
|
|||||||
|
|
||||||
func ProfileName(is_anon: Bool) -> some View {
|
func ProfileName(is_anon: Bool) -> some View {
|
||||||
let profile = damus.profiles.lookup(id: pubkey)
|
let profile = damus.profiles.lookup(id: pubkey)
|
||||||
let pk = is_anon ? "anon" : pubkey
|
let pk = is_anon ? ANON_PUBKEY : pubkey
|
||||||
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
|
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,19 +78,42 @@ func event_author_name(profiles: Profiles, pubkey: String) -> String {
|
|||||||
return Profile.displayName(profile: alice_prof, pubkey: pubkey).username.truncate(maxLength: 50)
|
return Profile.displayName(profile: alice_prof, pubkey: pubkey).username.truncate(maxLength: 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType) -> String {
|
func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [String] {
|
||||||
|
var seen = Set<String>()
|
||||||
|
var sorted = [String]()
|
||||||
|
|
||||||
if let zapgrp = group.zap_group {
|
if let zapgrp = group.zap_group {
|
||||||
let zap = zapgrp.zaps[ind]
|
let zaps = zapgrp.zaps
|
||||||
|
|
||||||
if zap.is_anon {
|
for i in 0..<zaps.count {
|
||||||
return NSLocalizedString("Anonymous", comment: "Placeholder author name of the anonymous person who zapped an event.")
|
let zap = zapgrp.zaps[i]
|
||||||
|
let pubkey: String
|
||||||
|
|
||||||
|
if zap.is_anon {
|
||||||
|
pubkey = ANON_PUBKEY
|
||||||
|
} else {
|
||||||
|
pubkey = zap.request.ev.pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seen.contains(pubkey) {
|
||||||
|
seen.insert(pubkey)
|
||||||
|
sorted.append(pubkey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return event_author_name(profiles: profiles, pubkey: zap.request.ev.pubkey)
|
|
||||||
} else {
|
} else {
|
||||||
let ev = group.events[ind]
|
let events = group.events
|
||||||
return event_author_name(profiles: profiles, pubkey: ev.pubkey)
|
|
||||||
|
for i in 0..<events.count {
|
||||||
|
let ev = events[i]
|
||||||
|
let pubkey = ev.pubkey
|
||||||
|
if !seen.contains(pubkey) {
|
||||||
|
seen.insert(pubkey)
|
||||||
|
sorted.append(pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,29 +153,29 @@ func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType
|
|||||||
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
|
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
|
||||||
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
|
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
|
||||||
*/
|
*/
|
||||||
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, nozaps: Bool, locale: Locale? = nil) -> String {
|
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, nozaps: Bool, pubkeys: [String], locale: Locale? = nil) -> String {
|
||||||
if group.events.count == 0 {
|
if pubkeys.count == 0 {
|
||||||
return "??"
|
return "??"
|
||||||
}
|
}
|
||||||
|
|
||||||
let verb = reacting_to_verb(group: group)
|
let verb = reacting_to_verb(group: group)
|
||||||
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev, group: group, nozaps: nozaps)
|
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev, group: group, nozaps: nozaps)
|
||||||
let localization_key = "\(verb)_\(reacting_to)_\(min(group.events.count, 3))"
|
let localization_key = "\(verb)_\(reacting_to)_\(min(pubkeys.count, 3))"
|
||||||
let format = localizedStringFormat(key: localization_key, locale: locale)
|
let format = localizedStringFormat(key: localization_key, locale: locale)
|
||||||
|
|
||||||
switch group.events.count {
|
switch pubkeys.count {
|
||||||
case 1:
|
case 1:
|
||||||
let display_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
let display_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
||||||
|
|
||||||
return String(format: format, locale: locale, display_name)
|
return String(format: format, locale: locale, display_name)
|
||||||
case 2:
|
case 2:
|
||||||
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
||||||
let bob_name = event_group_author_name(profiles: profiles, ind: 1, group: group)
|
let bob_name = event_author_name(profiles: profiles, pubkey: pubkeys[1])
|
||||||
|
|
||||||
return String(format: format, locale: locale, alice_name, bob_name)
|
return String(format: format, locale: locale, alice_name, bob_name)
|
||||||
default:
|
default:
|
||||||
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
||||||
let count = group.events.count - 1
|
let count = pubkeys.count - 1
|
||||||
|
|
||||||
return String(format: format, locale: locale, count, alice_name)
|
return String(format: format, locale: locale, count, alice_name)
|
||||||
}
|
}
|
||||||
@ -175,8 +198,8 @@ struct EventGroupView: View {
|
|||||||
let event: NostrEvent?
|
let event: NostrEvent?
|
||||||
let group: EventGroupType
|
let group: EventGroupType
|
||||||
|
|
||||||
var GroupDescription: some View {
|
func GroupDescription(_ pubkeys: [String]) -> some View {
|
||||||
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, nozaps: state.settings.nozaps))")
|
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, nozaps: state.settings.nozaps, pubkeys: pubkeys))")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
|
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
|
||||||
@ -216,12 +239,14 @@ struct EventGroupView: View {
|
|||||||
.frame(width: PFP_SIZE + 10)
|
.frame(width: PFP_SIZE + 10)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
ProfilePicturesView(state: state, pubkeys: group.events.map { $0.pubkey })
|
let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group)
|
||||||
|
|
||||||
|
ProfilePicturesView(state: state, pubkeys: unique_pubkeys)
|
||||||
|
|
||||||
if let event {
|
if let event {
|
||||||
let thread = ThreadModel(event: event, damus_state: state)
|
let thread = ThreadModel(event: event, damus_state: state)
|
||||||
let dest = ThreadView(state: state, thread: thread)
|
let dest = ThreadView(state: state, thread: thread)
|
||||||
GroupDescription
|
GroupDescription(unique_pubkeys)
|
||||||
if !state.settings.nozaps || !group.is_note_zap {
|
if !state.settings.nozaps || !group.is_note_zap {
|
||||||
NavigationLink(destination: dest) {
|
NavigationLink(destination: dest) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@ -234,7 +259,7 @@ struct EventGroupView: View {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GroupDescription
|
GroupDescription(unique_pubkeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,6 @@ struct MaybeAnonPfpView: View {
|
|||||||
|
|
||||||
struct MaybeAnonPfpView_Previews: PreviewProvider {
|
struct MaybeAnonPfpView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: "anon", size: PFP_SIZE)
|
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: ANON_PUBKEY, size: PFP_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,6 @@ struct ZapUserView: View {
|
|||||||
|
|
||||||
struct ZapUserView_Previews: PreviewProvider {
|
struct ZapUserView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ZapUserView(state: test_damus_state(), pubkey: "anon")
|
ZapUserView(state: test_damus_state(), pubkey: ANON_PUBKEY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,27 @@ final class EventGroupViewTests: XCTestCase {
|
|||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testEventAuthorName() {
|
||||||
|
let damusState = test_damus_state()
|
||||||
|
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk1"), "pk1:pk1")
|
||||||
|
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk2"), "pk2:pk2")
|
||||||
|
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "anon"), "Anonymous")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventGroupUniquePubkeys() {
|
||||||
|
let damusState = test_damus_state()
|
||||||
|
|
||||||
|
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
||||||
|
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
|
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
|
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
|
|
||||||
|
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: []))), [])
|
||||||
|
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1]))), ["pk1"])
|
||||||
|
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2]))), ["pk1", "pk2"])
|
||||||
|
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2, repost3]))), ["pk1", "pk2", "pk3"])
|
||||||
|
}
|
||||||
|
|
||||||
func testReactingToText() throws {
|
func testReactingToText() throws {
|
||||||
let enUsLocale = Locale(identifier: "en-US")
|
let enUsLocale = Locale(identifier: "en-US")
|
||||||
let damusState = test_damus_state()
|
let damusState = test_damus_state()
|
||||||
@ -25,18 +46,19 @@ final class EventGroupViewTests: XCTestCase {
|
|||||||
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
||||||
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
|
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
|
|
||||||
let nozaps = true
|
let nozaps = true
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "??")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, pubkeys: [], locale: enUsLocale), "??")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1"], locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1", "pk2"], locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1", "pk2", "pk3"], locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
|
||||||
|
|
||||||
Bundle.main.localizations.map { Locale(identifier: $0) }.forEach {
|
Bundle.main.localizations.map { Locale(identifier: $0) }.forEach {
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: $0), "??")
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, pubkeys: [], locale: $0), "??")
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1"], locale: $0))
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1", "pk2"], locale: $0))
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost3])), ev: test_event, nozaps: nozaps, pubkeys: ["pk1", "pk2", "pk3"], locale: $0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user