Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸ”€ :: (#1316) μŒμ•… μƒμ„Έμ—μ„œ μ•„ν‹°μŠ€νŠΈ 이름 클릭 μ‹œ ν¬λ ˆλ”§ ν˜Ήμ€ μ•„ν‹°μŠ€νŠΈ μƒμ„Έλ‘œ 이동 #1322

Merged
merged 6 commits into from
Nov 1, 2024
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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ArtistDomainInterface
import RxSwift

public final class FindArtistIDUseCaseSpy: FindArtistIDUseCase {
public private(set) var callCount = 0
public var handler: ((String) -> Single<String>) = { _ in .never() }
public func execute(name: String) -> Single<String> {
callCount += 1
return handler(name)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@testable import ArtistDomainTesting
import ArtistFeatureInterface
import BaseFeature
import BaseFeatureInterface
import Inject
Expand Down Expand Up @@ -48,6 +50,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
.just(.init(status: "", likes: 0))
}

let findArtistIDUseCase = FindArtistIDUseCaseSpy()
findArtistIDUseCase.handler = { _ in
.just("fgSXAKsq-Vo")
}

let reactor = MusicDetailReactor(
songIDs: [
"fgSXAKsq-Vo",
Expand All @@ -58,7 +65,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
selectedID: "DPEtmqvaKqY",
fetchSongUseCase: fetchSongUseCase,
addLikeSongUseCase: addLikeSongUseCase,
cancelLikeSongUseCase: cancelLikeSongUseCase
cancelLikeSongUseCase: cancelLikeSongUseCase,
findArtistIDUseCase: findArtistIDUseCase
)
let viewController = Inject.ViewControllerHost(
UINavigationController(
Expand All @@ -70,6 +78,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
containSongsFactory: DummyContainSongsFactory(),
textPopupFactory: DummyTextPopupFactory(),
karaokeFactory: DummyKaraokeFactory(),
artistDetailFactory: DummyArtistDetailFactory(),
playlistPresenterGlobalState: DummyPlaylistPresenterGlobalState()
)
)
Expand Down Expand Up @@ -129,3 +138,9 @@ final class DummyKaraokeFactory: KaraokeFactory {
UIViewController()
}
}

final class DummyArtistDetailFactory: ArtistDetailFactory {
func makeView(artistID: String) -> UIViewController {
UIViewController()
}
}
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 }
}
Loading
Loading