- 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>
97 lines
3.1 KiB
Swift
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."
|
|
}
|
|
}
|
|
}
|