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:
parent
820b35f5e6
commit
864560ef2e
@ -27,11 +27,7 @@ class SettingsPage extends StatelessWidget {
|
|||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.language),
|
leading: const Icon(Icons.language),
|
||||||
title: Text(l10n.language),
|
title: Text(l10n.language),
|
||||||
subtitle: Text(
|
subtitle: Text(settingsVm.getLanguageName(settingsVm.locale!)),
|
||||||
settingsVm.locale != null
|
|
||||||
? settingsVm.getLanguageName(settingsVm.locale!)
|
|
||||||
: l10n.systemDefault,
|
|
||||||
),
|
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () => _showLanguageDialog(context, settingsVm, l10n),
|
onTap: () => _showLanguageDialog(context, settingsVm, l10n),
|
||||||
),
|
),
|
||||||
@ -107,12 +103,12 @@ class _LanguageDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LanguageDialogState extends State<_LanguageDialog> {
|
class _LanguageDialogState extends State<_LanguageDialog> {
|
||||||
late Locale? _selectedLocale;
|
late String _selectedLanguage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedLocale = widget.vm.locale;
|
_selectedLanguage = widget.vm.locale!.languageCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -121,27 +117,27 @@ class _LanguageDialogState extends State<_LanguageDialog> {
|
|||||||
title: Text(widget.l10n.language),
|
title: Text(widget.l10n.language),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: SettingsViewModel.supportedLocales.map((locale) {
|
children: [
|
||||||
|
...SettingsViewModel.supportedLocales.map((locale) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(widget.vm.getLanguageName(locale)),
|
title: Text(widget.vm.getLanguageName(locale)),
|
||||||
leading: Radio<Locale>(
|
leading: Radio<String>(
|
||||||
value: locale,
|
value: locale.languageCode,
|
||||||
groupValue: _selectedLocale,
|
groupValue: _selectedLanguage,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() => _selectedLocale = value);
|
setState(() => _selectedLanguage = value!);
|
||||||
if (value != null) {
|
widget.vm.setLocale(locale);
|
||||||
widget.vm.setLocale(value);
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() => _selectedLocale = locale);
|
setState(() => _selectedLanguage = locale.languageCode);
|
||||||
widget.vm.setLocale(locale);
|
widget.vm.setLocale(locale);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class SettingsViewModel extends ChangeNotifier {
|
|||||||
];
|
];
|
||||||
|
|
||||||
void _loadSettings() {
|
void _loadSettings() {
|
||||||
_locale = dataSource.getLocale();
|
_locale = dataSource.getLocale() ?? const Locale('en');
|
||||||
_themeMode = dataSource.getThemeMode();
|
_themeMode = dataSource.getThemeMode();
|
||||||
logger.info('Settings loaded: locale=$_locale, themeMode=$_themeMode');
|
logger.info('Settings loaded: locale=$_locale, themeMode=$_themeMode');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,8 @@ class TaskRemoteDataSourceImpl implements TaskRemoteDataSource {
|
|||||||
final AppLogger logger;
|
final AppLogger logger;
|
||||||
final http.Client _client;
|
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({
|
TaskRemoteDataSourceImpl({
|
||||||
required this.logger,
|
required this.logger,
|
||||||
|
|||||||
@ -10,12 +10,25 @@ import '../widgets/task_tile.dart';
|
|||||||
import '../widgets/filter_chips.dart';
|
import '../widgets/filter_chips.dart';
|
||||||
|
|
||||||
class DailyAgendaPage extends StatelessWidget {
|
class DailyAgendaPage extends StatelessWidget {
|
||||||
const DailyAgendaPage({super.key});
|
final String? initialDate;
|
||||||
|
|
||||||
|
const DailyAgendaPage({super.key, this.initialDate});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ChangeNotifierProvider(
|
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(),
|
child: const _DailyAgendaView(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,10 +118,55 @@ class TaskTile extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
PopupMenuButton<String>(
|
||||||
icon: const Icon(Icons.schedule),
|
onSelected: (value) async {
|
||||||
onPressed: onReschedule,
|
if (value == 'reschedule') {
|
||||||
tooltip: l10n.rescheduleToTomorrow,
|
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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -12,7 +12,10 @@ class AppRouter {
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'daily',
|
name: 'daily',
|
||||||
builder: (context, state) => const DailyAgendaPage(),
|
builder: (context, state) {
|
||||||
|
final dateStr = state.uri.queryParameters['date'];
|
||||||
|
return DailyAgendaPage(initialDate: dateStr);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/calendar',
|
path: '/calendar',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user