From 42b3d0ab5f83fb7255e084e3d544fd6e029ae231 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Mon, 15 Jan 2024 17:58:04 +0900 Subject: [PATCH 1/3] fix: post QA -1 (#272) --- .../Home/Reactor/HomeViewReactor.swift | 12 ++-- .../ViewControllers/HomeViewController.swift | 8 +-- .../Home/Views/FamilyCollectionViewCell.swift | 56 ++++++++++++++++-- .../PostDetail/Reactor/EmojiReactor.swift | 2 +- .../ViewControllers/PostViewController.swift | 12 +--- .../PostDetail/Views/EmojiCountButton.swift | 59 +++++++++++-------- .../Views/PostCollectionViewCell.swift | 9 ++- .../PostDetail/Views/PostNavigationView.swift | 6 +- .../Sources/Emoji/DTO/FetchEmojiDTO.swift | 3 +- .../Post/PostList/DTO/PostListDTO.swift | 2 +- .../PostListAPI/PostListAPIWorker.swift | 13 ++-- .../PostList/PostListAPI/PostListAPIs.swift | 19 ++---- 12 files changed, 122 insertions(+), 79 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index ed0db8af7..7ee3bf3ab 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -22,7 +22,7 @@ public final class HomeViewReactor: Reactor { case setLoading(Bool) case setDidPost case setDescriptionText(String) - case showNoPostTodayView + case showNoPostTodayView(Bool) case setPostCollectionView([SectionModel]) case setRefreshing(Bool) } @@ -54,10 +54,12 @@ extension HomeViewReactor { .flatMap { postList in guard let postList, !postList.postLists.isEmpty else { - return Observable.just(Mutation.showNoPostTodayView) + return Observable.just(Mutation.showNoPostTodayView(true)) } - var observables = [Observable.just(Mutation.setPostCollectionView([ + var observables = [ + Observable.just(Mutation.showNoPostTodayView(false)), + Observable.just(Mutation.setPostCollectionView([ SectionModel(model: "section1", items: postList.postLists)]))] if postList.selfUploaded { @@ -81,8 +83,8 @@ extension HomeViewReactor { var newState = state switch mutation { - case .showNoPostTodayView: - newState.isShowingNoPostTodayView = true + case let .showNoPostTodayView(isShow): + newState.isShowingNoPostTodayView = isShow case let .setPostCollectionView(data): newState.feedSections = data case .setDidPost: diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index 3b35f13a1..3462095c6 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -293,12 +293,8 @@ public final class HomeViewController: BaseViewController { extension HomeViewController { private func setNoPostTodayView(_ isShow: Bool) { - if isShow { - noPostTodayView.isHidden = !isShow - postCollectionView.isHidden = isShow - } else { - noPostTodayView.isHidden = !isShow - } + noPostTodayView.isHidden = !isShow + postCollectionView.isHidden = isShow } private func hideCameraButton(_ isShow: Bool) { diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Views/FamilyCollectionViewCell.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Views/FamilyCollectionViewCell.swift index cf1a7337f..fa691a832 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Views/FamilyCollectionViewCell.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Views/FamilyCollectionViewCell.swift @@ -10,9 +10,12 @@ import Core import Domain final class FamilyCollectionViewCell: BaseCollectionViewCell { + typealias Layout = HomeAutoLayout.ProfileView static let id: String = "familyCollectionViewCell" - - private let profileView = ProfileView() + + private let defaultNameLabel = BibbiLabel(.head1, alignment: .center, textColor: .gray200) + private let imageView = UIImageView() + private let nameLabel = UILabel() override init(frame: CGRect) { super.init(frame: frame) @@ -25,22 +28,63 @@ final class FamilyCollectionViewCell: BaseCollectionViewCell { override func bind(reactor: HomeViewReactor) { } override func setupUI() { - addSubview(profileView) + addSubviews(imageView, nameLabel, defaultNameLabel) } override func setupAutoLayout() { - profileView.snp.makeConstraints { - $0.edges.equalToSuperview() + imageView.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview() + $0.width.height.equalTo(64) + $0.top.leading.equalToSuperview() + } + + nameLabel.snp.makeConstraints { + $0.top.equalTo(imageView.snp.bottom).offset(Layout.NameLabel.topOffset) + $0.horizontalEdges.equalToSuperview() + $0.bottom.equalToSuperview() + } + + defaultNameLabel.snp.makeConstraints { + $0.center.equalTo(imageView) } } override func setupAttributes() { + nameLabel.do { + $0.font = UIFont.pretendard(.caption) + $0.textAlignment = .center + $0.textColor = .gray300 + + $0.numberOfLines = 1 + $0.lineBreakMode = .byTruncatingTail + } + imageView.do { + $0.clipsToBounds = true + $0.contentMode = .scaleAspectFill + $0.layer.cornerRadius = 64 / 2 + } + } + + override func prepareForReuse() { + imageView.image = nil } } extension FamilyCollectionViewCell { func setCell(data: ProfileData) { - profileView.setProfile(profile: data) + if let profileImageURL = data.profileImageURL, + let url = URL(string: profileImageURL), !profileImageURL.isEmpty { + imageView.kf.setImage(with: url) + defaultNameLabel.isHidden = true + } else { + guard let name = data.name.first else { + return + } + defaultNameLabel.isHidden = false + imageView.backgroundColor = .gray800 + defaultNameLabel.text = "\(name)" + } + nameLabel.text = data.name } } diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/EmojiReactor.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/EmojiReactor.swift index b6a7b5148..4c17d60b1 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/EmojiReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/EmojiReactor.swift @@ -114,7 +114,7 @@ extension EmojiReactor { var newState = state switch mutation { case .showSelectableEmojiStackView: - newState.isShowingSelectableEmojiStackView.toggle() + newState.isShowingSelectableEmojiStackView = true case let .selectEmoji(emoji): guard let myMemberId = UserDefaults.standard.memberId, let fetchedEmojiList = newState.fetchedEmojiList else { diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift index 36b106854..5e6484f30 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift @@ -12,7 +12,6 @@ import Domain import RxDataSources import RxSwift -// index 조회 필요 => index 조회 이후 스크롤시 이전/이후 포스트 조회 할 수 있어야함 final class PostViewController: BaseViewController { private let backgroundImageView: UIImageView = UIImageView() private let blurEffectView: UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffect.Style.dark)) @@ -46,20 +45,14 @@ final class PostViewController: BaseViewController { .map { $0.selectedPost } .withUnretained(self) .observe(on: MainScheduler.instance) - .bind(onNext: { - $0.0.setBackgroundView(data: $0.1) - }) + .bind(onNext: { $0.0.setBackgroundView(data: $0.1) }) .disposed(by: disposeBag) reactor.state .map { $0.reactionMemberIds } - .asObservable() - .distinctUntilChanged() .withUnretained(self) .observe(on: MainScheduler.instance) - .bind(onNext: { - $0.0.showReactionSheet($0.1) - }) + .subscribe(onNext: { $0.0.showReactionSheet($0.1) }) .disposed(by: disposeBag) reactor.state @@ -162,6 +155,7 @@ extension PostViewController { private func showReactionSheet(_ memberIds: [String]) { if memberIds.isEmpty { return } + let reactionMembersViewController = ReactionDIContainer().makeViewController(memberIds: memberIds) if let sheet = reactionMembersViewController.sheetPresentationController { sheet.detents = [.medium()] diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/EmojiCountButton.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/EmojiCountButton.swift index 0c13d59d4..fa26a1c3d 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/EmojiCountButton.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/EmojiCountButton.swift @@ -16,26 +16,28 @@ import RxCocoa final class EmojiCountButton: BaseView { typealias Layout = PostAutoLayout.CollectionView.CollectionViewCell.EmojiCountStackView.EmojiCountButton + private enum GestureType { + case tap + case longPress + } + private let emojiImageView = UIImageView() private let countLabel = BibbiLabel(.body1Regular, alignment: .right) + private let tapGesture = UITapGestureRecognizer() + private let longPressGesture = UILongPressGestureRecognizer() - var isSelected: Bool = false { - didSet { - if isSelected { - setSelected() - } else { - setUnSelected() - } - } - } + let selectedRelay: BehaviorRelay = BehaviorRelay(value: false) convenience init(reactor: Reactor) { self.init(frame: .zero) self.reactor = reactor } - + override init(frame: CGRect) { super.init(frame: frame) + + self.addGestureRecognizer(tapGesture) + self.addGestureRecognizer(longPressGesture) } required init?(coder: NSCoder) { @@ -43,16 +45,29 @@ final class EmojiCountButton: BaseView { } override func bind(reactor: EmojiReactor) { - self.rx.tap + tapGesture.rx.event .throttle(RxConst.throttleInterval, scheduler: MainScheduler.instance) - .map { Reactor.Action.tappedSelectedEmojiCountButton(Emojis.emoji(forIndex: self.tag)) } + .map { _ in Reactor.Action.tappedSelectedEmojiCountButton(Emojis.emoji(forIndex: self.tag)) } .bind(to: reactor.action) .disposed(by: disposeBag) - self.rx.longPress + longPressGesture.rx.event .map { _ in Reactor.Action.longPressedEmojiCountButton(self.tag) } .bind(to: reactor.action) .disposed(by: disposeBag) + + longPressGesture.rx.event + .asObservable() + .map { $0.state } + .withUnretained(self) { ($0, $1) } + .bind(onNext: { $0.0.tapGesture.isEnabled = $0.1 == .ended }) + .disposed(by: disposeBag) + + selectedRelay + .withUnretained(self) + .observe(on: MainScheduler.instance) + .bind(onNext: { $0.0.setSelected($0.1) }) + .disposed(by: disposeBag) } override func setupUI() { @@ -81,14 +96,12 @@ final class EmojiCountButton: BaseView { } extension EmojiCountButton { - private func setUnSelected() { - backgroundColor = UIColor(red: 0.141, green: 0.141, blue: 0.153, alpha: 0.5) - layer.borderWidth = 0 - } - - private func setSelected() { - backgroundColor = UIColor(red: 0.141, green: 0.141, blue: 0.153, alpha: 1) - layer.borderWidth = 1 + private func setSelected(_ isSelected: Bool) { + let alpha = isSelected ? 1: 0.5 + let borderWidth: CGFloat = isSelected ? 1 : 0 + + backgroundColor = UIColor(red: 0.141, green: 0.141, blue: 0.153, alpha: alpha) + layer.borderWidth = borderWidth layer.borderColor = UIColor(red: 0.908, green: 0.908, blue: 0.908, alpha: 1).cgColor } } @@ -98,8 +111,4 @@ extension EmojiCountButton { emojiImageView.image = emoji.emoji.emojiImage countLabel.text = "\(emoji.count)" } - - func setCountLabel(_ count: Int) { - countLabel.text = "\(count)" - } } diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostCollectionViewCell.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostCollectionViewCell.swift index 60528b983..078e75fdd 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostCollectionViewCell.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostCollectionViewCell.swift @@ -61,6 +61,11 @@ final class PostCollectionViewCell: BaseCollectionViewCell { postImageView.image = nil } + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + super.touchesBegan(touches, with: event) + selectableEmojiStackView.isHidden = true + } + override func bind(reactor: EmojiReactor) { reactionButtons.enumerated().forEach { index, button in button.rx.tap @@ -89,7 +94,7 @@ final class PostCollectionViewCell: BaseCollectionViewCell { reactor.state .map { $0.isShowingSelectableEmojiStackView } - .distinctUntilChanged() + .filter { $0 } .withUnretained(self) .bind(onNext: { $0.0.showSelectableEmojiStackView($0.1) @@ -317,7 +322,7 @@ extension PostCollectionViewCell { let emojiCountButton = EmojiCountButton(reactor: reactor) emojiCountButton.tag = index + 1 - emojiCountButton.isSelected = emojiData.isSelfSelected + emojiCountButton.selectedRelay.accept(emojiData.isSelfSelected) emojiCountButton.setInitEmoji(emoji: EmojiData(emoji: Emojis.emoji(forIndex: index+1), count: emojiData.count)) emojiCountStackView.addArrangedSubview(emojiCountButton) } diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostNavigationView.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostNavigationView.swift index b02c863e2..a89649e72 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostNavigationView.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Views/PostNavigationView.swift @@ -100,11 +100,11 @@ final class PostNavigationView: BaseView { } extension PostNavigationView { - func setData(data: PostListData) { + private func setData(data: PostListData) { if let author = data.author, let profileImageURL = author.profileImageURL, let url = URL(string: profileImageURL), !profileImageURL.isEmpty { - profileImageView.load(url: url) + profileImageView.kf.setImage(with: url) defaultNameLabel.isHidden = true } else { if let author = data.author, @@ -114,6 +114,8 @@ extension PostNavigationView { defaultNameLabel.text = "알" } + profileImageView.kf.base.image = nil + defaultNameLabel.isHidden = false profileImageView.backgroundColor = .gray800 } diff --git a/14th-team5-iOS/Data/Sources/Emoji/DTO/FetchEmojiDTO.swift b/14th-team5-iOS/Data/Sources/Emoji/DTO/FetchEmojiDTO.swift index 270edeadb..9a3e1e27c 100644 --- a/14th-team5-iOS/Data/Sources/Emoji/DTO/FetchEmojiDTO.swift +++ b/14th-team5-iOS/Data/Sources/Emoji/DTO/FetchEmojiDTO.swift @@ -32,8 +32,7 @@ struct FetchEmojiResponse: Codable { } private func containsCurrentUser(memberIds: [String]) -> Bool { -// let currentMemberId = FamilyUserDefaults.returnMyMemberId() - let currentMemberId = "01HJBNWZGNP1KJNMKWVZJ039HY" + let currentMemberId = FamilyUserDefaults.returnMyMemberId() return memberIds.contains(currentMemberId) } } diff --git a/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift b/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift index caec5e3d8..5146a3040 100644 --- a/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift +++ b/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift @@ -12,7 +12,7 @@ public struct PostListRequestDTO: Codable { let page: Int let size: Int let date: String - let memberId: String + let memberId: String? let sort: String } diff --git a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift index 645096ba7..78e51c9e3 100644 --- a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift +++ b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift @@ -69,9 +69,9 @@ extension PostListAPIWorker: PostListRepositoryProtocol { } private func fetchTodayPostList(headers: [APIHeader]?, query: Domain.PostListQuery) -> RxSwift.Single { - let requestDTO = PostListRequestDTO(page: query.page, size: query.size, date: query.date, memberId: query.memberId, sort: query.sort) - let spec = PostListAPIs.fetchPostList(requestDTO).spec - return request(spec: spec, headers: headers) + let requestDTO = PostListRequestDTO(page: query.page, size: query.size, date: query.date, memberId: nil, sort: query.sort) + let spec = PostListAPIs.fetchPostList.spec + return request(spec: spec, headers: headers, parameters: requestDTO) .subscribe(on: Self.queue) .do { if let str = String(data: $0.1, encoding: .utf8) { @@ -81,9 +81,12 @@ extension PostListAPIWorker: PostListRepositoryProtocol { .map(PostListResponseDTO.self) .catchAndReturn(nil) .map { - let selfUploaded = $0?.results.map { $0.authorId == FamilyUserDefaults.getMyMemberId() }.isEmpty + let myMemberId = FamilyUserDefaults.getMyMemberId() + let familyCount = FamilyUserDefaults.getMemberCount() + + let selfUploaded = $0?.results.map { $0.authorId == myMemberId }.contains(true) ?? false let familyUploaded = $0?.results.count == FamilyUserDefaults.getMemberCount() - return $0?.toDomain(!(selfUploaded ?? true), familyUploaded) + return $0?.toDomain(selfUploaded, familyUploaded) } .asSingle() } diff --git a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIs.swift b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIs.swift index a10278a9f..01edad2e1 100644 --- a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIs.swift +++ b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIs.swift @@ -9,27 +9,16 @@ import Foundation import Core public enum PostListAPIs: API { - case fetchPostList(PostListRequestDTO) + case fetchPostList case fetchPostDetail(PostRequestDTO) var spec: APISpec { switch self { - case let .fetchPostList(query): - var urlString = "\(BibbiAPI.hostApi)/posts" - - var queryParams: [String] = [] - queryParams.append("page=\(query.page)") - queryParams.append("size=\(query.size)") - queryParams.append("date=\(query.date)") - queryParams.append("sort=\(query.sort)") - - if !queryParams.isEmpty { - urlString += "?" + queryParams.joined(separator: "&") - } - + case .fetchPostList: + let urlString = "\(BibbiAPI.hostApi)/posts" return APISpec(method: .get, url: urlString) case let .fetchPostDetail(query): - var urlString = "\(BibbiAPI.hostApi)/posts/\(query.postId)" + let urlString = "\(BibbiAPI.hostApi)/posts/\(query.postId)" return APISpec(method: .get, url: urlString) } } From 76352b5f466f5b826cb144034b3270af04e96cd4 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Mon, 15 Jan 2024 18:59:53 +0900 Subject: [PATCH 2/3] refactor: timer move to reactor(#272) --- .../Home/Reactor/HomeViewReactor.swift | 68 ++++- .../ViewControllers/HomeViewController.swift | 248 +++++++++--------- .../PostDetail/Reactor/PostReactor.swift | 6 +- .../ViewControllers/PostViewController.swift | 4 +- .../Extensions/UIViewController+Ext.swift | 18 -- 5 files changed, 191 insertions(+), 153 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index 7ee3bf3ab..efef9314a 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -6,6 +6,8 @@ // import Foundation +import UIKit + import Core import Domain @@ -16,6 +18,7 @@ public final class HomeViewReactor: Reactor { public enum Action { case getTodayPostList case refreshCollectionview + case startTimer } public enum Mutation { @@ -25,15 +28,22 @@ public final class HomeViewReactor: Reactor { case showNoPostTodayView(Bool) case setPostCollectionView([SectionModel]) case setRefreshing(Bool) + case hideCamerButton(Bool) + case setTimer(Int) + case setTimerColor(UIColor) } public struct State { var isRefreshing: Bool = false var showLoading: Bool = true var didPost: Bool = false - var descriptionText: String = HomeStrings.Description.standard var isShowingNoPostTodayView: Bool = false - var feedSections: [SectionModel] = [] + + @Pulse var timerLabelColor: UIColor = .white + @Pulse var timer: String = "" + @Pulse var isHideCameraButton: Bool = true + @Pulse var descriptionText: String = HomeStrings.Description.standard + @Pulse var feedSections: [SectionModel] = [] } public let initialState: State = State() @@ -60,12 +70,12 @@ extension HomeViewReactor { var observables = [ Observable.just(Mutation.showNoPostTodayView(false)), Observable.just(Mutation.setPostCollectionView([ - SectionModel(model: "section1", items: postList.postLists)]))] + SectionModel(model: "section1", items: postList.postLists)]))] if postList.selfUploaded { observables.append(Observable.just(Mutation.setDidPost)) } - + if postList.allFamilyMembersUploaded { observables.append(Observable.just(Mutation.setDescriptionText("우리 가족 모두가 사진을 올린 날🎉"))) } @@ -76,6 +86,32 @@ extension HomeViewReactor { case .refreshCollectionview: let getTodayPostListAction = Action.getTodayPostList return mutate(action: getTodayPostListAction) + case .startTimer: + return Observable.interval(.seconds(1), scheduler: MainScheduler.instance) + .startWith(0) + .flatMap {_ in + let time = self.calculateRemainingTime() + + // 시간 이외 + guard time > 0 else { + return Observable.concat([ + Observable.just(Mutation.hideCamerButton(true)), + Observable.just(Mutation.setDescriptionText(HomeStrings.Timer.notTime)), + Observable.just(Mutation.setTimer(time)) + ]) + } + + // 1시간 전 + if time <= 3600 && !self.currentState.didPost { + return Observable.concat([ + Observable.just(Mutation.setTimer(time)), + Observable.just(Mutation.setTimerColor(.warningRed)), + Observable.just(Mutation.setDescriptionText("시간이 얼마 남지 않았어요!")) + ]) + } + + return Observable.just(Mutation.setTimer(time)) + } } } @@ -95,8 +131,32 @@ extension HomeViewReactor { newState.showLoading = false case let .setRefreshing(isRefreshing): newState.isRefreshing = isRefreshing + case let .hideCamerButton(isHide): + newState.isHideCameraButton = isHide + case let .setTimer(time): + newState.timer = time.setTimerFormat() ?? "00:00:00" + case let .setTimerColor(color): + newState.timerLabelColor = color } return newState } } + +extension HomeViewReactor { + private func calculateRemainingTime() -> Int { + let calendar = Calendar.current + let currentTime = Date() + + let isAfterNoon = calendar.component(.hour, from: currentTime) >= 12 + + if isAfterNoon { + if let nextMidnight = calendar.date(bySettingHour: 0, minute: 0, second: 0, of: currentTime.addingTimeInterval(24 * 60 * 60)) { + let timeDifference = calendar.dateComponents([.second], from: currentTime, to: nextMidnight) + return max(0, timeDifference.second ?? 0) + } + } + + return 0 + } +} diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index 3462095c6..2edd889eb 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -44,132 +44,8 @@ public final class HomeViewController: BaseViewController { } public override func bind(reactor: HomeViewReactor) { - postCollectionView.rx.setDelegate(self) - .disposed(by: disposeBag) - - rx.viewWillAppear - .map { _ in Reactor.Action.getTodayPostList } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - Observable.interval(.seconds(1), scheduler: MainScheduler.instance) - .startWith(0) - .map { [weak self] _ in - guard let self = self else { return HomeStrings.Timer.notTime } - let time = self.calculateRemainingTime() - guard let timeString = time.setTimerFormat() else { - self.hideCameraButton(true) - return HomeStrings.Timer.notTime - } - - if time <= 3600 && !reactor.currentState.didPost { - self.timerLabel.textBibbiColor = .warningRed - self.descriptionLabel.text = "시간이 얼마 남지 않았어요!" - } - - return timeString - } - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] time in - guard let self = self else { return } - - self.timerLabel.text = time - }) - .disposed(by: disposeBag) - - navigationBarView.rx.didTapLeftBarButton - .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) - .withUnretained(self) - .subscribe { - $0.0.navigationController?.pushViewController( - FamilyManagementDIContainer().makeViewController(), - animated: true - ) - } - .disposed(by: disposeBag) - - navigationBarView.rx.didTapRightBarButton - .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) - .withUnretained(self) - .subscribe { - $0.0.navigationController?.pushViewController( - CalendarDIConatainer().makeViewController(), - animated: true - ) - } - .disposed(by: disposeBag) - - postCollectionView.rx.itemSelected - .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) - .bind(onNext: { [weak self] indexPath in - guard let self else { return } - let sectionModel = reactor.currentState.feedSections[indexPath.section] - let selectedItem = sectionModel.items[indexPath.item] - - self.navigationController?.pushViewController( - PostListsDIContainer().makeViewController( - postLists: sectionModel, - selectedIndex: indexPath - ), - animated: true - ) - }) - .disposed(by: disposeBag) - - refreshControl.rx.controlEvent(.valueChanged) - .map { Reactor.Action.refreshCollectionview } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .map { $0.isRefreshing } - .observe(on: MainScheduler.instance) - .bind(onNext: { [weak postCollectionView] isRefreshing in - if let refreshControl = postCollectionView?.refreshControl { - refreshControl.endRefreshing() - } - }) - .disposed(by: disposeBag) - - cameraButton.rx.tap - .throttle(RxConst.throttleInterval, scheduler: MainScheduler.instance) - .withUnretained(self) - .bind { owner, _ in - let cameraViewController = CameraDIContainer(cameraType: .feed).makeViewController() - owner.navigationController?.pushViewController(cameraViewController, animated: true) - }.disposed(by: disposeBag) - - reactor.state - .map { $0.descriptionText } - .distinctUntilChanged() - .compactMap({$0}) - .observe(on: Schedulers.main) - .bind(to: descriptionLabel.rx.text) - .disposed(by: disposeBag) - - reactor.state - .map { $0.feedSections } - .distinctUntilChanged() - .bind(to: postCollectionView.rx.items(dataSource: createFeedDataSource())) - .disposed(by: disposeBag) - - reactor.state - .map { $0.isShowingNoPostTodayView } - .distinctUntilChanged() - .observe(on: MainScheduler.instance) - .withUnretained(self) - .bind(onNext: { $0.0.setNoPostTodayView($0.1) }) - .disposed(by: disposeBag) - - reactor.state - .map { $0.didPost } - .observe(on: MainScheduler.instance) - .distinctUntilChanged() - .withUnretained(self) - .bind(onNext: { - $0.0.hideCameraButton($0.1) - }) - .disposed(by: disposeBag) + bindInput(reactor: reactor) + bindOutput(reactor: reactor) } public override func setupUI() { @@ -291,6 +167,126 @@ public final class HomeViewController: BaseViewController { } } +extension HomeViewController { + private func bindInput(reactor: HomeViewReactor) { + postCollectionView.rx.setDelegate(self) + .disposed(by: disposeBag) + + Observable.just(()) + .map { Reactor.Action.startTimer } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + rx.viewWillAppear + .map { _ in Reactor.Action.getTodayPostList } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + navigationBarView.rx.didTapLeftBarButton + .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) + .withUnretained(self) + .subscribe { + $0.0.navigationController?.pushViewController( + FamilyManagementDIContainer().makeViewController(), + animated: true + ) + } + .disposed(by: disposeBag) + + navigationBarView.rx.didTapRightBarButton + .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) + .withUnretained(self) + .subscribe { + $0.0.navigationController?.pushViewController( + CalendarDIConatainer().makeViewController(), + animated: true + ) + } + .disposed(by: disposeBag) + + postCollectionView.rx.itemSelected + .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) + .bind(onNext: { [weak self] indexPath in + guard let self else { return } + let sectionModel = reactor.currentState.feedSections[indexPath.section] + let selectedItem = sectionModel.items[indexPath.item] + + self.navigationController?.pushViewController( + PostListsDIContainer().makeViewController( + postLists: sectionModel, + selectedIndex: indexPath + ), + animated: true + ) + }) + .disposed(by: disposeBag) + + refreshControl.rx.controlEvent(.valueChanged) + .map { Reactor.Action.refreshCollectionview } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + cameraButton.rx.tap + .throttle(RxConst.throttleInterval, scheduler: MainScheduler.instance) + .withUnretained(self) + .bind { owner, _ in + let cameraViewController = CameraDIContainer(cameraType: .feed).makeViewController() + owner.navigationController?.pushViewController(cameraViewController, animated: true) + }.disposed(by: disposeBag) + } + + private func bindOutput(reactor: HomeViewReactor) { + reactor.state + .map { $0.isRefreshing } + .observe(on: MainScheduler.instance) + .bind(onNext: { [weak postCollectionView] isRefreshing in + if let refreshControl = postCollectionView?.refreshControl { + refreshControl.endRefreshing() + } + }) + .disposed(by: disposeBag) + + reactor.pulse(\.$timer) + .observe(on: Schedulers.main) + .bind(to: timerLabel.rx.text) + .disposed(by: disposeBag) + + reactor.pulse(\.$timerLabelColor) + .observe(on: Schedulers.main) + .bind(to: timerLabel.rx.textColor) + .disposed(by: disposeBag) + + reactor.pulse(\.$descriptionText) + .compactMap({$0}) + .observe(on: Schedulers.main) + .bind(to: descriptionLabel.rx.text) + .disposed(by: disposeBag) + + reactor.pulse(\.$feedSections) + .distinctUntilChanged() + .bind(to: postCollectionView.rx.items(dataSource: createFeedDataSource())) + .disposed(by: disposeBag) + + reactor.state + .map { $0.isShowingNoPostTodayView } + .distinctUntilChanged() + .observe(on: MainScheduler.instance) + .withUnretained(self) + .bind(onNext: { $0.0.setNoPostTodayView($0.1) }) + .disposed(by: disposeBag) + + reactor.state + .map { $0.didPost } + .observe(on: MainScheduler.instance) + .distinctUntilChanged() + .withUnretained(self) + .bind(onNext: { + $0.0.hideCameraButton($0.1) + }) + .disposed(by: disposeBag) + } +} + extension HomeViewController { private func setNoPostTodayView(_ isShow: Bool) { noPostTodayView.isHidden = !isShow diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/PostReactor.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/PostReactor.swift index 6c0488cb9..759ac6995 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/PostReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/Reactor/PostReactor.swift @@ -32,9 +32,9 @@ final class PostReactor: Reactor { var isPop: Bool = false var selectedPost: PostListData = .init(postId: "", author: .init(memberId: "", profileImageURL: "", name: ""), emojiCount: 0, imageURL: "", content: "", time: "") - var fetchedPost: PostData? = nil - var fetchedEmoji: FetchEmojiDataList = .init(emojis_memberIds: []) - var reactionMemberIds: [String] = [] + + @Pulse var fetchedPost: PostData? = nil + @Pulse var reactionMemberIds: [String] = [] } let initialState: State diff --git a/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift b/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift index 5e6484f30..1e27e174c 100644 --- a/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/PostDetail/ViewControllers/PostViewController.swift @@ -48,8 +48,7 @@ final class PostViewController: BaseViewController { .bind(onNext: { $0.0.setBackgroundView(data: $0.1) }) .disposed(by: disposeBag) - reactor.state - .map { $0.reactionMemberIds } + reactor.pulse(\.$reactionMemberIds) .withUnretained(self) .observe(on: MainScheduler.instance) .subscribe(onNext: { $0.0.showReactionSheet($0.1) }) @@ -161,6 +160,7 @@ extension PostViewController { sheet.detents = [.medium()] sheet.prefersGrabberVisible = true } + present(reactionMembersViewController, animated: true) } diff --git a/14th-team5-iOS/Core/Sources/Extensions/UIViewController+Ext.swift b/14th-team5-iOS/Core/Sources/Extensions/UIViewController+Ext.swift index f01982dfc..e5eb3c00c 100644 --- a/14th-team5-iOS/Core/Sources/Extensions/UIViewController+Ext.swift +++ b/14th-team5-iOS/Core/Sources/Extensions/UIViewController+Ext.swift @@ -291,21 +291,3 @@ extension UIViewController { ) } } - -extension UIViewController { - public func calculateRemainingTime() -> Int { - let calendar = Calendar.current - let currentTime = Date() - - let isAfterNoon = calendar.component(.hour, from: currentTime) >= 12 - - if isAfterNoon { - if let nextMidnight = calendar.date(bySettingHour: 0, minute: 0, second: 0, of: currentTime.addingTimeInterval(24 * 60 * 60)) { - let timeDifference = calendar.dateComponents([.second], from: currentTime, to: nextMidnight) - return max(0, timeDifference.second ?? 0) - } - } - - return 0 - } -} From 298dc1f09fcccaec284562f3b9f32a194827baad Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Mon, 15 Jan 2024 19:42:17 +0900 Subject: [PATCH 3/3] feat: remove comment(#272) --- .../Home/Reactor/HomeViewReactor.swift | 24 +++++++++++-------- .../ViewControllers/HomeViewController.swift | 15 +++++++++--- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index efef9314a..e9738ef24 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -38,11 +38,11 @@ public final class HomeViewReactor: Reactor { var showLoading: Bool = true var didPost: Bool = false var isShowingNoPostTodayView: Bool = false + var isHideCameraButton: Bool = true + var descriptionText: String = "" @Pulse var timerLabelColor: UIColor = .white @Pulse var timer: String = "" - @Pulse var isHideCameraButton: Bool = true - @Pulse var descriptionText: String = HomeStrings.Description.standard @Pulse var feedSections: [SectionModel] = [] } @@ -73,6 +73,7 @@ extension HomeViewReactor { SectionModel(model: "section1", items: postList.postLists)]))] if postList.selfUploaded { + observables.append(Observable.just(Mutation.hideCamerButton(true))) observables.append(Observable.just(Mutation.setDidPost)) } @@ -101,16 +102,19 @@ extension HomeViewReactor { ]) } - // 1시간 전 + var observables = [ + Observable.just(Mutation.setTimer(time)) + ] + if time <= 3600 && !self.currentState.didPost { - return Observable.concat([ - Observable.just(Mutation.setTimer(time)), - Observable.just(Mutation.setTimerColor(.warningRed)), - Observable.just(Mutation.setDescriptionText("시간이 얼마 남지 않았어요!")) - ]) + observables.append(Observable.just(Mutation.hideCamerButton(false))) + observables.append(Observable.just(Mutation.setTimerColor(.warningRed))) + observables.append(Observable.just(Mutation.setDescriptionText("시간이 얼마 남지 않았어요!"))) + } else { + observables.append(Observable.just(Mutation.setDescriptionText(HomeStrings.Description.standard))) } - - return Observable.just(Mutation.setTimer(time)) + + return Observable.concat(observables) } } } diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index 2edd889eb..2332f609d 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -255,9 +255,18 @@ extension HomeViewController { .observe(on: Schedulers.main) .bind(to: timerLabel.rx.textColor) .disposed(by: disposeBag) - - reactor.pulse(\.$descriptionText) - .compactMap({$0}) + + reactor.state + .map { $0.isHideCameraButton } + .distinctUntilChanged() + .observe(on: Schedulers.main) + .withUnretained(self) + .bind(onNext: { $0.0.hideCameraButton($0.1) }) + .disposed(by: disposeBag) + + reactor.state + .map { $0.descriptionText } + .distinctUntilChanged() .observe(on: Schedulers.main) .bind(to: descriptionLabel.rx.text) .disposed(by: disposeBag)