Account detail: Add also followed by section

This commit is contained in:
Thomas Ricouard 2022-12-22 12:26:11 +01:00
parent 084dd18362
commit fc77dd14fe
5 changed files with 94 additions and 25 deletions

View file

@ -34,8 +34,10 @@ public struct AccountDetailView: View {
ScrollViewOffsetReader { offset in
self.scrollOffset = offset
} content: {
LazyVStack {
LazyVStack(alignment: .leading) {
headerView
familliarFollowers
.offset(y: -36)
featuredTagsView
.offset(y: -36)
if isCurrentUser {
@ -151,6 +153,31 @@ public struct AccountDetailView: View {
}
}
@ViewBuilder
private var familliarFollowers: some View {
if !viewModel.familliarFollowers.isEmpty {
VStack(alignment: .leading, spacing: 0) {
Text("Also followed by")
.font(.headline)
.padding(.leading, DS.Constants.layoutPadding)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 0) {
ForEach(viewModel.familliarFollowers) { account in
AvatarView(url: account.avatar, size: .badge)
.onTapGesture {
routeurPath.navigate(to: .accountDetailWithAccount(account: account))
}
.padding(.leading, -4)
}
}
.padding(.leading, DS.Constants.layoutPadding + 4)
}
}
.padding(.top, 2)
.padding(.bottom, 12)
}
}
private var fieldSheetView: some View {
NavigationStack {
List {

View file

@ -50,6 +50,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
@Published var followedTags: [Tag] = []
@Published var featuredTags: [FeaturedTag] = []
@Published var fields: [Account.Field] = []
@Published var familliarFollowers: [Account] = []
@Published var selectedTab = Tab.statuses {
didSet {
reloadTabState()
@ -77,18 +78,25 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
func fetchAccount() async {
guard let client else { return }
do {
let account: Account = try await client.get(endpoint: Accounts.accounts(id: accountId))
self.fields = account.fields
if isCurrentUser {
self.followedTags = try await client.get(endpoint: Accounts.followedTags)
} else {
let relationships: [Relationshionship] = try await client.get(endpoint: Accounts.relationships(id: accountId))
self.relationship = relationships.first
}
self.featuredTags = try await client.get(endpoint: Accounts.featuredTags(id: accountId))
async let account: Account = client.get(endpoint: Accounts.accounts(id: accountId))
async let followedTags: [Tag] = client.get(endpoint: Accounts.followedTags)
async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(id: accountId))
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
let loadedAccount = try await account
self.featuredTags = try await featuredTags
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
self.title = account.displayName
accountState = .data(account: account)
self.fields = loadedAccount.fields
self.title = loadedAccount.displayName
if isCurrentUser {
self.followedTags = try await followedTags
} else {
let relationships = try await relationships
self.relationship = relationships.first
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
}
self.account = loadedAccount
accountState = .data(account: loadedAccount)
} catch {
accountState = .error(error: error)
}

View file

@ -1,32 +1,56 @@
import SwiftUI
public struct AvatarView: View {
public enum Size {
case profile, badge
var size: CGSize {
switch self {
case .profile:
return .init(width: 40, height: 40)
case .badge:
return .init(width: 28, height: 28)
}
}
}
@Environment(\.redactionReasons) private var reasons
public let url: URL
public let size: Size
public init(url: URL) {
public init(url: URL, size: Size = .profile) {
self.url = url
self.size = size
}
public var body: some View {
if reasons == .placeholder {
RoundedRectangle(cornerRadius: 4)
RoundedRectangle(cornerRadius: size == .profile ? 4 : size.size.width / 2)
.fill(.gray)
.frame(maxWidth: 40, maxHeight: 40)
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
} else {
AsyncImage(
url: url,
content: { image in
AsyncImage(url: url) { phase in
switch phase {
case .empty:
if size == .badge {
Circle()
.fill(.gray)
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
} else {
ProgressView()
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
}
case let .success(image):
image.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(4)
.frame(maxWidth: 40, maxHeight: 40)
},
placeholder: {
ProgressView()
.frame(maxWidth: 40, maxHeight: 40)
.cornerRadius(size == .profile ? 4 : size.size.width / 2)
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
case .failure:
EmptyView()
@unknown default:
EmptyView()
}
)
}
}
}
}

View file

@ -49,3 +49,8 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
emojis: [])
}
}
public struct FamilliarAccounts: Codable {
public let id: String
public let accounts: [Account]
}

View file

@ -10,6 +10,7 @@ public enum Accounts: Endpoint {
case relationships(id: String)
case follow(id: String)
case unfollow(id: String)
case familiarFollowers(withAccount: String)
public func path() -> String {
switch self {
@ -31,6 +32,8 @@ public enum Accounts: Endpoint {
return "accounts/\(id)/follow"
case .unfollow(let id):
return "accounts/\(id)/unfollow"
case .familiarFollowers:
return "accounts/familiar_followers"
}
}
@ -47,6 +50,8 @@ public enum Accounts: Endpoint {
return params
case let .relationships(id):
return [.init(name: "id", value: id)]
case let .familiarFollowers(withAccount):
return [.init(name: "id[]", value: withAccount)]
default:
return nil
}