sodalive-ios/SodaLive/Sources/Content/Playlist/Detail/ContentPlaylistDetailView.s...

338 lines
16 KiB
Swift

//
// ContentPlaylistDetailView.swift
// SodaLive
//
// Created by klaus on 12/9/24.
//
import SwiftUI
import Kingfisher
struct ContentPlaylistDetailView: View {
@StateObject var viewModel = ContentPlaylistDetailViewModel()
@StateObject var contentPlayerPlayManager = ContentPlayerPlayManager.shared
let playlistId: Int
@Binding var isShowing: Bool
@Binding var reloadData: Bool
@State private var isShowPopupMenu = false
@State private var isShowDeleteConfirm = false
@State private var isShowPlayer = false
@State private var isShowModify = false
@State private var playlist: [AudioContentPlaylistContent] = []
var body: some View {
BaseView(isLoading: $viewModel.isLoading) {
if reloadData {
Color.clear
LoadingView()
} else {
VStack(spacing: 21.3) {
HStack(spacing: 5.3) {
Image("ic_back")
.resizable()
.frame(width: 20, height: 20)
.padding(8)
.onTapGesture {
isShowing = false
}
Spacer()
Image("ic_edit_white")
.padding(8)
.onTapGesture {
isShowModify = true
}
Image("ic_seemore_vertical_white")
.padding(8)
.onTapGesture {
isShowPopupMenu = true
}
}
.padding(.horizontal, 13.3)
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.black)
ScrollView(.vertical, showsIndicators: false) {
if let response = viewModel.response {
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .top, spacing: 13.3) {
VStack(alignment: .center, spacing: 0) {
HStack(spacing: 0) {
KFImage(URL(string: response.playlistCoverImageList[0]))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 80, height: 80))
.resizable()
.scaledToFill()
.clipped()
.frame(maxWidth: .infinity, maxHeight: .infinity)
if response.playlistCoverImageList.count > 2 {
KFImage(URL(string: response.playlistCoverImageList[1]))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 80, height: 80))
.resizable()
.scaledToFill()
.clipped()
.frame(maxWidth: 40, maxHeight: 40)
}
}
HStack(spacing: 0) {
if response.playlistCoverImageList.count > 2 {
KFImage(URL(string: response.playlistCoverImageList[2]))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 80, height: 80))
.resizable()
.scaledToFill()
.clipped()
.frame(maxWidth: 40, maxHeight: 40)
}
if response.playlistCoverImageList.count > 3 {
KFImage(URL(string: response.playlistCoverImageList[3]))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 80, height: 80))
.resizable()
.scaledToFill()
.clipped()
.frame(maxWidth: 40, maxHeight: 40)
}
}
}
.frame(width: 80, height: 80)
.background(Color.graybb)
.cornerRadius(4)
VStack(alignment: .leading, spacing: 6.7) {
Text(response.title)
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color.grayd2)
.lineLimit(2)
.truncationMode(.tail)
Text(response.desc.prefix(100))
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.gray90)
.truncationMode(.tail)
}
}
HStack(spacing: 0) {
Text("만든 날짜 \(response.createdDate)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.gray90)
Spacer()
Text("\(response.contentCount)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
}
.padding(.top, 13.3)
HStack(spacing: 13.3) {
HStack(spacing: 5.3) {
Image("ic_playlist_play")
Text("Play")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.white)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 11)
.background(Color.button)
.cornerRadius(5.3)
.contentShape(Rectangle())
.onTapGesture {
ContentPlayManager.shared.stopAudio()
playlist = response.contentList
isShowPlayer = true
}
HStack(spacing: 5.3) {
Image("ic_playlist_shuffle")
Text("Shuffle")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.white)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 11)
.background(Color.button)
.cornerRadius(5.3)
.contentShape(Rectangle())
.onTapGesture {
ContentPlayManager.shared.stopAudio()
playlist = response.contentList.shuffled()
isShowPlayer = true
}
}
.padding(.top, 18)
LazyVStack(alignment: .leading, spacing: 0) {
ForEach(0..<response.contentList.count, id: \.self) {
PlaylistContentItemView(item: response.contentList[$0])
}
}
.padding(.top, 18)
}
.padding(.horizontal, 13.3)
}
}
if contentPlayerPlayManager.isShowingMiniPlayer {
HStack(spacing: 0) {
KFImage(URL(string: contentPlayerPlayManager.coverImageUrl))
.cancelOnDisappear(true)
.downsampling(
size: CGSize(
width: 36.7,
height: 36.7
)
)
.resizable()
.frame(width: 36.7, height: 36.7)
.cornerRadius(5.3)
VStack(alignment: .leading, spacing: 2.3) {
Text(contentPlayerPlayManager.title)
.font(.custom(Font.medium.rawValue, size: 13))
.foregroundColor(Color.grayee)
.lineLimit(2)
Text(contentPlayerPlayManager.nickname)
.font(.custom(Font.medium.rawValue, size: 11))
.foregroundColor(Color.grayd2)
}
.padding(.horizontal, 10.7)
Spacer()
Image(contentPlayerPlayManager.isPlaying ? "ic_noti_pause" : "btn_bar_play")
.resizable()
.frame(width: 25, height: 25)
.onTapGesture {
contentPlayerPlayManager.playOrPause()
}
Image("ic_noti_stop")
.resizable()
.frame(width: 25, height: 25)
.padding(.leading, 16)
.onTapGesture { contentPlayerPlayManager.resetPlayer() }
}
.padding(.vertical, 10.7)
.padding(.horizontal, 13.3)
.background(Color.gray22)
.contentShape(Rectangle())
.onTapGesture {
playlist = []
isShowPlayer = true
}
}
}
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) {
HStack {
Spacer()
Text(viewModel.errorMessage)
.padding(.vertical, 13.3)
.frame(width: screenSize().width - 66.7, alignment: .center)
.font(.custom(Font.medium.rawValue, size: 12))
.background(Color.button)
.foregroundColor(Color.white)
.multilineTextAlignment(.leading)
.cornerRadius(20)
.padding(.bottom, 66.7)
Spacer()
}
}
.onAppear {
viewModel.playlistId = playlistId
}
}
if isShowPopupMenu {
ZStack {
Color.black
.opacity(0.7)
.ignoresSafeArea()
.onTapGesture { isShowPopupMenu = false }
VStack(spacing: 0) {
Spacer()
HStack(spacing: 13.3) {
Text("삭제")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
Spacer()
}
.padding(.vertical, 8)
.padding(.horizontal, 26.7)
.contentShape(Rectangle())
.onTapGesture {
isShowPopupMenu = false
isShowDeleteConfirm = true
}
.padding(24)
.background(Color.gray22)
.cornerRadius(13.3, corners: [.topLeft, .topRight])
}
}
}
if isShowDeleteConfirm {
ZStack {
Color.black
.opacity(0.7)
.ignoresSafeArea()
.onTapGesture { isShowDeleteConfirm = false }
SodaDialog(
title: "재생 목록 삭제",
desc: viewModel.response != nil ? "\(viewModel.response!.title)을 삭제하시겠습니까?" : "삭제하시겠습니까?",
confirmButtonTitle: "삭제",
confirmButtonAction: {
isShowDeleteConfirm = false
viewModel.deletePlaylist {
reloadData = true
isShowing = false
}
},
cancelButtonTitle: "취소",
cancelButtonAction: { isShowDeleteConfirm = false }
)
}
}
if isShowModify {
ContentPlaylistModifyView(
playlistId: playlistId,
isShowing: $isShowModify,
reloadData: $reloadData
)
}
if isShowPlayer {
ContentPlayerView(isShowing: $isShowPlayer, playlist: playlist)
}
}
}
}
#Preview {
ContentPlaylistDetailView(
playlistId: 1,
isShowing: .constant(true),
reloadData: .constant(false)
)
}