Skip to content

Commit

Permalink
Merge pull request #124 from boostcampwm-2022/feature/FriendView
Browse files Browse the repository at this point in the history
Feature/friend view
  • Loading branch information
jinwoong16 committed Dec 9, 2022
2 parents b720787 + 0fab7cd commit 881f878
Show file tree
Hide file tree
Showing 12 changed files with 478 additions and 18 deletions.
44 changes: 44 additions & 0 deletions DailyQuest/DailyQuest.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
345687FA2937815900CA51E3 /* QuantityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F92937815900CA51E3 /* QuantityView.swift */; };
345687FE29378AB900CA51E3 /* EnrollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */; };
34642AB62925D9E40052FA0E /* UserInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34642AB52925D9E40052FA0E /* UserInfoView.swift */; };
3472E8F129420D9600BB304F /* FriendViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3472E8F029420D9600BB304F /* FriendViewModel.swift */; };
3472E8F52942135D00BB304F /* FriendQuestUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3472E8F42942135D00BB304F /* FriendQuestUseCase.swift */; };
3472E8F7294213BE00BB304F /* DefaultFriendQuestUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3472E8F6294213BE00BB304F /* DefaultFriendQuestUseCase.swift */; };
3472E8F929421C7C00BB304F /* FriendCalendarUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3472E8F829421C7C00BB304F /* FriendCalendarUseCase.swift */; };
3472E8FB29421CB500BB304F /* DefaultFriendCalendarUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3472E8FA29421CB500BB304F /* DefaultFriendCalendarUseCase.swift */; };
347D258B292C60F40038FCA2 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D258A292C60F40038FCA2 /* StatusView.swift */; };
347D258D292C6E220038FCA2 /* MessageBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D258C292C6E220038FCA2 /* MessageBubble.swift */; };
34874AA0292509A4000570DF /* QuestViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34874A9F292509A4000570DF /* QuestViewHeader.swift */; };
Expand Down Expand Up @@ -285,6 +290,11 @@
345687F92937815900CA51E3 /* QuantityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantityView.swift; sourceTree = "<group>"; };
345687FD29378AB900CA51E3 /* EnrollViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollViewModel.swift; sourceTree = "<group>"; };
34642AB52925D9E40052FA0E /* UserInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoView.swift; sourceTree = "<group>"; };
3472E8F029420D9600BB304F /* FriendViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendViewModel.swift; sourceTree = "<group>"; };
3472E8F42942135D00BB304F /* FriendQuestUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendQuestUseCase.swift; sourceTree = "<group>"; };
3472E8F6294213BE00BB304F /* DefaultFriendQuestUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultFriendQuestUseCase.swift; sourceTree = "<group>"; };
3472E8F829421C7C00BB304F /* FriendCalendarUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendCalendarUseCase.swift; sourceTree = "<group>"; };
3472E8FA29421CB500BB304F /* DefaultFriendCalendarUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultFriendCalendarUseCase.swift; sourceTree = "<group>"; };
347D258A292C60F40038FCA2 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
347D258C292C6E220038FCA2 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = "<group>"; };
34874A9F292509A4000570DF /* QuestViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestViewHeader.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -634,6 +644,7 @@
3449AD5E292219D600B87619 /* Common */ = {
isa = PBXGroup;
children = (
3472E8EF29420D8D00BB304F /* ViewModel */,
34FCD367293DEEBC00E0DC8A /* ViewController */,
3499551C292332DE007AB99E /* Cells */,
B50078D529222F3F0070AFC4 /* CircleCheckView.swift */,
Expand All @@ -654,6 +665,33 @@
path = View;
sourceTree = "<group>";
};
3472E8EF29420D8D00BB304F /* ViewModel */ = {
isa = PBXGroup;
children = (
3472E8F029420D9600BB304F /* FriendViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
3472E8F22942133B00BB304F /* Common */ = {
isa = PBXGroup;
children = (
3472E8F32942134000BB304F /* Protocols */,
3472E8FA29421CB500BB304F /* DefaultFriendCalendarUseCase.swift */,
3472E8F6294213BE00BB304F /* DefaultFriendQuestUseCase.swift */,
);
path = Common;
sourceTree = "<group>";
};
3472E8F32942134000BB304F /* Protocols */ = {
isa = PBXGroup;
children = (
3472E8F42942135D00BB304F /* FriendQuestUseCase.swift */,
3472E8F829421C7C00BB304F /* FriendCalendarUseCase.swift */,
);
path = Protocols;
sourceTree = "<group>";
};
34995510292321FD007AB99E /* Utils */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -684,6 +722,7 @@
34995516292329F4007AB99E /* UseCases */ = {
isa = PBXGroup;
children = (
3472E8F22942133B00BB304F /* Common */,
3417B1402935DA1B00900454 /* Settings */,
3417B13F2935DA1500900454 /* Browse */,
3416FC85292B549900B504C5 /* Home */,
Expand Down Expand Up @@ -1291,6 +1330,7 @@
buildActionMask = 2147483647;
files = (
34404D94293EF9850007E661 /* SettingsUseCase.swift in Sources */,
3472E8F929421C7C00BB304F /* FriendCalendarUseCase.swift in Sources */,
A5A8C8362936EC7E00988C88 /* SubQuestEntity.swift in Sources */,
34EC71D1292DF27E004813CB /* Date+.swift in Sources */,
34D8360129359C8A001DE9DF /* BrowseRepository.swift in Sources */,
Expand Down Expand Up @@ -1332,6 +1372,7 @@
A50F9A3429266F45005C00FE /* NetworkService.swift in Sources */,
A51F01DA292345990031ECA2 /* BrowseQuest.swift in Sources */,
A5AC96E629223F06003B7637 /* QuestsStorage.swift in Sources */,
3472E8F129420D9600BB304F /* FriendViewModel.swift in Sources */,
A51F01D52923407E0031ECA2 /* BrowseQuestEntity.swift in Sources */,
3416FC8A292B560800B504C5 /* DefaultQuestUseCase.swift in Sources */,
9BD8CCF32935BC0D00E6EA2F /* DefaultBrowseRepository.swift in Sources */,
Expand Down Expand Up @@ -1377,6 +1418,7 @@
A51189C329226E66008A9D33 /* QuestEntity+Mapping.swift in Sources */,
A51F01D3292340360031ECA2 /* BrowseQuestsStorage.swift in Sources */,
A50F9A3729266F6F005C00FE /* FirebaseService.swift in Sources */,
3472E8F52942135D00BB304F /* FriendQuestUseCase.swift in Sources */,
9B1CFB3F292B585700CCE97A /* QuestDTO+Mapping.swift in Sources */,
34874AA0292509A4000570DF /* QuestViewHeader.swift in Sources */,
342830F4292E19B700AE811B /* PlainCell.swift in Sources */,
Expand All @@ -1401,6 +1443,7 @@
34283101292E2D7A00AE811B /* ToggleItemViewModel.swift in Sources */,
34FCD366293DE62700E0DC8A /* DefaultEnrollUseCase.swift in Sources */,
A5AC96E829223F27003B7637 /* RealmQuestsStorage.swift in Sources */,
3472E8FB29421CB500BB304F /* DefaultFriendCalendarUseCase.swift in Sources */,
A5003AB4293F5FEC00082A9C /* SignUpViewModel.swift in Sources */,
345687F829374D2500CA51E3 /* DayNamePickerView.swift in Sources */,
349955122923220E007AB99E /* SwiftUIPreview.swift in Sources */,
Expand All @@ -1409,6 +1452,7 @@
9BED4DEE2941932200C60631 /* CameraIconView.swift in Sources */,
34ACC32F291DE9C000741371 /* SceneDelegate.swift in Sources */,
34A529D829247A87001BAD34 /* HomeCoordinator.swift in Sources */,
3472E8F7294213BE00BB304F /* DefaultFriendQuestUseCase.swift in Sources */,
34283103292E2D9B00AE811B /* ToggleCell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,50 @@ import UIKit
final class BrowseSceneDIContainer {

lazy var browseQuestsStorage: BrowseQuestsStorage = RealmBrowseQuestsStorage()
lazy var questsStorage: QuestsStorage = RealmQuestsStorage()

// MARK: - Repositories
func makeBrowseRepository() -> BrowseRepository {
return DefaultBrowseRepository(persistentStorage: browseQuestsStorage)
}

func makeQuestsRepository() -> QuestsRepository {
return DefaultQuestsRepository(persistentStorage: questsStorage)
}

// MARK: - Use Cases
func makeBrowseUseCase() -> BrowseUseCase {
return DefaultBrowseUseCase(browseRepository: makeBrowseRepository())
}

func makeFriendQuestUseCase() -> FriendQuestUseCase {
return DefaultFriendUseCase(questsRepository: makeQuestsRepository())
}

func makeFriendCalendarUseCase(with user: User) -> FriendCalendarUseCase {
return DefaultFriendCalendarUseCase(user: user, questsRepository: makeQuestsRepository())
}

// MARK: - View Models
func makeBrowseViewModel() -> BrowseViewModel {
return BrowseViewModel(browseUseCase: makeBrowseUseCase())
}

func makeFriendViewModel(with user: User) -> FriendViewModel {
return FriendViewModel(user: user,
friendQuestUseCase: makeFriendQuestUseCase(),
friendCalendarUseCase: makeFriendCalendarUseCase(with: user))
}

// MARK: - View Controller
func makeBrowseViewController() -> BrowseViewController {
return BrowseViewController.create(with: makeBrowseViewModel())
}

func makeFriendViewController(with user: User) -> FriendViewController {
return FriendViewController.create(with: makeFriendViewModel(with: user))
}

// MARK: - Flow
func makeBrowseCoordinator(navigationController: UINavigationController,
browseSceneDIContainer: BrowseSceneDIContainer) -> BrowseCoordinator {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// DefaultFriendCalendarUseCase.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/12/08.
//

import Foundation

import RxSwift

import RxSwift

final class DefaultFriendCalendarUseCase: FriendCalendarUseCase {
private let user: User
private let questsRepository: QuestsRepository
private let disposeBag = DisposeBag()

let currentMonth = BehaviorSubject<Date?>(value: Date())
let completionOfMonths = BehaviorSubject<[[DailyQuestCompletion]]>(value: [[], [], []])
let selectedDate = BehaviorSubject<Date>(value: Date())

init(user: User, questsRepository: QuestsRepository) {
self.user = user
self.questsRepository = questsRepository
}

func setupMonths() {
let currentMonth = try? currentMonth.value()
let startDayOfCurrentMonth = currentMonth?.startDayOfCurrentMonth

let months = startDayOfCurrentMonth

Observable.just(months)
.flatMap(fetchAMontlyCompletion(_:))
.map { [[], $0, []] }
.bind(to: completionOfMonths)
.disposed(by: disposeBag)
}

func selectDate(_ date: Date) {
selectedDate.onNext(date)
}
}

extension DefaultFriendCalendarUseCase {

private func calculateDailyState(_ quests: [Quest]) -> DailyQuestCompletion.State {
guard !quests.isEmpty else {
return .normal
}

let filteredQuests = quests.filter { !$0.state }

if filteredQuests.isEmpty {
return .done
} else {
return .notDone(filteredQuests.count)
}
}

private func fetchAMontlyCompletion(_ month: Date?) -> Observable<[DailyQuestCompletion]> {
guard let month = month else { return .empty() }

return Observable.from(month.rangeDaysOfMonth)
.concatMap { [weak self] date -> Observable<DailyQuestCompletion> in
guard let self else { return Observable.empty() }

return self.questsRepository
.fetch(by: self.user.uuid, date: date)
.map { quests -> DailyQuestCompletion in
let isSelected = (try? self.selectedDate.value().startOfDay == date) ?? false

return DailyQuestCompletion(
day: date,
state: self.calculateDailyState(quests),
isSelected: isSelected
)
}
}
.toArray()
.map { states in
let firstWeekStates = month.rangeFromStartWeekdayOfLastMonthToEndDayOfCurrentMonth
.map { date -> DailyQuestCompletion in
return DailyQuestCompletion(day: date, state: .hidden, isSelected: false)
}

return firstWeekStates + states
}
.asObservable()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// DefaultFriendQuestUseCase.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/12/08.
//

import Foundation

import RxSwift

final class DefaultFriendUseCase {
private let questsRepository: QuestsRepository

init(questsRepository: QuestsRepository) {
self.questsRepository = questsRepository
}
}

extension DefaultFriendUseCase: FriendQuestUseCase {
func fetch(with uuid: String, by date: Date) -> Observable<[Quest]> {
return questsRepository.fetch(by: uuid, date: date)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// FriendCalendarUseCase.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/12/08.
//

import Foundation

import RxSwift

protocol FriendCalendarUseCase {
var currentMonth: BehaviorSubject<Date?> { get }
var completionOfMonths: BehaviorSubject<[[DailyQuestCompletion]]> { get }
var selectedDate: BehaviorSubject<Date> { get }

func setupMonths()
func selectDate(_ date: Date)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// FriendQuestUseCase.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/12/08.
//

import Foundation

import RxSwift

protocol FriendQuestUseCase {
func fetch(with uuid: String, by date: Date) -> Observable<[Quest]>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@

import UIKit

import RxSwift

protocol BrowseCoordinator: Coordinator {
func showDetailFlow()
func showFriendFlow(with user: User)
}

final class DefaultBrowseCoordinator: BrowseCoordinator {
private var disposableBag = DisposeBag()

var finishDelegate: CoordinatorFinishDelegate?
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
Expand All @@ -26,10 +30,16 @@ final class DefaultBrowseCoordinator: BrowseCoordinator {
func start() {
let browseViewController = browseSceneDIContainer.makeBrowseViewController()
navigationController.pushViewController(browseViewController, animated: false)

browseViewController
.coordinatorPublisher
.subscribe(onNext: showFriendFlow(with:))
.disposed(by: disposableBag)
}

func showDetailFlow() {

func showFriendFlow(with user: User) {
let friendViewController = browseSceneDIContainer.makeFriendViewController(with: user)
navigationController.present(friendViewController, animated: true)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import RxSwift
import RxCocoa

final class BrowseViewController: UITableViewController {

var coordinatorPublisher = PublishSubject<User>()

private var viewModel: BrowseViewModel!
private var disposableBag = DisposeBag()

Expand Down Expand Up @@ -51,7 +54,13 @@ final class BrowseViewController: UITableViewController {
cell.setup(with: item)
}
.disposed(by: disposableBag)


tableView
.rx
.modelSelected(BrowseItemViewModel.self)
.map { $0.user }
.bind(to: coordinatorPublisher)
.disposed(by: disposableBag)
}
}

Expand Down
Loading

0 comments on commit 881f878

Please sign in to comment.