Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/Kaset/Services/Player/NowPlayingManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ final class NowPlayingManager {

// MARK: - Now Playing Claim

enum NativeClaimPlaybackState: Equatable, Sendable {
enum NativeClaimPlaybackState: Equatable {
case playing
case paused
}
Expand All @@ -159,7 +159,7 @@ final class NowPlayingManager {
case claim(title: String, artist: String, playbackState: NativeClaimPlaybackState)
}

struct ActiveVideoClaim: Equatable, Sendable {
struct ActiveVideoClaim: Equatable {
let title: String
let artist: String
let playbackState: NativeClaimPlaybackState
Expand Down
2 changes: 1 addition & 1 deletion Sources/Kaset/ViewModels/ArtistDiscographyViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class ArtistDiscographyViewModel {
private(set) var albums: [Album] = []

let destination: ArtistSeeAllDestination
private let client: any YTMusicClientProtocol
let client: any YTMusicClientProtocol
private let logger = DiagnosticsLogger.api

init(destination: ArtistSeeAllDestination, client: any YTMusicClientProtocol) {
Expand Down
80 changes: 58 additions & 22 deletions Sources/Kaset/Views/ArtistDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import SwiftUI

// MARK: - ArtistDetailView

/// Detail view for an artist showing their songs and albums.
struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
let artist: Artist
Expand Down Expand Up @@ -485,7 +487,9 @@ struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
self.sectionHeader(title: title, shelfKind: shelfKind)
} itemContent: { album in
NavigationLink(value: self.playlistFromAlbum(album)) {
self.albumCard(album)
PlayableArtistCard(playAction: self.quickPlayAction(for: album), thumbnailSize: 140) {
self.albumCard(album)
}
}
.buttonStyle(.plain)
}
Expand All @@ -500,7 +504,9 @@ struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
self.sectionHeader(title: title, shelfKind: .playlistsByArtist)
} itemContent: { playlist in
NavigationLink(value: playlist) {
self.playlistCard(playlist)
PlayableArtistCard(playAction: self.quickPlayAction(for: playlist), thumbnailSize: 140) {
self.playlistCard(playlist)
}
}
.buttonStyle(.plain)
}
Expand Down Expand Up @@ -532,6 +538,30 @@ struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
)
}

/// Quick-play action for an album card's hover play button.
private func quickPlayAction(for album: Album) -> () -> Void {
{
SongActionsHelper.playAlbum(
album,
client: self.viewModel.client,
playerService: self.playerService
)
}
}

/// Quick-play action for a playlist card's hover play button. Nil for
/// mood-category tiles, which navigate rather than play.
private func quickPlayAction(for playlist: Playlist) -> (() -> Void)? {
guard SongActionsHelper.canQuickPlayPlaylist(playlist) else { return nil }
return {
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
}
}

private func albumCard(_ album: Album) -> some View {
VStack(alignment: .leading, spacing: 8) {
// Thumbnail
Expand Down Expand Up @@ -767,7 +797,9 @@ struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
self.sectionHeader(title: "Singles & EPs", shelfKind: .singles)
} itemContent: { album in
NavigationLink(value: self.playlistFromAlbum(album)) {
self.albumCard(album)
PlayableArtistCard(playAction: self.quickPlayAction(for: album), thumbnailSize: 140) {
self.albumCard(album)
}
}
.buttonStyle(.plain)
}
Expand All @@ -784,28 +816,32 @@ struct ArtistDetailView: View { // swiftlint:disable:this type_body_length
self.sectionHeader(title: "Playlists", shelfKind: .playlistsByArtist)
} itemContent: { playlist in
NavigationLink(value: playlist) {
VStack(alignment: .leading, spacing: 8) {
CachedAsyncImage(url: playlist.thumbnailURL?.highQualityThumbnailURL, targetSize: CGSize(width: 140, height: 140)) { image in
image
PlayableArtistCard(playAction: self.quickPlayAction(for: playlist), thumbnailSize: 140) {
VStack(alignment: .leading, spacing: 8) {
CachedAsyncImage(
url: playlist.thumbnailURL?.highQualityThumbnailURL,
targetSize: CGSize(width: 140, height: 140)
) { image in image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
Rectangle()
.fill(.quaternary)
.overlay {
Image(systemName: "music.note.list")
.font(.largeTitle)
.foregroundStyle(.secondary)
}
} placeholder: {
Rectangle()
.fill(.quaternary)
.overlay {
Image(systemName: "music.note.list")
.font(.largeTitle)
.foregroundStyle(.secondary)
}
}
.frame(width: 140, height: 140)
.clipShape(.rect(cornerRadius: 8))

Text(playlist.title)
.font(.system(size: 12, weight: .medium))
.lineLimit(2)
.multilineTextAlignment(.leading)
.frame(width: 140, alignment: .leading)
}
.frame(width: 140, height: 140)
.clipShape(.rect(cornerRadius: 8))

Text(playlist.title)
.font(.system(size: 12, weight: .medium))
.lineLimit(2)
.multilineTextAlignment(.leading)
.frame(width: 140, alignment: .leading)
}
}
.buttonStyle(.plain)
Expand Down
16 changes: 15 additions & 1 deletion Sources/Kaset/Views/ArtistDiscographyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SwiftUI
/// View displaying an artist's full discography behind an Albums-shelf "See all".
struct ArtistDiscographyView: View {
@State var viewModel: ArtistDiscographyViewModel
@Environment(PlayerService.self) private var playerService

var body: some View {
Group {
Expand Down Expand Up @@ -44,7 +45,9 @@ struct ArtistDiscographyView: View {
) {
ForEach(self.viewModel.albums) { album in
NavigationLink(value: self.playlistFromAlbum(album)) {
self.albumCard(album)
PlayableArtistCard(playAction: self.quickPlayAction(for: album), thumbnailSize: 160) {
self.albumCard(album)
}
}
.buttonStyle(.plain)
}
Expand All @@ -53,6 +56,17 @@ struct ArtistDiscographyView: View {
}
}

/// Quick-play action for an album card's hover play button.
private func quickPlayAction(for album: Album) -> () -> Void {
{
SongActionsHelper.playAlbum(
album,
client: self.viewModel.client,
playerService: self.playerService
)
}
}

private func playlistFromAlbum(_ album: Album) -> Playlist {
Playlist(
id: album.id,
Expand Down
35 changes: 21 additions & 14 deletions Sources/Kaset/Views/ChartsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct ChartsView: View {
HomeSectionItemCard(
item: item,
rank: section.isChart ? index + 1 : nil,
playAction: self.playlistPlayAction(for: item)
playAction: self.quickPlayAction(for: item)
) {
self.playItem(item, in: section, at: index)
}
Expand All @@ -114,19 +114,26 @@ struct ChartsView: View {

// MARK: - Actions

private func playlistPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
guard case let .playlist(playlist) = item,
SongActionsHelper.canQuickPlayPlaylist(playlist)
else {
return nil
}

return {
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
private func quickPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
switch item {
case let .playlist(playlist) where SongActionsHelper.canQuickPlayPlaylist(playlist):
{
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
}
case let .album(album):
{
SongActionsHelper.playAlbum(
album,
client: self.viewModel.client,
playerService: self.playerService
)
}
default:
nil
}
}

Expand Down
35 changes: 21 additions & 14 deletions Sources/Kaset/Views/ExploreView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ struct ExploreView: View {
HomeSectionItemCard(
item: item,
rank: section.isChart ? index + 1 : nil,
playAction: self.playlistPlayAction(for: item)
playAction: self.quickPlayAction(for: item)
) {
self.playItem(item, in: section, at: index)
}
Expand All @@ -115,19 +115,26 @@ struct ExploreView: View {

// MARK: - Actions

private func playlistPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
guard case let .playlist(playlist) = item,
SongActionsHelper.canQuickPlayPlaylist(playlist)
else {
return nil
}

return {
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
private func quickPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
switch item {
case let .playlist(playlist) where SongActionsHelper.canQuickPlayPlaylist(playlist):
{
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
}
case let .album(album):
{
SongActionsHelper.playAlbum(
album,
client: self.viewModel.client,
playerService: self.playerService
)
}
default:
nil
}
}

Expand Down
35 changes: 21 additions & 14 deletions Sources/Kaset/Views/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ struct HomeView: View {
HomeSectionItemCard(
item: item,
rank: section.isChart ? index + 1 : nil,
playAction: self.playlistPlayAction(for: item)
playAction: self.quickPlayAction(for: item)
) {
self.playItem(item, in: section, at: index)
}
Expand All @@ -148,19 +148,26 @@ struct HomeView: View {

// MARK: - Context Menu

private func playlistPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
guard case let .playlist(playlist) = item,
SongActionsHelper.canQuickPlayPlaylist(playlist)
else {
return nil
}

return {
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
private func quickPlayAction(for item: HomeSectionItem) -> (() -> Void)? {
switch item {
case let .playlist(playlist) where SongActionsHelper.canQuickPlayPlaylist(playlist):
{
SongActionsHelper.playPlaylist(
playlist,
client: self.viewModel.client,
playerService: self.playerService
)
}
case let .album(album):
{
SongActionsHelper.playAlbum(
album,
client: self.viewModel.client,
playerService: self.playerService
)
}
default:
nil
}
}

Expand Down
Loading
Loading