From cc3faaf4521659c2c89b2ba45884be1a3b5582a5 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 02:49:44 +0900 Subject: [PATCH 1/6] feat: refactor family view (#263) --- .../Sources/Application/SceneDelegate.swift | 4 + .../Home/Dependency/HomeDIContainer.swift | 2 +- .../Dependency/HomeFamilyDIContainer.swift | 38 ++++ .../Home/Reactor/HomeFamilyViewReactor.swift | 117 ++++++++++ .../Home/Reactor/HomeViewReactor.swift | 105 ++------- .../HomeFamilyViewController.swift | 178 +++++++++++++++ .../ViewControllers/HomeViewController.swift | 214 ++++-------------- Tuist/master.key | 8 + fastlane/.env.rtf | 20 ++ 9 files changed, 430 insertions(+), 256 deletions(-) create mode 100644 14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift create mode 100644 14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift create mode 100644 14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift create mode 100644 fastlane/.env.rtf diff --git a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift index 7b12ac61c..e2f1ce846 100644 --- a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift +++ b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift @@ -29,6 +29,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL, let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { + + print("여기얌 \(App.Repository.token.accessToken.value?.accessToken)") + App.Repository.token.accessToken.accept(nil) + window = UIWindow(windowScene: scene) window?.rootViewController = UINavigationController(rootViewController: SplashDIContainer().makeViewController()) window?.makeKeyAndVisible() diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift index 8f0b02404..62d960384 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift @@ -44,7 +44,7 @@ public final class HomeDIContainer { } public func makeReactor() -> Reactor { - return HomeViewReactor(familyRepository: makeFamilyUseCase(), postRepository: makePostUseCase(), familyUseCase: makeInviteFamilyUseCase()) + return HomeViewReactor(postRepository: makePostUseCase()) } } diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift new file mode 100644 index 000000000..367c6727f --- /dev/null +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift @@ -0,0 +1,38 @@ +// +// HomeFamilyDIContainer.swift +// App +// +// Created by 마경미 on 14.01.24. +// + +import Foundation + +import Domain +import Data + +final class HomeFamilyDIContainer { + func makeViewController() -> HomeFamilyViewController { + return HomeFamilyViewController(reactor: makeReactor()) + } + + public func makeFamilyRepository() -> SearchFamilyRepository { + return FamilyAPIs.Worker() + } + + public func makeInviteFamilyRepository() -> FamilyRepositoryProtocol { + return FamilyRepository() + } + + func makeFamilyUseCase() -> SearchFamilyMemberUseCaseProtocol { + return SearchFamilyUseCase(searchFamilyRepository: makeFamilyRepository()) + } + + func makeInviteFamilyUseCase() -> InviteFamilyViewUseCaseProtocol { + return InviteFamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) + } + + public func makeReactor() -> HomeFamilyViewReactor { + return HomeFamilyViewReactor(searchFamilyUseCase: makeFamilyUseCase(), inviteFamilyUseCase: makeInviteFamilyUseCase()) + } + +} diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift new file mode 100644 index 000000000..07bf7e8f5 --- /dev/null +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift @@ -0,0 +1,117 @@ +// +// HomeFamilyViewReactor.swift +// App +// +// Created by 마경미 on 13.01.24. +// + +import Foundation + +import Core +import Domain + +import ReactorKit +import RxDataSources + +public final class HomeFamilyViewReactor: Reactor { + public enum Action { + case getFamilyMembers + case tapInviteFamily + } + + public enum Mutation { + case setLoading(Bool) + case showShareAcitivityView(URL?) + case showInviteFamilyView + case setFamilyCollectionView([SectionModel]) + case setCopySuccessToastMessageView + case setFetchFailureToastMessageView + case setSharePanel(String) + } + + public struct State { + @Pulse var familyInvitationLink: URL? + var showLoading: Bool = true + var isShowingInviteFamilyView: Bool = false + var familySections: [SectionModel] = [] + @Pulse var shouldPresentCopySuccessToastMessageView: Bool = false + @Pulse var shouldPresentFetchFailureToastMessageView: Bool = false + } + + public let initialState: State = State() + public let provider: GlobalStateProviderProtocol = GlobalStateProvider() + private let searchFamilyUseCase: SearchFamilyMemberUseCaseProtocol + private let inviteFamilyUseCase: InviteFamilyViewUseCaseProtocol + + init(searchFamilyUseCase: SearchFamilyMemberUseCaseProtocol, inviteFamilyUseCase: InviteFamilyViewUseCaseProtocol) { + self.inviteFamilyUseCase = inviteFamilyUseCase + self.searchFamilyUseCase = searchFamilyUseCase + } +} + +extension HomeFamilyViewReactor { + public func transform(mutation: Observable) -> Observable { + let eventMutation = provider.activityGlobalState.event + .flatMap { event -> Observable in + switch event { + case .didTapCopyInvitationUrlAction: + return Observable.just(.setCopySuccessToastMessageView) + } + } + + return Observable.merge(mutation, eventMutation) + } + + public func mutate(action: Action) -> Observable { + switch action { + case .tapInviteFamily: + return inviteFamilyUseCase.executeFetchInvitationUrl() + .map { + guard let invitationLink = $0?.url else { + return .setFetchFailureToastMessageView + } + return .setSharePanel(invitationLink) + } + case .getFamilyMembers: + let query: SearchFamilyQuery = SearchFamilyQuery(type: "FAMILY", page: 1, size: 20) + return searchFamilyUseCase.excute(query: query) + .asObservable() + .flatMap { familyMembers in + guard let familyMembers else { + return Observable.just(Mutation.showInviteFamilyView) + } + + if familyMembers.members.count == 1 { + return Observable.just(Mutation.showInviteFamilyView) + } else { + return Observable.just(.setFamilyCollectionView([ + SectionModel(model: "section1", items: familyMembers.members) + ])) + } + } + } + } + + public func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .showInviteFamilyView: + newState.isShowingInviteFamilyView = true + case let .setFamilyCollectionView(data): + newState.familySections = data + case let .showShareAcitivityView(url): + newState.familyInvitationLink = url + case .setLoading: + newState.showLoading = false + case .setCopySuccessToastMessageView: + newState.shouldPresentCopySuccessToastMessageView = true + case .setFetchFailureToastMessageView: + newState.shouldPresentFetchFailureToastMessageView = true + case let .setSharePanel(urlString): + newState.familyInvitationLink = URL(string: urlString) + } + + return newState + } +} diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index 64e0ff5c7..88cea7675 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -14,109 +14,41 @@ import RxDataSources public final class HomeViewReactor: Reactor { public enum Action { - case viewDidLoad - case getFamilyMembers case getTodayPostList - case tapInviteFamily + case refreshCollectionview } public enum Mutation { - case setFamilyResponse(FamilyResponse?) case setLoading(Bool) case setDidPost case setDescriptionText(String) - case showShareAcitivityView(URL?) - case showInviteFamilyView case showNoPostTodayView - case setFamilyCollectionView([SectionModel]) case setPostCollectionView([SectionModel]) - case setCopySuccessToastMessageView - case setFetchFailureToastMessageView - case setSharePanel(String) + case setRefreshing(Bool) } public struct State { - var familyResponse: FamilyResponse? - @Pulse var familyInvitationLink: URL? + var isRefreshing: Bool = false var showLoading: Bool = true var didPost: Bool = false var descriptionText: String = HomeStrings.Description.standard var isShowingNoPostTodayView: Bool = false - var isShowingInviteFamilyView: Bool = false - var familySections: [SectionModel] = [] var feedSections: [SectionModel] = [] - @Pulse var shouldPresentCopySuccessToastMessageView: Bool = false - @Pulse var shouldPresentFetchFailureToastMessageView: Bool = false } public let initialState: State = State() - public let provider: GlobalStateProviderProtocol = GlobalStateProvider() - private let familyRepository: SearchFamilyMemberUseCaseProtocol private let postRepository: PostListUseCaseProtocol - private let familyUseCase: FamilyViewUseCaseProtocol - init(familyRepository: SearchFamilyMemberUseCaseProtocol, postRepository: PostListUseCaseProtocol, familyUseCase: FamilyViewUseCaseProtocol) { - self.familyUseCase = familyUseCase - self.familyRepository = familyRepository + init(postRepository: PostListUseCaseProtocol) { self.postRepository = postRepository } } extension HomeViewReactor { - public func transform(mutation: Observable) -> Observable { - let eventMutation = provider.activityGlobalState.event - .flatMap { event -> Observable in - switch event { - case .didTapCopyInvitationUrlAction: - return Observable.just(.setCopySuccessToastMessageView) - } - } - - return Observable.merge(mutation, eventMutation) - } - public func mutate(action: Action) -> Observable { switch action { - case .viewDidLoad: -// guard App.Repository.member.familyId.value == nil else { -// return .empty() -// } -// -// return familyUseCase.executeCreateFamily() -// .map { -// guard let familyResponse: FamilyResponse = $0 else { -// return .setFamilyResponse(nil) -// } -// return .setFamilyResponse(familyResponse) -// } - return Observable.empty() - - case .tapInviteFamily: - return familyUseCase.executeFetchInvitationUrl() - .map { - guard let invitationLink = $0?.url else { - return .setFetchFailureToastMessageView - } - return .setSharePanel(invitationLink) - } - case .getFamilyMembers: - let query: SearchFamilyQuery = SearchFamilyQuery(type: "FAMILY", page: 1, size: 20) - return familyRepository.excute(query: query) - .asObservable() - .flatMap { familyMembers in - guard let familyMembers else { - return Observable.just(Mutation.showInviteFamilyView) - } - if familyMembers.members.count == 1 { - return Observable.just(Mutation.showInviteFamilyView) - } else { - return Observable.just(.setFamilyCollectionView([ - SectionModel(model: "section1", items: familyMembers.members) - ])) - } - } case .getTodayPostList: - let query: PostListQuery = PostListQuery(page: 1, size: 20, date: Date().toFormatString(with: "YYYY-MM-DD"), memberId: "", sort: .desc) + let query: PostListQuery = PostListQuery(page: 1, size: 20, date: "2024-01-13", memberId: "", sort: .desc) return postRepository.excute(query: query) .asObservable() .flatMap { postList in @@ -141,6 +73,13 @@ extension HomeViewReactor { return Observable.concat(observables) } + case .refreshCollectionview: +// Observable.just(Mutation.setRefreshing(true)) + let getTodayPostListAction = Action.getTodayPostList + return mutate(action: getTodayPostListAction) + .flatMap { _ in + return Observable.just(Mutation.setRefreshing(false)) + } } } @@ -148,34 +87,18 @@ extension HomeViewReactor { var newState = state switch mutation { - // case .setTimerStatus: - // newState.descriptionText = HomeStringLiterals.Description.standard - case .showInviteFamilyView: - newState.isShowingInviteFamilyView = true - case let .setFamilyCollectionView(data): - newState.familySections = data case .showNoPostTodayView: newState.isShowingNoPostTodayView = true case let .setPostCollectionView(data): newState.feedSections = data - case let .showShareAcitivityView(url): - newState.familyInvitationLink = url case .setDidPost: newState.didPost = true case .setDescriptionText(_): break case .setLoading: newState.showLoading = false - case .setCopySuccessToastMessageView: - newState.shouldPresentCopySuccessToastMessageView = true - case .setFetchFailureToastMessageView: - newState.shouldPresentFetchFailureToastMessageView = true - case let .setSharePanel(urlString): - newState.familyInvitationLink = URL(string: urlString) - case let .setFamilyResponse(familyResponse): - App.Repository.member.familyId.accept(familyResponse?.familyId) - - newState.familyResponse = familyResponse + case let .setRefreshing(isRefreshing): + newState.isRefreshing = isRefreshing } return newState diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift new file mode 100644 index 000000000..ab3ccd7d6 --- /dev/null +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift @@ -0,0 +1,178 @@ +// +// HomeFamilyViewController.swift +// App +// +// Created by 마경미 on 13.01.24. +// + +import UIKit + +import Core +import Domain + +import RxDataSources + +final class HomeFamilyViewController: BaseViewController { + private let inviteFamilyView: UIView = InviteFamilyView() + private let familyCollectionViewLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + private let familyCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + + override func viewDidLoad() { + super.viewDidLoad() + } + + deinit { + print("deinit HomeViewController") + } + + override func bind(reactor: HomeFamilyViewReactor) { + bindInput(reactor: reactor) + bindOutput(reactor: reactor) + } + + override func setupUI() { + view.addSubviews(familyCollectionView) + } + + override func setupAutoLayout() { + familyCollectionView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.horizontalEdges.equalToSuperview() + $0.height.equalTo(HomeAutoLayout.FamilyCollectionView.height) + } + } + + override func setupAttributes() { + familyCollectionViewLayout.do { + $0.scrollDirection = .horizontal + $0.sectionInset = UIEdgeInsets( + top: HomeAutoLayout.FamilyCollectionView.edgeInsetTop, + left: HomeAutoLayout.FamilyCollectionView.edgeInsetLeft, + bottom: HomeAutoLayout.FamilyCollectionView.edgeInsetBottom, + right: HomeAutoLayout.FamilyCollectionView.edgeInsetRight) + $0.minimumLineSpacing = HomeAutoLayout.FamilyCollectionView.minimumLineSpacing + } + + familyCollectionView.do { + $0.showsVerticalScrollIndicator = false + $0.showsHorizontalScrollIndicator = false + $0.register(FamilyCollectionViewCell.self, forCellWithReuseIdentifier: FamilyCollectionViewCell.id) + $0.backgroundColor = .clear + $0.collectionViewLayout = familyCollectionViewLayout + } + } +} + +extension HomeFamilyViewController { + private func bindInput(reactor: HomeFamilyViewReactor) { + familyCollectionView.rx.setDelegate(self) + .disposed(by: disposeBag) + + rx.viewWillAppear + .map { _ in Reactor.Action.getFamilyMembers } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + inviteFamilyView.rx.tap + .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) + .map { Reactor.Action.tapInviteFamily } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + familyCollectionView + .rx.modelSelected(ProfileData.self) + .map { $0.memberId } + .withUnretained(self) + .bind { owner, memberId in + let profileViewController = ProfileDIContainer(memberId: memberId).makeViewController() + self.navigationController?.pushViewController(profileViewController, animated: true) + }.disposed(by: disposeBag) + } + + private func bindOutput(reactor: HomeFamilyViewReactor) { + reactor.state + .map { $0.familySections } + .asObservable() + .bind(to: familyCollectionView.rx.items(dataSource: createFamilyDataSource())) + .disposed(by: disposeBag) + + reactor.state + .map { $0.isShowingInviteFamilyView } + .distinctUntilChanged() + .observe(on: Schedulers.main) + .withUnretained(self) + .bind(onNext: { $0.0.setFamilyInviteView($0.1) }) + .disposed(by: disposeBag) + + reactor.pulse(\.$familyInvitationLink) + .observe(on: Schedulers.main) + .withUnretained(self) + .bind(onNext: { + $0.0.makeInvitationUrlSharePanel( + $0.1, + provider: reactor.provider + ) + }) + .disposed(by: disposeBag) + + + reactor.pulse(\.$shouldPresentCopySuccessToastMessageView) + .skip(1) + .withUnretained(self) + .subscribe { + $0.0.makeBibbiToastView( + text: "링크가 복사되었어요", + symbol: "link", + width: 210 + ) + } + .disposed(by: disposeBag) + + reactor.pulse(\.$shouldPresentFetchFailureToastMessageView) + .skip(1) + .withUnretained(self) + .subscribe { + $0.0.makeBibbiToastView( + text: "잠시 후에 다시 시도해주세요", + symbol: "exclamationmark.triangle.fill", + palletteColors: [UIColor.systemYellow], + width: 230 + ) + } + .disposed(by: disposeBag) + } +} + +extension HomeFamilyViewController { + private func createFamilyDataSource() -> RxCollectionViewSectionedReloadDataSource> { + return RxCollectionViewSectionedReloadDataSource>( + configureCell: { (_, collectionView, indexPath, item) in + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FamilyCollectionViewCell.id, for: indexPath) as? FamilyCollectionViewCell else { + return UICollectionViewCell() + } + cell.setCell(data: item) + return cell + }) + } + + private func setFamilyInviteView(_ isShow: Bool) { + if isShow { + familyCollectionView.isHidden = isShow + view.addSubview(inviteFamilyView) + + inviteFamilyView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.horizontalEdges.equalToSuperview().inset(HomeAutoLayout.InviteFamilyView.horizontalInset) + $0.height.equalTo(HomeAutoLayout.InviteFamilyView.height) + } + } else { + inviteFamilyView.removeFromSuperview() + } + } +} + +extension HomeFamilyViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: HomeAutoLayout.FamilyCollectionView.cellWidth, height: HomeAutoLayout.FamilyCollectionView.cellHeight) + } +} diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index 13403ef13..49a7eebb7 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -19,8 +19,7 @@ import Domain public final class HomeViewController: BaseViewController { private let navigationBarView: BibbiNavigationBarView = BibbiNavigationBarView() - private let familyCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - private let inviteFamilyView: InviteFamilyView = InviteFamilyView() + private let familyViewController: HomeFamilyViewController = HomeFamilyDIContainer().makeViewController() private let dividerView: UIView = UIView() private let timerLabel: BibbiLabel = BibbiLabel(.head1, alignment: .center) private let descriptionLabel: UILabel = BibbiLabel(.body2Regular, alignment: .center, textColor: .gray300) @@ -29,9 +28,12 @@ public final class HomeViewController: BaseViewController { private let balloonView: BalloonView = BalloonView() private let cameraButton: UIButton = UIButton() + private let refreshControl: UIRefreshControl = UIRefreshControl() + public override func viewDidLoad() { super.viewDidLoad() + hideCameraButton(true) } public override func viewWillAppear(_ animated: Bool) { @@ -45,52 +47,38 @@ public final class HomeViewController: BaseViewController { } public override func bind(reactor: HomeViewReactor) { - familyCollectionView.rx.setDelegate(self) - .disposed(by: disposeBag) - postCollectionView.rx.setDelegate(self) .disposed(by: disposeBag) -// Observable.just(()) -// .take(1) -// .map { Reactor.Action.viewDidLoad } -// .bind(to: reactor.action) -// .disposed(by: disposeBag) - rx.viewWillAppear .map { _ in Reactor.Action.getTodayPostList } .bind(to: reactor.action) .disposed(by: disposeBag) - Observable.just(()) - .map { Reactor.Action.getFamilyMembers } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - Observable.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) +// Observable.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) @@ -114,15 +102,6 @@ public final class HomeViewController: BaseViewController { } .disposed(by: disposeBag) - familyCollectionView - .rx.modelSelected(ProfileData.self) - .map { $0.memberId } - .withUnretained(self) - .bind { owner, memberId in - let profileViewController = ProfileDIContainer(memberId: memberId).makeViewController() - self.navigationController?.pushViewController(profileViewController, animated: true) - }.disposed(by: disposeBag) - postCollectionView.rx.itemSelected .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) .bind(onNext: { [weak self] indexPath in @@ -140,6 +119,17 @@ public final class HomeViewController: BaseViewController { }) .disposed(by: disposeBag) + refreshControl.rx.controlEvent(.valueChanged) + .map { Reactor.Action.refreshCollectionview } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + reactor.state + .map { $0.isRefreshing } + .distinctUntilChanged() + .bind(to: refreshControl.rx.isRefreshing) + .disposed(by: disposeBag) + cameraButton.rx.tap .throttle(RxConst.throttleInterval, scheduler: MainScheduler.instance) .withUnretained(self) @@ -148,12 +138,6 @@ public final class HomeViewController: BaseViewController { owner.navigationController?.pushViewController(cameraViewController, animated: true) }.disposed(by: disposeBag) - inviteFamilyView.rx.tap - .throttle(RxConst.throttleInterval, scheduler: Schedulers.main) - .map { Reactor.Action.tapInviteFamily } - .bind(to: reactor.action) - .disposed(by: disposeBag) - reactor.state .map { $0.descriptionText } .distinctUntilChanged() @@ -162,26 +146,12 @@ public final class HomeViewController: BaseViewController { .bind(to: descriptionLabel.rx.text) .disposed(by: disposeBag) - reactor.state - .map { $0.familySections } - .asObservable() - .bind(to: familyCollectionView.rx.items(dataSource: createFamilyDataSource())) - .disposed(by: disposeBag) - reactor.state .map { $0.feedSections } .distinctUntilChanged() .bind(to: postCollectionView.rx.items(dataSource: createFeedDataSource())) .disposed(by: disposeBag) - reactor.state - .map { $0.isShowingInviteFamilyView } - .distinctUntilChanged() - .observe(on: Schedulers.main) - .withUnretained(self) - .bind(onNext: { $0.0.setFamilyInviteView($0.1) }) - .disposed(by: disposeBag) - reactor.state .map { $0.isShowingNoPostTodayView } .distinctUntilChanged() @@ -190,17 +160,6 @@ public final class HomeViewController: BaseViewController { .bind(onNext: { $0.0.setNoPostTodayView($0.1) }) .disposed(by: disposeBag) - reactor.pulse(\.$familyInvitationLink) - .observe(on: Schedulers.main) - .withUnretained(self) - .bind(onNext: { - $0.0.makeInvitationUrlSharePanel( - $0.1, - provider: reactor.provider - ) - }) - .disposed(by: disposeBag) - reactor.state .map { $0.didPost } .observe(on: MainScheduler.instance) @@ -210,38 +169,17 @@ public final class HomeViewController: BaseViewController { $0.0.hideCameraButton($0.1) }) .disposed(by: disposeBag) - - reactor.pulse(\.$shouldPresentCopySuccessToastMessageView) - .skip(1) - .withUnretained(self) - .subscribe { - $0.0.makeBibbiToastView( - text: "링크가 복사되었어요", - symbol: "link", - width: 210 - ) - } - .disposed(by: disposeBag) - - reactor.pulse(\.$shouldPresentFetchFailureToastMessageView) - .skip(1) - .withUnretained(self) - .subscribe { - $0.0.makeBibbiToastView( - text: "잠시 후에 다시 시도해주세요", - symbol: "exclamationmark.triangle.fill", - palletteColors: [UIColor.systemYellow], - width: 230 - ) - } - .disposed(by: disposeBag) } public override func setupUI() { super.setupUI() - view.addSubviews(navigationBarView, familyCollectionView, dividerView, timerLabel, descriptionLabel, - postCollectionView, noPostTodayView, balloonView, cameraButton) + addChild(familyViewController) + view.addSubviews(navigationBarView, dividerView, timerLabel, + descriptionLabel, postCollectionView, noPostTodayView, + balloonView, cameraButton, familyViewController.view) + + familyViewController.didMove(toParent: self) } public override func setupAutoLayout() { @@ -253,10 +191,10 @@ public final class HomeViewController: BaseViewController { $0.horizontalEdges.equalToSuperview() } - familyCollectionView.snp.makeConstraints { - $0.top.equalTo(navigationBarView.snp.bottom).offset(HomeAutoLayout.FamilyCollectionView.topInset) + familyViewController.view.snp.makeConstraints { + $0.top.equalTo(navigationBarView.snp.bottom).offset(24) $0.horizontalEdges.equalToSuperview() - $0.height.equalTo(HomeAutoLayout.FamilyCollectionView.height) + $0.height.equalTo(90) } dividerView.snp.makeConstraints { @@ -306,8 +244,7 @@ public final class HomeViewController: BaseViewController { public override func setupAttributes() { super.setupAttributes() - - let familyCollectionViewLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + let feedCollectionViewLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() navigationBarView.do { @@ -323,28 +260,6 @@ public final class HomeViewController: BaseViewController { $0.rightBarButtonItemYOffset = -10.0 } - familyCollectionViewLayout.do { - $0.scrollDirection = .horizontal - $0.sectionInset = UIEdgeInsets( - top: HomeAutoLayout.FamilyCollectionView.edgeInsetTop, - left: HomeAutoLayout.FamilyCollectionView.edgeInsetLeft, - bottom: HomeAutoLayout.FamilyCollectionView.edgeInsetBottom, - right: HomeAutoLayout.FamilyCollectionView.edgeInsetRight) - $0.minimumLineSpacing = HomeAutoLayout.FamilyCollectionView.minimumLineSpacing - } - - familyCollectionView.do { - $0.showsVerticalScrollIndicator = false - $0.showsHorizontalScrollIndicator = false - $0.register(FamilyCollectionViewCell.self, forCellWithReuseIdentifier: FamilyCollectionViewCell.id) - $0.backgroundColor = .clear - $0.collectionViewLayout = familyCollectionViewLayout - } - - inviteFamilyView.do { - $0.setLabel(caption: "이런, 아직 아무도 없군요!", title: "가족 초대하기") - } - dividerView.do { $0.backgroundColor = .gray900 } @@ -356,6 +271,7 @@ public final class HomeViewController: BaseViewController { } postCollectionView.do { + $0.refreshControl = refreshControl $0.register(FeedCollectionViewCell.self, forCellWithReuseIdentifier: FeedCollectionViewCell.id) $0.backgroundColor = .clear } @@ -375,21 +291,6 @@ public final class HomeViewController: BaseViewController { } extension HomeViewController { - private func setFamilyInviteView(_ isShow: Bool) { - if isShow { - familyCollectionView.isHidden = isShow - view.addSubview(inviteFamilyView) - - inviteFamilyView.snp.makeConstraints { - $0.top.equalTo(navigationBarView.snp.bottom).offset(HomeAutoLayout.InviteFamilyView.topInset) - $0.horizontalEdges.equalToSuperview().inset(HomeAutoLayout.InviteFamilyView.horizontalInset) - $0.height.equalTo(HomeAutoLayout.InviteFamilyView.height) - } - } else { - inviteFamilyView.removeFromSuperview() - } - } - private func setNoPostTodayView(_ isShow: Bool) { if isShow { noPostTodayView.isHidden = !isShow @@ -404,17 +305,6 @@ extension HomeViewController { cameraButton.isHidden = isShow } - private func createFamilyDataSource() -> RxCollectionViewSectionedReloadDataSource> { - return RxCollectionViewSectionedReloadDataSource>( - configureCell: { (_, collectionView, indexPath, item) in - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FamilyCollectionViewCell.id, for: indexPath) as? FamilyCollectionViewCell else { - return UICollectionViewCell() - } - cell.setCell(data: item) - return cell - }) - } - private func createFeedDataSource() -> RxCollectionViewSectionedReloadDataSource> { return RxCollectionViewSectionedReloadDataSource>( configureCell: { (_, collectionView, indexPath, item) in @@ -429,11 +319,7 @@ extension HomeViewController { extension HomeViewController: UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - if collectionView == familyCollectionView { - return CGSize(width: HomeAutoLayout.FamilyCollectionView.cellWidth, height: HomeAutoLayout.FamilyCollectionView.cellHeight) - } else { let width = (collectionView.frame.size.width - 10) / 2 return CGSize(width: width, height: width + 36) - } } } diff --git a/Tuist/master.key b/Tuist/master.key index e69de29bb..41aa9c6b6 100644 --- a/Tuist/master.key +++ b/Tuist/master.key @@ -0,0 +1,8 @@ +{\rtf1\ansi\ansicpg949\cocoartf2757 +\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} +{\colortbl;\red255\green255\blue255;\red0\green0\blue0;} +{\*\expandedcolortbl;;\csgray\c0;} +\paperw11900\paperh16840\margl1440\margr1440\vieww23040\viewh17400\viewkind0 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 + +\f0\fs28 \cf2 \CocoaLigature0 jW4XBNfSvzeHt3DjHI4ddss+0mppy7vB6wh5OLnneDY=} \ No newline at end of file diff --git a/fastlane/.env.rtf b/fastlane/.env.rtf new file mode 100644 index 000000000..652001af5 --- /dev/null +++ b/fastlane/.env.rtf @@ -0,0 +1,20 @@ +APP_NAME=Bibbi +WIDGET_NAME=WidgetExtension +PRD_SCHEME=App-PRD +DEV_SCHEME=App +BUNDLE_ID=com.5ing.bibbi +WIDGET_BUNDLE_ID=com.5ing.bibbi.widget + +APPLE_ID=55ing.team@gmail.com +APPLE_PASSWORD=1g2ZdeMJw7FVvS + +APP_STORE_CONNECT_API_KEY_KEY_ID=V74YKCCUMW +APP_STORE_CONNECT_API_KEY_ISSUER_ID=0337f6ec-46b1-488f-8868-ca7e96dea2b0 +APP_STORE_CONNECT_API_KEY_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR1RBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJIa3dkd0lCQVFRZ1ZMaXZBOVRLYWxQV0owK2sKbE1saDJiQURJN0ovUmFhVnhVNW5LLzhGc1AyZ0NnWUlLb1pJemowREFRZWhSQU5DQUFSRGtlR0JOcENhOEowRwpSZlVuOXpuRHJnT1FyN2g0Z2RIUjI1ZWt5RnlhYVZ6dHpwTXZRYklMMlFHZVptVnFhbndzZDQvbjl0aVRBTk9LClh4b1gyTERiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + +KEYCHAIN_NAME=BibbiKeychain +KEYCHAIN_PASSWORD=1g2ZdeMJw7FVvS + +SLACK_HOOK_URL=https://hooks.slack.com/services/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 + +PROJECT_PATH=./14th-team5-iOS/App/App.xcodeproj \ No newline at end of file From 55eb6b7276d5f27f471327adaec4d57cdc07cd1b Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 03:46:56 +0900 Subject: [PATCH 2/6] feat: add post refreshing (#263) --- .../App/Sources/Application/SceneDelegate.swift | 3 --- .../Home/Dependency/HomeDIContainer.swift | 2 +- .../Home/Reactor/HomeViewReactor.swift | 8 +++----- .../Home/ViewControllers/HomeViewController.swift | 14 +++++++++----- .../InviteFamily/UseCases/FamilyViewUseCase.swift | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift index e2f1ce846..a9588dbf7 100644 --- a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift +++ b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift @@ -29,9 +29,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL, let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { - - print("여기얌 \(App.Repository.token.accessToken.value?.accessToken)") - App.Repository.token.accessToken.accept(nil) window = UIWindow(windowScene: scene) window?.rootViewController = UINavigationController(rootViewController: SplashDIContainer().makeViewController()) diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift index 62d960384..b0febd803 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift @@ -39,7 +39,7 @@ public final class HomeDIContainer { return SearchFamilyUseCase(searchFamilyRepository: makeFamilyRepository()) } - func makeInviteFamilyUseCase() -> FamilyViewUseCaseProtocol { + func makeInviteFamilyUseCase() -> InviteFamilyViewUseCaseProtocol { return InviteFamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) } diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index 88cea7675..8e5ce1ad8 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -71,15 +71,13 @@ extension HomeViewReactor { observables.append(Observable.just(Mutation.setDescriptionText("우리 가족 모두가 사진을 올린 날🎉"))) } + observables.append(Observable.just(Mutation.setRefreshing(false))) return Observable.concat(observables) } case .refreshCollectionview: // Observable.just(Mutation.setRefreshing(true)) let getTodayPostListAction = Action.getTodayPostList return mutate(action: getTodayPostListAction) - .flatMap { _ in - return Observable.just(Mutation.setRefreshing(false)) - } } } @@ -93,8 +91,8 @@ extension HomeViewReactor { newState.feedSections = data case .setDidPost: newState.didPost = true - case .setDescriptionText(_): - break + case let .setDescriptionText(message): + newState.descriptionText = message case .setLoading: newState.showLoading = false case let .setRefreshing(isRefreshing): diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index 49a7eebb7..c2ff1c76a 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -33,7 +33,7 @@ public final class HomeViewController: BaseViewController { public override func viewDidLoad() { super.viewDidLoad() - hideCameraButton(true) + hideCameraButton(false) } public override func viewWillAppear(_ animated: Bool) { @@ -125,10 +125,14 @@ public final class HomeViewController: BaseViewController { .disposed(by: disposeBag) reactor.state - .map { $0.isRefreshing } - .distinctUntilChanged() - .bind(to: refreshControl.rx.isRefreshing) - .disposed(by: disposeBag) + .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) diff --git a/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift b/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift index bad7b8b71..e9779df65 100644 --- a/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift +++ b/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift @@ -9,13 +9,13 @@ import Foundation import RxSwift -public protocol FamilyViewUseCaseProtocol { +public protocol InviteFamilyViewUseCaseProtocol { func executeCreateFamily() -> Observable func executeFetchInvitationUrl() -> Observable func executeFetchFamilyMembers() -> Observable } -public final class InviteFamilyViewUseCase: FamilyViewUseCaseProtocol { +public final class InviteFamilyViewUseCase: InviteFamilyViewUseCaseProtocol { private let familyRepository: FamilyRepositoryProtocol public init(familyRepository: FamilyRepositoryProtocol) { From f1798c610fbbc64cac88d4ecc1816cfe6e8432d4 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 04:24:50 +0900 Subject: [PATCH 3/6] feat: add refresh at home family (#263) --- .../Sources/Application/SceneDelegate.swift | 1 - .../Home/Reactor/HomeFamilyViewReactor.swift | 26 ++++++---- .../Home/Reactor/HomeViewReactor.swift | 3 +- .../HomeFamilyViewController.swift | 21 ++++++-- .../ViewControllers/HomeViewController.swift | 51 +++++++++---------- 5 files changed, 60 insertions(+), 42 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift index a9588dbf7..7b12ac61c 100644 --- a/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift +++ b/14th-team5-iOS/App/Sources/Application/SceneDelegate.swift @@ -29,7 +29,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL, let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { - window = UIWindow(windowScene: scene) window?.rootViewController = UINavigationController(rootViewController: SplashDIContainer().makeViewController()) window?.makeKeyAndVisible() diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift index 07bf7e8f5..19ffde6f5 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift @@ -17,6 +17,7 @@ public final class HomeFamilyViewReactor: Reactor { public enum Action { case getFamilyMembers case tapInviteFamily + case refreshCollectionview } public enum Mutation { @@ -27,13 +28,15 @@ public final class HomeFamilyViewReactor: Reactor { case setCopySuccessToastMessageView case setFetchFailureToastMessageView case setSharePanel(String) + case setRefreshing(Bool) } public struct State { - @Pulse var familyInvitationLink: URL? + var isRefreshing: Bool = false var showLoading: Bool = true var isShowingInviteFamilyView: Bool = false var familySections: [SectionModel] = [] + @Pulse var familyInvitationLink: URL? @Pulse var shouldPresentCopySuccessToastMessageView: Bool = false @Pulse var shouldPresentFetchFailureToastMessageView: Bool = false } @@ -77,18 +80,21 @@ extension HomeFamilyViewReactor { return searchFamilyUseCase.excute(query: query) .asObservable() .flatMap { familyMembers in - guard let familyMembers else { + guard let familyMembers, + familyMembers.members.count > 1 else { return Observable.just(Mutation.showInviteFamilyView) } - if familyMembers.members.count == 1 { - return Observable.just(Mutation.showInviteFamilyView) - } else { - return Observable.just(.setFamilyCollectionView([ - SectionModel(model: "section1", items: familyMembers.members) - ])) - } + var observables = [Observable.just(Mutation.setFamilyCollectionView([ + SectionModel(model: "section1", items: familyMembers.members)]))] + + + observables.append(Observable.just(Mutation.setRefreshing(false))) + return Observable.concat(observables) } + case .refreshCollectionview: + let getFamilyMembersAction = Action.getFamilyMembers + return mutate(action: getFamilyMembersAction) } } @@ -110,6 +116,8 @@ extension HomeFamilyViewReactor { newState.shouldPresentFetchFailureToastMessageView = true case let .setSharePanel(urlString): newState.familyInvitationLink = URL(string: urlString) + case let .setRefreshing(isRefreshing): + newState.isRefreshing = isRefreshing } return newState diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index 8e5ce1ad8..5dc4cfe14 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -48,7 +48,7 @@ extension HomeViewReactor { public func mutate(action: Action) -> Observable { switch action { case .getTodayPostList: - let query: PostListQuery = PostListQuery(page: 1, size: 20, date: "2024-01-13", memberId: "", sort: .desc) + let query: PostListQuery = PostListQuery(page: 1, size: 20, date: Date().toFormatString(with: "YYYY-MM-DD"), memberId: "", sort: .desc) return postRepository.excute(query: query) .asObservable() .flatMap { postList in @@ -75,7 +75,6 @@ extension HomeViewReactor { return Observable.concat(observables) } case .refreshCollectionview: -// Observable.just(Mutation.setRefreshing(true)) let getTodayPostListAction = Action.getTodayPostList return mutate(action: getTodayPostListAction) } diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift index ab3ccd7d6..099069206 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeFamilyViewController.swift @@ -16,6 +16,7 @@ final class HomeFamilyViewController: BaseViewController private let inviteFamilyView: UIView = InviteFamilyView() private let familyCollectionViewLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() private let familyCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + private let refreshControl: UIRefreshControl = UIRefreshControl() override func viewDidLoad() { super.viewDidLoad() @@ -36,9 +37,7 @@ final class HomeFamilyViewController: BaseViewController override func setupAutoLayout() { familyCollectionView.snp.makeConstraints { - $0.top.equalToSuperview() - $0.horizontalEdges.equalToSuperview() - $0.height.equalTo(HomeAutoLayout.FamilyCollectionView.height) + $0.edges.equalToSuperview() } } @@ -54,6 +53,7 @@ final class HomeFamilyViewController: BaseViewController } familyCollectionView.do { + $0.refreshControl = refreshControl $0.showsVerticalScrollIndicator = false $0.showsHorizontalScrollIndicator = false $0.register(FamilyCollectionViewCell.self, forCellWithReuseIdentifier: FamilyCollectionViewCell.id) @@ -87,6 +87,11 @@ extension HomeFamilyViewController { let profileViewController = ProfileDIContainer(memberId: memberId).makeViewController() self.navigationController?.pushViewController(profileViewController, animated: true) }.disposed(by: disposeBag) + + refreshControl.rx.controlEvent(.valueChanged) + .map { Reactor.Action.refreshCollectionview } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindOutput(reactor: HomeFamilyViewReactor) { @@ -140,6 +145,16 @@ extension HomeFamilyViewController { ) } .disposed(by: disposeBag) + + reactor.state + .map { $0.isRefreshing } + .observe(on: Schedulers.main) + .bind(onNext: { [weak familyCollectionView] isRefreshing in + if let refreshControl = familyCollectionView?.refreshControl { + refreshControl.endRefreshing() + } + }) + .disposed(by: disposeBag) } } diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift index c2ff1c76a..3b35f13a1 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/ViewControllers/HomeViewController.swift @@ -27,13 +27,10 @@ public final class HomeViewController: BaseViewController { private let postCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) private let balloonView: BalloonView = BalloonView() private let cameraButton: UIButton = UIButton() - private let refreshControl: UIRefreshControl = UIRefreshControl() public override func viewDidLoad() { super.viewDidLoad() - - hideCameraButton(false) } public override func viewWillAppear(_ animated: Bool) { @@ -55,30 +52,30 @@ public final class HomeViewController: BaseViewController { .bind(to: reactor.action) .disposed(by: disposeBag) -// Observable.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) + Observable.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) From f0a77a72055cfacc1bcf553dc78db077445f6516 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 17:37:31 +0900 Subject: [PATCH 4/6] feat: rebase and rename (#263) --- .../Dependency/FamilyManagementDIContainer.swift | 2 +- .../Presentation/Home/Dependency/HomeDIContainer.swift | 4 ++-- .../Presentation/Home/Dependency/HomeFamilyDIContainer.swift | 4 ++-- .../Presentation/Home/Reactor/HomeFamilyViewReactor.swift | 4 ++-- .../JoinFamily/Dependency/JoinFamilyDIContainer.swift | 2 +- .../Family/InviteFamily/UseCases/FamilyViewUseCase.swift | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Presentation/FamilyManagement/Dependency/FamilyManagementDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/FamilyManagement/Dependency/FamilyManagementDIContainer.swift index f4a81a547..06167f620 100644 --- a/14th-team5-iOS/App/Sources/Presentation/FamilyManagement/Dependency/FamilyManagementDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/FamilyManagement/Dependency/FamilyManagementDIContainer.swift @@ -29,7 +29,7 @@ public final class FamilyManagementDIContainer: BaseDIContainer { } public func makeUsecase() -> UseCase { - return InviteFamilyViewUseCase(familyRepository: makeRepository()) + return FamilyViewUseCase(familyRepository: makeRepository()) } public func makeRepository() -> FamilyRepositoryProtocol { diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift index b0febd803..8dbfa632f 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeDIContainer.swift @@ -39,8 +39,8 @@ public final class HomeDIContainer { return SearchFamilyUseCase(searchFamilyRepository: makeFamilyRepository()) } - func makeInviteFamilyUseCase() -> InviteFamilyViewUseCaseProtocol { - return InviteFamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) + func makeInviteFamilyUseCase() -> FamilyViewUseCaseProtocol { + return FamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) } public func makeReactor() -> Reactor { diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift index 367c6727f..106cb87ca 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Dependency/HomeFamilyDIContainer.swift @@ -27,8 +27,8 @@ final class HomeFamilyDIContainer { return SearchFamilyUseCase(searchFamilyRepository: makeFamilyRepository()) } - func makeInviteFamilyUseCase() -> InviteFamilyViewUseCaseProtocol { - return InviteFamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) + func makeInviteFamilyUseCase() -> FamilyViewUseCaseProtocol { + return FamilyViewUseCase(familyRepository: makeInviteFamilyRepository()) } public func makeReactor() -> HomeFamilyViewReactor { diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift index 19ffde6f5..3ec1431e2 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeFamilyViewReactor.swift @@ -44,9 +44,9 @@ public final class HomeFamilyViewReactor: Reactor { public let initialState: State = State() public let provider: GlobalStateProviderProtocol = GlobalStateProvider() private let searchFamilyUseCase: SearchFamilyMemberUseCaseProtocol - private let inviteFamilyUseCase: InviteFamilyViewUseCaseProtocol + private let inviteFamilyUseCase: FamilyViewUseCaseProtocol - init(searchFamilyUseCase: SearchFamilyMemberUseCaseProtocol, inviteFamilyUseCase: InviteFamilyViewUseCaseProtocol) { + init(searchFamilyUseCase: SearchFamilyMemberUseCaseProtocol, inviteFamilyUseCase: FamilyViewUseCaseProtocol) { self.inviteFamilyUseCase = inviteFamilyUseCase self.searchFamilyUseCase = searchFamilyUseCase } diff --git a/14th-team5-iOS/App/Sources/Presentation/JoinFamily/Dependency/JoinFamilyDIContainer.swift b/14th-team5-iOS/App/Sources/Presentation/JoinFamily/Dependency/JoinFamilyDIContainer.swift index 5d1f08bfa..f9272ebb0 100644 --- a/14th-team5-iOS/App/Sources/Presentation/JoinFamily/Dependency/JoinFamilyDIContainer.swift +++ b/14th-team5-iOS/App/Sources/Presentation/JoinFamily/Dependency/JoinFamilyDIContainer.swift @@ -17,7 +17,7 @@ final class JoinFamilyDIContainer { } public func makeUsecase() -> FamilyViewUseCaseProtocol { - return InviteFamilyViewUseCase(familyRepository: makeRepository()) + return FamilyViewUseCase(familyRepository: makeRepository()) } public func makeRepository() -> FamilyRepositoryProtocol { diff --git a/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift b/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift index e9779df65..d0fbaf99a 100644 --- a/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift +++ b/14th-team5-iOS/Domain/Sources/Family/InviteFamily/UseCases/FamilyViewUseCase.swift @@ -9,13 +9,13 @@ import Foundation import RxSwift -public protocol InviteFamilyViewUseCaseProtocol { +public protocol FamilyViewUseCaseProtocol { func executeCreateFamily() -> Observable func executeFetchInvitationUrl() -> Observable func executeFetchFamilyMembers() -> Observable } -public final class InviteFamilyViewUseCase: InviteFamilyViewUseCaseProtocol { +public final class FamilyViewUseCase: FamilyViewUseCaseProtocol { private let familyRepository: FamilyRepositoryProtocol public init(familyRepository: FamilyRepositoryProtocol) { From f2067d9332a4f526eb6d312dc1ba610f9df50723 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 17:51:28 +0900 Subject: [PATCH 5/6] feat: fix the selfuploaded(#263) --- .../Presentation/Home/Reactor/HomeViewReactor.swift | 9 +++------ .../Data/Sources/Post/PostList/DTO/PostListDTO.swift | 2 +- .../Post/PostList/PostListAPI/PostListAPIWorker.swift | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift index 5dc4cfe14..ed0db8af7 100644 --- a/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift +++ b/14th-team5-iOS/App/Sources/Presentation/Home/Reactor/HomeViewReactor.swift @@ -52,17 +52,14 @@ extension HomeViewReactor { return postRepository.excute(query: query) .asObservable() .flatMap { postList in - guard let postList else { - return Observable.just(Mutation.showNoPostTodayView) - } - - if postList.postLists.isEmpty { + guard let postList, + !postList.postLists.isEmpty else { return Observable.just(Mutation.showNoPostTodayView) } var observables = [Observable.just(Mutation.setPostCollectionView([ SectionModel(model: "section1", items: postList.postLists)]))] - + if postList.selfUploaded { observables.append(Observable.just(Mutation.setDidPost)) } diff --git a/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift b/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift index 7b4c54311..caec5e3d8 100644 --- a/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift +++ b/14th-team5-iOS/Data/Sources/Post/PostList/DTO/PostListDTO.swift @@ -43,6 +43,6 @@ struct PostListResponseDTO: Codable { extension PostListResponseDTO { func toDomain(_ selfUploaded: Bool, _ allFamilyMembersUploaded: Bool) -> PostListPage { - return .init(currentPage: currentPage, totalPages: totalPage, postLists: results.map { $0.toDomain() }, allFamilyMembersUploaded: selfUploaded, selfUploaded: allFamilyMembersUploaded) + return .init(currentPage: currentPage, totalPages: totalPage, postLists: results.map { $0.toDomain() }, allFamilyMembersUploaded: allFamilyMembersUploaded, selfUploaded: selfUploaded) } } diff --git a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift index 20fa7ff3c..645096ba7 100644 --- a/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift +++ b/14th-team5-iOS/Data/Sources/Post/PostList/PostListAPI/PostListAPIWorker.swift @@ -83,7 +83,7 @@ extension PostListAPIWorker: PostListRepositoryProtocol { .map { let selfUploaded = $0?.results.map { $0.authorId == FamilyUserDefaults.getMyMemberId() }.isEmpty let familyUploaded = $0?.results.count == FamilyUserDefaults.getMemberCount() - return $0?.toDomain(selfUploaded ?? false, familyUploaded) + return $0?.toDomain(!(selfUploaded ?? true), familyUploaded) } .asSingle() } From 4571361d83973dcf3eefb4e60c020c49ac57e887 Mon Sep 17 00:00:00 2001 From: Kyoungmi <1mmgm.dev@gmail.com> Date: Sun, 14 Jan 2024 19:47:22 +0900 Subject: [PATCH 6/6] feat: remove keys --- Tuist/master.key | 8 -------- fastlane/.env.rtf | 20 -------------------- 2 files changed, 28 deletions(-) delete mode 100644 Tuist/master.key delete mode 100644 fastlane/.env.rtf diff --git a/Tuist/master.key b/Tuist/master.key deleted file mode 100644 index 41aa9c6b6..000000000 --- a/Tuist/master.key +++ /dev/null @@ -1,8 +0,0 @@ -{\rtf1\ansi\ansicpg949\cocoartf2757 -\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} -{\colortbl;\red255\green255\blue255;\red0\green0\blue0;} -{\*\expandedcolortbl;;\csgray\c0;} -\paperw11900\paperh16840\margl1440\margr1440\vieww23040\viewh17400\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 - -\f0\fs28 \cf2 \CocoaLigature0 jW4XBNfSvzeHt3DjHI4ddss+0mppy7vB6wh5OLnneDY=} \ No newline at end of file diff --git a/fastlane/.env.rtf b/fastlane/.env.rtf deleted file mode 100644 index 652001af5..000000000 --- a/fastlane/.env.rtf +++ /dev/null @@ -1,20 +0,0 @@ -APP_NAME=Bibbi -WIDGET_NAME=WidgetExtension -PRD_SCHEME=App-PRD -DEV_SCHEME=App -BUNDLE_ID=com.5ing.bibbi -WIDGET_BUNDLE_ID=com.5ing.bibbi.widget - -APPLE_ID=55ing.team@gmail.com -APPLE_PASSWORD=1g2ZdeMJw7FVvS - -APP_STORE_CONNECT_API_KEY_KEY_ID=V74YKCCUMW -APP_STORE_CONNECT_API_KEY_ISSUER_ID=0337f6ec-46b1-488f-8868-ca7e96dea2b0 -APP_STORE_CONNECT_API_KEY_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR1RBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJIa3dkd0lCQVFRZ1ZMaXZBOVRLYWxQV0owK2sKbE1saDJiQURJN0ovUmFhVnhVNW5LLzhGc1AyZ0NnWUlLb1pJemowREFRZWhSQU5DQUFSRGtlR0JOcENhOEowRwpSZlVuOXpuRHJnT1FyN2g0Z2RIUjI1ZWt5RnlhYVZ6dHpwTXZRYklMMlFHZVptVnFhbndzZDQvbjl0aVRBTk9LClh4b1gyTERiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= - -KEYCHAIN_NAME=BibbiKeychain -KEYCHAIN_PASSWORD=1g2ZdeMJw7FVvS - -SLACK_HOOK_URL=https://hooks.slack.com/services/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 - -PROJECT_PATH=./14th-team5-iOS/App/App.xcodeproj \ No newline at end of file