feat: make prev/next chapter buttons absolute navigation
Previously, prev/next buttons were relative to the currently playing chapter and only worked for one navigation step. They relied on audioPlayer.prevChapter and audioPlayer.nextChapter which were set when loading audio and never updated. Now buttons use absolute chapter navigation: - Previous: always navigate to (current chapter - 1) - Next: always navigate to (current chapter + 1) - Bounded by chapter 1 and max chapter number - Works continuously without stopping after one navigation Added AudioPlayerService.absolutePrevChapter and .absoluteNextChapter computed properties that calculate the prev/next chapter numbers based on the current chapter and total chapters list. Updated both mini player and full player to use absolute navigation.
This commit is contained in:
@@ -47,6 +47,20 @@ final class AudioPlayerService: ObservableObject {
|
||||
default: return true
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
|
||||
@@ -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