From e67fdd73a366c0b9673130fb3f12fffa9cae8d1b Mon Sep 17 00:00:00 2001 From: 0gonge Date: Mon, 12 Aug 2024 16:28:45 +0900 Subject: [PATCH 01/10] =?UTF-8?q?refactor:=20followviewmodel=20default=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyProfile/ViewModel/FollowViewModel.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift index 6ebb364..a4d1a58 100644 --- a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift +++ b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift @@ -42,7 +42,7 @@ class FollowViewModel { guard let self = self else { return } switch result { case .success(let response): - let followerList = response.content.map { + var followerList = response.content.map { Follower( id: $0.userInfo.id, username: $0.userInfo.nickname, @@ -50,7 +50,9 @@ class FollowViewModel { profileImage: $0.userInfo.profileImageUrl ) } + followerList = self.filterSpecialAccount(in: followerList) self.followers.value = followerList + self.isEmpty.value = followerList.isEmpty case .failure(let failure): print(failure) } @@ -64,7 +66,7 @@ class FollowViewModel { guard let self = self else { return } switch result { case .success(let response): - let followList = response.content.map { + var followList = response.content.map { Follower( id: $0.id, username: $0.nickname, @@ -72,12 +74,21 @@ class FollowViewModel { profileImage: $0.profileImageUrl ) } - self.followers.value = followList.reversed() + followList = self.filterSpecialAccount(in: followList) + self.followers.value = followList + self.isEmpty.value = followList.isEmpty case .failure(let failure): print(failure) } } } + + func filterSpecialAccount(in list: [Follower]) -> [Follower] { + var updatedList = list.filter {$0.username != "유영"} + let specialAccount = Follower(id: 0, username: "유영", isFollowing: true, profileImage: "") + updatedList.append(specialAccount) + return updatedList + } func toggleFollow(at index: Int) { guard index < followers.value.count else { return } From 6fc33ede59826a6277f8c04fbbb9d0bd663eeefd Mon Sep 17 00:00:00 2001 From: 0gonge Date: Tue, 20 Aug 2024 02:51:36 +0900 Subject: [PATCH 02/10] =?UTF-8?q?refactor:=20mediumbutton=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Sources/Components/MediumButton.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Common/Sources/Components/MediumButton.swift b/Common/Sources/Components/MediumButton.swift index 51eb6af..953bbe8 100644 --- a/Common/Sources/Components/MediumButton.swift +++ b/Common/Sources/Components/MediumButton.swift @@ -35,9 +35,11 @@ public class MediumButton: UIButton { case .active: backgroundColor = CommonAsset.recordyWhite.color setTitleColor(CommonAsset.recordyGrey09.color, for: .normal) + setTitle("팔로우", for: .normal) case .inactive: backgroundColor = CommonAsset.recordyGrey08.color setTitleColor(CommonAsset.recordyGrey01.color, for: .normal) + setTitle("팔로잉", for: .normal) } } } From 34053e1b687b10064126e7880f97d670f6d9de3b Mon Sep 17 00:00:00 2001 From: 0gonge Date: Wed, 4 Sep 2024 03:22:04 +0900 Subject: [PATCH 03/10] =?UTF-8?q?refactor:=20follower=20DTO=20cursorId=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Sources/Components/MediumButton.swift | 10 +- Core/Sources/Model/FollowerModel.swift | 18 --- .../Sources/Network/API/APITarget+Users.swift | 4 +- .../Users/DTO+GetFollowListRequest.swift | 6 +- .../Users/DTO+GetFollowerListRequest.swift | 4 +- .../Users/DTO+GetFollowListResponse.swift | 4 +- .../Controller/FollowViewController.swift | 139 +++++------------- .../Controller/MyRecordViewController.swift | 3 + .../Sources/MyProfile/View/FollowerCell.swift | 84 +++++++++++ .../MyProfile/ViewModel/FollowViewModel.swift | 118 ++++++++------- .../ViewModel/FollowingViewModel.swift | 30 ---- 11 files changed, 207 insertions(+), 213 deletions(-) delete mode 100644 Core/Sources/Model/FollowerModel.swift create mode 100644 Presentation/Sources/MyProfile/View/FollowerCell.swift delete mode 100644 Presentation/Sources/MyProfile/ViewModel/FollowingViewModel.swift diff --git a/Common/Sources/Components/MediumButton.swift b/Common/Sources/Components/MediumButton.swift index 953bbe8..3d5ebcd 100644 --- a/Common/Sources/Components/MediumButton.swift +++ b/Common/Sources/Components/MediumButton.swift @@ -9,7 +9,7 @@ public enum MediumState { public class MediumButton: UIButton { - public var mediumState: MediumState = .inactive { + public var mediumState: MediumState = .active { didSet { mediumButtonAppearance() } @@ -33,13 +33,13 @@ public class MediumButton: UIButton { private func mediumButtonAppearance() { switch mediumState { case .active: - backgroundColor = CommonAsset.recordyWhite.color - setTitleColor(CommonAsset.recordyGrey09.color, for: .normal) - setTitle("팔로우", for: .normal) - case .inactive: backgroundColor = CommonAsset.recordyGrey08.color setTitleColor(CommonAsset.recordyGrey01.color, for: .normal) setTitle("팔로잉", for: .normal) + case .inactive: + backgroundColor = CommonAsset.recordyWhite.color + setTitleColor(CommonAsset.recordyGrey09.color, for: .normal) + setTitle("팔로우", for: .normal) } } } diff --git a/Core/Sources/Model/FollowerModel.swift b/Core/Sources/Model/FollowerModel.swift deleted file mode 100644 index 53dc8f9..0000000 --- a/Core/Sources/Model/FollowerModel.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// FollowerModel.swift -// Presentation -// -// Created by 송여경 on 7/15/24. -// Copyright © 2024 com.recordy. All rights reserved. -// -import UIKit - -public struct Following { - let curser: Int - let hasNext: Bool - let username: String - let profileImage: UIImage - public var isFollowing: Bool - let isFollowButtonVisible: Bool -} - diff --git a/Core/Sources/Network/API/APITarget+Users.swift b/Core/Sources/Network/API/APITarget+Users.swift index f2d139e..166da06 100644 --- a/Core/Sources/Network/API/APITarget+Users.swift +++ b/Core/Sources/Network/API/APITarget+Users.swift @@ -98,7 +98,7 @@ extension APITarget.Users: TargetType { case .getfollowList(let getFollowListRequest): return .requestParameters( parameters: [ - "cursorId": getFollowListRequest.cursorId, +// "cursorId": getFollowListRequest.cursorId, "size": getFollowListRequest.size ], encoding: URLEncoding.queryString @@ -106,7 +106,7 @@ extension APITarget.Users: TargetType { case .getfollowerList(let getFollowerListRequest): return .requestParameters( parameters: [ - "cursorId": getFollowerListRequest.cursorId, +// "cursorId": getFollowerListRequest.cursorId, "size": getFollowerListRequest.size ], encoding: URLEncoding.queryString diff --git a/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowListRequest.swift b/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowListRequest.swift index 1501c90..2b23d6a 100644 --- a/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowListRequest.swift +++ b/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowListRequest.swift @@ -11,15 +11,15 @@ import Foundation extension DTO { public struct GetFollowListRequest: BaseRequest { /// 페이지네이션 커서 ID? - let cursorId: Int +// let cursorId: Int? /// 가져올 데이터 갯수 let size: Int public init( - cursorId: Int, +// cursorId: Int?, size: Int ) { - self.cursorId = cursorId +// self.cursorId = cursorId self.size = size } } diff --git a/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowerListRequest.swift b/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowerListRequest.swift index 167dcd2..44150eb 100644 --- a/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowerListRequest.swift +++ b/Core/Sources/Network/DTO/Request/Users/DTO+GetFollowerListRequest.swift @@ -11,12 +11,12 @@ import Foundation extension DTO { public struct GetFollowerListRequest: BaseRequest { /// 페이지네이션 커서 ID? - let cursorId: Int + let cursorId: Int? /// 가져올 데이터 갯수 let size: Int public init( - cursorId: Int, + cursorId: Int?, size: Int ) { self.cursorId = cursorId diff --git a/Core/Sources/Network/DTO/Response/Users/DTO+GetFollowListResponse.swift b/Core/Sources/Network/DTO/Response/Users/DTO+GetFollowListResponse.swift index 695c928..36a0fdc 100644 --- a/Core/Sources/Network/DTO/Response/Users/DTO+GetFollowListResponse.swift +++ b/Core/Sources/Network/DTO/Response/Users/DTO+GetFollowListResponse.swift @@ -11,14 +11,14 @@ import Foundation extension DTO { public struct GetFollowListResponse: BaseResponse { /// 다음 커서 - public let nextCursor: Int + public let nextCursor: Int? /// 다음 페이지 여부 public let hasNext: Bool /// 다음 커서 public let content: [Content] public init( - nextCursor: Int, + nextCursor: Int?, hasNext: Bool, content: [Content] ) { diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index cd7ee1f..a2e9f7c 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -9,15 +9,13 @@ enum FollowType { case follower case following } - -public class FollowViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { +public class FollowViewController: UIViewController { let followType: FollowType - private let viewModel: FollowViewModel - private let tableView = UITableView() - private let emptyView = FollowerEmptyView() - let settingButton = UIButton() - + let viewModel: FollowViewModel + let tableView = UITableView() + let emptyView = FollowerEmptyView() + init(followType: FollowType) { self.followType = followType self.viewModel = FollowViewModel(followType: followType) @@ -35,22 +33,23 @@ public class FollowViewController: UIViewController, UITableViewDataSource, UITa setAutoLayout() setTableView() bindViewModel() - viewModel.fetchUsers() + setTitle() } - private func setStyle() { + public func setStyle() { view.backgroundColor = .black } private func setUI() { - view.addSubview(emptyView) view.addSubview(tableView) + view.addSubview(emptyView) } private func setAutoLayout() { emptyView.snp.makeConstraints { $0.edges.equalTo(view.safeAreaLayoutGuide) } + tableView.snp.makeConstraints { $0.edges.equalTo(view.safeAreaLayoutGuide) } @@ -64,14 +63,31 @@ public class FollowViewController: UIViewController, UITableViewDataSource, UITa tableView.separatorStyle = .none } - public func bindViewModel() { + private func bindViewModel() { viewModel.followers.bind { [weak self] _ in guard let self = self else { return } self.tableView.reloadData() - self.tableView.isHidden = viewModel.followers.value.isEmpty + } + + viewModel.isEmpty.bind { [weak self] isEmpty in + guard let self = self else { return } + self.tableView.isHidden = isEmpty + self.emptyView.isHidden = !isEmpty } } + private func setTitle(){ + switch followType { + case .follower: + self.title = "팔로워" + case .following: + self.title = "팔로우" + } + } +} + +extension FollowViewController: UITableViewDataSource, UITableViewDelegate { + public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return viewModel.followers.value.count } @@ -81,12 +97,20 @@ public class FollowViewController: UIViewController, UITableViewDataSource, UITa withIdentifier: "FollowerCell", for: indexPath ) as! FollowerCell + let follower = viewModel.followers.value[indexPath.row] cell.configure(with: follower) + + if indexPath.row == 0 && followType == .following { + cell.followButton.isHidden = true + } else { + cell.followButton.isHidden = false + cell.updateFollowButton(isFollowed: follower.isFollowing) + } + cell.followButtonEvent = { [weak self] in guard let self = self else { return } - postFollowRequest(index: indexPath.row) - cell.updateFollowButton(isFollowed: follower.isFollowing) + self.viewModel.postFollowRequest(at: indexPath.row) } return cell } @@ -95,91 +119,4 @@ public class FollowViewController: UIViewController, UITableViewDataSource, UITa let userVC = OtherUserProfileViewController(id: viewModel.followers.value[indexPath.row].id) self.navigationController?.pushViewController(userVC, animated: true) } - - func postFollowRequest(index: Int) { - self.viewModel.followers.value[index].isFollowing.toggle() - let apiProvider = APIProvider() - let request = DTO.FollowRequest(followingId: viewModel.followers.value[index].id) - apiProvider.justRequest(.follow(request)) { result in - switch result { - case .success(let success): - print(success) - case .failure(let failure): - print(failure) - } - } - } -} - -public class FollowerCell: UITableViewCell { - let profileImageView = UIImageView() - let usernameLabel = UILabel() - let followButton = MediumButton() - - var followButtonEvent: (() -> Void)? - - public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - contentView.addSubview(profileImageView) - contentView.addSubview(usernameLabel) - contentView.addSubview(followButton) - - setStyle() - setAutoLayout() - followButton.addTarget(self, action: #selector(followButtonTapped), for: .touchUpInside) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setStyle() { - profileImageView.cornerRadius(10) - usernameLabel.font = RecordyFont.body2Bold.font - usernameLabel.textColor = CommonAsset.recordyGrey01.color - contentView.backgroundColor = .black - } - - private func setAutoLayout() { - profileImageView.snp.makeConstraints { - $0.top.equalTo(contentView.snp.top).offset(10) - $0.leading.equalTo(contentView.snp.leading).offset(20) - $0.centerY.equalTo(contentView.snp.centerY) - $0.width.equalTo(54) - $0.height.equalTo(54) - } - - usernameLabel.snp.makeConstraints { - $0.leading.equalTo(profileImageView.snp.trailing).offset(20) - $0.centerY.equalTo(contentView.snp.centerY) - } - - followButton.snp.makeConstraints { - $0.trailing.equalTo(contentView.snp.trailing).offset(-20) - $0.centerY.equalTo(contentView.snp.centerY) - $0.width.equalTo(76.adaptiveWidth) - $0.height.equalTo(36.adaptiveHeight) - } - } - - @objc private func followButtonTapped() { - self.followButtonEvent?() - } - - public func updateFollowButton(isFollowed: Bool) { - followButton.mediumState = isFollowed ? .active : .inactive - followButton.setTitle(isFollowed ? "팔로우" : "팔로잉", for: .normal) - } - - func configure(with follower: Follower) { - let url = URL(string: follower.profileImage) - profileImageView.kf.setImage(with: url) - usernameLabel.text = follower.username - if follower.username == "유영" { - followButton.isHidden = true - } else { - updateFollowButton(isFollowed: follower.isFollowing) - } - } } diff --git a/Presentation/Sources/MyProfile/Controller/MyRecordViewController.swift b/Presentation/Sources/MyProfile/Controller/MyRecordViewController.swift index a89ae84..37ed58d 100644 --- a/Presentation/Sources/MyProfile/Controller/MyRecordViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/MyRecordViewController.swift @@ -132,6 +132,9 @@ public class MyRecordViewController: UIViewController { $0.leading.trailing.bottom.equalToSuperview() } } + @objc private func didTapRecordButton() { + + } private func bindViewModel() { viewModel.isEmpty.bind { [weak self] isEmpty in diff --git a/Presentation/Sources/MyProfile/View/FollowerCell.swift b/Presentation/Sources/MyProfile/View/FollowerCell.swift new file mode 100644 index 0000000..548cf33 --- /dev/null +++ b/Presentation/Sources/MyProfile/View/FollowerCell.swift @@ -0,0 +1,84 @@ +// +// FollowerCell.swift +// Presentation +// +// Created by 송여경 on 8/20/24. +// Copyright © 2024 com.recordy. All rights reserved. +// +import UIKit +import SnapKit +import Then + +import Core +import Common + +class FollowerCell: UITableViewCell { + + let profileImageView = UIImageView() + let usernameLabel = UILabel() + let followButton = MediumButton() + + var followButtonEvent: (() -> Void)? + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(profileImageView) + contentView.addSubview(usernameLabel) + contentView.addSubview(followButton) + + setStyle() + setAutoLayout() + followButton.addTarget(self, action: #selector(followButtonTapped), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setStyle() { + profileImageView.cornerRadius(10) + usernameLabel.font = RecordyFont.body2Bold.font + usernameLabel.textColor = CommonAsset.recordyGrey01.color + contentView.backgroundColor = .black + } + + private func setAutoLayout() { + profileImageView.snp.makeConstraints { + $0.top.equalTo(contentView.snp.top).offset(10) + $0.leading.equalTo(contentView.snp.leading).offset(20) + $0.centerY.equalTo(contentView.snp.centerY) + $0.width.equalTo(54) + $0.height.equalTo(54) + } + + usernameLabel.snp.makeConstraints { + $0.leading.equalTo(profileImageView.snp.trailing).offset(20) + $0.centerY.equalTo(contentView.snp.centerY) + } + + followButton.snp.makeConstraints { + $0.trailing.equalTo(contentView.snp.trailing).offset(-20) + $0.centerY.equalTo(contentView.snp.centerY) + $0.width.equalTo(76.adaptiveWidth) + $0.height.equalTo(36.adaptiveHeight) + } + } + + @objc private func followButtonTapped() { + self.followButtonEvent?() + } + + func configure(with follower: Follower) { + let url = URL(string: follower.profileImage) + profileImageView.kf.setImage(with: url) + usernameLabel.text = follower.username + followButton.isHidden = follower.username == "유영" + updateFollowButton(isFollowed: follower.isFollowing) + } + + func updateFollowButton(isFollowed: Bool) { + followButton.mediumState = isFollowed ? .active : .inactive +// followButton.setTitle(isFollowed ? "팔로우" : "팔로잉", for: .normal) + } +} diff --git a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift index a4d1a58..16c9cbf 100644 --- a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift +++ b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift @@ -5,24 +5,25 @@ // Created by 송여경 on 7/13/24. // Copyright © 2024 com.recordy. All rights reserved. // - import Foundation -import UIKit - import Common import Core class FollowViewModel { - + let followType: FollowType var followers: Bindable<[Follower]> = Bindable([]) var isEmpty: Bindable = Bindable(false) + + var cursorId: Int? = nil + let apiProvider = APIProvider() - + init(followType: FollowType) { self.followType = followType + fetchUsers() } - + func fetchUsers() { switch followType { case .follower: @@ -32,68 +33,85 @@ class FollowViewModel { } } - func getFollowerList() { - let request = DTO.GetFollowerListRequest(cursorId: 0, size: 100) + private func getFollowerList() { + let request = DTO.GetFollowerListRequest(cursorId: cursorId, size: 100) apiProvider.requestResponsable( .getfollowerList(request), DTO.GetFollowerListResponse.self - ) { [weak self] - result in - guard let self = self else { return } - switch result { - case .success(let response): - var followerList = response.content.map { - Follower( - id: $0.userInfo.id, - username: $0.userInfo.nickname, - isFollowing: $0.following, - profileImage: $0.userInfo.profileImageUrl - ) - } - followerList = self.filterSpecialAccount(in: followerList) - self.followers.value = followerList - self.isEmpty.value = followerList.isEmpty - case .failure(let failure): - print(failure) - } + ) { [weak self] result in + guard let self = self else { return } + switch result { + case .success(let response): + self.handleFollowerResponse(response: response) + case .failure(let failure): + print(failure) } + } } - + func getFollowingList() { - let request = DTO.GetFollowListRequest(cursorId: 0, size: 100) - apiProvider.requestResponsable(.getfollowList(request), DTO.GetFollowListResponse.self) { [weak self] - result in + let request = DTO.GetFollowListRequest(size: 10) + + apiProvider.requestResponsable(.getfollowList(request), DTO.GetFollowListResponse.self) { [weak self] result in guard let self = self else { return } switch result { case .success(let response): - var followList = response.content.map { - Follower( - id: $0.id, - username: $0.nickname, - isFollowing: true, - profileImage: $0.profileImageUrl - ) - } - followList = self.filterSpecialAccount(in: followList) - self.followers.value = followList - self.isEmpty.value = followList.isEmpty + self.handleFollowingResponse(response: response) case .failure(let failure): - print(failure) + print("Decoding failed: \(failure)") } } } - func filterSpecialAccount(in list: [Follower]) -> [Follower] { - var updatedList = list.filter {$0.username != "유영"} - let specialAccount = Follower(id: 0, username: "유영", isFollowing: true, profileImage: "") - updatedList.append(specialAccount) - return updatedList + private func handleFollowerResponse(response: DTO.GetFollowerListResponse) { + let followerList = response.content.map { + Follower( + id: $0.userInfo.id, + username: $0.userInfo.nickname, + isFollowing: $0.following, + profileImage: $0.userInfo.profileImageUrl + ) + } + self.followers.value = followerList + self.isEmpty.value = followerList.isEmpty + } - + + private func handleFollowingResponse(response: DTO.GetFollowListResponse) { + let followList = response.content.map { + Follower( + id: $0.id, + username: $0.nickname, + isFollowing: true, + profileImage: $0.profileImageUrl + ) + } + + self.followers.value = followList.reversed() + } + func toggleFollow(at index: Int) { guard index < followers.value.count else { return } followers.value[index].isFollowing.toggle() followers.value = followers.value } + + func postFollowRequest(at index: Int) { + guard index < followers.value.count else { return } + let follower = followers.value[index] + let request = DTO.FollowRequest(followingId: follower.id) + + toggleFollow(at: index) + + apiProvider.justRequest(.follow(request)) { [weak self] result in + guard let self = self else { return } + switch result { + case .success(_): + print("Follow request successful") + case .failure(let failure): + print("Follow request failed: \(failure)") + self.toggleFollow(at: index) + } + } + } } - diff --git a/Presentation/Sources/MyProfile/ViewModel/FollowingViewModel.swift b/Presentation/Sources/MyProfile/ViewModel/FollowingViewModel.swift deleted file mode 100644 index 5ad6c94..0000000 --- a/Presentation/Sources/MyProfile/ViewModel/FollowingViewModel.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// FollowingViewModel.swift -// Presentation -// -// Created by 송여경 on 7/15/24. -// Copyright © 2024 com.recordy. All rights reserved. -// - -import Foundation - -import Core -import Common - -class FollowingViewModel { - var followings: Bindable<[Following]> = Bindable([]) - var isEmpty: Bindable = Bindable(true) - - func fetchFollowings() { - let fetchedFollowings: [Following] = [] - followings.value = fetchedFollowings - isEmpty.value = fetchedFollowings.isEmpty - } - - func toggleFollow(at index: Int) { - guard index < followings.value.count else { return } - followings.value[index].isFollowing.toggle() - followings.value = followings.value - } -} - From 2ee08267c1f724a774d8788f288bd6507e858396 Mon Sep 17 00:00:00 2001 From: 0gonge Date: Wed, 4 Sep 2024 03:35:07 +0900 Subject: [PATCH 04/10] =?UTF-8?q?refactor:=20follower,=20following=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=20=EC=84=A4=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyProfile/Controller/FollowViewController.swift | 3 +++ .../MyProfile/Controller/ProfileViewController.swift | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index a2e9f7c..45dba91 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -9,6 +9,7 @@ enum FollowType { case follower case following } + public class FollowViewController: UIViewController { let followType: FollowType @@ -34,6 +35,8 @@ public class FollowViewController: UIViewController { setTableView() bindViewModel() setTitle() + + self.navigationController?.navigationBar.topItem?.title = "" } public func setStyle() { diff --git a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift index aeda3cb..ae5442d 100644 --- a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift @@ -44,6 +44,7 @@ public class ProfileViewController: UIViewController { setAutoLayout() setDelegate() controlTypeChanged() + setTitle() } public override func viewWillAppear(_ animated: Bool) { @@ -82,7 +83,6 @@ public class ProfileViewController: UIViewController { } func setStyle() { - self.title = "프로필" let rightButton = UIButton(type: .system) rightButton.setImage(CommonAsset.settingIcon.image, for: .normal) rightButton.addTarget( @@ -191,7 +191,11 @@ public class ProfileViewController: UIViewController { let url = URL(string: user.profileImage)! self.profileInfoView.profileImage.kf.setImage(with: url) } - + + func setTitle(){ + self.title = "프로필" + } + func getUserProfile() { let apiProvider = APIProvider() let userId = UserDefaults.standard.integer(forKey: "userId") From 13d9bf4ec6751478b64807a671fbffe3478b55c6 Mon Sep 17 00:00:00 2001 From: 0gonge Date: Wed, 4 Sep 2024 03:43:22 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20title=20=EC=82=AC=EB=9D=BC?= =?UTF-8?q?=EC=A7=90=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/MyProfile/Controller/ProfileViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift index ae5442d..497fb83 100644 --- a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift @@ -54,6 +54,7 @@ public class ProfileViewController: UIViewController { getMyRecordList() getUserProfile() setObserver() + self.title = "프로필" } public override func viewWillDisappear(_ animated: Bool) { From 9b62d03006579c4ee7f025b392544d153b1e7f43 Mon Sep 17 00:00:00 2001 From: 0gonge Date: Wed, 4 Sep 2024 03:46:50 +0900 Subject: [PATCH 06/10] =?UTF-8?q?refactor:=20profileimage=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Presentation/Sources/MyProfile/View/FollowerCell.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Presentation/Sources/MyProfile/View/FollowerCell.swift b/Presentation/Sources/MyProfile/View/FollowerCell.swift index 548cf33..41534ee 100644 --- a/Presentation/Sources/MyProfile/View/FollowerCell.swift +++ b/Presentation/Sources/MyProfile/View/FollowerCell.swift @@ -37,7 +37,10 @@ class FollowerCell: UITableViewCell { } private func setStyle() { - profileImageView.cornerRadius(10) + profileImageView.layer.cornerRadius = 54/2 + profileImageView.clipsToBounds = true + profileImageView.contentMode = .scaleAspectFit + usernameLabel.font = RecordyFont.body2Bold.font usernameLabel.textColor = CommonAsset.recordyGrey01.color contentView.backgroundColor = .black @@ -79,6 +82,5 @@ class FollowerCell: UITableViewCell { func updateFollowButton(isFollowed: Bool) { followButton.mediumState = isFollowed ? .active : .inactive -// followButton.setTitle(isFollowed ? "팔로우" : "팔로잉", for: .normal) } } From 61f40a598dd69871f956fdbb1f9880b94269ba58 Mon Sep 17 00:00:00 2001 From: 0gonge Date: Thu, 5 Sep 2024 01:25:41 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor:=20codeReview=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/FollowViewController.swift | 48 +++++++++---------- .../Controller/ProfileViewController.swift | 5 -- .../Sources/MyProfile/View/FollowerCell.swift | 31 ++++++++---- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index 45dba91..8beb8ae 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -8,14 +8,23 @@ import Common enum FollowType { case follower case following + + var title: String{ + switch self { + case .follower: + return "팔로워" + case .following: + return "팔로우" + } + } } public class FollowViewController: UIViewController { - let followType: FollowType - let viewModel: FollowViewModel - let tableView = UITableView() - let emptyView = FollowerEmptyView() + private let followType: FollowType + private let viewModel: FollowViewModel + private let tableView = UITableView() + private let emptyView = FollowerEmptyView() init(followType: FollowType) { self.followType = followType @@ -32,15 +41,20 @@ public class FollowViewController: UIViewController { setStyle() setUI() setAutoLayout() - setTableView() - bindViewModel() - setTitle() + bind() self.navigationController?.navigationBar.topItem?.title = "" } - public func setStyle() { + private func setStyle() { view.backgroundColor = .black + tableView.dataSource = self + tableView.delegate = self + tableView.backgroundColor = .black + tableView.register(FollowerCell.self, forCellReuseIdentifier: "FollowerCell") + tableView.separatorStyle = .none + + self.title = followType.title } private func setUI() { @@ -58,15 +72,8 @@ public class FollowViewController: UIViewController { } } - private func setTableView() { - tableView.dataSource = self - tableView.delegate = self - tableView.backgroundColor = .black - tableView.register(FollowerCell.self, forCellReuseIdentifier: "FollowerCell") - tableView.separatorStyle = .none - } - private func bindViewModel() { + private func bind() { viewModel.followers.bind { [weak self] _ in guard let self = self else { return } self.tableView.reloadData() @@ -78,15 +85,6 @@ public class FollowViewController: UIViewController { self.emptyView.isHidden = !isEmpty } } - - private func setTitle(){ - switch followType { - case .follower: - self.title = "팔로워" - case .following: - self.title = "팔로우" - } - } } extension FollowViewController: UITableViewDataSource, UITableViewDelegate { diff --git a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift index 497fb83..163bcd9 100644 --- a/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/ProfileViewController.swift @@ -44,7 +44,6 @@ public class ProfileViewController: UIViewController { setAutoLayout() setDelegate() controlTypeChanged() - setTitle() } public override func viewWillAppear(_ animated: Bool) { @@ -193,10 +192,6 @@ public class ProfileViewController: UIViewController { self.profileInfoView.profileImage.kf.setImage(with: url) } - func setTitle(){ - self.title = "프로필" - } - func getUserProfile() { let apiProvider = APIProvider() let userId = UserDefaults.standard.integer(forKey: "userId") diff --git a/Presentation/Sources/MyProfile/View/FollowerCell.swift b/Presentation/Sources/MyProfile/View/FollowerCell.swift index 41534ee..5d569b2 100644 --- a/Presentation/Sources/MyProfile/View/FollowerCell.swift +++ b/Presentation/Sources/MyProfile/View/FollowerCell.swift @@ -23,11 +23,8 @@ class FollowerCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - contentView.addSubview(profileImageView) - contentView.addSubview(usernameLabel) - contentView.addSubview(followButton) - setStyle() + setUI() setAutoLayout() followButton.addTarget(self, action: #selector(followButtonTapped), for: .touchUpInside) } @@ -37,13 +34,27 @@ class FollowerCell: UITableViewCell { } private func setStyle() { - profileImageView.layer.cornerRadius = 54/2 - profileImageView.clipsToBounds = true - profileImageView.contentMode = .scaleAspectFit + profileImageView.do { + $0.layer.cornerRadius = 54/2 + $0.clipsToBounds = true + $0.contentMode = .scaleAspectFit + } + usernameLabel.do { + $0.font = RecordyFont.body2Bold.font + $0.textColor = CommonAsset.recordyGrey01.color + } - usernameLabel.font = RecordyFont.body2Bold.font - usernameLabel.textColor = CommonAsset.recordyGrey01.color - contentView.backgroundColor = .black + contentView.do{ + $0.backgroundColor = .black + } + } + + private func setUI() { + contentView.do { + $0.addSubview(profileImageView) + $0.addSubview(usernameLabel) + $0.addSubview(followButton) + } } private func setAutoLayout() { From bc1e4184803e29643f5154c80887bf255c24589d Mon Sep 17 00:00:00 2001 From: 0gonge Date: Thu, 5 Sep 2024 01:34:11 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20convention=EB=B0=98=EC=98=81(?= =?UTF-8?q?#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/FollowViewController.swift | 41 +++++++++++-------- .../Sources/MyProfile/View/FollowerCell.swift | 18 ++++---- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index 8beb8ae..d3e3804 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -9,7 +9,7 @@ enum FollowType { case follower case following - var title: String{ + var title: String { switch self { case .follower: return "팔로워" @@ -23,7 +23,10 @@ public class FollowViewController: UIViewController { private let followType: FollowType private let viewModel: FollowViewModel - private let tableView = UITableView() + private let tableView = UITableView().then { + $0.backgroundColor = .black + $0.separatorStyle = .none + } private let emptyView = FollowerEmptyView() init(followType: FollowType) { @@ -47,19 +50,24 @@ public class FollowViewController: UIViewController { } private func setStyle() { - view.backgroundColor = .black - tableView.dataSource = self - tableView.delegate = self - tableView.backgroundColor = .black - tableView.register(FollowerCell.self, forCellReuseIdentifier: "FollowerCell") - tableView.separatorStyle = .none + view.do { + $0.backgroundColor = .black + } + + tableView.do { + $0.dataSource = self + $0.delegate = self + $0.register(FollowerCell.self, forCellReuseIdentifier: "FollowerCell") + } self.title = followType.title } private func setUI() { - view.addSubview(tableView) - view.addSubview(emptyView) + view.do { + $0.addSubview(tableView) + $0.addSubview(emptyView) + } } private func setAutoLayout() { @@ -72,7 +80,6 @@ public class FollowViewController: UIViewController { } } - private func bind() { viewModel.followers.bind { [weak self] _ in guard let self = self else { return } @@ -102,11 +109,13 @@ extension FollowViewController: UITableViewDataSource, UITableViewDelegate { let follower = viewModel.followers.value[indexPath.row] cell.configure(with: follower) - if indexPath.row == 0 && followType == .following { - cell.followButton.isHidden = true - } else { - cell.followButton.isHidden = false - cell.updateFollowButton(isFollowed: follower.isFollowing) + cell.followButton.do { + if indexPath.row == 0 && followType == .following { + $0.isHidden = true + } else { + $0.isHidden = false + cell.updateFollowButton(isFollowed: follower.isFollowing) + } } cell.followButtonEvent = { [weak self] in diff --git a/Presentation/Sources/MyProfile/View/FollowerCell.swift b/Presentation/Sources/MyProfile/View/FollowerCell.swift index 5d569b2..f1c646c 100644 --- a/Presentation/Sources/MyProfile/View/FollowerCell.swift +++ b/Presentation/Sources/MyProfile/View/FollowerCell.swift @@ -26,7 +26,6 @@ class FollowerCell: UITableViewCell { setStyle() setUI() setAutoLayout() - followButton.addTarget(self, action: #selector(followButtonTapped), for: .touchUpInside) } required init?(coder: NSCoder) { @@ -35,26 +34,25 @@ class FollowerCell: UITableViewCell { private func setStyle() { profileImageView.do { - $0.layer.cornerRadius = 54/2 + $0.layer.cornerRadius = 54 / 2 $0.clipsToBounds = true $0.contentMode = .scaleAspectFit } + usernameLabel.do { $0.font = RecordyFont.body2Bold.font $0.textColor = CommonAsset.recordyGrey01.color } - contentView.do{ - $0.backgroundColor = .black - } + contentView.backgroundColor = .black + + followButton.addTarget(self, action: #selector(followButtonTapped), for: .touchUpInside) } private func setUI() { - contentView.do { - $0.addSubview(profileImageView) - $0.addSubview(usernameLabel) - $0.addSubview(followButton) - } + contentView.addSubview(profileImageView) + contentView.addSubview(usernameLabel) + contentView.addSubview(followButton) } private func setAutoLayout() { From dd7dec8246add5087a1ef95de948ecc1fefa973d Mon Sep 17 00:00:00 2001 From: 0gonge Date: Thu, 5 Sep 2024 01:49:39 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20Bindable=20to=20Closure=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/FollowViewController.swift | 14 ++++----- .../MyProfile/ViewModel/FollowViewModel.swift | 31 ++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index d3e3804..a6111fd 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -44,7 +44,7 @@ public class FollowViewController: UIViewController { setStyle() setUI() setAutoLayout() - bind() + bindViewModel() self.navigationController?.navigationBar.topItem?.title = "" } @@ -80,13 +80,13 @@ public class FollowViewController: UIViewController { } } - private func bind() { - viewModel.followers.bind { [weak self] _ in + private func bindViewModel() { + viewModel.followersDidChange = { [weak self] followers in guard let self = self else { return } self.tableView.reloadData() } - viewModel.isEmpty.bind { [weak self] isEmpty in + viewModel.isEmptyDidChange = { [weak self] isEmpty in guard let self = self else { return } self.tableView.isHidden = isEmpty self.emptyView.isHidden = !isEmpty @@ -97,7 +97,7 @@ public class FollowViewController: UIViewController { extension FollowViewController: UITableViewDataSource, UITableViewDelegate { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return viewModel.followers.value.count + return viewModel.followers.count } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -106,7 +106,7 @@ extension FollowViewController: UITableViewDataSource, UITableViewDelegate { for: indexPath ) as! FollowerCell - let follower = viewModel.followers.value[indexPath.row] + let follower = viewModel.followers[indexPath.row] cell.configure(with: follower) cell.followButton.do { @@ -126,7 +126,7 @@ extension FollowViewController: UITableViewDataSource, UITableViewDelegate { } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let userVC = OtherUserProfileViewController(id: viewModel.followers.value[indexPath.row].id) + let userVC = OtherUserProfileViewController(id: viewModel.followers[indexPath.row].id) self.navigationController?.pushViewController(userVC, animated: true) } } diff --git a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift index 16c9cbf..6eb2327 100644 --- a/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift +++ b/Presentation/Sources/MyProfile/ViewModel/FollowViewModel.swift @@ -12,13 +12,19 @@ import Core class FollowViewModel { let followType: FollowType - var followers: Bindable<[Follower]> = Bindable([]) - var isEmpty: Bindable = Bindable(false) - + var followers: [Follower] = [] { + didSet { + followersDidChange?(followers) + isEmptyDidChange?(followers.isEmpty) + } + } var cursorId: Int? = nil let apiProvider = APIProvider() + var followersDidChange: (([Follower]) -> Void)? + var isEmptyDidChange: ((Bool) -> Void)? + init(followType: FollowType) { self.followType = followType fetchUsers() @@ -72,9 +78,7 @@ class FollowViewModel { profileImage: $0.userInfo.profileImageUrl ) } - self.followers.value = followerList - self.isEmpty.value = followerList.isEmpty - + self.followers = followerList } private func handleFollowingResponse(response: DTO.GetFollowListResponse) { @@ -86,19 +90,18 @@ class FollowViewModel { profileImage: $0.profileImageUrl ) } - - self.followers.value = followList.reversed() - } + self.followers = followList.reversed() + } func toggleFollow(at index: Int) { - guard index < followers.value.count else { return } - followers.value[index].isFollowing.toggle() - followers.value = followers.value + guard index < followers.count else { return } + followers[index].isFollowing.toggle() + followersDidChange?(followers) } func postFollowRequest(at index: Int) { - guard index < followers.value.count else { return } - let follower = followers.value[index] + guard index < followers.count else { return } + let follower = followers[index] let request = DTO.FollowRequest(followingId: follower.id) toggleFollow(at: index) From 0b819543db1a5e9ad6abf18f8d005372b6fdad8c Mon Sep 17 00:00:00 2001 From: 0gonge Date: Fri, 6 Sep 2024 02:42:58 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20bind=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/MyProfile/Controller/FollowViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift index a6111fd..081f7b8 100644 --- a/Presentation/Sources/MyProfile/Controller/FollowViewController.swift +++ b/Presentation/Sources/MyProfile/Controller/FollowViewController.swift @@ -44,7 +44,7 @@ public class FollowViewController: UIViewController { setStyle() setUI() setAutoLayout() - bindViewModel() + bind() self.navigationController?.navigationBar.topItem?.title = "" } @@ -80,7 +80,7 @@ public class FollowViewController: UIViewController { } } - private func bindViewModel() { + private func bind() { viewModel.followersDidChange = { [weak self] followers in guard let self = self else { return } self.tableView.reloadData()