Skip to content

Commit cb68534

Browse files
mikebarkminCopilot
andcommitted
feat: improve error handling and notify users on failure
- Add showErrorSnackBar helper with error color styling - Fix _ChangePassphraseForm: inline error for wrong passphrase, loading state, success message, and clear fields on success - Add try/catch with error snackbars to groups_screen, group_detail_screen, and student_detail_screen - Use showErrorSnackBar in setup_screen, webdav_restore_flow, and database_selection_sheet - Add passphrase_changed translation key (EN + DE) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 822898f commit cb68534

10 files changed

Lines changed: 398 additions & 159 deletions

File tree

assets/translations/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
"open_student": "Schüler:in öffnen",
195195
"origin_note": "Hinweis",
196196
"overwrite": "Überschreiben",
197+
"passphrase_changed": "Passphrase geändert.",
197198
"passphrase_mismatch": "Die Passphrasen stimmen nicht überein.",
198199
"passphrase_required": "Bitte gib eine Passphrase ein.",
199200
"populate_from_group": "Aus Gruppe übernehmen",

assets/translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
"open_student": "Open student",
195195
"origin_note": "Note",
196196
"overwrite": "Overwrite",
197+
"passphrase_changed": "Passphrase changed.",
197198
"passphrase_mismatch": "Passphrases do not match.",
198199
"passphrase_required": "Please enter a passphrase.",
199200
"populate_from_group": "Populate from group",

lib/features/groups/group_detail_screen.dart

Lines changed: 166 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:convert';
2+
import 'dart:developer' as developer;
23
import 'dart:io';
34

45
import 'package:file_picker/file_picker.dart';
@@ -394,16 +395,27 @@ class GroupDetailScreen extends ConsumerWidget {
394395
return;
395396
}
396397

397-
await ref
398-
.read(studentRepositoryProvider)
399-
.addStudent(
400-
groupId: groupId,
401-
firstName: result.firstName,
402-
lastName: result.lastName,
403-
originNote: result.originNote,
404-
avatarJson: result.avatarJson,
405-
);
406-
_refreshStudentSection(ref, groupId);
398+
try {
399+
await ref
400+
.read(studentRepositoryProvider)
401+
.addStudent(
402+
groupId: groupId,
403+
firstName: result.firstName,
404+
lastName: result.lastName,
405+
originNote: result.originNote,
406+
avatarJson: result.avatarJson,
407+
);
408+
_refreshStudentSection(ref, groupId);
409+
} catch (e, st) {
410+
developer.log(
411+
'Failed to add student',
412+
name: 'classi.group_detail',
413+
level: 1000,
414+
error: e,
415+
stackTrace: st,
416+
);
417+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
418+
}
407419
}
408420

