feature/backend-rewrite #2

Open
kamil wants to merge 236 commits from feature/backend-rewrite into main
Showing only changes of commit fc5cd30c93 - Show all commits

View File

@@ -190,6 +190,7 @@ struct FullPlayerView: View {
@State private var isFavorited = false
@State private var isLiked = false
@State private var showingSpeedMenu = false
@State private var showingChaptersList = false
var body: some View {
ZStack {
@@ -515,14 +516,15 @@ struct FullPlayerView: View {
.disabled(true)
// Queue (show chapters list)
Button {} label: {
Button {
showingChaptersList = true
} label: {
Image(systemName: "list.bullet")
.font(.system(size: 22))
.foregroundStyle(.white.opacity(0.7))
.frame(maxWidth: .infinity)
}
.buttonStyle(.plain)
.disabled(true)
// Sleep timer (placeholder)
Button {} label: {
@@ -538,6 +540,31 @@ struct FullPlayerView: View {
.padding(.bottom, 24)
}
}
.sheet(isPresented: $showingChaptersList) {
ChaptersListSheet(
chapters: audioPlayer.chapters,
currentChapter: audioPlayer.chapter,
onChapterSelect: { selectedChapter in
showingChaptersList = false
// Post notification to navigate to selected chapter
if selectedChapter > audioPlayer.chapter {
NotificationCenter.default.post(
name: .skipToNextChapter,
object: nil,
userInfo: ["next": selectedChapter]
)
} else if selectedChapter < audioPlayer.chapter {
NotificationCenter.default.post(
name: .skipToPrevChapter,
object: nil,
userInfo: ["prev": selectedChapter]
)
}
}
)
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
}
private func formatTime(_ seconds: Double) -> String {
@@ -575,6 +602,92 @@ struct FullPlayerView: View {
}
}
// MARK: - Chapters List Sheet
struct ChaptersListSheet: View {
let chapters: [ChapterIndexBrief]
let currentChapter: Int
let onChapterSelect: (Int) -> Void
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
ScrollViewReader { proxy in
List {
ForEach(chapters, id: \.number) { chapter in
Button {
onChapterSelect(chapter.number)
} label: {
HStack(spacing: 12) {
// Chapter number badge
Text("\(chapter.number)")
.font(.caption.bold())
.foregroundStyle(chapter.number == currentChapter ? .white : .secondary)
.frame(width: 44, height: 44)
.background(
Circle()
.fill(chapter.number == currentChapter ? Color.amber : Color.gray.opacity(0.2))
)
// Chapter title
VStack(alignment: .leading, spacing: 4) {
Text(chapter.title.strippingTrailingDate())
.font(.subheadline.weight(chapter.number == currentChapter ? .semibold : .regular))
.foregroundStyle(chapter.number == currentChapter ? .primary : .primary)
.lineLimit(2)
if chapter.number == currentChapter {
Text("Now Playing")
.font(.caption2)
.foregroundStyle(.amber)
}
}
Spacer()
// Checkmark for current chapter
if chapter.number == currentChapter {
Image(systemName: "checkmark")
.font(.caption.bold())
.foregroundStyle(.amber)
}
}
.padding(.vertical, 8)
}
.buttonStyle(.plain)
.listRowBackground(
chapter.number == currentChapter
? Color.amber.opacity(0.1)
: Color.clear
)
.id(chapter.number)
}
}
.listStyle(.plain)
.onAppear {
// Scroll to current chapter on appear
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation {
proxy.scrollTo(currentChapter, anchor: .center)
}
}
}
}
.navigationTitle("Chapters")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
dismiss()
}
.fontWeight(.semibold)
}
}
}
}
}
// MARK: - Custom seek slider
// A thicker, rounded-thumb slider that matches the amber design language.