From 6759126956981abdb64f055757a571a72fa5861c Mon Sep 17 00:00:00 2001 From: nilsreichardt Date: Fri, 12 Jan 2024 23:30:39 +0100 Subject: [PATCH 1/2] Improve design of leave/delete group buttons --- .../src/pages/course/course_details.dart | 154 ++++++++---------- .../school_class/school_class_details.dart | 111 ++++++------- .../groups/src/widgets/danger_section.dart | 106 ++++++++++++ 3 files changed, 222 insertions(+), 149 deletions(-) create mode 100644 app/lib/groups/src/widgets/danger_section.dart diff --git a/app/lib/groups/src/pages/course/course_details.dart b/app/lib/groups/src/pages/course/course_details.dart index 67c0ba39c..3c34b9dd3 100644 --- a/app/lib/groups/src/pages/course/course_details.dart +++ b/app/lib/groups/src/pages/course/course_details.dart @@ -12,6 +12,7 @@ import 'package:app_functions/app_functions_ui.dart'; import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/material.dart'; import 'package:group_domain_models/group_domain_models.dart'; +import 'package:sharezone/groups/src/widgets/danger_section.dart'; import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/groups/analytics/group_analytics.dart'; import 'package:sharezone/groups/src/pages/course/course_card.dart'; @@ -178,21 +179,18 @@ class _CourseDetailsPage extends StatelessWidget { ); }, ), - SafeArea( - bottom: !isAdmin, - child: _LeaveCourseButton( - onDialogClose: (appFunction) => Navigator.pop( - context, - LeaveCourseDetailsPopOption(appFunction), - ), + _DangerSection( + onDeleteDialogClose: (appFunction) => Navigator.pop( + context, + DeleteCourseDetailsPopOption(appFunction), ), - ), - if (isAdmin) - _DeleteCourseButton( - onDialogClose: (appFunction) => Navigator.pop(context, - DeleteCourseDetailsPopOption(appFunction)), - courseName: course.name, + onLeaveDialogClose: (appFunction) => Navigator.pop( + context, + LeaveCourseDetailsPopOption(appFunction), ), + isAdmin: isAdmin, + courseName: course.name, + ), ], ), ), @@ -205,6 +203,63 @@ class _CourseDetailsPage extends StatelessWidget { } } +class _DangerSection extends StatelessWidget { + const _DangerSection({ + required this.onDeleteDialogClose, + required this.onLeaveDialogClose, + required this.isAdmin, + required this.courseName, + }); + + final Function(Future>) onDeleteDialogClose; + final Function(Future>) onLeaveDialogClose; + final bool isAdmin; + final String courseName; + + void _logCourseLeaveButtonViaCourseDetailsPage(Analytics analytics) { + analytics.log(NamedAnalyticsEvent( + name: "course_leave_button_via_course_details_page")); + } + + void _logCourseDeleteButtonViaCourseDetailsPage(Analytics analytics) { + analytics.log(NamedAnalyticsEvent( + name: "course_delete_button_via_course_details_page")); + } + + @override + Widget build(BuildContext context) { + return DangerSection( + deleteButtonLabel: const Text("KURS LÖSCHEN"), + onPressedDeleteButton: () async { + final bloc = BlocProvider.of(context); + final analytics = BlocProvider.of(context).analytics; + + _logCourseDeleteButtonViaCourseDetailsPage(analytics); + final result = await showDeleteCourseDialog(context, courseName); + + if (result == true) { + onDeleteDialogClose(bloc.deleteCourse()); + } + }, + leaveButtonLabel: const Text("KURS VERLASSEN"), + onPressedLeaveButton: () async { + final bloc = BlocProvider.of(context); + final analytics = BlocProvider.of(context).analytics; + + _logCourseLeaveButtonViaCourseDetailsPage(analytics); + final isLastMember = (await bloc.members.first).length <= 1; + if (!context.mounted) return; + final result = await showCourseLeaveDialog(context, isLastMember); + + if (result == true) { + onLeaveDialogClose(bloc.leaveCourse()); + } + }, + hasDeleteButton: isAdmin, + ); + } +} + class _ChangeColorIcon extends StatelessWidget { const _ChangeColorIcon({required this.courseID}); @@ -243,79 +298,6 @@ class _EditIcon extends StatelessWidget { } } -class _LeaveCourseButton extends StatelessWidget { - const _LeaveCourseButton({ - required this.onDialogClose, - }); - - final Function(Future>) onDialogClose; - - @override - Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - final analytics = BlocProvider.of(context).analytics; - return Padding( - padding: const EdgeInsets.only(bottom: 12), - child: DestroyButton( - title: const Text("KURS VERLASSEN"), - color: const Color(0xFFff7d7d), - onTap: () async { - _logCourseLeaveButtonViaCourseDetailsPage(analytics); - final isLastMember = (await bloc.members.first).length <= 1; - if (!context.mounted) return; - final result = await showCourseLeaveDialog(context, isLastMember); - if (result == true) { - onDialogClose(bloc.leaveCourse()); - } - }, - ), - ); - } - - void _logCourseLeaveButtonViaCourseDetailsPage(Analytics analytics) { - analytics.log(NamedAnalyticsEvent( - name: "course_leave_button_via_course_details_page")); - } -} - -class _DeleteCourseButton extends StatelessWidget { - const _DeleteCourseButton({ - required this.onDialogClose, - required this.courseName, - }); - - final Function(Future>) onDialogClose; - final String courseName; - - @override - Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - final analytics = BlocProvider.of(context).analytics; - return SafeArea( - bottom: true, - child: Padding( - padding: const EdgeInsets.only(bottom: 12), - child: DestroyButton( - title: const Text("KURS LÖSCHEN"), - onTap: () async { - _logCourseDeleteButtonViaCourseDetailsPage(analytics); - final result = await showDeleteCourseDialog(context, courseName); - if (result == true) { - final deleteCourseFunction = bloc.deleteCourse(); - onDialogClose(deleteCourseFunction); - } - }, - ), - ), - ); - } - - void _logCourseDeleteButtonViaCourseDetailsPage(Analytics analytics) { - analytics.log(NamedAnalyticsEvent( - name: "course_delete_button_via_course_details_page")); - } -} - class HelpCoursePageIconButton extends StatelessWidget { const HelpCoursePageIconButton({super.key}); diff --git a/app/lib/groups/src/pages/school_class/school_class_details.dart b/app/lib/groups/src/pages/school_class/school_class_details.dart index 631910af0..4cdd9f379 100644 --- a/app/lib/groups/src/pages/school_class/school_class_details.dart +++ b/app/lib/groups/src/pages/school_class/school_class_details.dart @@ -19,6 +19,7 @@ import 'package:sharezone/groups/src/pages/school_class/edit/school_class_edit_p import 'package:sharezone/groups/src/pages/school_class/my_school_class_bloc.dart'; import 'package:sharezone/groups/src/pages/school_class/school_class_details/school_class_member_option.dart'; import 'package:sharezone/groups/src/pages/school_class/school_class_page.dart'; +import 'package:sharezone/groups/src/widgets/danger_section.dart'; import 'package:sharezone/groups/src/widgets/group_share.dart'; import 'package:sharezone/groups/src/widgets/member_section.dart'; import 'package:sharezone/groups/src/widgets/sharecode_text.dart'; @@ -112,17 +113,17 @@ class SchoolClassDetailsPage extends StatelessWidget { ); }, ), - SafeArea( - bottom: !isAdmin, - child: _LeaveSchoolClassButton( - onDialogClose: (appFunction) => Navigator.pop(context, - LeaveSchoolClassDetailsPopOption(appFunction)), + _DangerSection( + onDeleteDialogClose: (appFunction) => Navigator.pop( + context, + DeleteSchoolClassDetailsPopOption(appFunction), ), - ), - if (isAdmin) - _DeleteSchoolClassButton( - onDialogClose: (appFunction) => Navigator.pop(context, - DeleteSchoolClassDetailsPopOption(appFunction))), + onLeaveDialogClose: (appFunction) => Navigator.pop( + context, + LeaveSchoolClassDetailsPopOption(appFunction), + ), + isAdmin: isAdmin, + ) ], ); }, @@ -133,75 +134,59 @@ class SchoolClassDetailsPage extends StatelessWidget { } } -class _EditIcon extends StatelessWidget { - const _EditIcon({ - required this.schoolClass, +class _DangerSection extends StatelessWidget { + const _DangerSection({ + required this.isAdmin, + required this.onDeleteDialogClose, + required this.onLeaveDialogClose, }); - final SchoolClass schoolClass; + final bool isAdmin; + final Function(Future>) onDeleteDialogClose; + final Function(Future>) onLeaveDialogClose; @override Widget build(BuildContext context) { - return IconButton( - tooltip: 'Bearbeiten', - icon: const Icon(Icons.edit), - onPressed: () => openSchoolClassEditPage(context, schoolClass), - ); - } -} + return DangerSection( + deleteButtonLabel: const Text("KLASSE LÖSCHEN"), + onPressedDeleteButton: () async { + final bloc = BlocProvider.of(context); -class _LeaveSchoolClassButton extends StatelessWidget { - const _LeaveSchoolClassButton({ - required this.onDialogClose, - }); - - final Function(Future>) onDialogClose; + final schoolClassDeleteType = + await showDeleteSchoolClassDialog(context); + if (schoolClassDeleteType != null) { + final deleteFuture = bloc.deleteSchoolClass(schoolClassDeleteType); + onLeaveDialogClose(deleteFuture); + } + }, + leaveButtonLabel: const Text("KLASSE VERLASSEN"), + onPressedLeaveButton: () async { + final bloc = BlocProvider.of(context); - @override - Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - return Padding( - padding: const EdgeInsets.only(bottom: 12), - child: DestroyButton( - color: const Color(0xFFff7d7d), - title: const Text("KLASSE VERLASSEN"), - onTap: () async { - final confirmed = await showLeaveSchoolClassDialog(context); - if (confirmed == true) { - onDialogClose(bloc.leaveSchoolClass()); - } - }, - ), + final confirmed = await showLeaveSchoolClassDialog(context); + if (confirmed == true) { + final leaveFuture = bloc.leaveSchoolClass(); + onLeaveDialogClose(leaveFuture); + } + }, + hasDeleteButton: isAdmin, ); } } -class _DeleteSchoolClassButton extends StatelessWidget { - const _DeleteSchoolClassButton({ - required this.onDialogClose, +class _EditIcon extends StatelessWidget { + const _EditIcon({ + required this.schoolClass, }); - final Function(Future>) onDialogClose; + final SchoolClass schoolClass; @override Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - return SafeArea( - bottom: true, - child: Padding( - padding: const EdgeInsets.only(bottom: 12), - child: DestroyButton( - title: const Text("KLASSE LÖSCHEN"), - color: Colors.redAccent, - onTap: () async { - final schoolClassDeleteType = - await showDeleteSchoolClassDialog(context); - if (schoolClassDeleteType != null) { - onDialogClose(bloc.deleteSchoolClass(schoolClassDeleteType)); - } - }, - ), - ), + return IconButton( + tooltip: 'Bearbeiten', + icon: const Icon(Icons.edit), + onPressed: () => openSchoolClassEditPage(context, schoolClass), ); } } diff --git a/app/lib/groups/src/widgets/danger_section.dart b/app/lib/groups/src/widgets/danger_section.dart new file mode 100644 index 000000000..38a1e5161 --- /dev/null +++ b/app/lib/groups/src/widgets/danger_section.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; + +class DangerSection extends StatelessWidget { + const DangerSection({ + super.key, + this.onPressedLeaveButton, + this.onPressedDeleteButton, + required this.hasDeleteButton, + required this.leaveButtonLabel, + required this.deleteButtonLabel, + }); + + final VoidCallback? onPressedLeaveButton; + final VoidCallback? onPressedDeleteButton; + final bool hasDeleteButton; + final Widget leaveButtonLabel; + final Widget deleteButtonLabel; + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Wrap( + alignment: WrapAlignment.center, + spacing: 12, + children: [ + _LeaveButton( + onPressed: onPressedLeaveButton, + label: leaveButtonLabel, + ), + if (hasDeleteButton) + _DeleteSchoolClassButton( + onPressed: onPressedDeleteButton, + label: deleteButtonLabel, + ), + ], + ), + ); + } +} + +class _LeaveButton extends StatelessWidget { + const _LeaveButton({ + required this.onPressed, + required this.label, + }); + + final VoidCallback? onPressed; + final Widget label; + + @override + Widget build(BuildContext context) { + return _DangerButton( + onPressed: onPressed, + label: label, + icon: const Icon(Icons.exit_to_app), + ); + } +} + +class _DangerButton extends StatelessWidget { + const _DangerButton({ + this.onPressed, + required this.label, + required this.icon, + }); + + final VoidCallback? onPressed; + final Widget label; + final Widget icon; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: FilledButton.icon( + icon: icon, + style: FilledButton.styleFrom( + shadowColor: Colors.transparent, + foregroundColor: Colors.red, + backgroundColor: Colors.red.withOpacity(0.2), + ), + onPressed: onPressed, + label: label, + ), + ); + } +} + +class _DeleteSchoolClassButton extends StatelessWidget { + const _DeleteSchoolClassButton({ + this.onPressed, + required this.label, + }); + + final VoidCallback? onPressed; + final Widget label; + + @override + Widget build(BuildContext context) { + return _DangerButton( + icon: const Icon(Icons.delete), + onPressed: onPressed, + label: label, + ); + } +} From ee4f06fca61596bcdc69cb750d300bd3edf8cdfc Mon Sep 17 00:00:00 2001 From: nilsreichardt Date: Fri, 12 Jan 2024 23:40:39 +0100 Subject: [PATCH 2/2] Add lh --- app/lib/groups/src/widgets/danger_section.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/lib/groups/src/widgets/danger_section.dart b/app/lib/groups/src/widgets/danger_section.dart index 38a1e5161..a0b295373 100644 --- a/app/lib/groups/src/widgets/danger_section.dart +++ b/app/lib/groups/src/widgets/danger_section.dart @@ -1,3 +1,11 @@ +// Copyright (c) 2024 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + import 'package:flutter/material.dart'; class DangerSection extends StatelessWidget {