v2 #1
@@ -48,6 +48,20 @@ final class AudioPlayerService: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Absolute previous chapter number (current - 1), or nil if at first chapter
|
||||
var absolutePrevChapter: Int? {
|
||||
guard chapter > 1 else { return nil }
|
||||
return chapter - 1
|
||||
}
|
||||
|
||||
/// Absolute next chapter number (current + 1), or nil if at last chapter
|
||||
var absoluteNextChapter: Int? {
|
||||
guard !chapters.isEmpty else { return nil }
|
||||
let maxChapter = chapters.map(\.number).max() ?? chapter
|
||||
guard chapter < maxChapter else { return nil }
|
||||
return chapter + 1
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var player: AVPlayer?
|
||||
|
||||
@@ -77,7 +77,7 @@ struct MiniPlayerView: View {
|
||||
// Previous chapter button
|
||||
if audioPlayer.status == .ready {
|
||||
Button {
|
||||
if let prev = audioPlayer.prevChapter {
|
||||
if let prev = audioPlayer.absolutePrevChapter {
|
||||
NotificationCenter.default.post(
|
||||
name: .skipToPrevChapter,
|
||||
object: nil,
|
||||
@@ -92,8 +92,8 @@ struct MiniPlayerView: View {
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(audioPlayer.prevChapter == nil)
|
||||
.opacity(audioPlayer.prevChapter == nil ? 0.4 : 1.0)
|
||||
.disabled(audioPlayer.absolutePrevChapter == nil)
|
||||
.opacity(audioPlayer.absolutePrevChapter == nil ? 0.4 : 1.0)
|
||||
}
|
||||
|
||||
// Status indicator or play/pause control
|
||||
@@ -126,7 +126,7 @@ struct MiniPlayerView: View {
|
||||
// Next chapter button
|
||||
if audioPlayer.status == .ready {
|
||||
Button {
|
||||
if let next = audioPlayer.nextChapter {
|
||||
if let next = audioPlayer.absoluteNextChapter {
|
||||
NotificationCenter.default.post(
|
||||
name: .skipToNextChapter,
|
||||
object: nil,
|
||||
@@ -158,8 +158,8 @@ struct MiniPlayerView: View {
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(audioPlayer.nextChapter == nil)
|
||||
.opacity(audioPlayer.nextChapter == nil ? 0.4 : 1.0)
|
||||
.disabled(audioPlayer.absoluteNextChapter == nil)
|
||||
.opacity(audioPlayer.absoluteNextChapter == nil ? 0.4 : 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,7 +456,7 @@ struct FullPlayerView: View {
|
||||
|
||||
// ← previous chapter
|
||||
Button {
|
||||
if let prev = audioPlayer.prevChapter {
|
||||
if let prev = audioPlayer.absolutePrevChapter {
|
||||
onDismiss()
|
||||
NotificationCenter.default.post(
|
||||
name: .skipToPrevChapter,
|
||||
@@ -471,8 +471,8 @@ struct FullPlayerView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(audioPlayer.prevChapter == nil)
|
||||
.opacity(audioPlayer.prevChapter == nil ? 0.5 : 1.0)
|
||||
.disabled(audioPlayer.absolutePrevChapter == nil)
|
||||
.opacity(audioPlayer.absolutePrevChapter == nil ? 0.5 : 1.0)
|
||||
|
||||
// play / pause
|
||||
Button { audioPlayer.togglePlayPause() } label: {
|
||||
@@ -495,7 +495,7 @@ struct FullPlayerView: View {
|
||||
|
||||
// → next chapter
|
||||
Button {
|
||||
if let next = audioPlayer.nextChapter {
|
||||
if let next = audioPlayer.absoluteNextChapter {
|
||||
onDismiss()
|
||||
NotificationCenter.default.post(
|
||||
name: .skipToNextChapter,
|
||||
@@ -527,8 +527,8 @@ struct FullPlayerView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(audioPlayer.nextChapter == nil)
|
||||
.opacity(audioPlayer.nextChapter == nil ? 0.5 : 1.0)
|
||||
.disabled(audioPlayer.absoluteNextChapter == nil)
|
||||
.opacity(audioPlayer.absoluteNextChapter == nil ? 0.5 : 1.0)
|
||||
|
||||
// → skip forward 15s
|
||||
Button { audioPlayer.skip(by: 15) } label: {
|
||||
|
||||
Reference in New Issue
Block a user