Add indication of followers you know in a profile
Changelog-Added: Add indication of followers you know in a profile
This commit is contained in:
@@ -11,6 +11,8 @@ import Foundation
|
||||
class Contacts {
|
||||
private var friends: Set<String> = Set()
|
||||
private var friend_of_friends: Set<String> = Set()
|
||||
/// Tracks which friends are friends of a given pubkey.
|
||||
private var pubkey_to_our_friends = [String : Set<String>]()
|
||||
private var muted: Set<String> = Set()
|
||||
|
||||
let our_pubkey: String
|
||||
@@ -58,6 +60,10 @@ class Contacts {
|
||||
|
||||
func remove_friend(_ pubkey: String) {
|
||||
friends.remove(pubkey)
|
||||
|
||||
pubkey_to_our_friends.forEach {
|
||||
pubkey_to_our_friends[$0.key]?.remove(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
func get_friend_list() -> [String] {
|
||||
@@ -73,6 +79,15 @@ class Contacts {
|
||||
for tag in contact.tags {
|
||||
if tag.count >= 2 && tag[0] == "p" {
|
||||
friend_of_friends.insert(tag[1])
|
||||
|
||||
// Exclude themself and us.
|
||||
if contact.pubkey != our_pubkey && contact.pubkey != tag[1] {
|
||||
if pubkey_to_our_friends[tag[1]] == nil {
|
||||
pubkey_to_our_friends[tag[1]] = Set<String>()
|
||||
}
|
||||
|
||||
pubkey_to_our_friends[tag[1]]?.insert(contact.pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +111,11 @@ class Contacts {
|
||||
func follow_state(_ pubkey: String) -> FollowState {
|
||||
return is_friend(pubkey) ? .follows : .unfollows
|
||||
}
|
||||
|
||||
/// Gets the list of pubkeys of our friends who follow the given pubkey.
|
||||
func get_friended_followers(_ pubkey: String) -> [String] {
|
||||
return Array((pubkey_to_our_friends[pubkey] ?? Set()))
|
||||
}
|
||||
}
|
||||
|
||||
func follow_user(pool: RelayPool, our_contacts: NostrEvent?, pubkey: String, privkey: String, follow: ReferencedId) -> NostrEvent? {
|
||||
|
||||
@@ -386,7 +386,7 @@ class HomeModel {
|
||||
|
||||
var contacts_filter = NostrFilter(kinds: [.metadata])
|
||||
contacts_filter.authors = friends
|
||||
|
||||
|
||||
var our_contacts_filter = NostrFilter(kinds: [.contacts, .metadata])
|
||||
our_contacts_filter.authors = [damus_state.pubkey]
|
||||
|
||||
|
||||
@@ -29,9 +29,27 @@ struct FollowUserView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct FollowersYouKnowView: View {
|
||||
let damus_state: DamusState
|
||||
let friended_followers: [String]
|
||||
|
||||
@EnvironmentObject var followers: FollowersModel
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading) {
|
||||
ForEach(friended_followers, id: \.self) { pk in
|
||||
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.navigationBarTitle(NSLocalizedString("Followers You Know", comment: "Navigation bar title for view that shows who is following a user."))
|
||||
}
|
||||
}
|
||||
|
||||
struct FollowersView: View {
|
||||
let damus_state: DamusState
|
||||
let whos: String
|
||||
|
||||
@EnvironmentObject var followers: FollowersModel
|
||||
|
||||
@@ -58,7 +76,6 @@ struct FollowingView: View {
|
||||
let damus_state: DamusState
|
||||
|
||||
let following: FollowingModel
|
||||
let whos: String
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
|
||||
38
damus/Views/Profile/CondensedProfilePicturesView.swift
Normal file
38
damus/Views/Profile/CondensedProfilePicturesView.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// CondensedProfilePicturesView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Terry Yiu on 6/19/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CondensedProfilePicturesView: View {
|
||||
let state: DamusState
|
||||
let pubkeys: [String]
|
||||
let maxPictures: Int
|
||||
|
||||
init(state: DamusState, pubkeys: [String], maxPictures: Int) {
|
||||
self.state = state
|
||||
self.pubkeys = pubkeys
|
||||
self.maxPictures = min(maxPictures, pubkeys.count)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
// Using ZStack to make profile pictures floating and stacked on top of each other.
|
||||
ZStack {
|
||||
ForEach((0..<maxPictures).reversed(), id: \.self) { index in
|
||||
ProfilePicView(pubkey: pubkeys[index], size: 32.0, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
|
||||
.offset(x: CGFloat(index) * 20)
|
||||
}
|
||||
}
|
||||
// Padding is needed so that other components drawn adjacent to this view don't get drawn on top.
|
||||
.padding(.trailing, CGFloat((maxPictures - 1) * 20))
|
||||
}
|
||||
}
|
||||
|
||||
struct CondensedProfilePicturesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CondensedProfilePicturesView(state: test_damus_state(), pubkeys: ["a", "b", "c", "d"], maxPictures: 3)
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,31 @@ func relaysCountString(_ count: Int, locale: Locale = Locale.current) -> String
|
||||
return String(format: format, locale: locale, count)
|
||||
}
|
||||
|
||||
func followedByString(_ friend_intersection: [String], profiles: Profiles, locale: Locale = Locale.current) -> String {
|
||||
let bundle = bundleForLocale(locale: locale)
|
||||
let names: [String] = friend_intersection.prefix(3).map {
|
||||
let profile = profiles.lookup(id: $0)
|
||||
return Profile.displayName(profile: profile, pubkey: $0).username.truncate(maxLength: 20)
|
||||
}
|
||||
|
||||
switch friend_intersection.count {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
let format = NSLocalizedString("Followed by %@", bundle: bundle, comment: "Text to indicate that the user is followed by one of our follows.")
|
||||
return String(format: format, locale: locale, names[0])
|
||||
case 2:
|
||||
let format = NSLocalizedString("Followed by %@ & %@", bundle: bundle, comment: "Text to indicate that the user is followed by two of our follows.")
|
||||
return String(format: format, locale: locale, names[0], names[1])
|
||||
case 3:
|
||||
let format = NSLocalizedString("Followed by %@, %@ & %@", bundle: bundle, comment: "Text to indicate that the user is followed by three of our follows.")
|
||||
return String(format: format, locale: locale, names[0], names[1], names[2])
|
||||
default:
|
||||
let format = localizedStringFormat(key: "followed_by_three_and_others", locale: locale)
|
||||
return String(format: format, locale: locale, friend_intersection.count - 3, names[0], names[1], names[2])
|
||||
}
|
||||
}
|
||||
|
||||
struct EditButton: View {
|
||||
let damus_state: DamusState
|
||||
|
||||
@@ -379,7 +404,7 @@ struct ProfileView: View {
|
||||
if let contact = profile.contacts {
|
||||
let contacts = contact.referenced_pubkeys.map { $0.ref_id }
|
||||
let following_model = FollowingModel(damus_state: damus_state, contacts: contacts)
|
||||
NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model, whos: profile.pubkey)) {
|
||||
NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model)) {
|
||||
HStack {
|
||||
let noun_text = Text(verbatim: "\(followingCountString(profile.following))").font(.subheadline).foregroundColor(.gray)
|
||||
Text("\(Text(verbatim: profile.following.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.")
|
||||
@@ -387,7 +412,7 @@ struct ProfileView: View {
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
let fview = FollowersView(damus_state: damus_state, whos: profile.pubkey)
|
||||
let fview = FollowersView(damus_state: damus_state)
|
||||
.environmentObject(followers)
|
||||
if followers.contacts != nil {
|
||||
NavigationLink(destination: fview) {
|
||||
@@ -420,6 +445,22 @@ struct ProfileView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if profile.pubkey != damus_state.pubkey {
|
||||
let friended_followers = damus_state.contacts.get_friended_followers(profile.pubkey)
|
||||
if !friended_followers.isEmpty {
|
||||
Spacer()
|
||||
|
||||
NavigationLink(destination: FollowersYouKnowView(damus_state: damus_state, friended_followers: friended_followers)) {
|
||||
HStack {
|
||||
CondensedProfilePicturesView(state: damus_state, pubkeys: friended_followers, maxPictures: 3)
|
||||
Text(followedByString(friended_followers, profiles: damus_state.profiles))
|
||||
.font(.subheadline).foregroundColor(.gray)
|
||||
.multilineTextAlignment(.leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,22 @@
|
||||
<string>... %d other notes ...</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>followed_by_three_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Followed by %2$@, %3$@, %4$@ & %1$d other</string>
|
||||
<key>other</key>
|
||||
<string>Followed by %2$@, %3$@, %4$@ & %1$d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>followers_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
|
||||
Reference in New Issue
Block a user