diff --git a/lib/utils/validators.dart b/lib/utils/validators.dart index e2df6af7e..bc38df304 100644 --- a/lib/utils/validators.dart +++ b/lib/utils/validators.dart @@ -1,13 +1,17 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; ///This class creats various validator methods for the application. +/// ///They are used to validate information given by the users. class Validator { - //Method to validate an organization's URL. + /// Method to validate an organization's URL. + /// + /// **params**: + /// * `value`: the URL of the organization + /// + /// **returns**: + /// * `String?`: error message if URL is invalid. static String? validateURL( String value, ) { @@ -21,7 +25,13 @@ class Validator { return null; } - //Method to validate a user's first name + /// Method to validate a user's first name. + /// + /// **params**: + /// * `value`: the value of the first name + /// + /// **returns**: + /// * `String?`: error message if first name is invalid. static String? validateFirstName(String value) { if (value.isEmpty) { return 'Firstname must not be left blank.'; @@ -35,7 +45,13 @@ class Validator { return null; } - //Method to validate a user's last name + /// Method to validate a user's last name. + /// + /// **params**: + /// * `value`: the value of the last name + /// + /// **returns**: + /// * `String?`: error message if last name is invalid. static String? validateLastName(String value) { if (value.isEmpty) { return 'Lastname must not be left blank.'; @@ -49,7 +65,13 @@ class Validator { return null; } - //Method to validate a user's email + /// Method to validate a user's email. + /// + /// **params**: + /// * `email`: the entered email + /// + /// **returns**: + /// * `String?`: error message if email is invalid. static String? validateEmail( String email, ) { @@ -66,7 +88,13 @@ class Validator { return null; } - //Method to validate password + /// Method to validate password. + /// + /// **params**: + /// * `password`: the entered password + /// + /// **returns**: + /// * `String?`: error message if password is invalid. static String? validatePassword( String password, ) { @@ -92,7 +120,14 @@ class Validator { return null; } - //Method to valid password re-entered for confirmation + /// Method to valid password re-entered for confirmation. + /// + /// **params**: + /// * `value`: the entered password + /// * `comparator`: the original password + /// + /// **returns**: + /// * `String?`: error message if password is invalid. static String? validatePasswordConfirm( String value, String comparator, @@ -103,7 +138,13 @@ class Validator { return null; } - //Method to validate already exisiting URL + /// Method to validate already exisiting URL. + /// + /// **params**: + /// * `url`: the entered URL + /// + /// **returns**: + /// * `Future`: true if URL exists, false otherwise. Future validateUrlExistence(String url) async { try { await http.get(Uri.parse(url)); @@ -114,7 +155,14 @@ class Validator { } } - //Method to validate event form + /// Method to validate event form. + /// + /// **params**: + /// * `value`: the value of the field + /// * `label`: the (optional) label of the field + /// + /// **returns**: + /// * `String?`: error message if field is invalid. static String? validateEventForm(String value, String? label) { if (value.isEmpty) { return '$label must not be left blank.'; @@ -127,4 +175,20 @@ class Validator { } return null; } + + /// Method to validate event time. + /// + /// **params**: + /// * `startTime`: the start time of the event + /// * `endTime`: the end time of the event + /// + /// **returns**: + /// * `String?`: error message if time is invalid. + static String? validateEventTime(TimeOfDay startTime, TimeOfDay endTime) { + if (startTime.hour > endTime.hour || + (startTime.hour == endTime.hour && startTime.minute > endTime.minute)) { + return 'Start time must be before or equal to end time'; + } + return null; + } } diff --git a/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart b/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart index 05655e7cb..c89c2b2f9 100644 --- a/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart +++ b/lib/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart @@ -39,7 +39,10 @@ class CreateEventViewModel extends BaseModel { TimeOfDay eventStartTime = TimeOfDay.now(); /// Event End Time. - TimeOfDay eventEndTime = TimeOfDay.now(); + TimeOfDay eventEndTime = TimeOfDay.now().replacing( + hour: (TimeOfDay.now().hour + (TimeOfDay.now().minute >= 30 ? 1 : 0)) % 24, + minute: (TimeOfDay.now().minute + 30) % 60, + ); /// Event Start Date. DateTime eventStartDate = DateTime.now(); diff --git a/lib/views/after_auth_screens/events/create_event_page.dart b/lib/views/after_auth_screens/events/create_event_page.dart index 595fc9faf..e31ec1b96 100644 --- a/lib/views/after_auth_screens/events/create_event_page.dart +++ b/lib/views/after_auth_screens/events/create_event_page.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:talawa/enums/enums.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/services/navigation_service.dart'; import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/app_localization.dart'; +import 'package:talawa/utils/validators.dart'; import 'package:talawa/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart'; import 'package:talawa/views/after_auth_screens/events/create_event_form.dart'; import 'package:talawa/views/base_view.dart'; @@ -164,9 +166,20 @@ class _CreateEventPageState extends State { initialTime: model.eventStartTime, ); - setState(() { - model.eventStartTime = time; - }); + final validationError = Validator.validateEventTime( + time, + model.eventEndTime, + ); + if (validationError != null) { + navigationService.showTalawaErrorSnackBar( + 'Start time must be before end time', + MessageType.error, + ); + } else { + setState(() { + model.eventStartTime = time; + }); + } }, ), SizedBox( @@ -207,22 +220,21 @@ class _CreateEventPageState extends State { final time = await customTimePicker( initialTime: model.eventEndTime, ); - final currTimeToInt = time.hour + time.minute / 60.0; - final startTime = model.eventStartTime; - final startTimeToInt = - startTime.hour + startTime.minute / 60; - final eventStartDate = model.eventStartDate; - final eventEndDate = model.eventEndDate; - if (startTimeToInt.compareTo(currTimeToInt) < 0 && - eventStartDate.compareTo(eventEndDate) < 0) { + final validationError = Validator.validateEventTime( + model.eventStartTime, + time, + ); + final showSnackBar = + navigationService.showTalawaErrorSnackBar; + if (validationError != null) { + showSnackBar( + 'Start time must be before end time', + MessageType.error, + ); + } else { setState(() { model.eventEndTime = time; }); - } else { - // ignore: undefined_method - navigationServiceLocal.showSnackBar( - "End time cannot be before the start time. ", - ); } }, ), diff --git a/pubspec.lock b/pubspec.lock index 61788cc69..37d215420 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -857,10 +857,10 @@ packages: dependency: "direct main" description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -1201,58 +1201,50 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" + sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "11.0.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" + sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e url: "https://pub.dev" source: hosted - version: "12.0.1" + version: "11.1.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" - url: "https://pub.dev" - source: hosted - version: "9.2.0" - permission_handler_html: - dependency: transitive - description: - name: permission_handler_html - sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8 + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "0.1.0+1" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "3.12.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.1.3" petitparser: dependency: transitive description: name: petitparser - sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "5.4.0" platform: dependency: transitive description: @@ -1425,10 +1417,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: @@ -1550,26 +1542,26 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_calendar - sha256: "68b7555324f76badae05cd12c2e6d55020896ab041d8c7e07ca7762b3235e76c" + sha256: "3a823c95c8f0b8b65f71bcbfcf81a5920ea71d20186796782ef26a3aa8acfeb0" url: "https://pub.dev" source: hosted - version: "23.2.6" + version: "22.2.12" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: "88366a5e95ccb4091feb531021567f76ed3ceddab7424de02a9ac4a335d22a44" + sha256: "9f0a4593f7642b2f106e329734d0e5fc746baf8d0a59495eec586cd0d9ba7d02" url: "https://pub.dev" source: hosted - version: "23.2.6" + version: "22.2.12" syncfusion_flutter_datepicker: dependency: "direct main" description: name: syncfusion_flutter_datepicker - sha256: "5e0a9647232fbbd31d1734f4f8eba517e78a6ea955739671182c9c7cbdd05d42" + sha256: c09d8a84f07da41c12b02ac350f4aab8b73be2c51095fca9f9404b2a5af67585 url: "https://pub.dev" source: hosted - version: "23.2.6" + version: "22.2.12" synchronized: dependency: transitive description: @@ -1821,10 +1813,10 @@ packages: dependency: transitive description: name: xml - sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.4.2" + version: "6.3.0" yaml: dependency: transitive description: @@ -1834,5 +1826,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <3.13.0" - flutter: ">=3.16.0" + dart: ">=3.2.0-194.0.dev <3.13.0" + flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index fa40ffa0d..e7f1dc7e7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: googleapis: 12.0.0 graphql_flutter: ^5.1.2 hive: ^2.2.3 - http: ^1.1.2 + http: any image_cropper: ^5.0.1 image_picker: ^1.0.4 intl: ^0.18.0 @@ -63,7 +63,7 @@ dependencies: mockito: ^5.4.3 network_image_mock: ^2.1.1 path_provider: ^2.1.1 - permission_handler: ^11.1.0 + permission_handler: any plugin_platform_interface: ^2.1.7 pointycastle: ^3.7.3 provider: ^6.1.1 @@ -73,8 +73,8 @@ dependencies: shared_preferences: ^2.2.2 shimmer: ^3.0.0 social_share: ^2.2.1 - syncfusion_flutter_calendar: 23.2.6 - syncfusion_flutter_datepicker: 23.2.6 + syncfusion_flutter_calendar: ^22.2.12 + syncfusion_flutter_datepicker: any timelines: ^0.1.0 tutorial_coach_mark: ^1.2.11 uni_links: ^0.5.1 diff --git a/test/utils_tests/validators_test.dart b/test/utils_tests/validators_test.dart index 051385618..97ed6f72b 100644 --- a/test/utils_tests/validators_test.dart +++ b/test/utils_tests/validators_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: talawa_api_doc // ignore_for_file: talawa_good_doc_comments +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:talawa/utils/validators.dart'; @@ -226,6 +227,16 @@ void main() { expect(result, null); }); + + test('Test validateEventTime for start time greater than end time', + () { + const startTime = TimeOfDay(hour: 14, minute: 30); + const endTime = TimeOfDay(hour: 12, minute: 30); + + final result = Validator.validateEventTime(startTime, endTime); + + expect(result, 'Start time must be before or equal to end time'); + }); }, ); }, diff --git a/test/views/after_auth_screens/events/create_event_page_test.dart b/test/views/after_auth_screens/events/create_event_page_test.dart index 8aa7b6b2e..2e794d78e 100644 --- a/test/views/after_auth_screens/events/create_event_page_test.dart +++ b/test/views/after_auth_screens/events/create_event_page_test.dart @@ -1,6 +1,3 @@ -// ignore_for_file: talawa_api_doc -// ignore_for_file: talawa_good_doc_comments - import 'dart:io'; import 'package:flutter/material.dart'; @@ -434,7 +431,7 @@ void main() { await tester.pump(); }); group('setState Coverage completion', () { - testWidgets('Tap on DataTimeTile date', (tester) async { + testWidgets('Tap on DateTimeTile date', (tester) async { await tester.pumpWidget( createEventScreen( themeMode: ThemeMode.dark, @@ -460,7 +457,9 @@ void main() { findsNWidgets(2), ); }); - testWidgets('Tap on DataTimeTile time', (tester) async { + testWidgets('Tap on DateTimeTile time', (tester) async { + final currentTime = DateTime.now(); + final futureTime = currentTime.add(const Duration(minutes: 30)); await tester.pumpWidget( createEventScreen( themeMode: ThemeMode.dark, @@ -477,11 +476,16 @@ void main() { await tester.tap(find.text('OK')); await tester.pump(); expect( - find.text(DateFormat.jm().format(DateTime.now())), - findsNWidgets(2), + find.text(DateFormat.jm().format(currentTime)), + findsOneWidget, + ); + + expect( + find.text(DateFormat.jm().format(futureTime)), + findsOneWidget, ); }); - testWidgets('Tap on DataTimeTile date', (tester) async { + testWidgets('Tap on DateTimeTile date', (tester) async { await tester.pumpWidget( createEventScreen( themeMode: ThemeMode.dark, @@ -506,7 +510,9 @@ void main() { findsNWidgets(2), ); }); - testWidgets('Tap on DataTimeTile time', (tester) async { + testWidgets('Tap on DateTimeTile time', (tester) async { + final currentTime = DateTime.now(); + final futureTime = currentTime.add(const Duration(minutes: 30)); await tester.pumpWidget( createEventScreen( themeMode: ThemeMode.dark, @@ -523,8 +529,13 @@ void main() { await tester.tap(find.text('OK')); await tester.pump(); expect( - find.text(DateFormat.jm().format(DateTime.now())), - findsNWidgets(2), + find.text(DateFormat.jm().format(currentTime)), + findsOneWidget, + ); + + expect( + find.text(DateFormat.jm().format(futureTime)), + findsOneWidget, ); }); });