From 2f7653d05cfda38c9ad13795635212fbbebc8653 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 23 Dec 2022 16:21:31 +0100 Subject: [PATCH] Refactor follow to shared FollowButton --- .../App/Tabs/Settings/SettingsTab.swift | 3 + .../Account/AccountDetailHeaderView.swift | 20 ++---- .../Sources/Account/AccountDetailView.swift | 19 +---- .../Sources/Account/Follow/FollowButton.swift | 72 +++++++++++++++++++ Packages/Explore/Package.swift | 2 + .../Sources/Explore/SuggestedAccountRow.swift | 35 +-------- 6 files changed, 89 insertions(+), 62 deletions(-) create mode 100644 Packages/Account/Sources/Account/Follow/FollowButton.swift diff --git a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift index 1eebef09..cbba3ec2 100644 --- a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift +++ b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift @@ -86,6 +86,9 @@ struct SettingsTabs: View { .cornerRadius(4) } } + Link(destination: URL(string: "https://github.com/Dimillian/IceCubesApp")!) { + Text("https://github.com/Dimillian/IceCubesApp") + } } } diff --git a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift index 1d0cdaa1..6e41ca55 100644 --- a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift +++ b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift @@ -10,8 +10,8 @@ struct AccountDetailHeaderView: View { let isCurrentUser: Bool let account: Account - @Binding var relationship: Relationshionship? - @Binding var following: Bool + let relationship: Relationshionship? + @Binding var scrollOffset: CGFloat private var bannerHeight: CGFloat { @@ -97,16 +97,9 @@ struct AccountDetailHeaderView: View { .foregroundColor(.gray) } Spacer() - if relationship != nil && !isCurrentUser { - Button { - following.toggle() - } label: { - if relationship?.requested == true { - Text("Requested") - } else { - Text(following ? "Following" : "Follow") - } - }.buttonStyle(.bordered) + if let relationship = relationship, !isCurrentUser { + FollowButton(viewModel: .init(accountId: account.id, + relationship: relationship)) } } Text(account.note.asSafeAttributedString) @@ -135,8 +128,7 @@ struct AccountDetailHeaderView_Previews: PreviewProvider { static var previews: some View { AccountDetailHeaderView(isCurrentUser: false, account: .placeholder(), - relationship: .constant(.placeholder()), - following: .constant(true), + relationship: .placeholder(), scrollOffset: .constant(0)) } } diff --git a/Packages/Account/Sources/Account/AccountDetailView.swift b/Packages/Account/Sources/Account/AccountDetailView.swift index 649aef2b..5cae44bf 100644 --- a/Packages/Account/Sources/Account/AccountDetailView.swift +++ b/Packages/Account/Sources/Account/AccountDetailView.swift @@ -86,26 +86,13 @@ public struct AccountDetailView: View { case .loading: AccountDetailHeaderView(isCurrentUser: isCurrentUser, account: .placeholder(), - relationship: .constant(.placeholder()), - following: .constant(false), + relationship: .placeholder(), scrollOffset: $scrollOffset) .redacted(reason: .placeholder) case let .data(account): AccountDetailHeaderView(isCurrentUser: isCurrentUser, account: account, - relationship: $viewModel.relationship, - following: - .init(get: { - viewModel.relationship?.following ?? false - }, set: { following in - Task { - if following { - await viewModel.follow() - } else { - await viewModel.unfollow() - } - } - }), + relationship: viewModel.relationship, scrollOffset: $scrollOffset) case let .error(error): Text("Error: \(error.localizedDescription)") @@ -156,7 +143,7 @@ public struct AccountDetailView: View { @ViewBuilder private var familliarFollowers: some View { if !viewModel.familliarFollowers.isEmpty { - VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 2) { Text("Also followed by") .font(.headline) .padding(.leading, DS.Constants.layoutPadding) diff --git a/Packages/Account/Sources/Account/Follow/FollowButton.swift b/Packages/Account/Sources/Account/Follow/FollowButton.swift new file mode 100644 index 00000000..317c04f7 --- /dev/null +++ b/Packages/Account/Sources/Account/Follow/FollowButton.swift @@ -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 + } + } +} diff --git a/Packages/Explore/Package.swift b/Packages/Explore/Package.swift index e411ce40..306d72e9 100644 --- a/Packages/Explore/Package.swift +++ b/Packages/Explore/Package.swift @@ -14,6 +14,7 @@ let package = Package( targets: ["Explore"]), ], dependencies: [ + .package(name: "Account", path: "../Account"), .package(name: "Network", path: "../Network"), .package(name: "Models", path: "../Models"), .package(name: "Env", path: "../Env"), @@ -24,6 +25,7 @@ let package = Package( .target( name: "Explore", dependencies: [ + .product(name: "Account", package: "Account"), .product(name: "Network", package: "Network"), .product(name: "Models", package: "Models"), .product(name: "Env", package: "Env"), diff --git a/Packages/Explore/Sources/Explore/SuggestedAccountRow.swift b/Packages/Explore/Sources/Explore/SuggestedAccountRow.swift index 78aa139d..152d081b 100644 --- a/Packages/Explore/Sources/Explore/SuggestedAccountRow.swift +++ b/Packages/Explore/Sources/Explore/SuggestedAccountRow.swift @@ -3,6 +3,7 @@ import Models import Network import DesignSystem import Env +import Account @MainActor class SuggestedAccountViewModel: ObservableObject { @@ -15,20 +16,6 @@ class SuggestedAccountViewModel: ObservableObject { self.account = account 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 { @@ -54,24 +41,8 @@ struct SuggestedAccountRow: View { }) } Spacer() - Button { - Task { - 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) + FollowButton(viewModel: .init(accountId: viewModel.account.id, + relationship: viewModel.relationShip)) } .onAppear { viewModel.client = client