Refactor follow to shared FollowButton

This commit is contained in:
Thomas Ricouard 2022-12-23 16:21:31 +01:00
parent 7068ad90bb
commit 2f7653d05c
6 changed files with 89 additions and 62 deletions

View file

@ -86,6 +86,9 @@ struct SettingsTabs: View {
.cornerRadius(4) .cornerRadius(4)
} }
} }
Link(destination: URL(string: "https://github.com/Dimillian/IceCubesApp")!) {
Text("https://github.com/Dimillian/IceCubesApp")
}
} }
} }

View file

@ -10,8 +10,8 @@ struct AccountDetailHeaderView: View {
let isCurrentUser: Bool let isCurrentUser: Bool
let account: Account let account: Account
@Binding var relationship: Relationshionship? let relationship: Relationshionship?
@Binding var following: Bool
@Binding var scrollOffset: CGFloat @Binding var scrollOffset: CGFloat
private var bannerHeight: CGFloat { private var bannerHeight: CGFloat {
@ -97,16 +97,9 @@ struct AccountDetailHeaderView: View {
.foregroundColor(.gray) .foregroundColor(.gray)
} }
Spacer() Spacer()
if relationship != nil && !isCurrentUser { if let relationship = relationship, !isCurrentUser {
Button { FollowButton(viewModel: .init(accountId: account.id,
following.toggle() relationship: relationship))
} label: {
if relationship?.requested == true {
Text("Requested")
} else {
Text(following ? "Following" : "Follow")
}
}.buttonStyle(.bordered)
} }
} }
Text(account.note.asSafeAttributedString) Text(account.note.asSafeAttributedString)
@ -135,8 +128,7 @@ struct AccountDetailHeaderView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AccountDetailHeaderView(isCurrentUser: false, AccountDetailHeaderView(isCurrentUser: false,
account: .placeholder(), account: .placeholder(),
relationship: .constant(.placeholder()), relationship: .placeholder(),
following: .constant(true),
scrollOffset: .constant(0)) scrollOffset: .constant(0))
} }
} }

View file

