From df66be26357b55b4d345e4390ceeca407a8e89df Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:15:54 +0100 Subject: [PATCH 01/16] Add missing UI elements. --- .../timetable_add_event_dialog.dart | 433 ++++++++++++++++++ .../timetable_page/timetable_page.dart | 4 +- .../timetable_page/timetable_page_fab.dart | 29 +- 3 files changed, 455 insertions(+), 11 deletions(-) create mode 100644 app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart new file mode 100644 index 000000000..41056558c --- /dev/null +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -0,0 +1,433 @@ +import 'package:flutter/material.dart'; +import 'package:platform_check/platform_check.dart'; +import 'package:sharezone/filesharing/dialog/course_tile.dart'; +import 'package:sharezone/markdown/markdown_support.dart'; +import 'package:sharezone/widgets/material/list_tile_with_description.dart'; +import 'package:sharezone/widgets/material/save_button.dart'; +import 'package:sharezone_widgets/sharezone_widgets.dart'; + +final _titleNode = FocusNode(); + +class TimetableAddEventDialog extends StatelessWidget { + const TimetableAddEventDialog({super.key}); + + static const tag = "timetable-event-dialog"; + + @override + Widget build(BuildContext context) { + return PopScope( + // canPop: false, + onPopInvoked: (didPop) async { + if (didPop) return; + + // final hasInputChanged = hasModifiedData(); + const hasInputChanged = false; + final navigator = Navigator.of(context); + if (!hasInputChanged) { + navigator.pop(); + return; + } + + final shouldPop = await warnUserAboutLeavingForm(context); + if (shouldPop && context.mounted) { + navigator.pop(); + } + }, + child: Scaffold( + body: Column( + children: [ + _AppBar( + // editMode: widget.isEditing, + editMode: false, + focusNodeTitle: _titleNode, + // onCloseTap: () => leaveDialog(), + onCloseTap: () {}, + titleField: _TitleField( + focusNode: _titleNode, + // state: state, + )), + Expanded( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + const _CourseTile(), + const _MobileDivider(), + const _DateAndTimePicker(), + const _MobileDivider(), + _DescriptionFieldBase( + onChanged: (p0) {}, + prefilledDescription: '', + ), + const _MobileDivider(), + const _SendNotification(), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + +class _MobileDivider extends StatelessWidget { + const _MobileDivider(); + + @override + Widget build(BuildContext context) { + if (PlatformCheck.isDesktopOrWeb) return const SizedBox(height: 4); + return const Divider(height: 0); + } +} + +class _AppBar extends StatelessWidget { + const _AppBar({ + required this.editMode, + required this.focusNodeTitle, + required this.onCloseTap, + required this.titleField, + }); + + final bool editMode; + final VoidCallback onCloseTap; + final Widget titleField; + + final FocusNode focusNodeTitle; + + @override + Widget build(BuildContext context) { + return Material( + color: Theme.of(context).isDarkTheme + ? Theme.of(context).appBarTheme.backgroundColor + : Theme.of(context).primaryColor, + elevation: 1, + child: SafeArea( + top: true, + bottom: false, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(4, 6, 6, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: onCloseTap, + tooltip: "Schließen", + ), + _SaveButton( + editMode: editMode, + ), + ], + ), + ), + titleField, + ], + ), + ), + ); + } +} + +class _SaveButton extends StatelessWidget { + const _SaveButton({this.editMode = false}); + + final bool editMode; + + Future onPressed(BuildContext context) async { + // final bloc = bloc_lib.BlocProvider.of(context); + // try { + // bloc.add(const Save()); + // } on Exception catch (e) { + // log("Exception when submitting: $e", error: e); + // showSnackSec( + // text: + // "Es gab einen unbekannten Fehler (${e.toString()}) 😖 Bitte kontaktiere den Support!", + // context: context, + // seconds: 5, + // ); + // } + } + + void hideSendDataToFrankfurtSnackBar(BuildContext context) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + } + + @override + Widget build(BuildContext context) { + return SaveButton( + // key: HwDialogKeys.saveButton, + tooltip: "Termin speichern", + onPressed: () => onPressed(context), + ); + } +} + +class _TitleField extends StatelessWidget { + const _TitleField({ + required this.focusNode, + // required this.state, + }); + + // final Ready state; + final FocusNode focusNode; + + @override + Widget build(BuildContext context) { + // final bloc = bloc_lib.BlocProvider.of(context); + return MaxWidthConstraintBox( + child: _TitleFieldBase( + // prefilledTitle: state.title.$1, + prefilledTitle: null, + focusNode: focusNode, + onChanged: (newTitle) { + // bloc.add(TitleChanged(newTitle)); + }, + // errorText: state.title.error is EmptyTitleException + // ? HwDialogErrorStrings.emptyTitle + // : state.title.error?.toString(), + ), + ); + } +} + +class _TitleFieldBase extends StatelessWidget { + const _TitleFieldBase({ + required this.prefilledTitle, + required this.onChanged, + this.errorText, + this.focusNode, + }); + + final String? prefilledTitle; + final String? errorText; + final FocusNode? focusNode; + final Function(String) onChanged; + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height / 3, + ), + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20) + .add(const EdgeInsets.only(top: 8)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PrefilledTextField( + // key: HwDialogKeys.titleTextField, + prefilledText: prefilledTitle, + focusNode: focusNode, + cursorColor: Colors.white, + maxLines: null, + style: const TextStyle( + color: Colors.white, + fontSize: 22, + fontWeight: FontWeight.w400, + ), + decoration: const InputDecoration( + hintText: "Titel eingeben (z.B. Sportfest)", + hintStyle: TextStyle(color: Colors.white), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + fillColor: Colors.transparent, + ), + onChanged: onChanged, + textCapitalization: TextCapitalization.sentences, + ), + Text( + errorText ?? "", + style: TextStyle(color: Colors.red[700], fontSize: 12), + ), + const SizedBox(height: 10), + ], + ), + ), + ), + ); + } +} + +class _CourseTile extends StatelessWidget { + const _CourseTile(); + + @override + Widget build(BuildContext context) { + return MaxWidthConstraintBox( + child: SafeArea( + top: false, + bottom: false, + child: CourseTileBase( + // key: HwDialogKeys.courseTile, + courseName: null, + errorText: null, + onTap: () {}, + ), + ), + ); + } +} + +class _DateAndTimePicker extends StatelessWidget { + const _DateAndTimePicker(); + + @override + Widget build(BuildContext context) { + return MaxWidthConstraintBox( + child: SafeArea( + top: false, + bottom: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DefaultTextStyle.merge( + style: const TextStyle( + color: null, + // color: state.dueDate.error != null ? Colors.red : null, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: const Icon(Icons.today), + title: const Text('Do. 1 Feb. 2024'), + trailing: const Text('11:30'), + onTap: () {}, + ), + ListTile( + leading: const SizedBox(), + title: const Text('Do. 1 Feb. 2024'), + trailing: const Text('12:30'), + onTap: () {}, + ), + ListTile( + leading: const SizedBox(), + title: OutlinedButton( + onPressed: () {}, + child: const Text('Schulstunde auswählen'), + ), + trailing: const SizedBox(), + ) + ], + ), + ), + ], + ), + ), + ); + } +} + +class _DescriptionFieldBase extends StatelessWidget { + const _DescriptionFieldBase({ + required this.onChanged, + required this.prefilledDescription, + }); + + final Function(String) onChanged; + final String? prefilledDescription; + + @override + Widget build(BuildContext context) { + return MaxWidthConstraintBox( + child: SafeArea( + top: false, + bottom: false, + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: const Icon(Icons.subject), + title: PrefilledTextField( + // key: HwDialogKeys.descriptionField, + prefilledText: prefilledDescription, + maxLines: null, + scrollPadding: const EdgeInsets.all(16.0), + keyboardType: TextInputType.multiline, + decoration: const InputDecoration( + hintText: "Zusatzinformationen eingeben", + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + errorBorder: InputBorder.none, + fillColor: Colors.transparent, + ), + onChanged: onChanged, + textCapitalization: TextCapitalization.sentences, + ), + ), + const Padding( + padding: EdgeInsets.fromLTRB(16, 0, 16, 12), + child: MarkdownSupport(), + ), + ], + ), + ), + ), + ); + } +} + +class _SendNotification extends StatelessWidget { + const _SendNotification(); + + @override + Widget build(BuildContext context) { + return MaxWidthConstraintBox( + child: SafeArea( + top: false, + bottom: false, + child: _SendNotificationBase( + title: "Kursmitglieder benachrichtigen", + onChanged: (newValue) {}, + sendNotification: true, + description: + "Sende eine Benachrichtigung an deine Kursmitglieder, dass du einen neuen Termin erstellt hast.", + ), + ), + ); + } +} + +class _SendNotificationBase extends StatelessWidget { + const _SendNotificationBase({ + required this.title, + required this.sendNotification, + required this.onChanged, + this.description, + }); + + final String title; + final String? description; + final bool sendNotification; + final Function(bool) onChanged; + + @override + Widget build(BuildContext context) { + return ListTileWithDescription( + // key: HwDialogKeys.notifyCourseMembersTile, + leading: const Icon(Icons.notifications_active), + title: Text(title), + trailing: Switch.adaptive( + onChanged: onChanged, + value: sendNotification, + ), + onTap: () => onChanged(!sendNotification), + description: description != null ? Text(description!) : null, + ); + } +} diff --git a/app/lib/timetable/timetable_page/timetable_page.dart b/app/lib/timetable/timetable_page/timetable_page.dart index a72d531bb..37cfd6e38 100644 --- a/app/lib/timetable/timetable_page/timetable_page.dart +++ b/app/lib/timetable/timetable_page/timetable_page.dart @@ -8,11 +8,12 @@ import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:group_domain_models/group_domain_models.dart'; -import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/calendrical_events/models/calendrical_event.dart'; +import 'package:sharezone/main/application_bloc.dart'; import 'package:sharezone/navigation/logic/navigation_bloc.dart'; import 'package:sharezone/navigation/models/navigation_item.dart'; import 'package:sharezone/navigation/scaffold/app_bar_configuration.dart'; @@ -32,6 +33,7 @@ import 'package:sharezone/timetable/timetable_page/timetable_event_details.dart' import 'package:sharezone/widgets/material/modal_bottom_sheet_big_icon_button.dart'; import 'package:sharezone_widgets/sharezone_widgets.dart'; +import '../timetable_add_event/timetable_add_event_dialog.dart'; import 'lesson/timetable_lesson_tile.dart'; import 'school_class_filter/school_class_filter.dart'; diff --git a/app/lib/timetable/timetable_page/timetable_page_fab.dart b/app/lib/timetable/timetable_page/timetable_page_fab.dart index 9adb37b41..601a7bf6a 100644 --- a/app/lib/timetable/timetable_page/timetable_page_fab.dart +++ b/app/lib/timetable/timetable_page/timetable_page_fab.dart @@ -43,18 +43,27 @@ Future showTimetableAddEventPage( BuildContext context, { required bool isExam, }) async { - final result = await Navigator.push( - context, - IgnoreWillPopScopeWhenIosSwipeBackRoute( - builder: (context) => TimetableAddEventPage(isExam: isExam), - settings: const RouteSettings(name: TimetableAddEventPage.tag))); - if (result != null) { - await waitingForPopAnimation(); - if (!context.mounted) return null; + if (kDebugMode) { + await Navigator.push( + context, + IgnoreWillPopScopeWhenIosSwipeBackRoute( + builder: (context) => const TimetableAddEventDialog(), + settings: const RouteSettings(name: TimetableAddEventDialog.tag))); + return null; + } else { + final result = await Navigator.push( + context, + IgnoreWillPopScopeWhenIosSwipeBackRoute( + builder: (context) => TimetableAddEventPage(isExam: isExam), + settings: const RouteSettings(name: TimetableAddEventPage.tag))); + if (result != null) { + await waitingForPopAnimation(); + if (!context.mounted) return null; - showDataArrivalConfirmedSnackbar(context: context); + showDataArrivalConfirmedSnackbar(context: context); + } + return result; } - return result; } Future openTimetableAddSheet(BuildContext context) async { From 94f29509690f78c2c34db6e000b419ae2159c354 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:52:56 +0100 Subject: [PATCH 02/16] Add license header --- .../timetable_add_event/timetable_add_event_dialog.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 41056558c..7dbfbfd08 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.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'; import 'package:platform_check/platform_check.dart'; import 'package:sharezone/filesharing/dialog/course_tile.dart'; From 970dbc3d07020001be5a9c7ad6e99ed3fd81dc78 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:55:22 +0100 Subject: [PATCH 03/16] Always show new dialog --- app/lib/timetable/timetable_page/timetable_page_fab.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/timetable/timetable_page/timetable_page_fab.dart b/app/lib/timetable/timetable_page/timetable_page_fab.dart index 601a7bf6a..e53b617b7 100644 --- a/app/lib/timetable/timetable_page/timetable_page_fab.dart +++ b/app/lib/timetable/timetable_page/timetable_page_fab.dart @@ -43,7 +43,7 @@ Future showTimetableAddEventPage( BuildContext context, { required bool isExam, }) async { - if (kDebugMode) { + if (true) { await Navigator.push( context, IgnoreWillPopScopeWhenIosSwipeBackRoute( From 9144dbc5a5e6653fb9f7c76c5285b86065ec3878 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:55:40 +0100 Subject: [PATCH 04/16] . --- app/lib/timetable/timetable_page/timetable_page.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/app/lib/timetable/timetable_page/timetable_page.dart b/app/lib/timetable/timetable_page/timetable_page.dart index 37cfd6e38..d46eb1a0d 100644 --- a/app/lib/timetable/timetable_page/timetable_page.dart +++ b/app/lib/timetable/timetable_page/timetable_page.dart @@ -8,7 +8,6 @@ import 'package:bloc_provider/bloc_provider.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:group_domain_models/group_domain_models.dart'; From a7d0894f65d403aad05e3c1241fa9b834ce1478b Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:54:54 +0100 Subject: [PATCH 05/16] Create _DateAndTimeTile --- .../timetable_add_event_dialog.dart | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 7dbfbfd08..cdb04def3 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -6,6 +6,7 @@ // // SPDX-License-Identifier: EUPL-1.2 +import 'package:date/date.dart'; import 'package:flutter/material.dart'; import 'package:platform_check/platform_check.dart'; import 'package:sharezone/filesharing/dialog/course_tile.dart'; @@ -13,6 +14,7 @@ import 'package:sharezone/markdown/markdown_support.dart'; import 'package:sharezone/widgets/material/list_tile_with_description.dart'; import 'package:sharezone/widgets/material/save_button.dart'; import 'package:sharezone_widgets/sharezone_widgets.dart'; +import 'package:time/time.dart'; final _titleNode = FocusNode(); @@ -308,26 +310,28 @@ class _DateAndTimePicker extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - ListTile( + _DateAndTimeTile( leading: const Icon(Icons.today), - title: const Text('Do. 1 Feb. 2024'), - trailing: const Text('11:30'), - onTap: () {}, + date: Date('2024-02-03'), + time: Time(hour: 11, minute: 00), ), - ListTile( - leading: const SizedBox(), - title: const Text('Do. 1 Feb. 2024'), - trailing: const Text('12:30'), - onTap: () {}, + _DateAndTimeTile( + date: Date('2024-02-03'), + time: Time(hour: 12, minute: 30), ), - ListTile( - leading: const SizedBox(), - title: OutlinedButton( - onPressed: () {}, - child: const Text('Schulstunde auswählen'), - ), - trailing: const SizedBox(), - ) + Row( + children: [ + const SizedBox(width: 34), + OutlinedButton( + onPressed: () {}, + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14))), + child: const Text('Schulstunde auswählen'), + ), + ], + ), + const SizedBox(height: 10), ], ), ), @@ -338,6 +342,37 @@ class _DateAndTimePicker extends StatelessWidget { } } +class _DateAndTimeTile extends StatelessWidget { + const _DateAndTimeTile({ + super.key, + this.leading, + this.date, + this.time, + }); + + final Widget? leading; + final Date? date; + final Time? time; + + @override + Widget build(BuildContext context) { + return ListTile( + leading: leading ?? const SizedBox(), + title: Text(date?.parser.toYMMMEd ?? 'Datum auswählen...'), + // trailing: const Text('11:30'), + trailing: TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).textTheme.bodyMedium!.color, + textStyle: const TextStyle(fontSize: 15), + ), + onPressed: () {}, + child: Text(time?.toString() ?? ''), + ), + onTap: () {}, + ); + } +} + class _DescriptionFieldBase extends StatelessWidget { const _DescriptionFieldBase({ required this.onChanged, From f308d36445d356075be4a0e019a48c0e3b705d89 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:46:29 +0100 Subject: [PATCH 06/16] Differentiate between events/exams --- TEMP.md | 14 +++ .../timetable_add_event_dialog.dart | 88 ++++++++++++++++--- .../timetable_page/timetable_page_fab.dart | 4 +- 3 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 TEMP.md diff --git a/TEMP.md b/TEMP.md new file mode 100644 index 000000000..f11766242 --- /dev/null +++ b/TEMP.md @@ -0,0 +1,14 @@ +* Date picker für Datum öffnen +* Time picker für Uhrzeit öffnen +* Erstmal irgendeine leere Seite für die Stundenauswahl öffnen + +* Unteres Datumsfeld ausgrauen? +* Hatten bei Terminen "Ort" und "Zusatzinformationen", bei Klausuren "Raum" und "Themen der Prüfung". So übernehmen? +* Titel Hint-Text bei Klausur umändern + +UI Checklist: + +* [ ] White mode überprüft +* [ ] Dark mode überprüft +* [ ] Handy-Layout überprüft +* [ ] Desktop-Layout überprüft diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index cdb04def3..18da390cf 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -19,7 +19,9 @@ import 'package:time/time.dart'; final _titleNode = FocusNode(); class TimetableAddEventDialog extends StatelessWidget { - const TimetableAddEventDialog({super.key}); + const TimetableAddEventDialog({super.key, required this.isExam}); + + final bool isExam; static const tag = "timetable-event-dialog"; @@ -51,9 +53,12 @@ class TimetableAddEventDialog extends StatelessWidget { editMode: false, focusNodeTitle: _titleNode, // onCloseTap: () => leaveDialog(), - onCloseTap: () {}, + onCloseTap: () { + Navigator.pop(context); + }, titleField: _TitleField( focusNode: _titleNode, + isExam: isExam, // state: state, )), Expanded( @@ -62,16 +67,19 @@ class TimetableAddEventDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 8), const _CourseTile(), const _MobileDivider(), const _DateAndTimePicker(), const _MobileDivider(), _DescriptionFieldBase( + hintText: + isExam ? 'Themen der Prüfung' : 'Zusatzinformationen', onChanged: (p0) {}, prefilledDescription: '', ), const _MobileDivider(), + const _Location(), + const _MobileDivider(), const _SendNotification(), ], ), @@ -151,6 +159,7 @@ class _SaveButton extends StatelessWidget { final bool editMode; Future onPressed(BuildContext context) async { + Navigator.pop(context); // final bloc = bloc_lib.BlocProvider.of(context); // try { // bloc.add(const Save()); @@ -182,11 +191,13 @@ class _SaveButton extends StatelessWidget { class _TitleField extends StatelessWidget { const _TitleField({ required this.focusNode, + required this.isExam, // required this.state, }); // final Ready state; final FocusNode focusNode; + final bool isExam; @override Widget build(BuildContext context) { @@ -199,6 +210,9 @@ class _TitleField extends StatelessWidget { onChanged: (newTitle) { // bloc.add(TitleChanged(newTitle)); }, + hintText: isExam + ? 'Titel (z.B. Statistik-Klausur)' + : 'Titel eingeben (z.B. Sportfest)', // errorText: state.title.error is EmptyTitleException // ? HwDialogErrorStrings.emptyTitle // : state.title.error?.toString(), @@ -213,11 +227,13 @@ class _TitleFieldBase extends StatelessWidget { required this.onChanged, this.errorText, this.focusNode, + required this.hintText, }); final String? prefilledTitle; final String? errorText; final FocusNode? focusNode; + final String hintText; final Function(String) onChanged; @override @@ -244,9 +260,9 @@ class _TitleFieldBase extends StatelessWidget { fontSize: 22, fontWeight: FontWeight.w400, ), - decoration: const InputDecoration( - hintText: "Titel eingeben (z.B. Sportfest)", - hintStyle: TextStyle(color: Colors.white), + decoration: InputDecoration( + hintText: hintText, + hintStyle: const TextStyle(color: Colors.white), border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, @@ -318,6 +334,7 @@ class _DateAndTimePicker extends StatelessWidget { _DateAndTimeTile( date: Date('2024-02-03'), time: Time(hour: 12, minute: 30), + isDatePickingEnabled: false, ), Row( children: [ @@ -348,17 +365,26 @@ class _DateAndTimeTile extends StatelessWidget { this.leading, this.date, this.time, + this.isDatePickingEnabled = true, }); final Widget? leading; final Date? date; final Time? time; + final bool isDatePickingEnabled; @override Widget build(BuildContext context) { return ListTile( leading: leading ?? const SizedBox(), - title: Text(date?.parser.toYMMMEd ?? 'Datum auswählen...'), + title: Text( + date?.parser.toYMMMEd ?? 'Datum auswählen...', + style: TextStyle( + color: isDatePickingEnabled + ? Theme.of(context).textTheme.bodyMedium!.color + : Theme.of(context).disabledColor, + ), + ), // trailing: const Text('11:30'), trailing: TextButton( style: TextButton.styleFrom( @@ -368,7 +394,7 @@ class _DateAndTimeTile extends StatelessWidget { onPressed: () {}, child: Text(time?.toString() ?? ''), ), - onTap: () {}, + onTap: isDatePickingEnabled ? () {} : null, ); } } @@ -377,10 +403,12 @@ class _DescriptionFieldBase extends StatelessWidget { const _DescriptionFieldBase({ required this.onChanged, required this.prefilledDescription, + required this.hintText, }); final Function(String) onChanged; final String? prefilledDescription; + final String hintText; @override Widget build(BuildContext context) { @@ -401,8 +429,8 @@ class _DescriptionFieldBase extends StatelessWidget { maxLines: null, scrollPadding: const EdgeInsets.all(16.0), keyboardType: TextInputType.multiline, - decoration: const InputDecoration( - hintText: "Zusatzinformationen eingeben", + decoration: InputDecoration( + hintText: hintText, border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, @@ -425,6 +453,45 @@ class _DescriptionFieldBase extends StatelessWidget { } } +class _Location extends StatelessWidget { + const _Location(); + + @override + Widget build(BuildContext context) { + return MaxWidthConstraintBox( + child: SafeArea( + top: false, + bottom: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: const Icon(Icons.location_pin), + title: PrefilledTextField( + // key: , + prefilledText: '', + maxLines: null, + scrollPadding: const EdgeInsets.all(16.0), + keyboardType: TextInputType.multiline, + decoration: const InputDecoration( + hintText: "Ort/Raum", + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + errorBorder: InputBorder.none, + fillColor: Colors.transparent, + ), + onChanged: (_) {}, + textCapitalization: TextCapitalization.sentences, + ), + ), + ], + ), + ), + ); + } +} + class _SendNotification extends StatelessWidget { const _SendNotification(); @@ -438,6 +505,7 @@ class _SendNotification extends StatelessWidget { title: "Kursmitglieder benachrichtigen", onChanged: (newValue) {}, sendNotification: true, + // TODO: Termin/Klausur je nach dem auswählen description: "Sende eine Benachrichtigung an deine Kursmitglieder, dass du einen neuen Termin erstellt hast.", ), diff --git a/app/lib/timetable/timetable_page/timetable_page_fab.dart b/app/lib/timetable/timetable_page/timetable_page_fab.dart index e53b617b7..8912b6eb3 100644 --- a/app/lib/timetable/timetable_page/timetable_page_fab.dart +++ b/app/lib/timetable/timetable_page/timetable_page_fab.dart @@ -47,7 +47,9 @@ Future showTimetableAddEventPage( await Navigator.push( context, IgnoreWillPopScopeWhenIosSwipeBackRoute( - builder: (context) => const TimetableAddEventDialog(), + builder: (context) => TimetableAddEventDialog( + isExam: isExam, + ), settings: const RouteSettings(name: TimetableAddEventDialog.tag))); return null; } else { From 35a2f0a01fca9146f65a855525d2d3be8e05bb72 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:53:10 +0100 Subject: [PATCH 07/16] Open date picker when date is pressed. --- .../timetable_add_event_dialog.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 18da390cf..613fd4988 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -6,6 +6,7 @@ // // SPDX-License-Identifier: EUPL-1.2 +import 'package:clock/clock.dart'; import 'package:date/date.dart'; import 'package:flutter/material.dart'; import 'package:platform_check/platform_check.dart'; @@ -394,7 +395,17 @@ class _DateAndTimeTile extends StatelessWidget { onPressed: () {}, child: Text(time?.toString() ?? ''), ), - onTap: isDatePickingEnabled ? () {} : null, + onTap: isDatePickingEnabled + ? () async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: clock.now(), + firstDate: DateTime(2015, 8), + lastDate: DateTime(2101), + ); + print('picked: $picked'); + } + : null, ); } } From 9ece9fadc814e88f3d95c9dffa9fc264f67cfaf2 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:54:15 +0100 Subject: [PATCH 08/16] . --- TEMP.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TEMP.md b/TEMP.md index f11766242..f3803cd4c 100644 --- a/TEMP.md +++ b/TEMP.md @@ -2,9 +2,7 @@ * Time picker für Uhrzeit öffnen * Erstmal irgendeine leere Seite für die Stundenauswahl öffnen -* Unteres Datumsfeld ausgrauen? -* Hatten bei Terminen "Ort" und "Zusatzinformationen", bei Klausuren "Raum" und "Themen der Prüfung". So übernehmen? -* Titel Hint-Text bei Klausur umändern +* Neue UI fürs editieren nehmen? UI Checklist: From a95e32fad5cd268133761eb75fd72f8cd6b7c9d9 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:57:05 +0100 Subject: [PATCH 09/16] Open time picker when pressing on time. --- .../timetable_add_event/timetable_add_event_dialog.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 613fd4988..035835791 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -392,7 +392,13 @@ class _DateAndTimeTile extends StatelessWidget { foregroundColor: Theme.of(context).textTheme.bodyMedium!.color, textStyle: const TextStyle(fontSize: 15), ), - onPressed: () {}, + onPressed: () async { + final picked = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(clock.now()), + ); + print('picked: $picked'); + }, child: Text(time?.toString() ?? ''), ), onTap: isDatePickingEnabled From 6dc12787d3f83ed7693aa2985b74ac39ec04f4b7 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:01:09 +0100 Subject: [PATCH 10/16] Add stub lesson picker page. --- .../timetable_add_event_dialog.dart | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 035835791..042bf0af9 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -341,7 +341,14 @@ class _DateAndTimePicker extends StatelessWidget { children: [ const SizedBox(width: 34), OutlinedButton( - onPressed: () {}, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const _LessonPickerPage(), + ), + ); + }, style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14))), @@ -360,6 +367,17 @@ class _DateAndTimePicker extends StatelessWidget { } } +class _LessonPickerPage extends StatelessWidget { + const _LessonPickerPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center(child: Text('Hier soll die Stunde ausgewählt werden...')), + ); + } +} + class _DateAndTimeTile extends StatelessWidget { const _DateAndTimeTile({ super.key, From b4ba851e61fd01588cd191259908cb7e7a613ac0 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:05:18 +0100 Subject: [PATCH 11/16] =?UTF-8?q?Remove=20"Schulstunde=20ausw=C3=A4hlen"?= =?UTF-8?q?=20if=20not=20in=20debug=20mode.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../timetable_add_event_dialog.dart | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 042bf0af9..c32cef9f6 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -8,6 +8,7 @@ import 'package:clock/clock.dart'; import 'package:date/date.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:platform_check/platform_check.dart'; import 'package:sharezone/filesharing/dialog/course_tile.dart'; @@ -337,26 +338,28 @@ class _DateAndTimePicker extends StatelessWidget { time: Time(hour: 12, minute: 30), isDatePickingEnabled: false, ), - Row( - children: [ - const SizedBox(width: 34), - OutlinedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const _LessonPickerPage(), - ), - ); - }, - style: OutlinedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14))), - child: const Text('Schulstunde auswählen'), - ), - ], - ), - const SizedBox(height: 10), + if (kDebugMode) ...[ + Row( + children: [ + const SizedBox(width: 34), + OutlinedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const _LessonPickerPage(), + ), + ); + }, + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14))), + child: const Text('Schulstunde auswählen'), + ), + ], + ), + const SizedBox(height: 10), + ], ], ), ), From 57b156850dc6e9560d940558f2de7938fc9cd29a Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:20:41 +0100 Subject: [PATCH 12/16] More "isExam" --- .../timetable_add_event_dialog.dart | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index c32cef9f6..d7daf4b61 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -58,6 +58,7 @@ class TimetableAddEventDialog extends StatelessWidget { onCloseTap: () { Navigator.pop(context); }, + isExam: isExam, titleField: _TitleField( focusNode: _titleNode, isExam: isExam, @@ -82,7 +83,7 @@ class TimetableAddEventDialog extends StatelessWidget { const _MobileDivider(), const _Location(), const _MobileDivider(), - const _SendNotification(), + _SendNotification(isExam: isExam), ], ), ), @@ -110,11 +111,13 @@ class _AppBar extends StatelessWidget { required this.focusNodeTitle, required this.onCloseTap, required this.titleField, + required this.isExam, }); final bool editMode; final VoidCallback onCloseTap; final Widget titleField; + final bool isExam; final FocusNode focusNodeTitle; @@ -143,6 +146,7 @@ class _AppBar extends StatelessWidget { ), _SaveButton( editMode: editMode, + isExam: isExam, ), ], ), @@ -156,9 +160,10 @@ class _AppBar extends StatelessWidget { } class _SaveButton extends StatelessWidget { - const _SaveButton({this.editMode = false}); + const _SaveButton({this.editMode = false, required this.isExam}); final bool editMode; + final bool isExam; Future onPressed(BuildContext context) async { Navigator.pop(context); @@ -184,7 +189,7 @@ class _SaveButton extends StatelessWidget { Widget build(BuildContext context) { return SaveButton( // key: HwDialogKeys.saveButton, - tooltip: "Termin speichern", + tooltip: isExam ? "Klausur speichern" : "Termin speichern", onPressed: () => onPressed(context), ); } @@ -531,7 +536,9 @@ class _Location extends StatelessWidget { } class _SendNotification extends StatelessWidget { - const _SendNotification(); + const _SendNotification({required this.isExam}); + + final bool isExam; @override Widget build(BuildContext context) { @@ -543,9 +550,8 @@ class _SendNotification extends StatelessWidget { title: "Kursmitglieder benachrichtigen", onChanged: (newValue) {}, sendNotification: true, - // TODO: Termin/Klausur je nach dem auswählen description: - "Sende eine Benachrichtigung an deine Kursmitglieder, dass du einen neuen Termin erstellt hast.", + "Sende eine Benachrichtigung an deine Kursmitglieder, dass du ${isExam ? 'eine neue Klausur' : 'einen neuen Termin'} erstellt hast.", ), ), ); From 3e249961c775581b51d2ca3a00c6e1f09852fbca Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:23:05 +0100 Subject: [PATCH 13/16] =?UTF-8?q?Make=20"Stunde=20ausw=C3=A4hlen"=20invisi?= =?UTF-8?q?ble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../timetable_add_event/timetable_add_event_dialog.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index d7daf4b61..24a526bbd 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -8,7 +8,6 @@ import 'package:clock/clock.dart'; import 'package:date/date.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:platform_check/platform_check.dart'; import 'package:sharezone/filesharing/dialog/course_tile.dart'; @@ -343,7 +342,8 @@ class _DateAndTimePicker extends StatelessWidget { time: Time(hour: 12, minute: 30), isDatePickingEnabled: false, ), - if (kDebugMode) ...[ + // ignore: dead_code + if (false) ...[ Row( children: [ const SizedBox(width: 34), From 41a8ae7a08c3c5bb080ee718191bd98faef33382 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:26:08 +0100 Subject: [PATCH 14/16] Fix lints. --- .../timetable_add_event_dialog.dart | 22 +++++++++---------- .../timetable_page/timetable_page_fab.dart | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 24a526bbd..256e335a5 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -6,6 +6,8 @@ // // SPDX-License-Identifier: EUPL-1.2 +import 'dart:developer'; + import 'package:clock/clock.dart'; import 'package:date/date.dart'; import 'package:flutter/material.dart'; @@ -41,10 +43,10 @@ class TimetableAddEventDialog extends StatelessWidget { return; } - final shouldPop = await warnUserAboutLeavingForm(context); - if (shouldPop && context.mounted) { - navigator.pop(); - } + // final shouldPop = await warnUserAboutLeavingForm(context); + // if (shouldPop && context.mounted) { + // navigator.pop(); + // } }, child: Scaffold( body: Column( @@ -231,13 +233,11 @@ class _TitleFieldBase extends StatelessWidget { const _TitleFieldBase({ required this.prefilledTitle, required this.onChanged, - this.errorText, this.focusNode, required this.hintText, }); final String? prefilledTitle; - final String? errorText; final FocusNode? focusNode; final String hintText; final Function(String) onChanged; @@ -279,7 +279,8 @@ class _TitleFieldBase extends StatelessWidget { textCapitalization: TextCapitalization.sentences, ), Text( - errorText ?? "", + // errorText ?? "", + "", style: TextStyle(color: Colors.red[700], fontSize: 12), ), const SizedBox(height: 10), @@ -376,7 +377,7 @@ class _DateAndTimePicker extends StatelessWidget { } class _LessonPickerPage extends StatelessWidget { - const _LessonPickerPage({super.key}); + const _LessonPickerPage(); @override Widget build(BuildContext context) { @@ -388,7 +389,6 @@ class _LessonPickerPage extends StatelessWidget { class _DateAndTimeTile extends StatelessWidget { const _DateAndTimeTile({ - super.key, this.leading, this.date, this.time, @@ -423,7 +423,7 @@ class _DateAndTimeTile extends StatelessWidget { context: context, initialTime: TimeOfDay.fromDateTime(clock.now()), ); - print('picked: $picked'); + log('picked: $picked'); }, child: Text(time?.toString() ?? ''), ), @@ -435,7 +435,7 @@ class _DateAndTimeTile extends StatelessWidget { firstDate: DateTime(2015, 8), lastDate: DateTime(2101), ); - print('picked: $picked'); + log('picked: $picked'); } : null, ); diff --git a/app/lib/timetable/timetable_page/timetable_page_fab.dart b/app/lib/timetable/timetable_page/timetable_page_fab.dart index 8912b6eb3..de6ef9339 100644 --- a/app/lib/timetable/timetable_page/timetable_page_fab.dart +++ b/app/lib/timetable/timetable_page/timetable_page_fab.dart @@ -43,7 +43,8 @@ Future showTimetableAddEventPage( BuildContext context, { required bool isExam, }) async { - if (true) { + // ignore: dead_code + if (false) { await Navigator.push( context, IgnoreWillPopScopeWhenIosSwipeBackRoute( From 74919022d720cfe5b75bfa88e2819e854e81d379 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:27:29 +0100 Subject: [PATCH 15/16] Remove TEMP --- TEMP.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 TEMP.md diff --git a/TEMP.md b/TEMP.md deleted file mode 100644 index f3803cd4c..000000000 --- a/TEMP.md +++ /dev/null @@ -1,12 +0,0 @@ -* Date picker für Datum öffnen -* Time picker für Uhrzeit öffnen -* Erstmal irgendeine leere Seite für die Stundenauswahl öffnen - -* Neue UI fürs editieren nehmen? - -UI Checklist: - -* [ ] White mode überprüft -* [ ] Dark mode überprüft -* [ ] Handy-Layout überprüft -* [ ] Desktop-Layout überprüft From 84ba5825bafe232ecc7dd9cc012610b253dba2e9 Mon Sep 17 00:00:00 2001 From: Jonas Sander <29028262+Jonas-Sander@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:40:04 +0100 Subject: [PATCH 16/16] Room: use keyboardType TextInputType.text --- .../timetable_add_event/timetable_add_event_dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart index 256e335a5..7fdbe8e50 100644 --- a/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart +++ b/app/lib/timetable/timetable_add_event/timetable_add_event_dialog.dart @@ -515,7 +515,7 @@ class _Location extends StatelessWidget { prefilledText: '', maxLines: null, scrollPadding: const EdgeInsets.all(16.0), - keyboardType: TextInputType.multiline, + keyboardType: TextInputType.text, decoration: const InputDecoration( hintText: "Ort/Raum", border: InputBorder.none,