128 lines
3.9 KiB
Dart
128 lines
3.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../models/scripture.dart';
|
|
import '../models/cycle_entry.dart';
|
|
// For IterableExtension
|
|
|
|
// State for ScriptureProvider
|
|
class ScriptureState {
|
|
final Scripture? currentScripture;
|
|
final CyclePhase? currentPhase;
|
|
final int currentIndex; // Index within the phase-specific list
|
|
final int? maxIndex; // Max number of scriptures for the current phase
|
|
|
|
ScriptureState({
|
|
this.currentScripture,
|
|
this.currentPhase,
|
|
this.currentIndex = 0,
|
|
this.maxIndex,
|
|
});
|
|
|
|
ScriptureState copyWith({
|
|
Scripture? currentScripture,
|
|
CyclePhase? currentPhase,
|
|
int? currentIndex,
|
|
int? maxIndex,
|
|
}) {
|
|
return ScriptureState(
|
|
currentScripture: currentScripture ?? this.currentScripture,
|
|
currentPhase: currentPhase ?? this.currentPhase,
|
|
currentIndex: currentIndex ?? this.currentIndex,
|
|
maxIndex: maxIndex ?? this.maxIndex,
|
|
);
|
|
}
|
|
}
|
|
|
|
// StateNotifier for ScriptureProvider
|
|
class ScriptureNotifier extends StateNotifier<ScriptureState> {
|
|
final ScriptureDatabase _scriptureDatabase;
|
|
|
|
ScriptureNotifier(this._scriptureDatabase) : super(ScriptureState()) {
|
|
// We don't initialize here directly, as we need the phase from other providers.
|
|
// Initialization will be triggered by the UI.
|
|
}
|
|
|
|
// Initialize/refresh scripture for a given phase
|
|
// This should be called by the consuming widget when the phase changes or on initial load.
|
|
Future<void> initializeScripture(CyclePhase phase) async {
|
|
// Only re-initialize if the phase has changed or no scripture is currently set
|
|
if (state.currentPhase != phase || state.currentScripture == null) {
|
|
final scriptureCount =
|
|
_scriptureDatabase.getScriptureCountForPhase(phase.name);
|
|
if (scriptureCount > 0) {
|
|
// Use day of year to get a stable initial scripture for the day
|
|
final dayOfYear = DateTime.now()
|
|
.difference(DateTime(DateTime.now().year, 1, 1))
|
|
.inDays;
|
|
final initialIndex = dayOfYear % scriptureCount;
|
|
state = state.copyWith(
|
|
currentPhase: phase,
|
|
currentIndex: initialIndex,
|
|
maxIndex: scriptureCount,
|
|
currentScripture: _scriptureDatabase.getScriptureForPhaseByIndex(
|
|
phase.name, initialIndex),
|
|
);
|
|
} else {
|
|
state = state.copyWith(
|
|
currentPhase: phase,
|
|
currentScripture: null,
|
|
currentIndex: 0,
|
|
maxIndex: 0,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void getNextScripture() {
|
|
if (state.currentPhase == null ||
|
|
state.maxIndex == null ||
|
|
state.maxIndex == 0) {
|
|
return;
|
|
}
|
|
|
|
final nextIndex = (state.currentIndex + 1) % state.maxIndex!;
|
|
_updateScripture(nextIndex);
|
|
}
|
|
|
|
void getPreviousScripture() {
|
|
if (state.currentPhase == null ||
|
|
state.maxIndex == null ||
|
|
state.maxIndex == 0) {
|
|
return;
|
|
}
|
|
|
|
final prevIndex =
|
|
(state.currentIndex - 1 + state.maxIndex!) % state.maxIndex!;
|
|
_updateScripture(prevIndex);
|
|
}
|
|
|
|
void getRandomScripture() {
|
|
if (state.currentPhase == null ||
|
|
state.maxIndex == null ||
|
|
state.maxIndex == 0) {
|
|
return;
|
|
}
|
|
|
|
// Use a proper random number generator for better randomness
|
|
final randomIndex = DateTime.now().microsecondsSinceEpoch %
|
|
state.maxIndex!; // Still using timestamp for simplicity
|
|
_updateScripture(randomIndex);
|
|
}
|
|
|
|
void _updateScripture(int newIndex) {
|
|
if (state.currentPhase == null) return;
|
|
final newScripture = _scriptureDatabase.getScriptureForPhaseByIndex(
|
|
state.currentPhase!.name, newIndex);
|
|
state = state.copyWith(
|
|
currentIndex: newIndex,
|
|
currentScripture: newScripture,
|
|
);
|
|
}
|
|
}
|
|
|
|
final scriptureDatabaseProvider = Provider((ref) => ScriptureDatabase());
|
|
|
|
final scriptureProvider =
|
|
StateNotifierProvider<ScriptureNotifier, ScriptureState>((ref) {
|
|
return ScriptureNotifier(ref.watch(scriptureDatabaseProvider));
|
|
});
|