Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔀 :: [#97] 공지 Feature Refresh 기능 추가 #104

Merged
merged 3 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions Projects/Feature/NoticeFeature/Sources/Scene/NoticeStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ final class NoticeStore: BaseStore {
var currentUserRole: UserRoleType = .member
var isEditingMode = false
var selectedNotice: Set<Int> = []
var isRefreshing = false
}
enum Action {
case viewDidLoad
case viewWillAppear
case fetchNoticeList
case editButtonDidTap
case noticeDidTap(Int)
}
Expand All @@ -49,6 +50,7 @@ final class NoticeStore: BaseStore {
case insertSelectedNotice(Int)
case removeSelectedNotice(Int)
case removeAllSelectedNotice
case updateIsRefreshing(Bool)
}
}

Expand All @@ -58,8 +60,8 @@ extension NoticeStore {
case .viewDidLoad:
return viewDidLoad()

case .viewWillAppear:
return viewWillAppear()
case .fetchNoticeList:
return fetchNoticeList()

case .editButtonDidTap:
return .merge(
Expand Down Expand Up @@ -97,20 +99,25 @@ extension NoticeStore {

case .removeAllSelectedNotice:
newState.selectedNotice.removeAll()

case let .updateIsRefreshing(isRefreshing):
newState.isRefreshing = isRefreshing
}
return newState
}
}

// MARK: - Action
private extension NoticeStore {
func viewWillAppear() -> SideEffect<Mutation, Never> {
return SideEffect<[NoticeModel], Error>
func fetchNoticeList() -> SideEffect<Mutation, Never> {
let noticeEffect = SideEffect<[NoticeModel], Error>
.tryAsync { [fetchNoticeListUseCase] in
try await fetchNoticeListUseCase()
}
.map(Mutation.updateNoticeList)
.catchToNever()
.eraseToSideEffect()
return self.makeRefreshingSideEffect(noticeEffect)
}

func viewDidLoad() -> SideEffect<Mutation, Never> {
Expand All @@ -132,7 +139,10 @@ private extension NoticeStore {
: Mutation.insertSelectedNotice(noticeID)
return .just(mutation)
}
}

// MARK: - Mutate
private extension NoticeStore {
func noticeListToSections(noticeList: [NoticeModel]) -> [SectiondNoticeTuple] {
return noticeList.reduce(
into: [SectiondNoticeTuple]()
Expand All @@ -150,3 +160,19 @@ private extension NoticeStore {
}
}
}

// MARK: - Reusable
private extension NoticeStore {
func makeRefreshingSideEffect(
_ publisher: SideEffect<Mutation, Never>
) -> SideEffect<Mutation, Never> {
let startLoadingPublisher = SideEffect<Mutation, Never>
.just(Mutation.updateIsRefreshing(true))
let endLoadingPublisher = SideEffect<Mutation, Never>
.just(Mutation.updateIsRefreshing(false))
return startLoadingPublisher
.append(publisher)
.append(endLoadingPublisher)
.eraseToSideEffect()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class NoticeViewController: BaseViewController<NoticeStore> {
.then {
$0.register(cellType: NoticeCell.self)
}
private let noticeRefreshControl = UIRefreshControl()
private lazy var noticeTableAdapter = TableViewAdapter<GenericSectionModel<NoticeModel>>(
tableView: noticeTableView
) { tableView, indexPath, notice in
Expand All @@ -59,6 +60,7 @@ final class NoticeViewController: BaseViewController<NoticeStore> {
headerStackView
noticeTableView
}
noticeTableView.refreshControl = noticeRefreshControl
}

override func setLayout() {
Expand Down Expand Up @@ -86,7 +88,7 @@ final class NoticeViewController: BaseViewController<NoticeStore> {
.store(in: &subscription)

viewWillAppearPublisher
.map { Store.Action.viewWillAppear }
.map { Store.Action.fetchNoticeList }
.sink(receiveValue: store.send(_:))
.store(in: &subscription)

Expand All @@ -101,6 +103,11 @@ final class NoticeViewController: BaseViewController<NoticeStore> {
.map(Store.Action.noticeDidTap)
.sink(receiveValue: store.send(_:))
.store(in: &subscription)

noticeRefreshControl.controlPublisher(for: .valueChanged)
.map { _ in Store.Action.fetchNoticeList }
.sink(receiveValue: store.send(_:))
.store(in: &subscription)
}

override func bindState() {
Expand Down Expand Up @@ -154,6 +161,13 @@ final class NoticeViewController: BaseViewController<NoticeStore> {
self?.transformWriteOrRemoveButton(isEditMode: isEditingMode)
})
.store(in: &subscription)

sharedState
.map(\.isRefreshing)
.sink(with: noticeRefreshControl) { refreshControl, isRefreshing in
isRefreshing ? refreshControl.beginRefreshing() : refreshControl.endRefreshing()
}
.store(in: &subscription)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ final class NoticeFeatureTests: XCTestCase {
]
fetchNoticeListUseCase.fetchNoticeListReturn = expected

sut.state.map(\.noticeList).sink { _ in
sut.state.map(\.noticeList).removeDuplicates().sink { _ in
expectation.fulfill()
}
.store(in: &subscription)

XCTAssertEqual(fetchNoticeListUseCase.fetchNoticeListCallCount, 0)
sut.send(.viewWillAppear)
sut.send(.fetchNoticeList)

wait(for: [expectation], timeout: 1.0)
XCTAssertEqual(expected, sut.currentState.noticeList)
Expand Down
Loading