Fix calendar navigation, task deletion, and language defaults

- Add date parameter support to DailyAgendaPage for calendar navigation
- Add popup menu to task tile with delete and reschedule options
- Set default locale to English when none is saved
- Update API base URL comment for desktop vs Android
This commit is contained in:
m3mo 2026-02-02 21:07:27 +01:00
parent 820b35f5e6
commit 864560ef2e
6 changed files with 93 additions and 35 deletions

View File

@ -27,11 +27,7 @@ class SettingsPage extends StatelessWidget {
ListTile(
leading: const Icon(Icons.language),
title: Text(l10n.language),
subtitle: Text(
settingsVm.locale != null
? settingsVm.getLanguageName(settingsVm.locale!)
: l10n.systemDefault,
),
subtitle: Text(settingsVm.getLanguageName(settingsVm.locale!)),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showLanguageDialog(context, settingsVm, l10n),
),
@ -107,12 +103,12 @@ class _LanguageDialog extends StatefulWidget {
}
class _LanguageDialogState extends State<_LanguageDialog> {
late Locale? _selectedLocale;
late String _selectedLanguage;
@override
void initState() {
super.initState();
_selectedLocale = widget.vm.locale;
_selectedLanguage = widget.vm.locale!.languageCode;
}
@override
@ -121,27 +117,27 @@ class _LanguageDialogState extends State<_LanguageDialog> {
title: Text(widget.l10n.language),
content: Column(
mainAxisSize: MainAxisSize.min,
children: SettingsViewModel.supportedLocales.map((locale) {
return ListTile(
title: Text(widget.vm.getLanguageName(locale)),
leading: Radio<Locale>(
value: locale,
groupValue: _selectedLocale,
onChanged: (value) {
setState(() => _selectedLocale = value);
if (value != null) {
widget.vm.setLocale(value);
children: [
...SettingsViewModel.supportedLocales.map((locale) {
return ListTile(
title: Text(widget.vm.getLanguageName(locale)),
leading: Radio<String>(
value: locale.languageCode,
groupValue: _selectedLanguage,
onChanged: (value) {
setState(() => _selectedLanguage = value!);
widget.vm.setLocale(locale);
Navigator.pop(context);
}
},
),
onTap: () {
setState(() => _selectedLanguage = locale.languageCode);
widget.vm.setLocale(locale);
Navigator.pop(context);
},
),
onTap: () {
setState(() => _selectedLocale = locale);
widget.vm.setLocale(locale);
Navigator.pop(context);
},
);
}).toList(),
);
}),
],
),
actions: [
TextButton(

View File

@ -26,7 +26,7 @@ class SettingsViewModel extends ChangeNotifier {
];
void _loadSettings() {
_locale = dataSource.getLocale();
_locale = dataSource.getLocale() ?? const Locale('en');
_themeMode = dataSource.getThemeMode();
logger.info('Settings loaded: locale=$_locale, themeMode=$_themeMode');
}

View File

@ -21,7 +21,8 @@ class TaskRemoteDataSourceImpl implements TaskRemoteDataSource {
final AppLogger logger;
final http.Client _client;
static const String _baseUrl = 'http://10.0.2.2:8000';
// Use 10.0.2.2 for Android emulator, localhost for desktop/web
static const String _baseUrl = 'http://localhost:8000';
TaskRemoteDataSourceImpl({
required this.logger,

View File

@ -10,12 +10,25 @@ import '../widgets/task_tile.dart';
import '../widgets/filter_chips.dart';
class DailyAgendaPage extends StatelessWidget {
const DailyAgendaPage({super.key});
final String? initialDate;
const DailyAgendaPage({super.key, this.initialDate});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => getIt<DailyTasksViewModel>()..loadTasks(),
create: (_) {
final vm = getIt<DailyTasksViewModel>();
if (initialDate != null) {
final date = DateTime.tryParse(initialDate!);
if (date != null) {
vm.setSelectedDate(date);
return vm;
}
}
vm.loadTasks();
return vm;
},
child: const _DailyAgendaView(),
);
}

View File

@ -118,10 +118,55 @@ class TaskTile extends StatelessWidget {
],
),
),
IconButton(
icon: const Icon(Icons.schedule),
onPressed: onReschedule,
tooltip: l10n.rescheduleToTomorrow,
PopupMenuButton<String>(
onSelected: (value) async {
if (value == 'reschedule') {
onReschedule();
} else if (value == 'delete') {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(l10n.deleteTask),
content: Text(l10n.deleteTaskConfirm),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text(l10n.cancel),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text(l10n.delete),
),
],
),
);
if (confirm == true) {
onDelete();
}
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'reschedule',
child: Row(
children: [
const Icon(Icons.schedule),
const SizedBox(width: 8),
Text(l10n.rescheduleToTomorrow),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
Icon(Icons.delete, color: Theme.of(context).colorScheme.error),
const SizedBox(width: 8),
Text(l10n.delete, style: TextStyle(color: Theme.of(context).colorScheme.error)),
],
),
),
],
),
],
),

View File

@ -12,7 +12,10 @@ class AppRouter {
GoRoute(
path: '/',
name: 'daily',
builder: (context, state) => const DailyAgendaPage(),
builder: (context, state) {
final dateStr = state.uri.queryParameters['date'];
return DailyAgendaPage(initialDate: dateStr);
},
),
GoRoute(
path: '/calendar',