409421
Future<void> _batchCreateStudents(
@@ -416,21 +428,33 @@ class GroupDetailScreen extends ConsumerWidget {
416428
return;
417429
}
418430

419-
await ref
420-
.read(studentRepositoryProvider)
421-
.addStudents(groupId: groupId, students: drafts);
422-
_refreshStudentSection(ref, groupId);
423-
if (!context.mounted) {
424-
return;
425-
}
426-
427-
ScaffoldMessenger.of(context).showSnackBar(
428-
SnackBar(
429-
content: Text(
430-
'students_created'.tr(namedArgs: {'count': drafts.length.toString()}),
431+
try {
432+
await ref
433+
.read(studentRepositoryProvider)
434+
.addStudents(groupId: groupId, students: drafts);
435+
_refreshStudentSection(ref, groupId);
436+
if (!context.mounted) {
437+
return;
438+
}
439+
ScaffoldMessenger.of(context).showSnackBar(
440+
SnackBar(
441+
content: Text(
442+
'students_created'.tr(
443+
namedArgs: {'count': drafts.length.toString()},
444+
),
445+
),
431446
),
432-
),
433-
);
447+
);
448+
} catch (e, st) {
449+
developer.log(
450+
'Failed to batch create students',
451+
name: 'classi.group_detail',
452+
level: 1000,
453+
error: e,
454+
stackTrace: st,
455+
);
456+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
457+
}
434458
}
435459

436460
Future<void> _importWebUntisStudents(
@@ -518,14 +542,25 @@ class GroupDetailScreen extends ConsumerWidget {
518542
return;
519543
}
520544

521-
await ref
522-
.read(listRepositoryProvider)
523-
.createListWithOptions(
524-
groupId: group.id,
525-
name: result.name,
526-
populateFromGroupStudents: result.populateFromGroupStudents,
527-
sortField: ref.read(studentSortFieldProvider),
528-
);
545+
try {
546+
await ref
547+
.read(listRepositoryProvider)
548+
.createListWithOptions(
549+
groupId: group.id,
550+
name: result.name,
551+
populateFromGroupStudents: result.populateFromGroupStudents,
552+
sortField: ref.read(studentSortFieldProvider),
553+
);
554+
} catch (e, st) {
555+
developer.log(
556+
'Failed to create list',
557+
name: 'classi.group_detail',
558+
level: 1000,
559+
error: e,
560+
stackTrace: st,
561+
);
562+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
563+
}
529564
}
530565

531566
Future<void> _addGroupNote(
@@ -545,15 +580,26 @@ class GroupDetailScreen extends ConsumerWidget {
545580
return;
546581
}
547582

548-
await ref
549-
.read(noteRepositoryProvider)
550-
.saveNote(
551-
body: result.body,
552-
groupId: result.groupId,
553-
studentIds: result.studentIds,
554-
isTodo: result.isTodo,
555-
createdAt: result.createdAt,
556-
);
583+
try {
584+
await ref
585+
.read(noteRepositoryProvider)
586+
.saveNote(
587+
body: result.body,
588+
groupId: result.groupId,
589+
studentIds: result.studentIds,
590+
isTodo: result.isTodo,
591+
createdAt: result.createdAt,
592+
);
593+
} catch (e, st) {
594+
developer.log(
595+
'Failed to add note',
596+
name: 'classi.group_detail',
597+
level: 1000,
598+
error: e,
599+
stackTrace: st,
600+
);
601+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
602+
}
557603
}
558604

559605
Future<void> _editList(
@@ -571,9 +617,20 @@ class GroupDetailScreen extends ConsumerWidget {
571617
return;
572618
}
573619

574-
await ref
575-
.read(listRepositoryProvider)
576-
.renameList(listId: list.id, name: name);
620+
try {
621+
await ref
622+
.read(listRepositoryProvider)
623+
.renameList(listId: list.id, name: name);
624+
} catch (e, st) {
625+
developer.log(
626+
'Failed to rename list',
627+
name: 'classi.group_detail',
628+
level: 1000,
629+
error: e,
630+
stackTrace: st,
631+
);
632+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
633+
}
577634
}
578635

579636
Future<String?> _showListNameDialog({
@@ -618,7 +675,18 @@ class GroupDetailScreen extends ConsumerWidget {
618675
body: list.name,
619676
);
620677
if (confirmed) {
621-
await ref.read(listRepositoryProvider).deleteList(list.id);
678+
try {
679+
await ref.read(listRepositoryProvider).deleteList(list.id);
680+
} catch (e, st) {
681+
developer.log(
682+
'Failed to delete list',
683+
name: 'classi.group_detail',
684+
level: 1000,
685+
error: e,
686+
stackTrace: st,
687+
);
688+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
689+
}
622690
}
623691
}
624692

@@ -644,16 +712,27 @@ class GroupDetailScreen extends ConsumerWidget {
644712
return;
645713
}
646714

647-
await ref
648-
.read(noteRepositoryProvider)
649-
.updateNote(
650-
note: note,
651-
body: result.body,
652-
groupId: result.groupId,
653-
studentIds: result.studentIds,
654-
isTodo: result.isTodo,
655-
createdAt: result.createdAt,
656-
);
715+
try {
716+
await ref
717+
.read(noteRepositoryProvider)
718+
.updateNote(
719+
note: note,
720+
body: result.body,
721+
groupId: result.groupId,
722+
studentIds: result.studentIds,
723+
isTodo: result.isTodo,
724+
createdAt: result.createdAt,
725+
);
726+
} catch (e, st) {
727+
developer.log(
728+
'Failed to update note',
729+
name: 'classi.group_detail',
730+
level: 1000,
731+
error: e,
732+
stackTrace: st,
733+
);
734+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
735+
}
657736
}
658737

659738
Future<void> _deleteNote(
@@ -667,7 +746,18 @@ class GroupDetailScreen extends ConsumerWidget {
667746
body: note.body,
668747
);
669748
if (confirmed) {
670-
await ref.read(noteRepositoryProvider).deleteNote(note.id);
749+
try {
750+
await ref.read(noteRepositoryProvider).deleteNote(note.id);
751+
} catch (e, st) {
752+
developer.log(
753+
'Failed to delete note',
754+
name: 'classi.group_detail',
755+
level: 1000,
756+
error: e,
757+
stackTrace: st,
758+
);
759+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
760+
}
671761
}
672762
}
673763

@@ -694,15 +784,26 @@ class GroupDetailScreen extends ConsumerWidget {
694784
return;
695785
}
696786

697-
await ref
698-
.read(groupRepositoryProvider)
699-
.updateGroup(
700-
id: group.id,
701-
name: result.name,
702-
colorHex: result.colorHex,
703-
gradeScale: result.gradeScale,
704-
gradeCategories: result.gradeCategories,
705-
);
787+
try {
788+
await ref
789+
.read(groupRepositoryProvider)
790+
.updateGroup(
791+
id: group.id,
792+
name: result.name,
793+
colorHex: result.colorHex,
794+
gradeScale: result.gradeScale,
795+
gradeCategories: result.gradeCategories,
796+
);
797+
} catch (e, st) {
798+
developer.log(
799+
'Failed to edit group',
800+
name: 'classi.group_detail',
801+
level: 1000,
802+
error: e,
803+
stackTrace: st,
804+
);
805+
if (context.mounted) showErrorSnackBar(context, 'generic_error'.tr());
806+
}
706807
}
707808
}
708809

0 commit comments

Comments
 (0)