diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift index 4bdeafaa1..e218c8147 100644 --- a/Projects/App/Sources/Application/NeedleGenerated.swift +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -767,6 +767,9 @@ private class NewSongsContentDependency93a05f20fa300c5bbec3Provider: NewSongsCon var fetchNewSongsUseCase: any FetchNewSongsUseCase { return appComponent.fetchNewSongsUseCase } + var fetchChartUpdateTimeUseCase: any FetchChartUpdateTimeUseCase { + return appComponent.fetchChartUpdateTimeUseCase + } var containSongsComponent: ContainSongsComponent { return appComponent.containSongsComponent } @@ -1169,6 +1172,7 @@ extension ProfilePopComponent: Registration { extension NewSongsContentComponent: Registration { public func registerItems() { keyPathToName[\NewSongsContentDependency.fetchNewSongsUseCase] = "fetchNewSongsUseCase-any FetchNewSongsUseCase" + keyPathToName[\NewSongsContentDependency.fetchChartUpdateTimeUseCase] = "fetchChartUpdateTimeUseCase-any FetchChartUpdateTimeUseCase" keyPathToName[\NewSongsContentDependency.containSongsComponent] = "containSongsComponent-ContainSongsComponent" } } diff --git a/Projects/Features/CommonFeature/Sources/Components/NewSongsContentComponent.swift b/Projects/Features/CommonFeature/Sources/Components/NewSongsContentComponent.swift index d73b8aab5..3962d88cd 100644 --- a/Projects/Features/CommonFeature/Sources/Components/NewSongsContentComponent.swift +++ b/Projects/Features/CommonFeature/Sources/Components/NewSongsContentComponent.swift @@ -13,6 +13,7 @@ import DataMappingModule public protocol NewSongsContentDependency: Dependency { var fetchNewSongsUseCase: any FetchNewSongsUseCase { get } + var fetchChartUpdateTimeUseCase: any FetchChartUpdateTimeUseCase { get } var containSongsComponent: ContainSongsComponent { get } } @@ -21,9 +22,10 @@ public final class NewSongsContentComponent: Component CGFloat { - return 80 + return 80 + 22 } public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let base = UIView(frame: CGRect(x: 0, y: 0, width: APP_WIDTH(), height: 88+22)) + let view = PlayButtonGroupView(frame: CGRect(x: 0, y: 0, width: APP_WIDTH(), height: 80)) view.delegate = self - return view + base.addSubview(view) + + let time = ChartUpdateTimeView(frame: CGRect(x: 0, y: view.frame.height, width: APP_WIDTH(), height: 22)) + time.setUpdateTime(updateTime: self.output.updateTime.value) + base.addSubview(time) + + return base } } diff --git a/Projects/Features/CommonFeature/Sources/ViewModels/NewSongsContentViewModel.swift b/Projects/Features/CommonFeature/Sources/ViewModels/NewSongsContentViewModel.swift index c822bdb76..4f71b77ef 100644 --- a/Projects/Features/CommonFeature/Sources/ViewModels/NewSongsContentViewModel.swift +++ b/Projects/Features/CommonFeature/Sources/ViewModels/NewSongsContentViewModel.swift @@ -18,15 +18,18 @@ public final class NewSongsContentViewModel: ViewModelType { public let type: NewSongGroupType private let disposeBag = DisposeBag() private let fetchNewSongsUseCase: FetchNewSongsUseCase - + private let fetchChartUpdateTimeUseCase: FetchChartUpdateTimeUseCase + deinit { DEBUG_LOG("❌ \(Self.self) Deinit") } public init( type: NewSongGroupType, - fetchNewSongsUseCase: FetchNewSongsUseCase + fetchNewSongsUseCase: FetchNewSongsUseCase, + fetchChartUpdateTimeUseCase: FetchChartUpdateTimeUseCase ) { self.type = type self.fetchNewSongsUseCase = fetchNewSongsUseCase + self.fetchChartUpdateTimeUseCase = fetchChartUpdateTimeUseCase } public struct Input { @@ -42,14 +45,26 @@ public final class NewSongsContentViewModel: ViewModelType { var updateTime: BehaviorRelay = BehaviorRelay(value: "") let indexOfSelectedSongs: BehaviorRelay<[Int]> = BehaviorRelay(value: []) let songEntityOfSelectedSongs: BehaviorRelay<[SongEntity]> = BehaviorRelay(value: []) - let groupPlaySongs: PublishSubject<[SongEntity]> = PublishSubject() var canLoadMore: BehaviorRelay = BehaviorRelay(value: true) } public func transform(from input: Input) -> Output { let output = Output() - let refresh = Observable.combineLatest(output.dataSource, input.pageID) { (dataSource, pageID) -> [NewSongsEntity] in + let chartUpdateTime = self.fetchChartUpdateTimeUseCase + .execute(type: .total) + .catchAndReturn("팬치들 미안해요 ㅠㅠ 잠시만 기다려주세요") // 이스터에그 🥰 + .asObservable() + + chartUpdateTime + .take(1) + .bind(to: output.updateTime) + .disposed(by: disposeBag) + + let refresh = Observable.combineLatest( + output.dataSource, + input.pageID + ) { (dataSource, pageID) -> [NewSongsEntity] in return pageID == 1 ? [] : dataSource } @@ -59,8 +74,8 @@ public final class NewSongsContentViewModel: ViewModelType { input.pageID .flatMap { (pageID) -> Single<[NewSongsEntity]> in return fetchNewSongsUseCase - .execute(type: type, page: pageID, limit: 100) - .catchAndReturn([]) + .execute(type: type, page: pageID, limit: 100) + .catchAndReturn([]) } .asObservable() .do(onNext: { (model) in @@ -76,8 +91,13 @@ public final class NewSongsContentViewModel: ViewModelType { .disposed(by: disposeBag) input.refreshPulled - .map { _ in 1 } - .bind(to: input.pageID) + .do(onNext: { _ in + input.pageID.accept(1) + }) + .flatMap{ _ -> Observable in + return chartUpdateTime + } + .bind(to: output.updateTime) .disposed(by: disposeBag) input.songTapped @@ -166,7 +186,9 @@ public final class NewSongsContentViewModel: ViewModelType { return dataSource.shuffled() } } - .bind(to: output.groupPlaySongs) + .subscribe(onNext: { (songs) in + PlayState.shared.loadAndAppendSongsToPlaylist(songs) + }) .disposed(by: disposeBag) return output diff --git a/Projects/Features/CommonFeature/Sources/Views/ChartUpdateTimeView.swift b/Projects/Features/CommonFeature/Sources/Views/ChartUpdateTimeView.swift new file mode 100644 index 000000000..511694c0e --- /dev/null +++ b/Projects/Features/CommonFeature/Sources/Views/ChartUpdateTimeView.swift @@ -0,0 +1,63 @@ +// +// PlayButtonForNewSongsView.swift +// CommonFeature +// +// Created by KTH on 2023/11/16. +// Copyright © 2023 yongbeomkwak. All rights reserved. +// + +import UIKit +import DesignSystem +import RxRelay +import RxSwift +import SnapKit +import Then + +public final class ChartUpdateTimeView: UIView { + private let updateTimeLabel = UILabel().then { + $0.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + $0.textColor = DesignSystemAsset.GrayColor.gray600.color + } + private let updateTimeImageView = UIImageView().then { + $0.image = DesignSystemAsset.Chart.check.image + } + + public override init(frame: CGRect) { + super.init(frame: frame) + self.setupView() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.setupView() + } + + public func setUpdateTime(updateTime: String) { + let attributedString = NSMutableAttributedString(string: updateTime) + attributedString.addAttributes([.font: DesignSystemFontFamily.Pretendard.light.font(size: 12), + .foregroundColor: DesignSystemAsset.GrayColor.gray600.color, + .kern: -0.5], + range: NSRange(location: 0, length: attributedString.string.count)) + updateTimeLabel.attributedText = attributedString + } +} + +extension ChartUpdateTimeView { + private func setupView() { + self.backgroundColor = DesignSystemAsset.GrayColor.gray100.color + + [updateTimeImageView, updateTimeLabel].forEach { self.addSubview($0) } + + updateTimeImageView.snp.makeConstraints { + $0.top.equalTo(1) + $0.width.height.equalTo(16) + $0.leading.equalTo(20) + } + + updateTimeLabel.snp.makeConstraints { + $0.top.equalTo(0) + $0.height.equalTo(18) + $0.leading.equalTo(updateTimeImageView.snp.trailing).offset(2) + } + } +}