From d5ab2914ebc9df9bd9ce1622f10a752dc160db5d Mon Sep 17 00:00:00 2001 From: Hamp Date: Wed, 17 Apr 2024 18:21:12 +0900 Subject: [PATCH 01/37] =?UTF-8?q?:recycle:=20::=20playlistHeader=EB=A5=BC?= =?UTF-8?q?=20Model=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Model/PlayListHeader.swift | 8 ++++++++ .../Sources/PlayListDetailViewModel.swift | 13 +++---------- 2 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift diff --git a/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift b/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift new file mode 100644 index 000000000..d18767b69 --- /dev/null +++ b/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift @@ -0,0 +1,8 @@ +import Foundation + +internal struct PlayListHeader { + var title: String + var songCount: String + var image: String + var version: Int +} diff --git a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift index 8d19ab591..554a6ad54 100644 --- a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift +++ b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift @@ -18,13 +18,6 @@ import RxSwift import SongsDomainInterface import Utility -struct PlayListHeaderInfo { - var title: String - var songCount: String - var image: String - var version: Int -} - public final class PlayListDetailViewModel: ViewModelType { var type: PlayListType! var id: String! @@ -49,7 +42,7 @@ public final class PlayListDetailViewModel: ViewModelType { } public struct Output { - let headerInfo: PublishRelay = PublishRelay() + let headerInfo: PublishRelay = PublishRelay() let dataSource: BehaviorRelay<[PlayListDetailSectionModel]> = BehaviorRelay(value: []) let backUpdataSource: BehaviorRelay<[PlayListDetailSectionModel]> = BehaviorRelay(value: []) let indexOfSelectedSongs: BehaviorRelay<[Int]> = BehaviorRelay(value: []) @@ -104,7 +97,7 @@ public final class PlayListDetailViewModel: ViewModelType { .do(onNext: { [weak self] model in guard let self = self else { return } output.headerInfo.accept( - PlayListHeaderInfo( + PlayListHeader( title: model.title, songCount: "\(model.songs.count)곡", image: self.type == .wmRecommend ? @@ -122,7 +115,7 @@ public final class PlayListDetailViewModel: ViewModelType { input.playListNameLoad .skip(1) .withLatestFrom(output.headerInfo) { ($0, $1) } - .map { PlayListHeaderInfo(title: $0.0, songCount: $0.1.songCount, image: $0.1.image, version: $0.1.version) + .map { PlayListHeader(title: $0.0, songCount: $0.1.songCount, image: $0.1.image, version: $0.1.version) } .bind(to: output.headerInfo) .disposed(by: disposeBag) From 9ea01555123bb380606db7270543b7ab4efda2fc Mon Sep 17 00:00:00 2001 From: Hamp Date: Wed, 17 Apr 2024 23:33:16 +0900 Subject: [PATCH 02/37] =?UTF-8?q?:zap:=20::=20=EB=A6=AC=EC=97=91=ED=84=B0?= =?UTF-8?q?=ED=82=B7=EC=9C=BC=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Model/PlayListHeader.swift | 2 +- .../Sources/Model/PlaylistMetaData.swift | 16 +++ .../Sources/PlayListDetailViewModel.swift | 2 +- .../Reactors/PlaylistDetailReactor.swift | 116 ++++++++++++++++++ 4 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift create mode 100644 Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift diff --git a/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift b/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift index d18767b69..823c535ce 100644 --- a/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift +++ b/Projects/Features/PlaylistFeature/Sources/Model/PlayListHeader.swift @@ -1,6 +1,6 @@ import Foundation -internal struct PlayListHeader { +public struct PlayListHeader { var title: String var songCount: String var image: String diff --git a/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift b/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift new file mode 100644 index 000000000..f54ef1726 --- /dev/null +++ b/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift @@ -0,0 +1,16 @@ +// +// PlaylistMetaData.swift +// PlaylistFeature +// +// Created by yongbeomkwak on 4/17/24. +// Copyright © 2024 yongbeomkwak. All rights reserved. +// + +import Foundation + +public struct PlaylistMetaData { + + let list: [PlayListDetailSectionModel] + let header: PlayListHeader + +} diff --git a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift index 554a6ad54..08bfce015 100644 --- a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift +++ b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift @@ -18,6 +18,7 @@ import RxSwift import SongsDomainInterface import Utility + public final class PlayListDetailViewModel: ViewModelType { var type: PlayListType! var id: String! @@ -29,7 +30,6 @@ public final class PlayListDetailViewModel: ViewModelType { var disposeBag = DisposeBag() public struct Input { - let textString: BehaviorRelay = BehaviorRelay(value: "") let itemMoved: PublishSubject = PublishSubject() let playListNameLoad: BehaviorRelay = BehaviorRelay(value: "") let cancelEdit: PublishSubject = PublishSubject() diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift new file mode 100644 index 000000000..c5530bb87 --- /dev/null +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift @@ -0,0 +1,116 @@ +import ReactorKit +import AuthDomainInterface +import BaseDomainInterface +import BaseFeature +import ErrorModule +import Foundation +import PlayListDomainInterface +import RxCocoa +import RxRelay +import RxSwift +import SongsDomainInterface +import Utility + +public final class PlaylistDetailReactor: Reactor { + public enum Action { + case viewDidLoad + case itemMoved + } + public enum Mutation { + case itemMoved(ItemMovedEvent) + case updateData(PlaylistMetaData) + case startEditing + case cancelEditing + + } + + public struct State { + var dataSource: [PlayListDetailSectionModel] + var backupDataSource: [PlayListDetailSectionModel] + var header: PlayListHeader + var selectedItemCount: Int + } + public var initialState: State + private let type: PlayListType! + private let key: String! + private let fetchPlayListDetailUseCase: any FetchPlayListDetailUseCase + private let editPlayListUseCase: any EditPlayListUseCase + private let removeSongsUseCase: any RemoveSongsUseCase + private let logoutUseCase: any LogoutUseCase + var disposeBag = DisposeBag() + + public init( + key: String, + type: PlayListType, + fetchPlayListDetailUseCase: any FetchPlayListDetailUseCase, + editPlayListUseCase: any EditPlayListUseCase, + removeSongsUseCase: any RemoveSongsUseCase, + logoutUseCase: any LogoutUseCase + ) { + self.key = key + self.type = type + self.fetchPlayListDetailUseCase = fetchPlayListDetailUseCase + self.editPlayListUseCase = editPlayListUseCase + self.removeSongsUseCase = removeSongsUseCase + self.logoutUseCase = logoutUseCase + self.initialState = .init( + dataSource: [], backupDataSource: [], header: PlayListHeader(title: "", songCount: "", image: "", version: 0), selectedItemCount: 0) + } + + public func mutate(action: Action) -> Observable { + switch action { + case .viewDidLoad: + return fetchData() + + case .itemMoved: + return .empty() + } + } + + public func reduce(state: State, mutation: Mutation) -> State { + var newState = state + switch mutation { + + case .itemMoved((let sourceIndex, let destinationIndex)): // TODO: remove insert + break + case .updateData(let metadata): + newState.backupDataSource = metadata.list + newState.dataSource = metadata.list + newState.header = metadata.header + + case .startEditing: break + + case .cancelEditing: break + + } + + return newState + } + +} + +// MARK: - Mutate + +private extension PlaylistDetailReactor { + func fetchData() -> Observable { + return fetchPlayListDetailUseCase.execute(id: key, type: type) + .catchAndReturn(PlayListDetailEntity(key: key, title: "", songs: [], image: "", image_square_version: 0, image_round_version: 0, version: 0) + ) + .asObservable() + .map{ [weak self] result in + + guard let self else { + return PlaylistMetaData(list: [], header: PlayListHeader(title: "", songCount: "", image: "", version: 0)) + } + return PlaylistMetaData(list: [PlayListDetailSectionModel(model: 0, items: result.songs)], + header:PlayListHeader( + title: result.title, + songCount: "\(result.songs.count)곡", + image: self.type == .wmRecommend ? result.key : result.image, + version: self.type == .wmRecommend ? result.image_square_version : result.version) + ) + } + .map(Mutation.updateData) + + } +} From 354e3d850eba76fb2e8f201651c5783a88675710 Mon Sep 17 00:00:00 2001 From: Hamp Date: Wed, 17 Apr 2024 23:33:43 +0900 Subject: [PATCH 03/37] =?UTF-8?q?:zap:=20::=20=ED=8F=AC=EB=A7=B7=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Model/PlaylistMetaData.swift | 2 - .../Sources/PlayListDetailViewModel.swift | 1 - .../Reactors/PlaylistDetailReactor.swift | 73 ++++++++++++------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift b/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift index f54ef1726..acf79c591 100644 --- a/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift +++ b/Projects/Features/PlaylistFeature/Sources/Model/PlaylistMetaData.swift @@ -9,8 +9,6 @@ import Foundation public struct PlaylistMetaData { - let list: [PlayListDetailSectionModel] let header: PlayListHeader - } diff --git a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift index 08bfce015..25f3d793d 100644 --- a/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift +++ b/Projects/Features/PlaylistFeature/Sources/PlayListDetailViewModel.swift @@ -18,7 +18,6 @@ import RxSwift import SongsDomainInterface import Utility - public final class PlayListDetailViewModel: ViewModelType { var type: PlayListType! var id: String! diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift index c5530bb87..367aa2cac 100644 --- a/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/PlaylistDetailReactor.swift @@ -1,10 +1,10 @@ -import ReactorKit import AuthDomainInterface import BaseDomainInterface import BaseFeature import ErrorModule import Foundation import PlayListDomainInterface +import ReactorKit import RxCocoa import RxRelay import RxSwift @@ -16,29 +16,30 @@ public final class PlaylistDetailReactor: Reactor { case viewDidLoad case itemMoved } + public enum Mutation { case itemMoved(ItemMovedEvent) case updateData(PlaylistMetaData) case startEditing case cancelEditing - } - + public struct State { var dataSource: [PlayListDetailSectionModel] var backupDataSource: [PlayListDetailSectionModel] var header: PlayListHeader var selectedItemCount: Int } + public var initialState: State private let type: PlayListType! private let key: String! private let fetchPlayListDetailUseCase: any FetchPlayListDetailUseCase private let editPlayListUseCase: any EditPlayListUseCase - private let removeSongsUseCase: any RemoveSongsUseCase + private let removeSongsUseCase: any RemoveSongsUseCase private let logoutUseCase: any LogoutUseCase var disposeBag = DisposeBag() - + public init( key: String, type: PlayListType, @@ -54,39 +55,42 @@ public final class PlaylistDetailReactor: Reactor { self.removeSongsUseCase = removeSongsUseCase self.logoutUseCase = logoutUseCase self.initialState = .init( - dataSource: [], backupDataSource: [], header: PlayListHeader(title: "", songCount: "", image: "", version: 0), selectedItemCount: 0) + dataSource: [], backupDataSource: [], header: PlayListHeader( + title: "", + songCount: "", + image: "", + version: 0 + ), selectedItemCount: 0 + ) } - + public func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: return fetchData() - + case .itemMoved: return .empty() } } - + public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - - case .itemMoved((let sourceIndex, let destinationIndex)): // TODO: remove insert + case let .itemMoved((sourceIndex, destinationIndex)): // TODO: remove insert break - case .updateData(let metadata): + case let .updateData(metadata): newState.backupDataSource = metadata.list newState.dataSource = metadata.list newState.header = metadata.header - + case .startEditing: break - + case .cancelEditing: break - - } + } return newState } - } // MARK: - Mutate @@ -94,23 +98,36 @@ public final class PlaylistDetailReactor: Reactor { private extension PlaylistDetailReactor { func fetchData() -> Observable { return fetchPlayListDetailUseCase.execute(id: key, type: type) - .catchAndReturn(PlayListDetailEntity(key: key, title: "", songs: [], image: "", image_square_version: 0, image_round_version: 0, version: 0) + .catchAndReturn( + PlayListDetailEntity( + key: key, + title: "", + songs: [], + image: "", + image_square_version: 0, + image_round_version: 0, + version: 0 + ) ) .asObservable() - .map{ [weak self] result in - + .map { [weak self] result in + guard let self else { - return PlaylistMetaData(list: [], header: PlayListHeader(title: "", songCount: "", image: "", version: 0)) + return PlaylistMetaData( + list: [], + header: PlayListHeader(title: "", songCount: "", image: "", version: 0) + ) } - return PlaylistMetaData(list: [PlayListDetailSectionModel(model: 0, items: result.songs)], - header:PlayListHeader( - title: result.title, - songCount: "\(result.songs.count)곡", - image: self.type == .wmRecommend ? result.key : result.image, - version: self.type == .wmRecommend ? result.image_square_version : result.version) + return PlaylistMetaData( + list: [PlayListDetailSectionModel(model: 0, items: result.songs)], + header: PlayListHeader( + title: result.title, + songCount: "\(result.songs.count)곡", + image: self.type == .wmRecommend ? result.key : result.image, + version: self.type == .wmRecommend ? result.image_square_version : result.version + ) ) } .map(Mutation.updateData) - } } From 9553749255a48673b6e4698f1e462826f364a7ad Mon Sep 17 00:00:00 2001 From: yongbeomkwak Date: Sun, 21 Apr 2024 04:04:33 +0900 Subject: [PATCH 04/37] =?UTF-8?q?:lipstick:=20::=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=82=BD=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ArtistMusicContentViewController.swift | 2 +- .../Views/ArtistPlayButtonGroupView.swift | 4 +- .../BaseStoryboardReactorViewController.swift | 10 +- .../Sources/Views/PlayButtonGroupView.swift | 6 +- .../NewSongsContentViewController.swift | 2 +- .../Resources/Playlist.storyboard | 12 - .../Components/PlayListDetailComponent.swift | 7 +- .../Reactors/PlaylistDetailReactor.swift | 74 ++- .../PlayListDetailViewController.swift | 590 +++++------------- 9 files changed, 232 insertions(+), 475 deletions(-) diff --git a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift index cb5f10d1f..ef8f63339 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift @@ -184,7 +184,7 @@ extension ArtistMusicContentViewController: UITableViewDelegate { } extension ArtistMusicContentViewController: PlayButtonGroupViewDelegate { - public func pressPlay(_ event: PlayEvent) { + public func play(_ event: PlayEvent) { let songs: [SongEntity] = output.dataSource.value.map { return SongEntity( id: $0.songId, diff --git a/Projects/Features/ArtistFeature/Sources/Views/ArtistPlayButtonGroupView.swift b/Projects/Features/ArtistFeature/Sources/Views/ArtistPlayButtonGroupView.swift index 5464669e5..f73c77324 100644 --- a/Projects/Features/ArtistFeature/Sources/Views/ArtistPlayButtonGroupView.swift +++ b/Projects/Features/ArtistFeature/Sources/Views/ArtistPlayButtonGroupView.swift @@ -30,11 +30,11 @@ public class ArtistPlayButtonGroupView: UIView { } @IBAction func allPlayButtonAction(_ sender: Any) { - delegate?.pressPlay(.allPlay) + delegate?.play(.allPlay) } @IBAction func shufflePlayButtonAction(_ sender: Any) { - delegate?.pressPlay(.shufflePlay) + delegate?.play(.shufflePlay) } } diff --git a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift index 2386d5cc2..9aed9c35e 100644 --- a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift +++ b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift @@ -10,10 +10,10 @@ open class BaseStoryboardReactorViewController: UIViewController, St ViewControllerFromStoryBoard { public var disposeBag = DisposeBag() - @available(*, unavailable) - public required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } +// @available(*, unavailable) +// public required init?(coder: NSCoder) { +// fatalError("init(coder:) has not been implemented") +// } override open func viewDidLoad() { super.viewDidLoad() @@ -21,7 +21,7 @@ open class BaseStoryboardReactorViewController: UIViewController, St configureNavigation() } - public func bind(reactor: R) { + open func bind(reactor: R) { bindState(reactor: reactor) bindAction(reactor: reactor) } diff --git a/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift b/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift index 4f4500cce..58c29b7de 100644 --- a/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift @@ -15,7 +15,7 @@ public enum PlayEvent { } public protocol PlayButtonGroupViewDelegate: AnyObject { - func pressPlay(_ event: PlayEvent) + func play(_ event: PlayEvent) } public class PlayButtonGroupView: UIView { @@ -28,11 +28,11 @@ public class PlayButtonGroupView: UIView { public weak var delegate: PlayButtonGroupViewDelegate? @IBAction func pressAllPlay(_ sender: UIButton) { - delegate?.pressPlay(.allPlay) + delegate?.play(.allPlay) } @IBAction func pressSufflePlay(_ sender: UIButton) { - delegate?.pressPlay(.shufflePlay) + delegate?.play(.shufflePlay) } override public init(frame: CGRect) { diff --git a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift index a8d6fe070..4d2cd909d 100644 --- a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift +++ b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift @@ -201,7 +201,7 @@ extension NewSongsContentViewController: UITableViewDelegate { } extension NewSongsContentViewController: PlayButtonGroupViewDelegate { - public func pressPlay(_ event: PlayEvent) { + public func play(_ event: PlayEvent) { input.groupPlayTapped.onNext(event) } } diff --git a/Projects/Features/PlaylistFeature/Resources/Playlist.storyboard b/Projects/Features/PlaylistFeature/Resources/Playlist.storyboard index f745c456b..1c5b418db 100644 --- a/Projects/Features/PlaylistFeature/Resources/Playlist.storyboard +++ b/Projects/Features/PlaylistFeature/Resources/Playlist.storyboard @@ -28,9 +28,6 @@ - - -