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

[Feat] #54 - 랭킹 리스트 뷰 비즈니스 로직 구현 #55

Merged
merged 6 commits into from
Dec 27, 2022
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 @@ -23,5 +23,9 @@ public class RankingRepository {
}

extension RankingRepository: RankingRepositoryInterface {

public func fetchRankingListModel() -> AnyPublisher<[Domain.RankingModel], Error> {
return Future<[RankingModel], Error> { promise in
Copy link
Member

Choose a reason for hiding this comment

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

오우 거대한 더미 데이터.. 만드느라 수고하셨습니다..!

promise(.success([RankingModel.init(username: "1등이다", usreId: 1, score: 94, sentence: "제가 1등입니다"), RankingModel.init(username: "헬로", usreId: 2, score: 92, sentence: "2등"), RankingModel.init(username: "이름이 긴 사람", usreId: 3, score: 91, sentence: "제가 3등입니다 말이 길어지면 어케"), RankingModel.init(username: "더미데이름", usreId: 4, score: 86, sentence: "4444"), RankingModel.init(username: "레포지토리", usreId: 5, score: 65, sentence: "5"), RankingModel.init(username: "인터페이스", usreId: 6, score: 53, sentence: "ㅎㄴ마ㄷ한마디"), RankingModel.init(username: "솝트", usreId: 7, score: 53, sentence: "하남ㄴㅁㅇㄹㅁㅋㅌㅊㅍ"), RankingModel.init(username: "노가다", usreId: 8, score: 52, sentence: "한마디 띄우기"), RankingModel.init(username: "9등이다", usreId: 9, score: 40, sentence: "한마디 최고글자길이는몇일까여?"), RankingModel.init(username: "10등이다", usreId: 10, score: 35, sentence: "5166"), RankingModel.init(username: "11등이다", usreId: 11, score: 32, sentence: "더메대충이데머"), RankingModel.init(username: "12등이다", usreId: 12, score: 12, sentence: "대충 더미데잍"), RankingModel.init(username: "안녕하세요", usreId: 13, score: 7, sentence: "531542642"), RankingModel.init(username: "14등이다", usreId: 14, score: 5, sentence: "한마디 귀찮아"), RankingModel.init(username: "15등이다", usreId: 15, score: 3, sentence: "")]))
}.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Network

extension RankingEntity {

public func toDomain() -> RankingModel {
return RankingModel.init()
}
// public func toDomain() -> RankingModel {
// return RankingModel.init()
// }
}
21 changes: 18 additions & 3 deletions SOPT-Stamp-iOS/Projects/Domain/Sources/Model/RankingModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,24 @@

import Foundation

public struct RankingModel {
public struct RankingModel: Hashable {
public let username: String
public let userId: Int
public let score: Int
public let sentence: String

public init(username: String, usreId: Int, score: Int, sentence: String) {
self.username = username
self.userId = usreId
self.score = score
self.sentence = sentence
}
}

public init() {

public struct RankingChartModel: Hashable {
public let ranking: [RankingModel]

public init(ranking: [RankingModel]) {
self.ranking = ranking
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
import Combine

public protocol RankingRepositoryInterface {

func fetchRankingListModel() -> AnyPublisher<[RankingModel], Error>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,32 @@

import Combine

public protocol RankingUseCase {
import Core

public protocol RankingUseCase {
func fetchRankingList()
var rankingListModelFetched: PassthroughSubject<[RankingModel], Error> { get }
}

public class DefaultRankingUseCase {

private let repository: RankingRepositoryInterface
private var cancelBag = Set<AnyCancellable>()

private var cancelBag = CancelBag()
public var rankingListModelFetched = PassthroughSubject<[RankingModel], Error>()

public init(repository: RankingRepositoryInterface) {
self.repository = repository
}
}

extension DefaultRankingUseCase: RankingUseCase {

public func fetchRankingList() {
self.repository.fetchRankingListModel()
.withUnretained(self)
.sink { completion in
print(completion)
} receiveValue: { owner, model in
owner.rankingListModelFetched.send(model)
}.store(in: self.cancelBag)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,16 @@ extension ChartRectangleView {
}

extension ChartRectangleView {
static func ==(left: ChartRectangleView, right: ChartRectangleView) -> Bool {
public func setData(score: Int, username: String) {
self.usernameLabel.text = username
self.scoreLabel.text = "\(score)점"
self.scoreLabel.partFontChange(targetString: "점",
font: DSKitFontFamily.Pretendard.medium.font(size: 12))
}
}

extension ChartRectangleView {
static func == (left: ChartRectangleView, right: ChartRectangleView) -> Bool {
Copy link
Member

Choose a reason for hiding this comment

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

오옹 신기하다

return left.viewLevel == right.viewLevel
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,15 @@ extension SpeechBalloonView {
make.top.equalToSuperview()

let standardSize = calculateLabelSize(sentence: sentence)
if standardSize < 108.adjusted {
make.width.equalTo(108.adjusted + 20)
let smallBalloonMinimumWidth: CGFloat = 108
let smallBalloonCompensates: CGFloat = 10
let smallBalloonWidth = smallBalloonMinimumWidth - smallBalloonCompensates
let largeBalloonCompensates: CGFloat = 30 * standardSize / 266.adjusted

if standardSize < smallBalloonWidth.adjusted {
make.width.equalTo(smallBalloonWidth.adjusted + smallBalloonCompensates)
} else if standardSize < 266.adjusted {
make.width.equalTo(standardSize + 40)
make.width.equalTo(standardSize + largeBalloonCompensates)
} else {
make.width.lessThanOrEqualToSuperview()
sentenceLabel.lineBreakMode = .byTruncatingTail
Expand Down Expand Up @@ -121,9 +126,9 @@ extension SpeechBalloonView {
private func calculateLabelSize(sentence: String) -> CGFloat {
let tempLabel = BalloonPaddingLabel()
tempLabel.text = sentence
tempLabel.sizeToFit()
tempLabel.setTypoStyle(.subtitle3)
return tempLabel.frame.width
tempLabel.sizeToFit()
return tempLabel.intrinsicContentSize.width
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,20 @@ extension RankingChartCVC {
baloonViews.removeAll()
}

public func setData(model: String) {
public func setData(model: RankingChartModel) {

for (index, sentence) in ["안녕하세요", "제가 일등일 수도 있습니다 하하", "그래"].enumerated() {
// 데이터 바인딩을 위한 모델 순서 재정렬
let arrangedModel = [model.ranking[1], model.ranking[0], model.ranking[2]]
let sentences = arrangedModel.map { $0.sentence }

self.setSpeechBalloonViews(sentences: sentences)
self.setChartData(chartRectangleModel: arrangedModel)
}

private func setSpeechBalloonViews(sentences: [String]) {

// 말풍선 text 설정
for (index, sentence) in sentences.enumerated() {
var baloonView: SpeechBalloonView
if index == 0 {
baloonView = SpeechBalloonView.init(level: .rankTwo, sentence: sentence)
Expand All @@ -127,4 +138,18 @@ extension RankingChartCVC {
}
}
}

private func setChartData(chartRectangleModel: [RankingModel]) {
for (index, rectangle) in chartStackView.subviews.enumerated() {
guard let chartRectangle = rectangle as? ChartRectangleView else { return }
chartRectangle.setData(score: chartRectangleModel[index].score,
username: chartRectangleModel[index].username)
}
}
}

extension RankingChartCVC: RankingListTappble {
func getModelItem() -> RankingListTapItem? {
return RankingListTapItem.init(username: "유저", sentence: "한마디", userId: 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ final class RankingListCVC: UICollectionViewCell, UICollectionViewRegisterable {

static var isFromNib: Bool = false

private var model: RankingModel?

// MARK: - UI Components

private let rankLabel: UILabel = {
Expand Down Expand Up @@ -116,7 +118,22 @@ extension RankingListCVC {

extension RankingListCVC {

public func setData(model: String) {

public func setData(model: RankingModel, rank: Int) {
self.model = model
rankLabel.text = String(rank)
usernameLabel.text = model.username
sentenceLabel.text = model.sentence
scoreLabel.text = "\(model.score)점"
scoreLabel.partFontChange(targetString: "점",
font: DSKitFontFamily.Pretendard.medium.font(size: 12))
}
}

extension RankingListCVC: RankingListTappble {
func getModelItem() -> RankingListTapItem? {
guard let model = model else { return nil }
return RankingListTapItem.init(username: model.username,
sentence: model.sentence,
userId: model.userId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// RankingListTappable.swift
// Presentation
//
// Created by Junho Lee on 2022/12/22.
// Copyright © 2022 SOPT-Stamp-iOS. All rights reserved.
//

import Foundation

protocol RankingListTappble {
func getModelItem() -> RankingListTapItem?
}

public struct RankingListTapItem {
public let username: String
public let sentence: String
public let userId: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import UIKit

import Core
import Domain
import DSKit

import Combine
Expand Down Expand Up @@ -37,7 +38,6 @@ public class RankingVC: UIViewController {
cv.showsVerticalScrollIndicator = true
cv.backgroundColor = .white
cv.refreshControl = refresher
refresher.addTarget(self, action: #selector(fetchData(_:)), for: .valueChanged)
return cv
}()

Expand All @@ -56,7 +56,6 @@ public class RankingVC: UIViewController {
self.registerCells()
self.bindViewModels()
self.setDataSource()
self.applySnapshot()
}
}

Expand Down Expand Up @@ -89,8 +88,23 @@ extension RankingVC {
extension RankingVC {

private func bindViewModels() {
let input = RankingViewModel.Input()
let refreshStarted = refresher.publisher(for: .valueChanged)
.map { _ in () }
.eraseToAnyPublisher()
.asDriver()

let input = RankingViewModel.Input(viewDidLoad: Driver.just(()),
refreshStarted: refreshStarted)

let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag)

output.$rankingListModel
.dropFirst()
.withUnretained(self)
.sink { owner, model in
owner.applySnapshot(model: model)
owner.endRefresh()
}.store(in: self.cancelBag)
}

private func setDelegate() {
Expand All @@ -106,51 +120,50 @@ extension RankingVC {
dataSource = UICollectionViewDiffableDataSource(collectionView: rankingCollectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
switch RankingSection.type(indexPath.section) {
case .chart:
guard let chartCell = collectionView.dequeueReusableCell(withReuseIdentifier: RankingChartCVC.className, for: indexPath) as? RankingChartCVC else { return UICollectionViewCell() }
chartCell.setData(model: "")
guard let chartCell = collectionView.dequeueReusableCell(withReuseIdentifier: RankingChartCVC.className, for: indexPath) as? RankingChartCVC,
let chartCellModel = itemIdentifier as? RankingChartModel else { return UICollectionViewCell() }
chartCell.setData(model: chartCellModel)

return chartCell

case .list:
guard let rankingListCell = collectionView.dequeueReusableCell(withReuseIdentifier: RankingListCVC.className, for: indexPath) as? RankingListCVC else { return UICollectionViewCell() }
guard let index = itemIdentifier as? Int else { return UICollectionViewCell() }
guard let rankingListCell = collectionView.dequeueReusableCell(withReuseIdentifier: RankingListCVC.className, for: indexPath) as? RankingListCVC,
let rankingListCellModel = itemIdentifier as? RankingModel else { return UICollectionViewCell() }
rankingListCell.setData(model: rankingListCellModel, rank: indexPath.row + 1 + 3)

return rankingListCell
}
})
}

func applySnapshot() {
func applySnapshot(model: [RankingModel]) {
var snapshot = NSDiffableDataSourceSnapshot<RankingSection, AnyHashable>()
snapshot.appendSections([.chart, .list])
snapshot.appendItems([-1], toSection: .chart)
var tempItems: [Int] = []
for i in 0..<50 {
tempItems.append(i)
}
snapshot.appendItems(tempItems, toSection: .list)
guard let chartCellModels = Array(model[0...2]) as? [RankingModel],
let rankingListModel = Array(model[3...model.count-1]) as? [RankingModel] else { return }
let chartCellModel = RankingChartModel.init(ranking: chartCellModels)
snapshot.appendItems([chartCellModel], toSection: .chart)
snapshot.appendItems(rankingListModel, toSection: .list)
dataSource.apply(snapshot, animatingDifferences: false)
self.view.setNeedsLayout()
}

@objc
private func fetchData(_ sender: Any) {
DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
self.refresher.endRefreshing()
}
private func endRefresh() {
self.refresher.endRefreshing()
}
}

extension RankingVC: UICollectionViewDelegate {
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// guard let tappedCell = collectionView.cellForItem(at: indexPath) as? MissionListCVC,
// let model = tappedCell.model,
// let starLevel = StarViewLevel.init(rawValue: model.level)else { return }
// let sceneType = model.toListDetailSceneType()

let otherUserMissionListVC = factory.makeMissionListVC(sceneType: .ranking(userName: "유저",
sentence: "한마디입니다",
userId: 2))
guard let tappedCell = collectionView.cellForItem(at: indexPath) as? RankingListTappble,
let item = tappedCell.getModelItem() else { return }
self.pushToOtherUserMissionListVC(item: item)
}

private func pushToOtherUserMissionListVC(item: RankingListTapItem) {
let otherUserMissionListVC = factory.makeMissionListVC(sceneType: .ranking(userName: item.username,
sentence: item.sentence,
userId: item.userId))
self.navigationController?.pushViewController(otherUserMissionListVC, animated: true)
}
}
Loading