Files
bible/Bible-Week/ChapterReaderView.swift
Matheus A Silveira 80185726af Adiciona Plano Bíblia em 180 Dias com lembrete diário e ícone do app
- Leitor bíblico consumindo a API NBV (bible.falehandix.com.br): lista de
  livros por testamento e leitura de capítulos
- Plano de 180 dias com três trilhas diárias (História, Sabedoria e
  Profetas, Novo Testamento) geradas deterministicamente sobre o cânon
  embutido (1.189 capítulos, cobertura testada)
- Lembrete diário via notificação local recorrente (UNCalendarNotificationTrigger,
  identificador fixo, sem duplicatas), com pedido de autorização em contexto,
  configurações de horário/som, e deep link da notificação para a Leitura de Hoje
- Estado do plano em SwiftData: progresso, pausa/retomada, reinício e
  conclusão preservando preferências do lembrete
- 15 testes unitários (Testing) e 2 testes de UI (XCUIAutomation)
- Ícone do app a partir do logo biblia.png

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 18:54:48 -03:00

97 lines
3.1 KiB
Swift

//
// ChapterReaderView.swift
// Bible-Week
//
// Seleciona o capítulo e exibe o texto dos versículos.
//
import SwiftUI
struct ChapterReaderView: View {
let book: Book
@State private var selectedChapter = 1
@State private var chapter: Chapter?
@State private var isLoading = false
@State private var errorMessage: String?
var body: some View {
VStack(spacing: 0) {
chapterPicker
Divider()
content
}
.navigationTitle(book.name)
.navigationBarTitleDisplayMode(.inline)
.task(id: selectedChapter) {
await loadChapter()
}
}
/// Seletor horizontal com todos os capítulos do livro.
private var chapterPicker: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(1...book.chapterCount, id: \.self) { number in
Button {
selectedChapter = number
} label: {
Text("\(number)")
.font(.subheadline.weight(.medium))
.frame(minWidth: 36, minHeight: 36)
.background(
Circle()
.fill(number == selectedChapter ? Color.accentColor : Color(.secondarySystemBackground))
)
.foregroundStyle(number == selectedChapter ? .white : .primary)
}
}
}
.padding(.horizontal)
.padding(.vertical, 8)
}
}
@ViewBuilder
private var content: some View {
if isLoading {
Spacer()
ProgressView("Carregando capítulo...")
Spacer()
} else if let errorMessage {
Spacer()
ContentUnavailableView {
Label("Erro ao carregar", systemImage: "exclamationmark.triangle")
} description: {
Text(errorMessage)
} actions: {
Button("Tentar novamente") { Task { await loadChapter() } }
}
Spacer()
} else if let chapter {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
ForEach(chapter.verses) { verse in
Text("\(Text("\(verse.verse)").font(.caption2).foregroundColor(.accentColor).baselineOffset(4)) \(verse.text)")
.font(.body)
.fixedSize(horizontal: false, vertical: true)
}
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
private func loadChapter() async {
isLoading = true
errorMessage = nil
defer { isLoading = false }
do {
chapter = try await BibleAPI.chapter(book: book.code, chapter: selectedChapter)
} catch {
errorMessage = "Verifique sua conexão e tente novamente."
}
}
}