192 lines
7.7 KiB
Swift
192 lines
7.7 KiB
Swift
//
|
|
// ContentDetailPlayView.swift
|
|
// SodaLive
|
|
//
|
|
// Created by klaus on 2023/08/13.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Kingfisher
|
|
import Sliders
|
|
|
|
struct ContentDetailPlayView: View {
|
|
|
|
let audioContent: GetAudioContentDetailResponse
|
|
let isAlertPreview: Bool
|
|
@Binding var isShowPreviewAlert: Bool
|
|
|
|
@StateObject var contentPlayManager = ContentPlayManager.shared
|
|
|
|
@State private var isRepeat = UserDefaults.bool(forKey: .isContentPlayLoop)
|
|
@State private var isEditing = false
|
|
@State private var progress: TimeInterval = 0
|
|
@State private var timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
ZStack {
|
|
KFImage(URL(string: audioContent.coverImageUrl))
|
|
.resizable()
|
|
.scaledToFill()
|
|
.frame(
|
|
width: screenSize().width - 26.7,
|
|
height: screenSize().width - 26.7,
|
|
alignment: .center
|
|
)
|
|
.cornerRadius(10.7, corners: [.topLeft, .topRight])
|
|
|
|
if let _ = audioContent.totalContentCount, let remainingContentCount = audioContent.remainingContentCount, remainingContentCount <= 0, audioContent.creator.creatorId != UserDefaults.int(forKey: .userId), !audioContent.existOrdered {
|
|
Text("Sold Out")
|
|
.font(.custom(Font.bold.rawValue, size: 36.7))
|
|
.foregroundColor(.white)
|
|
.frame(
|
|
width: screenSize().width - 26.7,
|
|
height: screenSize().width - 26.7,
|
|
alignment: .center
|
|
)
|
|
.background(Color.black.opacity(0.6))
|
|
} else if audioContent.releaseDate == nil && !isAlertPreview || (audioContent.isActivePreview && !audioContent.contentUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) {
|
|
Image(isPlaying() ? "btn_audio_content_pause" : isAlertPreview ? "btn_audio_content_preview_play" : "btn_audio_content_play")
|
|
.onTapGesture {
|
|
if isPlaying() {
|
|
contentPlayManager.pauseAudio()
|
|
} else {
|
|
contentPlayManager.startTimer = startTimer
|
|
contentPlayManager.stopTimer = stopTimer
|
|
|
|
contentPlayManager.playAudio(
|
|
contentId: audioContent.contentId,
|
|
title: audioContent.title,
|
|
nickname: audioContent.creator.nickname,
|
|
coverImage: audioContent.coverImageUrl,
|
|
contentUrl: audioContent.contentUrl,
|
|
isFree: audioContent.price <= 0,
|
|
isPreview: !audioContent.existOrdered && audioContent.price > 0
|
|
)
|
|
isShowPreviewAlert = true
|
|
}
|
|
}
|
|
} else if audioContent.releaseDate == nil {
|
|
Text("해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다.")
|
|
.font(.custom(Font.medium.rawValue, size: 16.7))
|
|
.foregroundColor(Color.grayee)
|
|
.padding(13.3)
|
|
.background(Color.gray33.opacity(0.5))
|
|
.cornerRadius(46.7)
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 13.3) {
|
|
Spacer()
|
|
|
|
ValueSlider(
|
|
value: audioContent.contentId == contentPlayManager.contentId ? $progress : .constant(0),
|
|
in: sliderRange(),
|
|
onEditingChanged: { editing in
|
|
isEditing = editing
|
|
if !editing {
|
|
contentPlayManager.setCurrentTime(progress)
|
|
}
|
|
}
|
|
)
|
|
.valueSliderStyle(
|
|
HorizontalValueSliderStyle(
|
|
track: HorizontalValueTrack(
|
|
view: Rectangle().foregroundColor(Color(hex: "9970ff")),
|
|
mask: Rectangle()
|
|
)
|
|
.background(Rectangle().foregroundColor(Color(hex: "979797").opacity(0.3)))
|
|
.frame(height: 5.3),
|
|
thumbSize: CGSizeZero,
|
|
options: .interactiveTrack
|
|
)
|
|
)
|
|
.frame(height: 5.3)
|
|
}
|
|
|
|
if contentPlayManager.isLoading {
|
|
LoadingView()
|
|
}
|
|
}
|
|
.frame(
|
|
width: screenSize().width - 26.7,
|
|
height: screenSize().width - 26.7
|
|
)
|
|
|
|
HStack(spacing: 0) {
|
|
Text("\(getProgress()) / \(getDuration())")
|
|
.font(.custom(Font.medium.rawValue, size: 14.7))
|
|
.foregroundColor(.white)
|
|
|
|
Spacer()
|
|
|
|
Image(
|
|
isRepeat ?
|
|
"btn_player_repeat" :
|
|
"btn_player_repeat_done"
|
|
)
|
|
.onTapGesture {
|
|
isRepeat = !UserDefaults.bool(forKey: .isContentPlayLoop)
|
|
UserDefaults.set(
|
|
isRepeat,
|
|
forKey: .isContentPlayLoop
|
|
)
|
|
}
|
|
}
|
|
.frame(width: screenSize().width - 40)
|
|
}
|
|
.onAppear {
|
|
if !isPlaying() {
|
|
stopTimer()
|
|
}
|
|
}
|
|
.onReceive(timer) { _ in
|
|
guard let player = contentPlayManager.player, !isEditing else { return }
|
|
self.progress = player.currentTime
|
|
}
|
|
}
|
|
|
|
private func isPlaying() -> Bool {
|
|
return contentPlayManager.contentId == audioContent.contentId && contentPlayManager.isPlaying
|
|
}
|
|
|
|
private func sliderRange() -> ClosedRange<Double> {
|
|
if audioContent.contentId == contentPlayManager.contentId {
|
|
return 0...contentPlayManager.duration
|
|
} else {
|
|
return 0...0
|
|
}
|
|
}
|
|
|
|
private func getProgress() -> String {
|
|
if audioContent.contentId == contentPlayManager.contentId {
|
|
return secondsToMinutesSeconds(seconds: Int(progress))
|
|
} else {
|
|
return secondsToMinutesSeconds(seconds: 0)
|
|
}
|
|
}
|
|
|
|
private func getDuration() -> String {
|
|
if audioContent.contentId == contentPlayManager.contentId {
|
|
return secondsToMinutesSeconds(seconds: Int(contentPlayManager.duration))
|
|
} else {
|
|
return audioContent.duration
|
|
}
|
|
}
|
|
|
|
private func secondsToMinutesSeconds(seconds: Int) -> String {
|
|
let hours = String(format: "%02d", seconds / 3600)
|
|
let minute = String(format: "%02d", (seconds % 3600) / 60)
|
|
let second = String(format: "%02d", seconds % 60)
|
|
|
|
return "\(hours):\(minute):\(second)"
|
|
}
|
|
|
|
private func startTimer() {
|
|
timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
|
|
}
|
|
|
|
private func stopTimer() {
|
|
timer.upstream.connect().cancel()
|
|
}
|
|
}
|