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

[Feature] NotificationList: 디테일 액션 챙기기 완료 #342

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
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@ extension PokeNotificationRepository: PokeNotificationRepositoryInterface {
.map { (users: $0.history.map { $0.toDomain() }, page: $0.pageNum) }
.eraseToAnyPublisher()
}

public func poke(userId: Int, message: String) -> AnyPublisher<PokeUserModel, PokeError> {
self.pokeService
.poke(userId: userId, message: message)
.mapErrorToPokeError()
.map { $0.toDomain() }
.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ extension PokeOnboardingRepository: PokeOnboardingRepositoryInterface {

public func getMesseageTemplates(type: PokeMessageType) -> AnyPublisher<PokeMessagesModel, Error> {
self.pokeService
.getPokeMessages(messageType: type.rawValue) // messageType domain화
.map { $0.toDomain() }
.getPokeMessages(messageType: type.rawValue)
.map { $0.messages.map { $0.toDomain() } }
.eraseToAnyPublisher()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

import Combine

public protocol PokeNotificationRepositoryInterface {
public protocol PokeNotificationRepositoryInterface: PokeRepositoryInterface {
func getWhoPokedMeList(page: Int) -> AnyPublisher<(users: [PokeUserModel], page: Int), Error>
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ import Core

public protocol PokeNotificationUsecase {
func getWhoPokedMeList()
func poke(user: PokeUserModel, message: PokeMessageModel)

var pokedMeList: PassthroughSubject<[PokeUserModel], Never> { get }
var pokedResponse: PassthroughSubject<(response: PokeUserModel, isNewlyAddedFriend: Bool), Never> { get }
var errorMessage: PassthroughSubject<String?, Never> { get }
}

public final class DefaultPokeNotificationUsecase {
private let repository: PokeNotificationRepositoryInterface
private let cancelBag = CancelBag()
private var pageIndex: Int = 0
private var reachedToPageLimit = false

// MARK: UsecaseProtocol
public let pokedMeList = PassthroughSubject<[PokeUserModel], Never>()
public let pokedResponse = PassthroughSubject<(response: PokeUserModel, isNewlyAddedFriend: Bool), Never>()
public let errorMessage = PassthroughSubject<String?, Never>()

public init(repository: PokeNotificationRepositoryInterface) {
self.repository = repository
Expand All @@ -31,13 +37,39 @@ public final class DefaultPokeNotificationUsecase {

extension DefaultPokeNotificationUsecase: PokeNotificationUsecase {
public func getWhoPokedMeList() {
guard !self.reachedToPageLimit else { return }

self.repository
.getWhoPokedMeList(page: self.pageIndex)
.sink(
receiveCompletion: { _ in },
receiveValue: { [weak self] userModels, pageIndex in
// TBD: paging endindex를 주세요

self?.pageIndex = pageIndex + 1
self?.pokedMeList.send(userModels)
}).store(in: self.cancelBag)
}

public func poke(user: PokeUserModel, message: PokeMessageModel) {
self.repository
.poke(userId: user.userId, message: message.content)
.catch { [weak self] error in
let message = error.toastMessage
self?.errorMessage.send(message)
return Empty<PokeUserModel, Never>()
}
.sink(
receiveCompletion: { _ in },
receiveValue: { [weak self] userModel in
let verifyIsNewlyAddedFriend = {
guard user.isFirstMeet else { return false }

return user.isFirstMeet && userModel.isFirstMeet == false
}
Comment on lines +65 to +69
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

야무지네요!


self?.pokedResponse.send((response: userModel, isNewlyAddedFriend: verifyIsNewlyAddedFriend()))
}
).store(in: self.cancelBag)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ extension BottomSheetManager {
sheet.largestUndimmedDetentIdentifier = .none
sheet.prefersGrabberVisible = true

view?.present(viewController, animated: true)
view?.present(viewController, animated: true) ?? UIApplication
.getMostTopViewController()?
.present(viewController, animated: true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public protocol PokeNotificationViewControllable: ViewControllable { }

public protocol PokeNotificationCoordinatable {
var onNaviBackTapped: (() -> Void)? { get set }
var onPokeButtonTapped: ((PokeUserModel) -> Driver<(PokeUserModel, PokeMessageModel)>)? { get set }
var onNewFriendAdded: ((_ friendName: String) -> Void)? { get set }
}

public typealias PokeNotificationViewModelType = ViewModelType & PokeNotificationCoordinatable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,6 @@ extension PokeNotificationListContentView {
}

extension PokeNotificationListContentView {
public func configure(with model: NotificationListContentModel) {
self.userId = model.userId
self.profileImageView.setImage(with: model.avatarUrl, relation: model.pokeRelation)
self.nameLabel.text = model.name
self.partInfoLabel.text = model.partInfomation
self.descriptionLabel.attributedText = model.description.applyMDSFont()
self.pokeChipView.configure(with: model.chipInfo)
self.pokeKokButton.isEnabled = !model.isPoked
}

public func configure(with model: PokeUserModel) {
self.user = model
self.userId = model.userId
Expand All @@ -180,13 +170,13 @@ extension PokeNotificationListContentView {
self.setData(with: newUserModel)
}

public func poked() {
// TBD
}

public func signalForPokeButtonClicked() -> Driver<Int?> {
self.pokeKokButton.tap.map { self.userId }.asDriver()
public func signalForPokeButtonClicked() -> Driver<PokeUserModel> {
self.pokeKokButton
.tap
.compactMap { [weak self] _ in self?.user }
.asDriver()
}

}

// NOTE(@승호): MDSFont 적용하고 DSKit으로 옮기고 적용하기.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,32 @@ public final class PokeNotificationListCoordinator: DefaultCoordinator {

extension PokeNotificationListCoordinator {
private func showPokeNotificationListView() {
let viewController = self.factory.makePokeNotificationList()

var viewController = self.factory.makePokeNotificationList()

viewController.vm.onPokeButtonTapped = { [weak self] userModel in
guard let bottomSheet = self?.factory
.makePokeMessageTemplateBottomSheet(messageType: .pokeSomeone)
.vc
.viewController as? PokeMessageTemplateBottomSheet
else { return .empty() }

let bottomSheetManager = BottomSheetManager(configuration: .messageTemplate())
bottomSheetManager.present(toPresent: bottomSheet, on: self?.rootController)

return bottomSheet
.signalForClick()
.map { (userModel, $0) }
.asDriver()
}

viewController.vm.onNewFriendAdded = { [weak self] friendName in
guard let self else { return }

let pokeMakingFriendCompletedVC = self.factory.makePokeMakingFriendCompleted(friendName: friendName).viewController
pokeMakingFriendCompletedVC.modalPresentationStyle = .overFullScreen
viewController.vc.viewController.present(pokeMakingFriendCompletedVC, animated: false)
}

self.rootController = viewController.vc.asNavigationController
self.router.push(viewController.vc)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public final class PokeMessageTemplateBottomSheet: UIViewController, PokeMessage
private let viewModel: PokeMessageTemplateViewModel

// MARK: Combine
private let viewWillAppear = PassthroughSubject<Void, Never>()
private let viewDidLoaded = PassthroughSubject<Void, Never>()
private let messageModelSubject = PassthroughSubject<PokeMessageModel, Error>()
private var cancelBag = CancelBag()

Expand All @@ -63,6 +63,8 @@ public final class PokeMessageTemplateBottomSheet: UIViewController, PokeMessage
self.initializeViews()
self.setupConstraints()
self.bindViewModels()

self.viewDidLoaded.send(())
}

required init?(coder: NSCoder) {
Expand All @@ -72,7 +74,6 @@ public final class PokeMessageTemplateBottomSheet: UIViewController, PokeMessage
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

self.viewWillAppear.send(())
}
}

Expand Down Expand Up @@ -127,7 +128,7 @@ extension PokeMessageTemplateBottomSheet {
// MARK: - ViewModel Methods
extension PokeMessageTemplateBottomSheet {
private func bindViewModels() {
let input = PokeMessageTemplateViewModel.Input(viewWillAppear: self.viewWillAppear.asDriver())
let input = PokeMessageTemplateViewModel.Input(viewDidLoaded: self.viewDidLoaded.asDriver())
let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag)

output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public final class PokeMessageTemplateViewModel: PokeMessageTemplatesViewModelTy
public var messageType: PokeMessageType

public struct Input {
let viewWillAppear: Driver<Void>
let viewDidLoaded: Driver<Void>
}

public struct Output {
Expand All @@ -38,7 +38,7 @@ extension PokeMessageTemplateViewModel {
let output = Output()
self.bindOutput(output: output, cancelBag: cancelBag)

input.viewWillAppear
input.viewDidLoaded
.sink(receiveValue: { [weak self] _ in
guard let self = self else { return }
self.usecase.getPokeMessageTemplates(type: self.messageType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ public final class PokeNotificationViewController: UIViewController, PokeNotific
}

// MARK: - Views
private lazy var navigationBar = OPNavigationBar(self, type: .oneLeftButton)
private lazy var navigationBar = OPNavigationBar(
self,
type: .oneLeftButton,
backgroundColor: DSKitAsset.Colors.gray950.color
)
.addMiddleLabel(title: "찌르기 알림", font: UIFont.MDS.body2)
.setLeftButtonImage(DSKitAsset.Assets.chevronLeft.image.withTintColor(DSKitAsset.Colors.gray30.color))

Expand Down Expand Up @@ -56,6 +60,7 @@ public final class PokeNotificationViewController: UIViewController, PokeNotific
PokeNotificationContentCell.self,
forCellReuseIdentifier: PokeNotificationContentCell.className
)
$0.backgroundColor = DSKitAsset.Colors.gray950.color
$0.estimatedRowHeight = 88.f
}

Expand Down Expand Up @@ -131,7 +136,23 @@ extension PokeNotificationViewController {
}

private func bindViews() {

self.tableView
.publisher(for: \.contentOffset)
.map { [weak self] offset in
guard
!offset.y.isZero,
let tableView = self?.tableView,
tableView.contentOffset.y >= tableView.contentSize.height - tableView.frame.size.height
else { return false }

return true
}
.removeDuplicates()
.sink(receiveValue: { [weak self] reachedToBottom in
guard reachedToBottom else { return }

self?.reachToBottomSubject.send(())
}).store(in: self.cancelBag)
}

private func bindViewModels() {
Expand All @@ -146,7 +167,19 @@ extension PokeNotificationViewController {
output
.pokeToMeHistoryList
.sink(receiveValue: { [weak self] userModels in
self?.userModels = userModels
self?.userModels.append(contentsOf: userModels)
self?.tableView.reloadData()
}).store(in: self.cancelBag)

output
.pokedResult
.asDriver()
.sink(receiveValue: { [weak self] pokedResult in
guard
let pokedUserIndex = self?.userModels.firstIndex(where: { $0.userId == pokedResult.userId })
else { return }

self?.userModels[pokedUserIndex] = pokedResult
self?.tableView.reloadData()
}).store(in: self.cancelBag)
}
Expand All @@ -167,26 +200,13 @@ extension PokeNotificationViewController: UITableViewDataSource {
let model = self.userModels[safe: indexPath.item]
else { return UITableViewCell() }

cell.configure(
with: .init(
userId: model.userId,
avatarUrl: model.profileImage,
pokeRelation: model.pokeRelation,
name: model.name,
partInfomation: model.part,
description: model.message,
chipInfo: model.mutualRelationMessage,
isPoked: model.isAlreadyPoke,
isFirstMeet: model.isFirstMeet
)
)

cell.configure(with: model)

cell.signalForClick()
.sink(receiveValue: { [weak self] _ in
// self?.pokedActionSubject.send(<#T##input: PokeUserModel##PokeUserModel#>)
.sink(receiveValue: { [weak self] userModel in
self?.pokedActionSubject.send(userModel)
}).store(in: cell.cancelBag)

return cell
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import UIKit

import Core
import DSKit
import Domain

public final class PokeNotificationContentCell: UITableViewCell {
private enum Metric {
Expand Down Expand Up @@ -59,11 +60,11 @@ extension PokeNotificationContentCell {
}

extension PokeNotificationContentCell {
public func configure(with model: NotificationListContentModel) {
public func configure(with model: PokeUserModel) {
self.notificationListContentView.configure(with: model)
}

public func signalForClick() -> Driver<Int?> {
public func signalForClick() -> Driver<PokeUserModel> {
self.notificationListContentView.signalForPokeButtonClicked()
}
}
Loading