look aheead limit defined by user
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
||||
import '../models/bank.dart';
|
||||
import '../models/transaction_meta.dart';
|
||||
import '../providers/bank_provider.dart';
|
||||
import '../providers/preferences_provider.dart';
|
||||
import '../providers/tx_meta_provider.dart';
|
||||
import '../screens/transaction_detail_screen.dart';
|
||||
import '../utils/app_theme.dart';
|
||||
@@ -37,8 +38,6 @@ class _LookAheadScreenState extends State<LookAheadScreen> {
|
||||
List<RecurringItem>? _cachedItems;
|
||||
int? _itemsCacheKey;
|
||||
|
||||
static const _maxYearsAhead = 5;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -51,11 +50,6 @@ class _LookAheadScreenState extends State<LookAheadScreen> {
|
||||
|
||||
DateTime get _minMonth => monthStart(_today);
|
||||
|
||||
DateTime get _maxMonth {
|
||||
final t = _today;
|
||||
return DateTime(t.year + _maxYearsAhead, t.month);
|
||||
}
|
||||
|
||||
/// Inclusive end date for the selected period.
|
||||
DateTime get _through {
|
||||
if (_period == _PeriodMode.yearly) {
|
||||
@@ -69,50 +63,67 @@ class _LookAheadScreenState extends State<LookAheadScreen> {
|
||||
return formatMonthYear(_cursor);
|
||||
}
|
||||
|
||||
bool get _canGoBack {
|
||||
DateTime _maxMonthFor(int maxYears) {
|
||||
final t = _today;
|
||||
return DateTime(t.year + maxYears, t.month);
|
||||
}
|
||||
|
||||
bool _canGoBack() {
|
||||
if (_period == _PeriodMode.yearly) {
|
||||
return _cursor.year > _today.year;
|
||||
}
|
||||
return monthStart(_cursor).isAfter(_minMonth);
|
||||
}
|
||||
|
||||
bool get _canGoForward {
|
||||
bool _canGoForward(int maxYears) {
|
||||
if (_period == _PeriodMode.yearly) {
|
||||
return _cursor.year < _today.year + _maxYearsAhead;
|
||||
return _cursor.year < _today.year + maxYears;
|
||||
}
|
||||
return monthStart(_cursor).isBefore(_maxMonth);
|
||||
return monthStart(_cursor).isBefore(_maxMonthFor(maxYears));
|
||||
}
|
||||
|
||||
void _setPeriod(_PeriodMode mode) {
|
||||
void _clampCursor(int maxYears) {
|
||||
if (_period == _PeriodMode.yearly) {
|
||||
if (_cursor.year > _today.year + maxYears) {
|
||||
_cursor = DateTime(_today.year + maxYears);
|
||||
}
|
||||
} else {
|
||||
final maxMonth = _maxMonthFor(maxYears);
|
||||
if (_cursor.isAfter(maxMonth)) _cursor = maxMonth;
|
||||
}
|
||||
}
|
||||
|
||||
void _setPeriod(_PeriodMode mode, int maxYears) {
|
||||
if (mode == _period) return;
|
||||
setState(() {
|
||||
_period = mode;
|
||||
if (mode == _PeriodMode.yearly) {
|
||||
_cursor = DateTime(_cursor.year);
|
||||
if (_cursor.year < _today.year) _cursor = DateTime(_today.year);
|
||||
if (_cursor.year > _today.year + _maxYearsAhead) {
|
||||
_cursor = DateTime(_today.year + _maxYearsAhead);
|
||||
if (_cursor.year > _today.year + maxYears) {
|
||||
_cursor = DateTime(_today.year + maxYears);
|
||||
}
|
||||
} else {
|
||||
final month = DateTime(_cursor.year, _today.month);
|
||||
_cursor = month.isBefore(_minMonth) ? _minMonth : monthStart(month);
|
||||
if (_cursor.isAfter(_maxMonth)) _cursor = _maxMonth;
|
||||
final maxMonth = _maxMonthFor(maxYears);
|
||||
if (_cursor.isAfter(maxMonth)) _cursor = maxMonth;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _go(int delta) {
|
||||
void _go(int delta, int maxYears) {
|
||||
setState(() {
|
||||
if (_period == _PeriodMode.yearly) {
|
||||
final next = DateTime(_cursor.year + delta);
|
||||
if (next.year < _today.year ||
|
||||
next.year > _today.year + _maxYearsAhead) {
|
||||
if (next.year < _today.year || next.year > _today.year + maxYears) {
|
||||
return;
|
||||
}
|
||||
_cursor = next;
|
||||
} else {
|
||||
final next = addMonths(monthStart(_cursor), delta);
|
||||
if (next.isBefore(_minMonth) || next.isAfter(_maxMonth)) return;
|
||||
final maxMonth = _maxMonthFor(maxYears);
|
||||
if (next.isBefore(_minMonth) || next.isAfter(maxMonth)) return;
|
||||
_cursor = next;
|
||||
}
|
||||
});
|
||||
@@ -131,6 +142,18 @@ class _LookAheadScreenState extends State<LookAheadScreen> {
|
||||
Widget _buildContent(BuildContext context) {
|
||||
final bank = context.watch<BankController>();
|
||||
final meta = context.watch<TxMetaController>();
|
||||
final maxYears =
|
||||
context.watch<PreferencesController>().lookAheadMaxYears;
|
||||
final maxMonth = _maxMonthFor(maxYears);
|
||||
final needsClamp = _period == _PeriodMode.yearly
|
||||
? _cursor.year > _today.year + maxYears
|
||||
: _cursor.isAfter(maxMonth);
|
||||
if (needsClamp) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
setState(() => _clampCursor(maxYears));
|
||||
});
|
||||
}
|
||||
final theme = Theme.of(context);
|
||||
final colors = theme.colorScheme;
|
||||
|
||||
@@ -202,15 +225,15 @@ class _LookAheadScreenState extends State<LookAheadScreen> {
|
||||
children: [
|
||||
_PeriodToolbar(
|
||||
period: _period,
|
||||
onPeriodChanged: _setPeriod,
|
||||
onPeriodChanged: (mode) => _setPeriod(mode, maxYears),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_PeriodNavigator(
|
||||
title: _periodTitle,
|
||||
canGoBack: _canGoBack,
|
||||
canGoForward: _canGoForward,
|
||||
onBack: () => _go(-1),
|
||||
onForward: () => _go(1),
|
||||
canGoBack: _canGoBack(),
|
||||
canGoForward: _canGoForward(maxYears),
|
||||
onBack: () => _go(-1, maxYears),
|
||||
onForward: () => _go(1, maxYears),
|
||||
backTooltip:
|
||||
_period == _PeriodMode.yearly ? 'Previous year' : 'Previous month',
|
||||
forwardTooltip:
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../providers/bank_provider.dart';
|
||||
import '../providers/preferences_provider.dart';
|
||||
import '../providers/theme_provider.dart';
|
||||
import '../utils/connect_bank.dart';
|
||||
import '../utils/constants.dart';
|
||||
@@ -110,6 +111,7 @@ class SettingsScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = context.watch<ThemeController>();
|
||||
final prefs = context.watch<PreferencesController>();
|
||||
final bank = context.watch<BankController>();
|
||||
final theme = Theme.of(context);
|
||||
|
||||
@@ -150,6 +152,31 @@ class SettingsScreen extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
Text('Look ahead', style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.event_repeat_outlined),
|
||||
title: const Text('Projection horizon'),
|
||||
subtitle: Text(
|
||||
'Navigate up to ${prefs.lookAheadMaxYears} year'
|
||||
'${prefs.lookAheadMaxYears == 1 ? '' : 's'} ahead',
|
||||
),
|
||||
trailing: DropdownButton<int>(
|
||||
value: prefs.lookAheadMaxYears,
|
||||
underline: const SizedBox.shrink(),
|
||||
items: [
|
||||
for (final y in const [1, 2, 3, 5, 7, 10, 15, 20, 30])
|
||||
DropdownMenuItem(value: y, child: Text('$y')),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
prefs.setLookAheadMaxYears(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
Text('Backend', style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
Card(
|
||||
|
||||
Reference in New Issue
Block a user