damus

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

commit 04bce34297c09d4f9cfd74c7a1c6c12fc6c9dc2a
parent af8ce3d32d33bd63ad11c620c880681ad8455834
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 14 Mar 2023 16:26:52 -0600

Don't show both realname and username if they are the same

Changelog-Changed: Don't show both realname and username if they are the same

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 8++++++++
Mdamus/ContentView.swift | 4++--
Mdamus/Nostr/Nostr.swift | 13++-----------
Adamus/Util/DisplayName.swift | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/Events/ReplyDescription.swift | 2+-
Mdamus/Views/NoteContentView.swift | 2+-
Mdamus/Views/Notifications/EventGroupView.swift | 2+-
Adamus/Views/Profile/EventProfileName.swift | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Views/Profile/ProfileNameView.swift | 36++++++++++++++++++------------------
Mdamus/Views/ProfileName.swift | 91+++++++++----------------------------------------------------------------------
Mdamus/Views/ReplyView.swift | 2+-
Mdamus/Views/Zaps/CustomizeZapView.swift | 2+-
12 files changed, 207 insertions(+), 117 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -135,6 +135,8 @@ 4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; }; 4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; }; 4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */; }; + 4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; }; + 4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; }; 4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */; }; 4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */; }; 4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; }; @@ -489,6 +491,8 @@ 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; }; 4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; }; 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatroomMetadata.swift; sourceTree = "<group>"; }; + 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; }; + 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; }; 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapView.swift; sourceTree = "<group>"; }; 4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaybeAnonPfpView.swift; sourceTree = "<group>"; }; 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; }; @@ -896,6 +900,7 @@ 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */, 3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */, 4C30AC7729A577AB00E2BD5A /* EventCache.swift */, + 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */, ); path = Util; sourceTree = "<group>"; @@ -939,6 +944,7 @@ 4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */, 4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */, 4C9F18E329ABDE6D008C55EC /* MaybeAnonPfpView.swift */, + 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */, ); path = Profile; sourceTree = "<group>"; @@ -1395,6 +1401,7 @@ 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */, 4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */, F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */, + 4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */, 4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */, 4C649844285A952100EAE2B3 /* LocalUserConfig.swift in Sources */, 4C75EFB328049D640006080F /* NostrEvent.swift in Sources */, @@ -1514,6 +1521,7 @@ 4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */, 4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */, 4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */, + 4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */, 7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */, 4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */, 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -487,7 +487,7 @@ struct ContentView: View { }, message: { if let pubkey = self.blocking { let profile = damus_state!.profiles.lookup(id: pubkey) - let name = Profile.displayName(profile: profile, pubkey: pubkey) + let name = Profile.displayName(profile: profile, pubkey: pubkey).username Text("\(name) has been blocked", comment: "Alert message that informs a user was blocked.") } else { Text("User has been blocked", comment: "Alert message that informs a user was blocked.") @@ -555,7 +555,7 @@ struct ContentView: View { }, message: { if let pubkey = blocking { let profile = damus_state?.profiles.lookup(id: pubkey) - let name = Profile.displayName(profile: profile, pubkey: pubkey) + let name = Profile.displayName(profile: profile, pubkey: pubkey).username Text("Block \(name)?", comment: "Alert message prompt to ask if a user should be blocked.") } else { Text("Could not find user to block...", comment: "Alert message to indicate that the blocked user could not be found.") diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift @@ -140,17 +140,8 @@ struct Profile: Codable { try container.encode(value) } - static func displayName(profile: Profile?, pubkey: String) -> String { - if pubkey == "anon" { - return "Anonymous" - } - - if let name = profile?.name, !name.isEmpty { - return name - } else { - let pk = bech32_nopre_pubkey(pubkey) ?? pubkey - return abbrev_pubkey(pk) - } + static func displayName(profile: Profile?, pubkey: String) -> DisplayName { + return parse_display_name(profile: profile, pubkey: pubkey) } } diff --git a/damus/Util/DisplayName.swift b/damus/Util/DisplayName.swift @@ -0,0 +1,66 @@ +// +// DisplayName.swift +// damus +// +// Created by William Casarin on 2023-03-14. +// + +import Foundation + + +struct BothNames { + let username: String + let display_name: String +} + +enum DisplayName { + case both(BothNames) + case one(String) + + var display_name: String { + switch self { + case .one(let one): + return one + case .both(let b): + return b.display_name + } + } + + var username: String { + switch self { + case .one(let one): + return one + case .both(let b): + return b.username + } + } +} + + +func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName { + if pubkey == "anon" { + return .one("Anonymous") + } + + guard let profile else { + return .one(abbrev_bech32_pubkey(pubkey: pubkey)) + } + + let name = profile.name?.isEmpty == false ? profile.name : nil + let disp_name = profile.display_name?.isEmpty == false ? profile.display_name : nil + + if let name, let disp_name, name != disp_name { + return .both(BothNames(username: name, display_name: disp_name)) + } + + if let one = name ?? disp_name { + return .one(one) + } + + return .one(abbrev_bech32_pubkey(pubkey: pubkey)) +} + +func abbrev_bech32_pubkey(pubkey: String) -> String { + let pk = bech32_nopre_pubkey(pubkey) ?? pubkey + return abbrev_pubkey(pk) +} diff --git a/damus/Views/Events/ReplyDescription.swift b/damus/Views/Events/ReplyDescription.swift @@ -39,7 +39,7 @@ func reply_desc(profiles: Profiles, event: NostrEvent, locale: Locale = Locale.c let names: [String] = pubkeys.map { let prof = profiles.lookup(id: $0) - return Profile.displayName(profile: prof, pubkey: $0) + return Profile.displayName(profile: prof, pubkey: $0).username } let uniqueNames = NSOrderedSet(array: names).array as! [String] diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -158,7 +158,7 @@ func mention_str(_ m: Mention, profiles: Profiles) -> AttributedString { case .pubkey: let pk = m.ref.ref_id let profile = profiles.lookup(id: pk) - let disp = Profile.displayName(profile: profile, pubkey: pk) + let disp = Profile.displayName(profile: profile, pubkey: pk).username var attributedString = AttributedString(stringLiteral: "@\(disp)") attributedString.link = URL(string: "nostr:\(encode_pubkey_uri(m.ref))") attributedString.foregroundColor = Color("DamusPurple") diff --git a/damus/Views/Notifications/EventGroupView.swift b/damus/Views/Notifications/EventGroupView.swift @@ -61,7 +61,7 @@ func determine_reacting_to(our_pubkey: String, ev: NostrEvent?) -> ReactingTo { func event_author_name(profiles: Profiles, pubkey: String) -> String { let alice_prof = profiles.lookup(id: pubkey) - return Profile.displayName(profile: alice_prof, pubkey: pubkey) + return Profile.displayName(profile: alice_prof, pubkey: pubkey).username } func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType) -> String { diff --git a/damus/Views/Profile/EventProfileName.swift b/damus/Views/Profile/EventProfileName.swift @@ -0,0 +1,96 @@ +// +// EventProfileName.swift +// damus +// +// Created by William Casarin on 2023-03-14. +// + +import SwiftUI + +/// Profile Name used when displaying an event in the timeline +struct EventProfileName: View { + let damus_state: DamusState + let pubkey: String + let profile: Profile? + let prefix: String + + let show_friend_confirmed: Bool + + @State var display_name: DisplayName? + @State var nip05: NIP05? + + let size: EventViewKind + + init(pubkey: String, profile: Profile?, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) { + self.damus_state = damus + self.pubkey = pubkey + self.profile = profile + self.prefix = "" + self.show_friend_confirmed = show_friend_confirmed + self.size = size + } + + init(pubkey: String, profile: Profile?, prefix: String, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) { + self.damus_state = damus + self.pubkey = pubkey + self.profile = profile + self.prefix = prefix + self.show_friend_confirmed = show_friend_confirmed + self.size = size + } + + var friend_icon: String? { + return get_friend_icon(contacts: damus_state.contacts, pubkey: pubkey, show_confirmed: show_friend_confirmed) + } + + var current_nip05: NIP05? { + nip05 ?? damus_state.profiles.is_validated(pubkey) + } + + var current_display_name: DisplayName { + return display_name ?? Profile.displayName(profile: profile, pubkey: pubkey) + } + + var body: some View { + HStack(spacing: 2) { + switch current_display_name { + case .one(let one): + Text(one) + .font(.body.weight(.bold)) + + case .both(let both): + Text(both.display_name) + .font(.body.weight(.bold)) + + Text(verbatim: "@\(both.username)") + .foregroundColor(.gray) + .font(eventviewsize_to_font(size)) + } + + if let nip05 = current_nip05 { + NIP05Badge(nip05: nip05, pubkey: pubkey, contacts: damus_state.contacts, show_domain: false, clickable: false) + } + + if let frend = friend_icon, current_nip05 == nil { + Label("", systemImage: frend) + .foregroundColor(.gray) + .font(.footnote) + } + } + .onReceive(handle_notify(.profile_updated)) { notif in + let update = notif.object as! ProfileUpdate + if update.pubkey != pubkey { + return + } + display_name = Profile.displayName(profile: update.profile, pubkey: pubkey) + nip05 = damus_state.profiles.is_validated(pubkey) + } + } +} + + +struct EventProfileName_Previews: PreviewProvider { + static var previews: some View { + EventProfileName(pubkey: "pk", profile: nil, damus: test_damus_state(), show_friend_confirmed: true) + } +} diff --git a/damus/Views/Profile/ProfileNameView.swift b/damus/Views/Profile/ProfileNameView.swift @@ -17,35 +17,35 @@ struct ProfileNameView: View { var body: some View { Group { - if let real_name = profile?.display_name { - VStack(alignment: .leading, spacing: 0) { - Text(real_name) - .font(.title3.weight(.bold)) + VStack(alignment: .leading) { + switch Profile.displayName(profile: profile, pubkey: pubkey) { + case .one: HStack(alignment: .center, spacing: spacing) { - ProfileName(pubkey: pubkey, profile: profile, prefix: "@", damus: damus, show_friend_confirmed: true) - .font(.callout) - .foregroundColor(.gray) - + ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true) + .font(.title3.weight(.bold)) if follows_you { FollowsYou() } } - Spacer() - KeyView(pubkey: pubkey) - .pubkey_context_menu(bech32_pubkey: pubkey) - } - } else { - VStack(alignment: .leading) { + case .both(let both): + Text(both.display_name) + .font(.title3.weight(.bold)) + HStack(alignment: .center, spacing: spacing) { - ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true) - .font(.title3.weight(.bold)) + ProfileName(pubkey: pubkey, profile: profile, prefix: "@", damus: damus, show_friend_confirmed: true) + .font(.callout) + .foregroundColor(.gray) + if follows_you { FollowsYou() } } - KeyView(pubkey: pubkey) - .pubkey_context_menu(bech32_pubkey: pubkey) } + + Spacer() + + KeyView(pubkey: pubkey) + .pubkey_context_menu(bech32_pubkey: pubkey) } } } diff --git a/damus/Views/ProfileName.swift b/damus/Views/ProfileName.swift @@ -32,7 +32,7 @@ struct ProfileName: View { let show_friend_confirmed: Bool let show_nip5_domain: Bool - @State var display_name: String? + @State var display_name: DisplayName? @State var nip05: NIP05? init(pubkey: String, profile: Profile?, damus: DamusState, show_friend_confirmed: Bool, show_nip5_domain: Bool = true) { @@ -65,9 +65,17 @@ struct ProfileName: View { return get_nip05_color(pubkey: pubkey, contacts: damus_state.contacts) } + var current_display_name: DisplayName { + return display_name ?? Profile.displayName(profile: profile, pubkey: pubkey) + } + + var name_choice: String { + return prefix == "@" ? current_display_name.username : current_display_name.display_name + } + var body: some View { HStack(spacing: 2) { - Text(verbatim: "\(prefix)\(String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))") + Text(verbatim: "\(prefix)\(name_choice)") .font(.body) .fontWeight(prefix == "@" ? .none : .bold) if let nip05 = current_nip05 { @@ -89,85 +97,6 @@ struct ProfileName: View { } } -/// Profile Name used when displaying an event in the timeline -struct EventProfileName: View { - let damus_state: DamusState - let pubkey: String - let profile: Profile? - let prefix: String - - let show_friend_confirmed: Bool - - @State var display_name: String? - @State var nip05: NIP05? - - let size: EventViewKind - - init(pubkey: String, profile: Profile?, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) { - self.damus_state = damus - self.pubkey = pubkey - self.profile = profile - self.prefix = "" - self.show_friend_confirmed = show_friend_confirmed - self.size = size - } - - init(pubkey: String, profile: Profile?, prefix: String, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) { - self.damus_state = damus - self.pubkey = pubkey - self.profile = profile - self.prefix = prefix - self.show_friend_confirmed = show_friend_confirmed - self.size = size - } - - var friend_icon: String? { - return get_friend_icon(contacts: damus_state.contacts, pubkey: pubkey, show_confirmed: show_friend_confirmed) - } - - var current_nip05: NIP05? { - nip05 ?? damus_state.profiles.is_validated(pubkey) - } - - var body: some View { - HStack(spacing: 2) { - if let real_name = profile?.display_name { - Text(real_name) - .font(.body.weight(.bold)) - - + Text(verbatim: real_name.isEmpty ? "" : " ") - - + Text(verbatim: "@\(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))") - .foregroundColor(.gray) - .font(eventviewsize_to_font(size)) - - } else { - Text(verbatim: "\(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))") - .font(eventviewsize_to_font(size)) - .fontWeight(.bold) - } - - if let nip05 = current_nip05 { - NIP05Badge(nip05: nip05, pubkey: pubkey, contacts: damus_state.contacts, show_domain: false, clickable: false) - } - - if let frend = friend_icon, current_nip05 == nil { - Label("", systemImage: frend) - .foregroundColor(.gray) - .font(.footnote) - } - } - .onReceive(handle_notify(.profile_updated)) { notif in - let update = notif.object as! ProfileUpdate - if update.pubkey != pubkey { - return - } - display_name = Profile.displayName(profile: update.profile, pubkey: pubkey) - nip05 = damus_state.profiles.is_validated(pubkey) - } - } -} - struct ProfileName_Previews: PreviewProvider { static var previews: some View { ProfileName(pubkey: diff --git a/damus/Views/ReplyView.swift b/damus/Views/ReplyView.swift @@ -31,7 +31,7 @@ struct ReplyView: View { .map { pubkey in let pk = pubkey.ref_id let prof = damus.profiles.lookup(id: pk) - return Profile.displayName(profile: prof, pubkey: pk) + return Profile.displayName(profile: prof, pubkey: pk).username } .joined(separator: ", ") Text(names) diff --git a/damus/Views/Zaps/CustomizeZapView.swift b/damus/Views/Zaps/CustomizeZapView.swift @@ -90,7 +90,7 @@ struct CustomizeZapView: View { case .priv: let pk = event.pubkey let prof = state.profiles.lookup(id: pk) - let name = Profile.displayName(profile: prof, pubkey: pk) + let name = Profile.displayName(profile: prof, pubkey: pk).username return String.localizedStringWithFormat(NSLocalizedString("private_zap_description", value: "Only '%@' can see that you zapped them", comment: "Description of private zap type where the zap is sent privately and does not identify the user to the public."), name) case .non_zap: return NSLocalizedString("No zaps are sent, only a lightning payment.", comment: "Description of non-zap type where sats are sent to the user's wallet as a regular Lightning payment, not as a zap.")