Skip to content

Commit

Permalink
refactor: timer move to reactor(#272)
Browse files Browse the repository at this point in the history
  • Loading branch information
akrudal committed Jan 15, 2024
1 parent 87222b6 commit 9b840d9
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//

import Foundation
import UIKit

import Core
import Domain

Expand All @@ -16,6 +18,7 @@ public final class HomeViewReactor: Reactor {
public enum Action {
case getTodayPostList
case refreshCollectionview
case startTimer
}

public enum Mutation {
Expand All @@ -25,15 +28,22 @@ public final class HomeViewReactor: Reactor {
case showNoPostTodayView(Bool)
case setPostCollectionView([SectionModel<String, PostListData>])
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<String, PostListData>] = []

@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<String, PostListData>] = []
}

public let initialState: State = State()
Expand All @@ -60,12 +70,12 @@ extension HomeViewReactor {
var observables = [
Observable.just(Mutation.showNoPostTodayView(false)),
Observable.just(Mutation.setPostCollectionView([
SectionModel<String, PostListData>(model: "section1", items: postList.postLists)]))]
SectionModel<String, PostListData>(model: "section1", items: postList.postLists)]))]

if postList.selfUploaded {
observables.append(Observable.just(Mutation.setDidPost))
}

if postList.allFamilyMembersUploaded {
observables.append(Observable.just(Mutation.setDescriptionText("우리 가족 모두가 사진을 올린 날🎉")))
}
Expand All @@ -76,6 +86,32 @@ extension HomeViewReactor {
case .refreshCollectionview:
let getTodayPostListAction = Action.getTodayPostList
return mutate(action: getTodayPostListAction)
case .startTimer:
return Observable<Int>.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))
}
}
}

Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,132 +44,8 @@ public final class HomeViewController: BaseViewController<HomeViewReactor> {
}

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<Int>.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() {
Expand Down Expand Up @@ -291,6 +167,126 @@ public final class HomeViewController: BaseViewController<HomeViewReactor> {
}
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 9b840d9

Please sign in to comment.