@ -86,26 +86,13 @@ public struct AccountDetailView: View {
case .loading: case .loading:
AccountDetailHeaderView(isCurrentUser: isCurrentUser, AccountDetailHeaderView(isCurrentUser: isCurrentUser,
account: .placeholder(), account: .placeholder(),
relationship: .constant(.placeholder()), relationship: .placeholder(),
following: .constant(false),
scrollOffset: $scrollOffset) scrollOffset: $scrollOffset)
.redacted(reason: .placeholder) .redacted(reason: .placeholder)
case let .data(account): case let .data(account):
AccountDetailHeaderView(isCurrentUser: isCurrentUser, AccountDetailHeaderView(isCurrentUser: isCurrentUser,
account: account, account: account,
relationship: $viewModel.relationship, relationship: viewModel.relationship,
following:
.init(get: {
viewModel.relationship?.following ?? false
}, set: { following in
Task {
if following {
await viewModel.follow()
} else {
await viewModel.unfollow()
}
}
}),
scrollOffset: $scrollOffset) scrollOffset: $scrollOffset)
case let .error(error): case let .error(error):
Text("Error: \(error.localizedDescription)") Text("Error: \(error.localizedDescription)")
@ -156,7 +143,7 @@ public struct AccountDetailView: View {
@ViewBuilder @ViewBuilder
private var familliarFollowers: some View { private var familliarFollowers: some View {
if !viewModel.familliarFollowers.isEmpty { if !viewModel.familliarFollowers.isEmpty {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 2) {
Text("Also followed by") Text("Also followed by")
.font(.headline) .font(.headline)
.padding(.leading, DS.Constants.layoutPadding) .padding(.leading, DS.Constants.layoutPadding)

View file

@ -0,0 +1,72 @@
import Foundation
import SwiftUI
import Models
import Network
@MainActor
public class FollowButtonViewModel: ObservableObject {
var client: Client?
public let accountId: String
@Published private(set) public var relationship: Relationshionship
@Published private(set) public var isUpdating: Bool = false
public init(accountId: String, relationship: Relationshionship) {
self.accountId = accountId
self.relationship = relationship
}
func follow() async {
guard let client else { return }
isUpdating = true
do {
relationship = try await client.post(endpoint: Accounts.follow(id: accountId))
} catch {
print("Error while following: \(error.localizedDescription)")
}
isUpdating = false
}
func unfollow() async {
guard let client else { return }
isUpdating = true
do {
relationship = try await client.post(endpoint: Accounts.unfollow(id: accountId))
} catch {
print("Error while unfollowing: \(error.localizedDescription)")
}
isUpdating = false
}
}
public struct FollowButton: View {
@EnvironmentObject private var client: Client
@StateObject private var viewModel: FollowButtonViewModel
public init(viewModel: FollowButtonViewModel) {
_viewModel = StateObject(wrappedValue: viewModel)
}
public var body: some View {
Button {
Task {
if viewModel.relationship.following {
await viewModel.unfollow()
} else {
await viewModel.follow()
}
}
} label: {
if viewModel.relationship.requested == true {
Text("Requested")
} else {
Text(viewModel.relationship.following ? "Following" : "Follow")
}
}
.buttonStyle(.bordered)
.disabled(viewModel.isUpdating)
.onAppear {
viewModel.client = client
}
}
}

View file

@ -14,6 +14,7 @@ let package = Package(
targets: ["Explore"]), targets: ["Explore"]),
], ],
dependencies: [ dependencies: [
.package(name: "Account", path: "../Account"),
.package(name: "Network", path: "../Network"), .package(name: "Network", path: "../Network"),
.package(name: "Models", path: "../Models"), .package(name: "Models", path: "../Models"),
.package(name: "Env", path: "../Env"), .package(name: "Env", path: "../Env"),
@ -24,6 +25,7 @@ let package = Package(
.target( .target(
name: "Explore", name: "Explore",
dependencies: [ dependencies: [
.product(name: "Account", package: "Account"),
.product(name: "Network", package: "Network"), .product(name: "Network", package: "Network"),
.product(name: "Models", package: "Models"), .product(name: "Models", package: "Models"),
.product(name: "Env", package: "Env"), .product(name: "Env", package: "Env"),

View file

@ -3,6 +3,7 @@ import Models
import Network import Network
import DesignSystem import DesignSystem
import Env import Env
import Account
@MainActor @MainActor
class SuggestedAccountViewModel: ObservableObject { class SuggestedAccountViewModel: ObservableObject {
@ -15,20 +16,6 @@ class SuggestedAccountViewModel: ObservableObject {
self.account = account self.account = account
self.relationShip = relationShip self.relationShip = relationShip
} }
func follow() async {
guard let client else { return }
do {
self.relationShip = try await client.post(endpoint: Accounts.follow(id: account.id))
} catch {}
}
func unfollow() async {
guard let client else { return }
do {
self.relationShip = try await client.post(endpoint: Accounts.unfollow(id: account.id))
} catch {}
}
} }
struct SuggestedAccountRow: View { struct SuggestedAccountRow: View {
@ -54,24 +41,8 @@ struct SuggestedAccountRow: View {
}) })
} }
Spacer() Spacer()
Button { FollowButton(viewModel: .init(accountId: viewModel.account.id,
Task { relationship: viewModel.relationShip))
if viewModel.relationShip.following {
await viewModel.unfollow()
} else {
await viewModel.follow()
}
}
} label: {
if viewModel.relationShip.requested {
Text("Requested")
.font(.callout)
} else {
Text(viewModel.relationShip.following ? "Unfollow" : "Follow")
.font(.callout)
}
}
.buttonStyle(.bordered)
} }
.onAppear { .onAppear {
viewModel.client = client viewModel.client = client