Skip to content

Commit

Permalink
✨ :: [#1316] 음악 상세에서 아티스트 이름 클릭 시 크레딧 혹은 아티스트 상세로 이동"
Browse files Browse the repository at this point in the history
  • Loading branch information
baekteun committed Oct 28, 2024
1 parent 3739097 commit 8bf629b
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 8 deletions.
6 changes: 6 additions & 0 deletions Projects/App/Sources/Application/AppComponent+Artist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public extension AppComponent {
}
}

var findArtistIDUseCase: any FindArtistIDUseCase {
shared {
FindArtistIDUseCaseImpl(artistRepository: artistRepository)
}
}

// MARK: Artist Detail > Artist Music
var artistMusicComponent: ArtistMusicComponent {
ArtistMusicComponent(parent: self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public protocol RemoteArtistDataSource {
func fetchArtistSongList(id: String, sort: ArtistSongSortType, page: Int) -> Single<[ArtistSongListEntity]>
func fetchArtistSubscriptionStatus(id: String) -> Single<ArtistSubscriptionStatusEntity>
func subscriptionArtist(id: String, on: Bool) -> Completable
func findArtistID(name: String) -> Single<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public protocol ArtistRepository {
func fetchArtistSongList(id: String, sort: ArtistSongSortType, page: Int) -> Single<[ArtistSongListEntity]>
func fetchArtistSubscriptionStatus(id: String) -> Single<ArtistSubscriptionStatusEntity>
func subscriptionArtist(id: String, on: Bool) -> Completable
func findArtistID(name: String) -> Single<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import RxSwift

public protocol FindArtistIDUseCase {
func execute(name: String) -> Single<String>
}
15 changes: 13 additions & 2 deletions Projects/Domains/ArtistDomain/Sources/API/ArtistAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum ArtistAPI {
case fetchArtistSongList(id: String, sort: ArtistSongSortType, page: Int)
case fetchSubscriptionStatus(id: String)
case subscriptionArtist(id: String, on: Bool)
case findArtistID(name: String)
}

extension ArtistAPI: WMAPI {
Expand All @@ -29,6 +30,8 @@ extension ArtistAPI: WMAPI {
return "/\(id)/subscription"
case let .subscriptionArtist(id, _):
return "/\(id)/subscription"
case let .findArtistID(name):
return "/find"
}
}

Expand All @@ -37,7 +40,8 @@ extension ArtistAPI: WMAPI {
case .fetchArtistList,
.fetchArtistDetail,
.fetchArtistSongList,
.fetchSubscriptionStatus:
.fetchSubscriptionStatus,
.findArtistID:
return .get
case let .subscriptionArtist(_, on):
return on ? .post : .delete
Expand All @@ -59,12 +63,19 @@ extension ArtistAPI: WMAPI {
],
encoding: URLEncoding.queryString
)
case let .findArtistID(name):
return .requestParameters(
parameters: [
"name": name
],
encoding: URLEncoding.queryString
)
}
}

public var jwtTokenType: JwtTokenType {
switch self {
case .fetchArtistList, .fetchArtistDetail, .fetchArtistSongList:
case .fetchArtistList, .fetchArtistDetail, .fetchArtistSongList, .findArtistID:
return .none
case .fetchSubscriptionStatus, .subscriptionArtist:
return .accessToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ public final class RemoteArtistDataSourceImpl: BaseRemoteDataSource<ArtistAPI>,
request(.subscriptionArtist(id: id, on: on))
.asCompletable()
}

public func findArtistID(name: String) -> Single<String> {
request(.findArtistID(name: name))
.map(FindArtistIDResponseDTO.self)
.map { $0.id }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ public final class ArtistRepositoryImpl: ArtistRepository {
public func subscriptionArtist(id: String, on: Bool) -> Completable {
remoteArtistDataSource.subscriptionArtist(id: id, on: on)
}

public func findArtistID(name: String) -> Single<String> {
remoteArtistDataSource.findArtistID(name: name)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct FindArtistIDResponseDTO: Decodable {
let id: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ArtistDomainInterface
import RxSwift

public struct FindArtistIDUseCaseImpl: FindArtistIDUseCase {
private let artistRepository: any ArtistRepository

public init(
artistRepository: any ArtistRepository
) {
self.artistRepository = artistRepository
}

public func execute(name: String) -> Single<String> {
return artistRepository.findArtistID(name: name)
}
}
10 changes: 7 additions & 3 deletions Projects/Features/MusicDetailFeature/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@ let project = Project.module(
.feature(target: .LyricHighlightingFeature, type: .interface),
.feature(target: .SongCreditFeature, type: .interface),
.feature(target: .SignInFeature, type: .interface),
.feature(target: .ArtistFeature, type: .interface),
.domain(target: .SongsDomain, type: .interface),
.domain(target: .LikeDomain, type: .interface)
.domain(target: .LikeDomain, type: .interface),
.domain(target: .ArtistDomain, type: .interface)
]),
.tests(module: .feature(.MusicDetailFeature), dependencies: [
.feature(target: .MusicDetailFeature),
.domain(target: .SongsDomain, type: .testing),
.domain(target: .LikeDomain, type: .testing)
.domain(target: .LikeDomain, type: .testing),
.domain(target: .ArtistDomain, type: .testing)
]),
.demo(module: .feature(.MusicDetailFeature), dependencies: [
.feature(target: .MusicDetailFeature),
.domain(target: .SongsDomain, type: .testing),
.domain(target: .LikeDomain, type: .testing)
.domain(target: .LikeDomain, type: .testing),
.domain(target: .ArtistDomain, type: .testing)
])
]
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import ArtistDomainInterface
import ArtistFeatureInterface
import BaseFeature
import BaseFeatureInterface
import LikeDomainInterface
Expand All @@ -17,9 +19,11 @@ public protocol MusicDetailDependency: Dependency {
var containSongsFactory: any ContainSongsFactory { get }
var karaokeFactory: any KaraokeFactory { get }
var textPopupFactory: any TextPopupFactory { get }
var artistDetailFactory: any ArtistDetailFactory { get }
var playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol { get }
var addLikeSongUseCase: any AddLikeSongUseCase { get }
var cancelLikeSongUseCase: any CancelLikeSongUseCase { get }
var findArtistIDUseCase: any FindArtistIDUseCase { get }
}

public final class MusicDetailComponent: Component<MusicDetailDependency>, MusicDetailFactory {
Expand All @@ -29,7 +33,8 @@ public final class MusicDetailComponent: Component<MusicDetailDependency>, Music
selectedID: selectedID,
fetchSongUseCase: dependency.fetchSongUseCase,
addLikeSongUseCase: dependency.addLikeSongUseCase,
cancelLikeSongUseCase: dependency.cancelLikeSongUseCase
cancelLikeSongUseCase: dependency.cancelLikeSongUseCase,
findArtistIDUseCase: dependency.findArtistIDUseCase
)

let viewController = MusicDetailViewController(
Expand All @@ -40,6 +45,7 @@ public final class MusicDetailComponent: Component<MusicDetailDependency>, Music
containSongsFactory: dependency.containSongsFactory,
textPopupFactory: dependency.textPopupFactory,
karaokeFactory: dependency.karaokeFactory,
artistDetailFactory: dependency.artistDetailFactory,
playlistPresenterGlobalState: dependency.playlistPresenterGlobalState
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ArtistDomainInterface
import BaseFeature
import Foundation
import Kingfisher
Expand Down Expand Up @@ -30,6 +31,7 @@ final class MusicDetailReactor: Reactor {
case musicPickButtonDidTap
case playListButtonDidTap
case dismissButtonDidTap
case didTapArtistLabel
}

enum Mutation {
Expand All @@ -48,6 +50,7 @@ final class MusicDetailReactor: Reactor {
case textPopup(text: String, completion: () -> Void)
case signin
case karaoke(ky: Int?, tj: Int?)
case artist(artistID: String)
}

struct State {
Expand All @@ -71,6 +74,7 @@ final class MusicDetailReactor: Reactor {
private let fetchSongUseCase: any FetchSongUseCase
private let addLikeSongUseCase: any AddLikeSongUseCase
private let cancelLikeSongUseCase: any CancelLikeSongUseCase
private let findArtistIDUseCase: any FindArtistIDUseCase
private var shouldRefreshLikeList = false

private var pendingLikeRequests: [String: LikeRequest] = [:]
Expand All @@ -83,7 +87,8 @@ final class MusicDetailReactor: Reactor {
selectedID: String,
fetchSongUseCase: any FetchSongUseCase,
addLikeSongUseCase: any AddLikeSongUseCase,
cancelLikeSongUseCase: any CancelLikeSongUseCase
cancelLikeSongUseCase: any CancelLikeSongUseCase,
findArtistIDUseCase: any FindArtistIDUseCase
) {
let selectedIndex = songIDs.firstIndex(of: selectedID) ?? 0
self.initialState = .init(
Expand All @@ -94,6 +99,7 @@ final class MusicDetailReactor: Reactor {
self.fetchSongUseCase = fetchSongUseCase
self.addLikeSongUseCase = addLikeSongUseCase
self.cancelLikeSongUseCase = cancelLikeSongUseCase
self.findArtistIDUseCase = findArtistIDUseCase

let urls = [
songIDs[safe: selectedIndex - 1],
Expand Down Expand Up @@ -133,6 +139,8 @@ final class MusicDetailReactor: Reactor {
return playListButtonDidTap()
case .dismissButtonDidTap:
return navigateMutation(navigate: .dismiss)
case .didTapArtistLabel:
return didTapArtistLabel()
}
}

Expand Down Expand Up @@ -416,6 +424,25 @@ private extension MusicDetailReactor {
LogManager.analytics(log)
return navigateMutation(navigate: .playlist(id: song.videoID))
}

func didTapArtistLabel() -> Observable<Mutation> {
guard let selectedSong = currentState.selectedSong else { return .empty() }
let artists = selectedSong.artistString.components(separatedBy: ",")

if artists.count == 1, let artistName = artists.first {
return findArtistIDUseCase.execute(name: artistName)
.asObservable()
.flatMap {
return Observable.concat(
.just(Mutation.navigate(.artist(artistID: $0))),
.just(.navigate(nil))
)
}
.catchAndReturn(.navigate(.credit(id: selectedSong.videoID)))
} else {
return navigateMutation(navigate: .credit(id: selectedSong.videoID))
}
}
}

// MARK: - Private Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private protocol MusicDetailActionProtocol {
var playlistButtonDidTap: Observable<Void> { get }
var creditButtonDidTap: Observable<Void> { get }
var dismissButtonDidTap: Observable<Void> { get }
var didTapArtistLabel: Observable<Void> { get }
}

final class MusicDetailView: UIView {
Expand Down Expand Up @@ -256,4 +257,5 @@ extension Reactive: MusicDetailActionProtocol where Base: MusicDetailView {
var playlistButtonDidTap: Observable<Void> { base.musicToolbarView.rx.playlistButtonDidTap }
var creditButtonDidTap: Observable<Void> { base.creditButton.rx.tap.asObservable() }
var dismissButtonDidTap: Observable<Void> { base.dismissButton.rx.tap.asObservable() }
var didTapArtistLabel: Observable<Void> { base.musicControlView.rx.didTapArtistLabel }
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ArtistFeatureInterface
import BaseFeature
import BaseFeatureInterface
import DesignSystem
Expand All @@ -20,6 +21,7 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
private let containSongsFactory: any ContainSongsFactory
private let textPopupFactory: any TextPopupFactory
private let karaokeFactory: any KaraokeFactory
private let artistDetailFactory: any ArtistDetailFactory
private let playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol

init(
Expand All @@ -30,6 +32,7 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
containSongsFactory: any ContainSongsFactory,
textPopupFactory: any TextPopupFactory,
karaokeFactory: any KaraokeFactory,
artistDetailFactory: any ArtistDetailFactory,
playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol
) {
self.lyricHighlightingFactory = lyricHighlightingFactory
Expand All @@ -38,6 +41,7 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
self.containSongsFactory = containSongsFactory
self.textPopupFactory = textPopupFactory
self.karaokeFactory = karaokeFactory
self.artistDetailFactory = artistDetailFactory
self.playlistPresenterGlobalState = playlistPresenterGlobalState
super.init(reactor: reactor)
}
Expand Down Expand Up @@ -72,6 +76,8 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
alternativeImageURL: youtubeURLGenerator.generateThumbnailURL(id: $0)
)
}
.uniqued()
.toArray()
}
.bind { [musicDetailView] thumbnailModels in
musicDetailView.updateThumbnails(thumbnailModels: thumbnailModels) {
Expand Down Expand Up @@ -145,6 +151,8 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
owner.presentSignIn()
case let .karaoke(ky, tj):
owner.presentKaraokeSheet(ky: ky, tj: tj)
case let .artist(artistID):
owner.navigateArtist(artistID: artistID)
case .dismiss:
owner.dismiss()
}
Expand Down Expand Up @@ -214,6 +222,11 @@ final class MusicDetailViewController: BaseReactorViewController<MusicDetailReac
.map { Reactor.Action.dismissButtonDidTap }
.bind(to: reactor.action)
.disposed(by: disposeBag)

musicDetailView.rx.didTapArtistLabel
.map { Reactor.Action.didTapArtistLabel }
.bind(to: reactor.action)
.disposed(by: disposeBag)
}
}

Expand Down Expand Up @@ -269,6 +282,11 @@ private extension MusicDetailViewController {
self.present(viewController, animated: true)
}

func navigateArtist(artistID: String) {
let viewController = artistDetailFactory.makeView(artistID: artistID)
self.navigationController?.pushViewController(viewController, animated: true)
}

func dismiss() {
self.dismiss(animated: true)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DesignSystem
import RxCocoa
import RxGesture
import RxSwift
import SnapKit
import Then
Expand All @@ -20,6 +21,7 @@ private protocol MusicControlActionProtocol {
var nextMusicButtonDidTap: Observable<Void> { get }
var singingRoomButtonDidTap: Observable<Void> { get }
var lyricsButtonDidTap: Observable<Void> { get }
var didTapArtistLabel: Observable<Void> { get }
}

final class MusicControlView: UIView {
Expand All @@ -28,7 +30,7 @@ final class MusicControlView: UIView {
textColor: DesignSystemAsset.BlueGrayColor.gray25.color,
font: .t4(weight: .medium)
)
private let artistLabel: WMFlowLabel = WMFlowLabel(
fileprivate let artistLabel: WMFlowLabel = WMFlowLabel(
text: "",
textColor: DesignSystemAsset.BlueGrayColor.gray100.color.withAlphaComponent(0.6),
font: .t5(weight: .medium)
Expand Down Expand Up @@ -214,4 +216,11 @@ extension Reactive: MusicControlActionProtocol where Base: MusicControlView {
var lyricsButtonDidTap: Observable<Void> {
base.lyricsRoomButton.rx.tap.asObservable()
}

var didTapArtistLabel: Observable<Void> {
base.artistLabel.rx.tapGesture()
.when(.recognized)
.map { _ in () }
.asObservable()
}
}
6 changes: 6 additions & 0 deletions Projects/Modules/Utility/Sources/Utils/UniquedSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,9 @@ extension UniquedSequence: Sequence {
}

extension UniquedSequence: LazySequenceProtocol where Base: LazySequenceProtocol {}

public extension UniquedSequence {
func toArray() -> [Base.Element] {
Array(self)
}
}

0 comments on commit 8bf629b

Please sign in to comment.