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>
This commit is contained in:
94
Bible-Week/ReadingPlanState.swift
Normal file
94
Bible-Week/ReadingPlanState.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// ReadingPlanState.swift
|
||||
// Bible-Week
|
||||
//
|
||||
// Estado persistente do Plano Bíblia em 180 Dias: progresso de leitura
|
||||
// e preferências do lembrete diário.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
@Model
|
||||
final class ReadingPlanState {
|
||||
var startedAt: Date
|
||||
var isPaused: Bool
|
||||
|
||||
/// Chaves "dia-trilha" das leituras concluídas, ex.: "34-nt".
|
||||
var completedKeys: [String]
|
||||
|
||||
// Preferências do lembrete diário. Sobrevivem a pausa e reinício do plano.
|
||||
var reminderEnabled: Bool
|
||||
var reminderHour: Int
|
||||
var reminderMinute: Int
|
||||
var reminderSoundEnabled: Bool
|
||||
var hasSeenReminderPrompt: Bool
|
||||
|
||||
init(startedAt: Date = .now) {
|
||||
self.startedAt = startedAt
|
||||
self.isPaused = false
|
||||
self.completedKeys = []
|
||||
self.reminderEnabled = false
|
||||
self.reminderHour = 20
|
||||
self.reminderMinute = 0
|
||||
self.reminderSoundEnabled = true
|
||||
self.hasSeenReminderPrompt = false
|
||||
}
|
||||
|
||||
// MARK: - Progresso
|
||||
|
||||
private static func key(day: Int, track: PlanTrack) -> String {
|
||||
"\(day)-\(track.rawValue)"
|
||||
}
|
||||
|
||||
func isCompleted(day: Int, track: PlanTrack) -> Bool {
|
||||
completedKeys.contains(Self.key(day: day, track: track))
|
||||
}
|
||||
|
||||
func setCompleted(_ completed: Bool, day: Int, track: PlanTrack) {
|
||||
let key = Self.key(day: day, track: track)
|
||||
if completed {
|
||||
if !completedKeys.contains(key) { completedKeys.append(key) }
|
||||
} else {
|
||||
completedKeys.removeAll { $0 == key }
|
||||
}
|
||||
}
|
||||
|
||||
func isDayCompleted(_ day: Int) -> Bool {
|
||||
PlanTrack.allCases.allSatisfy { isCompleted(day: day, track: $0) }
|
||||
}
|
||||
|
||||
/// Primeiro dia com leitura pendente (1...180). Se tudo concluído, 180.
|
||||
var currentDay: Int {
|
||||
(1...PlanSchedule.totalDays).first { !isDayCompleted($0) } ?? PlanSchedule.totalDays
|
||||
}
|
||||
|
||||
var completedDaysCount: Int {
|
||||
(1...PlanSchedule.totalDays).count { isDayCompleted($0) }
|
||||
}
|
||||
|
||||
var isCompleted: Bool {
|
||||
completedDaysCount == PlanSchedule.totalDays
|
||||
}
|
||||
|
||||
/// Fração concluída do plano (0...1).
|
||||
var progress: Double {
|
||||
Double(completedDaysCount) / Double(PlanSchedule.totalDays)
|
||||
}
|
||||
|
||||
// MARK: - Lembrete
|
||||
|
||||
/// Horário do lembrete formatado, ex.: "20:00".
|
||||
var reminderTimeText: String {
|
||||
String(format: "%02d:%02d", reminderHour, reminderMinute)
|
||||
}
|
||||
|
||||
// MARK: - Ciclo de vida do plano
|
||||
|
||||
/// Recomeça o plano do dia 1, preservando as preferências de lembrete.
|
||||
func restart() {
|
||||
completedKeys = []
|
||||
startedAt = .now
|
||||
isPaused = false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user