From 467796fd8e2e6f570b5a0b60a4ab167e47c90bb3 Mon Sep 17 00:00:00 2001 From: Peter Harrison Date: Mon, 19 Feb 2024 10:17:53 -0800 Subject: [PATCH 1/8] Upgraded github actions to the latest versions --- .github/workflows/pull-request.yml | 14 +++++++------- .github/workflows/push.yaml | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 3199395e6..514e140d7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest #needs: PR-Greeting steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 @@ -50,7 +50,7 @@ jobs: - name: Running count lines run: ./.github/workflows/countline.py --exclude_directories test/ --exclude_files lib/custom_painters/talawa_logo.dart lib/custom_painters/language_icon.dart lib/custom_painters/whatsapp_logo.dart lib/utils/queries.dart lib/view_model/after_auth_view_models/profile_view_models/profile_page_view_model.dart lib/view_model/pre_auth_view_models/select_organization_view_model.dart lib/views/after_auth_screens/profile/profile_page.dart lib/view_model/main_screen_view_model.dart lib/views/after_auth_screens/events/create_event_page.dart - name: setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 - name: Check for presence of ignore directives corresponding to custom lints run: chmod +x ./.github/workflows/check_ignore.py - name: Run check_ignore @@ -89,7 +89,7 @@ jobs: # - name: Echo the GitHub context for troubleshooting # run: echo "${{ toJSON(github) }}" # - name: setup python -# uses: actions/setup-python@v4 +# uses: actions/setup-python@v5 # - name: Granting permission to documentationcheck.py # run: chmod +x ./.github/workflows/documentationcheck.py # - name: execute py script @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest needs: Flutter-Codebase-Check steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' # See 'Supported distributions' for available options @@ -119,7 +119,7 @@ jobs: - name: Codebase testing run: flutter test --coverage - name: Present and upload coverage to Codecov as ${{env.CODECOV_UNIQUE_NAME}} - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} verbose: true @@ -136,7 +136,7 @@ jobs: runs-on: ubuntu-latest needs: Flutter-Codebase-Check steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' # See 'Supported distributions' for available options @@ -155,7 +155,7 @@ jobs: runs-on: macos-latest needs: Flutter-Codebase-Check steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: '3.16.0' diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 929de9d25..0d6d1997e 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -30,7 +30,7 @@ jobs: name: Checking codebase runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # This is important to fetch all history for all branches and tags. # This could be important for our documentation generation process. @@ -51,7 +51,7 @@ jobs: - name: Checking for correct formatting of code run: dart format --set-exit-if-changed . - name: setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 - name: Check for presence of ignore directives corresponding to custom lints run: chmod +x ./.github/workflows/check_ignore.py - name: Run check_ignore @@ -77,7 +77,7 @@ jobs: if: github.ref == 'refs/heads/automated-docs' environment: TALAWA_ENVIRONMENT steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' # See 'Supported distributions' for available options @@ -140,7 +140,7 @@ jobs: if: github.ref == 'refs/heads/automated-docs' needs: Update-Documentation steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dmnemec/copy_file_to_another_repo_action@v1.1.1 env: API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB_NEW }} @@ -161,7 +161,7 @@ jobs: needs: Flutter-Codebase-Check # needs: Update-Documentation steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' # See 'Supported distributions' for available options @@ -177,7 +177,7 @@ jobs: - name: Codebase testing run: flutter test --coverage - name: Present and upload coverage to Codecov as ${{env.CODECOV_UNIQUE_NAME}} - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} verbose: true @@ -192,7 +192,7 @@ jobs: runs-on: ubuntu-latest needs: Flutter-Testing steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' # See 'Supported distributions' for available options @@ -228,7 +228,7 @@ jobs: runs-on: macos-latest needs: Flutter-Testing steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: '3.16.0' From ef29d9f5e60511bc71ea7ff21f63249377e49e25 Mon Sep 17 00:00:00 2001 From: Yousef Rabia <78663127+Yousef-Rabia@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:46:33 +0200 Subject: [PATCH 2/8] Remove the geolocator package (#2388) --- pubspec.lock | 90 ++++++---------------------------------------------- pubspec.yaml | 1 - 2 files changed, 9 insertions(+), 82 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index f6e7eace8..a3637f7ce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -557,54 +557,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - geolocator: - dependency: "direct main" - description: - name: geolocator - sha256: "694ec58afe97787b5b72b8a0ab78c1a9244811c3c10e72c4362ef3c0ceb005cd" - url: "https://pub.dev" - source: hosted - version: "11.0.0" - geolocator_android: - dependency: transitive - description: - name: geolocator_android - sha256: "06e37fa32392f69f133e166ef6b358a8b6afddbf4c418fc236988184cc115a49" - url: "https://pub.dev" - source: hosted - version: "4.4.1" - geolocator_apple: - dependency: transitive - description: - name: geolocator_apple - sha256: "79babf44b692ec5e789d322dc736ef71586056e8e6828f747c9e005456b248bf" - url: "https://pub.dev" - source: hosted - version: "2.3.5" - geolocator_platform_interface: - dependency: transitive - description: - name: geolocator_platform_interface - sha256: "6c8d494d6948757c56720b778af742f6973f31fca1f702a7539b8917e4a2468a" - url: "https://pub.dev" - source: hosted - version: "4.2.0" - geolocator_web: - dependency: transitive - description: - name: geolocator_web - sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - geolocator_windows: - dependency: transitive - description: - name: geolocator_windows - sha256: a92fae29779d5c6dc60e8411302f5221ade464968fe80a36d330e80a71f087af - url: "https://pub.dev" - source: hosted - version: "0.2.2" get_it: dependency: "direct main" description: @@ -885,30 +837,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.1" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" - url: "https://pub.dev" - source: hosted - version: "10.0.0" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 - url: "https://pub.dev" - source: hosted - version: "2.0.1" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 - url: "https://pub.dev" - source: hosted - version: "2.0.1" lint: dependency: "direct dev" description: @@ -929,26 +857,26 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" mime: dependency: transitive description: @@ -1033,10 +961,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" path_parsing: dependency: transitive description: @@ -1795,4 +1723,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.2.0 <3.13.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 732a47ed6..3799fb579 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,7 +45,6 @@ dependencies: flutter_svg: ^2.0.9 font_awesome_flutter: ^10.7.0 geocoding: ^2.1.1 - geolocator: ^11.0.0 get_it: ^7.6.6 graphql_flutter: ^5.1.2 hive: ^2.2.3 From 6f8fdcb7ff865ae39fb671223463729b70ac3aa0 Mon Sep 17 00:00:00 2001 From: Insiya <114379635+Insiyaya@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:46:58 +0530 Subject: [PATCH 3/8] added additional test cases to edit profile page test file (#2331) * added additional test cases to edit profile page test file * updates the code * fixed failing tests * fixed * Done some corrections in previous code * Update edit_profile_page_test.dart * Update edit_profile_page_test.dart * Made some changes to run the tests Made some changes to run the tests --- .../profile/edit_profile_page.dart | 1 + .../profile/edit_profile_page_test.dart | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/lib/views/after_auth_screens/profile/edit_profile_page.dart b/lib/views/after_auth_screens/profile/edit_profile_page.dart index 32de5599d..6704aea7b 100644 --- a/lib/views/after_auth_screens/profile/edit_profile_page.dart +++ b/lib/views/after_auth_screens/profile/edit_profile_page.dart @@ -150,6 +150,7 @@ class _EditProfilePageState extends State { Flexible( // Text field for first name with value text of user's first name. child: TextFormField( + key: const Key('FirstNameTextField'), controller: model.firstNameTextController ..text = model.user.firstName!, focusNode: model.firstNameFocus, diff --git a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart index fb3284606..f0e29ac44 100644 --- a/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart +++ b/test/widget_tests/after_auth_screens/profile/edit_profile_page_test.dart @@ -452,5 +452,24 @@ Future main() async { expect(model.imageFile, null); verify(notifyListenerCallback()); }); + + // Testing onPressed for firstName + testWidgets("Testing if firstName text field gets focus", (tester) async { + userConfig.updateUser( + User(firstName: 'Test', lastName: 'Test', email: 'test@test.com'), + ); + await tester.pumpWidget(createChangePassScreenDark()); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('FirstNameTextField'))); + await tester.pumpAndSettle(); + + // Verify the focus + expect( + FocusScope.of(tester.element(find.byType(EditProfilePage))) + .focusedChild! + .hasPrimaryFocus, + true, + ); + }); }); } From f25d198d56caec7630a7590a2d6bf282d91a3f6f Mon Sep 17 00:00:00 2001 From: Shaik Azad <120930148+Azad99-9@users.noreply.github.com> Date: Sun, 25 Feb 2024 22:38:42 +0530 Subject: [PATCH 4/8] Built User Interface for creating recurring events. (#2386) * Built User Interface for creating recurring events. * covered missing lines --- lib/constants/routing_constants.dart | 3 + lib/locator.dart | 2 + lib/router.dart | 8 + .../event_calendar_view_model.dart | 104 ++++++++ .../events/create_custom_recurring_event.dart | 168 +++++++++++++ .../events/create_event_page.dart | 35 ++- .../events/event_calendar.dart | 161 ++++++------ ...create_recurring_event_helper_widgets.dart | 231 +++++++++++++++++ lib/widgets/custom_weekday_selector.dart | 85 +++++++ lib/widgets/recurrence_dialog.dart | 63 +++++ test/router_test.dart | 13 + .../create_custom_recurring_event_test.dart | 233 ++++++++++++++++++ .../events/create_event_page_test.dart | 42 +++- .../events/event_calendar_test.dart | 45 ++++ 14 files changed, 1093 insertions(+), 100 deletions(-) create mode 100644 lib/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart create mode 100644 lib/views/after_auth_screens/events/create_custom_recurring_event.dart create mode 100644 lib/widgets/create_recurring_event_helper_widgets.dart create mode 100644 lib/widgets/custom_weekday_selector.dart create mode 100644 lib/widgets/recurrence_dialog.dart create mode 100644 test/views/after_auth_screens/events/create_custom_recurring_event_test.dart diff --git a/lib/constants/routing_constants.dart b/lib/constants/routing_constants.dart index b0742e241..07fe6cbc0 100644 --- a/lib/constants/routing_constants.dart +++ b/lib/constants/routing_constants.dart @@ -61,6 +61,9 @@ class Routes { /// static variables. static const String createEventPage = "/createEventPage"; + /// static variables. + static const String customRecurrencePage = "/customRecurrencePage"; + /// static variables. static const String profilePage = "/profilePage"; diff --git a/lib/locator.dart b/lib/locator.dart index dc1b148b5..495e8fad1 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -23,6 +23,7 @@ import 'package:talawa/view_model/after_auth_view_models/add_post_view_models/ad import 'package:talawa/view_model/after_auth_view_models/chat_view_models/direct_chat_view_model.dart'; import 'package:talawa/view_model/after_auth_view_models/event_view_models/create_event_view_model.dart'; import 'package:talawa/view_model/after_auth_view_models/event_view_models/edit_event_view_model.dart'; +import 'package:talawa/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart'; import 'package:talawa/view_model/after_auth_view_models/event_view_models/event_info_view_model.dart'; import 'package:talawa/view_model/after_auth_view_models/event_view_models/explore_events_view_model.dart'; import 'package:talawa/view_model/after_auth_view_models/feed_view_models/organization_feed_view_model.dart'; @@ -135,6 +136,7 @@ void setupLocator() { locator.registerFactory(() => EditProfilePageViewModel()); locator.registerFactory(() => CreateEventViewModel()); locator.registerFactory(() => EditEventViewModel()); + locator.registerFactory(() => EventCalendarViewModel()); locator.registerFactory(() => AddPostViewModel()); locator.registerFactory(() => EventInfoViewModel()); locator.registerFactory(() => AppSettingViewModel()); diff --git a/lib/router.dart b/lib/router.dart index e5040ace2..5c5f9b3a3 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -12,6 +12,7 @@ import 'package:talawa/views/after_auth_screens/add_post_page.dart'; import 'package:talawa/views/after_auth_screens/app_settings/app_settings_page.dart'; import 'package:talawa/views/after_auth_screens/chat/chat_message_screen.dart'; import 'package:talawa/views/after_auth_screens/chat/select_contact.dart'; +import 'package:talawa/views/after_auth_screens/events/create_custom_recurring_event.dart'; import 'package:talawa/views/after_auth_screens/events/create_event_page.dart'; import 'package:talawa/views/after_auth_screens/events/edit_event_page.dart'; import 'package:talawa/views/after_auth_screens/events/event_calendar.dart'; @@ -196,6 +197,13 @@ Route generateRoute(RouteSettings settings) { builder: (context) => const CreateEventPage(key: Key('CreateEvent')), ); + // Returns the CreateEventPage Widget + case Routes.customRecurrencePage: + return MaterialPageRoute( + builder: (context) => + CustomRecurrencePage(key: const Key('CreateEvent')), + ); + // Returns the ProfilePage Widget case Routes.profilePage: return MaterialPageRoute( diff --git a/lib/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart b/lib/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart new file mode 100644 index 000000000..03b86bfe0 --- /dev/null +++ b/lib/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart @@ -0,0 +1,104 @@ +import 'package:flutter/scheduler.dart'; +import 'package:syncfusion_flutter_calendar/calendar.dart'; +import 'package:syncfusion_flutter_datepicker/datepicker.dart'; +import 'package:talawa/models/events/event_model.dart'; +import 'package:talawa/view_model/base_view_model.dart'; + +/// ViewModel for managing events and calendar view. +/// +/// This ViewModel handles operations related to events and calendar views. +class EventCalendarViewModel extends BaseModel { + /// List of events managed by the ViewModel. + late List _eventList = []; + + /// Controller for managing the calendar. + final CalendarController _calendarController = CalendarController(); + + /// Controller for managing the date range picker. + final DateRangePickerController _dateRangePickerController = + DateRangePickerController(); + + /// The current view of the calendar. + late CalendarView _calendarView; + + /// Getter for accessing the calendar controller. + CalendarController get calendarController => _calendarController; + + /// Getter for accessing the date range picker controller. + DateRangePickerController get dateRangePickerController => + _dateRangePickerController; + + /// Getter for accessing the current view of the calendar. + CalendarView get calendarView => _calendarView; + + /// Getter for accessing the current list of events. + List get eventList => _eventList; + + /// Initializes the view model. + /// + /// **params**: + /// * `eventList`: list of events. + /// + /// **returns**: + /// None + void initialize(List eventList) { + _eventList = eventList; + _calendarView = CalendarView.schedule; + } + + /// The function to triggered when the view is changed. + /// + /// **params**: + /// * `viewChangedDetails`: The dates that visible on the view changes in SfCalendar. type is ViewChangedDetails + /// + /// **returns**: + /// None + void viewChanged(ViewChangedDetails viewChangedDetails) { + print("came"); + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + _dateRangePickerController.selectedDate = + viewChangedDetails.visibleDates[0]; + _dateRangePickerController.displayDate = + viewChangedDetails.visibleDates[0]; + }); + } + + /// Changes the view of the calendar. + /// + /// **params**: + /// * `view`: String representing the desired view ("Day", "Month", "Schedule"). + /// + /// **returns**: + /// None + void changeView(String view) { + switch (view) { + case "Day": + _calendarView = CalendarView.day; + break; + case "Month": + _calendarView = CalendarView.month; + break; + case "Schedule": + _calendarView = CalendarView.schedule; + break; + default: + break; + } + calendarController.view = _calendarView; + notifyListeners(); + } + + /// function to be triggered when selection is changed. + /// + /// + /// **params**: + /// * `args`: Object of type DateRangePickerSelectionChangedArgs, The selected dates or ranges changes in the SfDateRangePicker. + /// + /// **returns**: + /// None + void selectionChanged(DateRangePickerSelectionChangedArgs args) { + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + _calendarController.displayDate = args.value as DateTime?; + }); + } +} diff --git a/lib/views/after_auth_screens/events/create_custom_recurring_event.dart b/lib/views/after_auth_screens/events/create_custom_recurring_event.dart new file mode 100644 index 000000000..ce038eb00 --- /dev/null +++ b/lib/views/after_auth_screens/events/create_custom_recurring_event.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/utils/app_localization.dart'; +import 'package:talawa/widgets/create_recurring_event_helper_widgets.dart'; +import 'package:talawa/widgets/custom_weekday_selector.dart'; + +/// custom padding. +final _sectionPadding = SizeConfig.blockSizeHorizontal! * 8; + +/// Enables users to define their customRecurrence event. +class CustomRecurrencePage extends StatelessWidget { + CustomRecurrencePage({super.key}); + + final TextEditingController _repeatFrequencyTextController = + TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).primaryColor, + elevation: 1, + centerTitle: true, + title: Text( + AppLocalizations.of(context)!.strictTranslate('Custom recurrence'), + style: Theme.of(context).textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.w600, + fontSize: 20, + ), + ), + actions: [ + TextButton( + onPressed: () {}, + child: const Text('Done'), + ), + ], + ), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: _buildRecurrenceOptions(context), + ), + ); + } + + /// Utility to build recurrence options widgets. + /// + /// **params**: + /// * `context`: BuildContext of the widget. + /// + /// **returns**: + /// * `Column`: Column of recurrence options widgets. + Column _buildRecurrenceOptions(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(_sectionPadding), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + inputFieldHeading('Repeats every'), + _section1InputFields(), + ], + ), + ), + buildCustomDivider(context), + Container( + padding: EdgeInsets.fromLTRB( + _sectionPadding, + _sectionPadding, + 90, + _sectionPadding, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + inputFieldHeading('Repeats on'), + CustomWeekDaySelector(), + ], + ), + ), + buildCustomDivider(context), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + EdgeInsets.only(top: _sectionPadding, left: _sectionPadding), + child: inputFieldHeading('Ends'), + ), + const EventEndOptions(), + ], + ), + ], + ); + } + + /// Input Fields of repeats every section. + /// + /// **params**: + /// None + /// + /// **returns**: + /// * `Row`: Row of Custom input fields. + Row _section1InputFields() { + return Row( + children: [ + SizedBox( + width: SizeConfig.screenWidth! * 0.15, + child: CustomTextField( + key: const Key('inputsection1TextField'), + maxTextLength: 2, + textEditingController: _repeatFrequencyTextController, + ), + ), + SizedBox( + width: SizeConfig.screenWidth! * 0.03, + ), + CustomRectangle( + child: Container( + padding: const EdgeInsets.all(12), + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment + .center, // Align the row's contents to the center horizontally + children: [ + RecurrenceFrequencyDropdown(), + ], + ), + ), + ), + ), + ], + ); + } + + /// Custom heading for input fields. + /// + /// **params**: + /// * `title`: Text displayed as heading. + /// + /// **returns**: + /// * `Container`: Container containing the heading text. + Container inputFieldHeading(String title) => Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title), + const SizedBox( + height: 12, + ), + ], + ), + ); + + /// Divider with custom properties. + /// + /// **params**: + /// * `context`: BuildContext of the widget. + /// + /// **returns**: + /// * `Divider`: Custom divider. + Divider buildCustomDivider(BuildContext context) { + return Divider( + color: Theme.of(context).hintColor, + ); + } +} 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 c3e7e84ef..d9047e987 100644 --- a/lib/views/after_auth_screens/events/create_event_page.dart +++ b/lib/views/after_auth_screens/events/create_event_page.dart @@ -13,6 +13,7 @@ import 'package:talawa/widgets/add_members_bottom_sheet.dart'; import 'package:talawa/widgets/date_time_picker.dart'; import 'package:talawa/widgets/event_date_time_tile.dart'; import 'package:talawa/widgets/member_name_tile.dart'; +import 'package:talawa/widgets/recurrence_dialog.dart'; /// CreateEventPage returns a widget that has mutable state _CreateEventPageState. class CreateEventPage extends StatefulWidget { @@ -250,18 +251,28 @@ class _CreateEventPageState extends State { SizedBox( height: SizeConfig.screenHeight! * 0.026, ), - Row( - children: [ - const Icon(Icons.restore), - SizedBox( - width: SizeConfig.screenWidth! * 0.045, - ), - Text( - AppLocalizations.of(context)! - .strictTranslate('Does not repeat'), - style: subtitleTextStyle, - ), - ], + InkWell( + child: Row( + children: [ + const Icon(Icons.restore), + SizedBox( + width: SizeConfig.screenWidth! * 0.045, + ), + Text( + AppLocalizations.of(context)! + .strictTranslate('Does not repeat'), + style: subtitleTextStyle, + ), + ], + ), + onTap: () { + showDialog( + context: context, + builder: (context) { + return const ShowRecurrenceDialog(); + }, + ); + }, ), SizedBox(height: SizeConfig.screenHeight! * 0.026), const Divider(), diff --git a/lib/views/after_auth_screens/events/event_calendar.dart b/lib/views/after_auth_screens/events/event_calendar.dart index a8aa6e1fc..5e6779f6c 100644 --- a/lib/views/after_auth_screens/events/event_calendar.dart +++ b/lib/views/after_auth_screens/events/event_calendar.dart @@ -1,107 +1,102 @@ import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:intl/intl.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; import 'package:syncfusion_flutter_datepicker/datepicker.dart'; import 'package:talawa/models/events/event_model.dart'; +import 'package:talawa/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart'; +import 'package:talawa/views/base_view.dart'; import 'package:talawa/widgets/date_time_picker.dart'; /// EventCalendar returns a widget that has mutable state _EventCalendarState. -class EventCalendar extends StatefulWidget { +class EventCalendar extends StatelessWidget { const EventCalendar(this.eventList, {super.key}); /// List of events that needs to bge passed when the calling this widget. final List eventList; @override - State createState() => _EventCalendarState(); -} - -/// _EventCalendarState returns a widget for Event Calender. -class _EventCalendarState extends State { - final CalendarController _calendarController = CalendarController(); - final DateRangePickerController _dateRangePickerController = - DateRangePickerController(); - - /// The function to triggered when the view is changed. - /// - /// - /// **params**: - /// * `viewChangedDetails`: The dates that visible on the view changes in SfCalendar. type is ViewChangedDetails - /// - /// **returns**: - /// None - void viewChanged(ViewChangedDetails viewChangedDetails) { - SchedulerBinding.instance.addPostFrameCallback((timeStamp) { - _dateRangePickerController.selectedDate = - viewChangedDetails.visibleDates[0]; - _dateRangePickerController.displayDate = - viewChangedDetails.visibleDates[0]; - }); + Widget build(BuildContext context) { + return BaseView( + onModelReady: (model) => model.initialize(eventList), + builder: (context, model, child) { + print(model.calendarView); + return Scaffold( + // header of the page. + appBar: AppBar( + title: const Text('Event Calendar'), + actions: [ + IconButton( + // button to select the date and time of an event. + onPressed: () async { + // initially pickedDate is initialised with current time. + final pickedDate = + await customDatePicker(initialDate: DateTime.now()); + model.selectionChanged( + DateRangePickerSelectionChangedArgs(pickedDate), + ); + }, + icon: const Icon(Icons.date_range), + ), + calendarViewSelection(model), + ], + ), + body: Column( + children: [ + // SizedBox( + // height: 100, + // // The SfDateRangePicker widget provides four different types of views to display. + // //It can be assigned to the widget constructor by using the view property. + // child: SfDateRangePicker( + // view: DateRangePickerView.month, + // controller: model.dateRangePickerController, + // showNavigationArrow: true, + // allowViewNavigation: false, + // monthViewSettings: const DateRangePickerMonthViewSettings( + // numberOfWeeksInView: 1, + // dayFormat: 'EEE', + // ), + // onSelectionChanged: model.selectionChanged, + // ), + // ), + Expanded( + child: SfCalendar( + view: model.calendarView, + headerHeight: 60, + viewHeaderHeight: 60, + controller: model.calendarController, + dataSource: _getCalendarDataSource(eventList), + onViewChanged: model.viewChanged, + ), + ), + ], + ), + ); + }, + ); } - /// function to be triggered when selection is changed. - /// + /// Popupmenu Button to select calendar view. /// /// **params**: - /// * `args`: Object of type DateRangePickerSelectionChangedArgs, The selected dates or ranges changes in the SfDateRangePicker. + /// * `model`: EventCalendarViewModel. /// /// **returns**: - /// None - void selectionChanged(DateRangePickerSelectionChangedArgs args) { - SchedulerBinding.instance.addPostFrameCallback((timeStamp) { - _calendarController.displayDate = args.value as DateTime?; - }); - } - - @override - Widget build(BuildContext context) { - print('hi'); - return Scaffold( - // header of the page. - appBar: AppBar( - title: const Text('Event Calendar'), - actions: [ - IconButton( - // button to select the date and time of an event. - onPressed: () async { - // initially pickedDate is initialised with current time. - final pickedDate = - await customDatePicker(initialDate: DateTime.now()); - selectionChanged(DateRangePickerSelectionChangedArgs(pickedDate)); - }, - icon: const Icon(Icons.date_range), - ), - ], - ), - body: Column( - children: [ - SizedBox( - height: 100, - // The SfDateRangePicker widget provides four different types of views to display. - //It can be assigned to the widget constructor by using the view property. - child: SfDateRangePicker( - controller: _dateRangePickerController, - showNavigationArrow: true, - allowViewNavigation: false, - monthViewSettings: const DateRangePickerMonthViewSettings( - numberOfWeeksInView: 1, - dayFormat: 'EEE', - ), - onSelectionChanged: selectionChanged, - ), - ), - Expanded( - child: SfCalendar( - headerHeight: 0, - viewHeaderHeight: 0, - controller: _calendarController, - dataSource: _getCalendarDataSource(widget.eventList), - onViewChanged: viewChanged, + /// * `PopupMenuButton`: custom PopupMenuButton.. + PopupMenuButton calendarViewSelection(EventCalendarViewModel model) { + final List views = ["Day", "Month", "Schedule"]; + return PopupMenuButton( + itemBuilder: (context) { + return >[ + for (final view in views) + PopupMenuItem( + value: view, + child: Text(view), ), - ), - ], - ), + ]; + }, + onSelected: (value) { + model.changeView(value); + }, ); } } diff --git a/lib/widgets/create_recurring_event_helper_widgets.dart b/lib/widgets/create_recurring_event_helper_widgets.dart new file mode 100644 index 000000000..d10f88c62 --- /dev/null +++ b/lib/widgets/create_recurring_event_helper_widgets.dart @@ -0,0 +1,231 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/widgets/date_time_picker.dart'; + +/// Builds a rectangle with custom properties. +class CustomRectangle extends StatelessWidget { + const CustomRectangle({ + super.key, + required this.child, + }); + + /// widget that is wrapped with CustomRectangle. + final Widget child; + + @override + Widget build(BuildContext context) { + final repeatEveryBoxDecoration = BoxDecoration( + border: Border.all( + color: Theme.of(context).textTheme.bodyLarge!.color!, + width: 1, + ), + borderRadius: BorderRadius.circular(5), + ); + return Container( + decoration: repeatEveryBoxDecoration, + child: Center(child: child), + ); + } +} + +/// Builds a TextField with custom properties. +class CustomTextField extends StatelessWidget { + const CustomTextField({ + super.key, + required this.maxTextLength, + required this.textEditingController, + }); + + /// Max textLength the text field allows. + final int maxTextLength; + + /// Controller of textField. + final TextEditingController textEditingController; + + @override + Widget build(BuildContext context) { + final outLineBorder = OutlineInputBorder( + borderSide: + BorderSide(color: Theme.of(context).textTheme.bodyLarge!.color!), + ); + return SizedBox( + width: SizeConfig.screenWidth! * 0.15, + child: TextField( + maxLength: maxTextLength, + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + controller: textEditingController, + decoration: InputDecoration( + counterText: "", + enabledBorder: outLineBorder, + focusedBorder: outLineBorder, + border: outLineBorder, + contentPadding: const EdgeInsets.symmetric(vertical: 14), + ), + ), + ); + } +} + +/// Recurrence Frequency selection widget. +class RecurrenceFrequencyDropdown extends StatefulWidget { + @override + _RecurrenceFrequencyDropdownState createState() => + _RecurrenceFrequencyDropdownState(); +} + +class _RecurrenceFrequencyDropdownState + extends State { + /// Frequency options. + List options = ['day', 'week', 'month', 'year']; + + late String _selectedOption = 'week'; + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + offset: const Offset(0, 40), + itemBuilder: (BuildContext context) { + return >[ + for (int i = 0; i < options.length; i++) + PopupMenuItem( + value: options[i], + child: Text(options[i]), + ), + ]; + }, + onSelected: (String value) { + setState(() { + _selectedOption = value; + }); + }, + tooltip: 'Select option', + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(_selectedOption), + const Icon(Icons.arrow_drop_down), + ], + ), + ); + } +} + +/// Event ending configuration options. +class EventEndOptions extends StatefulWidget { + const EventEndOptions({super.key}); + + @override + State createState() => _EventEndOptionsState(); +} + +class _EventEndOptionsState extends State { + /// Event ending frequency. + static List frequency = [ + 'Never', + 'On', + 'After', + ]; + String _selectedFrequency = frequency[0]; + DateTime _endDate = DateTime.now(); + final TextEditingController _repeatFrequencyTextController = + TextEditingController(); + + /// Custom inline width. + static const inlineWidth = SizedBox( + width: 8, + ); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + radioButton(const Key('neverRadioButton'), Text(frequency[0]), 0), + radioButton( + const Key('onRadioButton'), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(frequency[1]), + inlineWidth, + CustomRectangle( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: IconButton( + key: const Key('dateSelectorCalendar'), + // button to select the date and time of an event. + onPressed: () async { + // initially pickedDate is initialised with current end time. + final pickedDate = + await customDatePicker(initialDate: _endDate); + setState(() { + _endDate = pickedDate; + }); + }, + icon: Text(formatDate(_endDate.toString().split(" ")[0])), + ), + ), + ), + ], + ), + 1, + ), + radioButton( + const Key('afterRadioButton'), + Row( + children: [ + Text(frequency[2]), + inlineWidth, + CustomTextField( + maxTextLength: 3, + textEditingController: _repeatFrequencyTextController, + ), + inlineWidth, + const Text('occurrence'), + ], + ), + 2, + ), + ], + ); + } + + /// Custom radio button to select event ending frequency. + /// + /// **params**: + /// * `key`: Uniquely identifies the radioButton. + /// * `child`: RadioListTile widget. + /// * `index`: index of [frequency]. + /// + /// **returns**: + /// * `Theme`: custom theme. + Theme radioButton(Key key, Widget child, int index) { + return Theme( + key: key, + data: Theme.of(context).copyWith(focusColor: Colors.transparent), + child: RadioListTile( + title: child, + value: frequency[index], + groupValue: _selectedFrequency, + onChanged: (value) { + setState(() { + _selectedFrequency = value!; + }); + }, + ), + ); + } + + /// Formats input date into [MMM d, yyyy] format. + /// + /// **params**: + /// * `inputDate`: Unformatted date. + /// + /// **returns**: + /// * `String`: formatted date. + String formatDate(String inputDate) { + final DateTime dateTime = DateTime.parse(inputDate); + return DateFormat("MMM d, yyyy").format(dateTime); + } +} diff --git a/lib/widgets/custom_weekday_selector.dart b/lib/widgets/custom_weekday_selector.dart new file mode 100644 index 000000000..e19d33fe7 --- /dev/null +++ b/lib/widgets/custom_weekday_selector.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +/// Custom Widget to select weekdays. +class CustomWeekDaySelector extends StatefulWidget { + @override + _CustomWeekDaySelectorState createState() => _CustomWeekDaySelectorState(); +} + +class _CustomWeekDaySelectorState extends State { + /// Keeps track of week days that are selected. + List isSelected = List.generate(7, (_) => false); + + @override + Widget build(BuildContext context) { + print('came'); + return Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate( + 7, + (index) => GestureDetector( + onTap: () { + setState(() { + isSelected[index] = !isSelected[index]; + }); + }, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.transparent, + border: Border.all( + width: 0.5, + color: isSelected[index] + ? Theme.of(context).colorScheme.primary + : Theme.of(context).dividerColor, + ), + borderRadius: BorderRadius.circular(15), + ), + child: Center( + child: Text( + _getWeekdayAbbreviation(index), + style: TextStyle( + color: isSelected[index] + ? Theme.of(context).colorScheme.primary + : Theme.of(context).dividerColor, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + ); + } + + /// Maps isSelected index to weekday. + /// + /// **params**: + /// * `index`: of isSelected list. + /// + /// **returns**: + /// * `String`: Weekday as string. + String _getWeekdayAbbreviation(int index) { + switch (index) { + case 0: + return 'S'; + case 1: + return 'M'; + case 2: + return 'T'; + case 3: + return 'W'; + case 4: + return 'T'; + case 5: + return 'F'; + case 6: + return 'S'; + default: + return ''; + } + } +} diff --git a/lib/widgets/recurrence_dialog.dart b/lib/widgets/recurrence_dialog.dart new file mode 100644 index 000000000..402daea91 --- /dev/null +++ b/lib/widgets/recurrence_dialog.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:talawa/constants/routing_constants.dart'; +import 'package:talawa/locator.dart'; +import 'package:talawa/services/size_config.dart'; + +/// Dialog for showing recurrence options. +class ShowRecurrenceDialog extends StatefulWidget { + const ShowRecurrenceDialog({super.key}); + + @override + State createState() => _ShowRecurrenceDialogState(); +} + +class _ShowRecurrenceDialogState extends State { + static const List _frequencies = [ + "Does not repeat", + "Every day", + "Every week", + "Every month", + "Every year", + "Custom...", + ]; + String _frequency = _frequencies[0]; + @override + Widget build(BuildContext context) { + return Dialog( + child: SizedBox( + height: SizeConfig.screenHeight! * 0.5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + radioButton(_frequencies[0]), + radioButton(_frequencies[1]), + radioButton(_frequencies[2]), + radioButton(_frequencies[3]), + radioButton(_frequencies[4]), + radioButton(_frequencies[5]), + ], + ), + ), + ); + } + + // custom radio list tile. + RadioListTile radioButton( + String frequency, + ) { + return RadioListTile( + title: Text(frequency), + value: frequency, + groupValue: _frequency, + onChanged: (value) { + // navigate to custom recurrence page when pressed custom... button. + if (value == _frequencies[5]) { + navigationService.pushScreen(Routes.customRecurrencePage); + } + setState(() { + _frequency = value!; + }); + }, + ); + } +} diff --git a/test/router_test.dart b/test/router_test.dart index ec22cb1e9..344771cf1 100644 --- a/test/router_test.dart +++ b/test/router_test.dart @@ -19,6 +19,7 @@ import 'package:talawa/views/after_auth_screens/add_post_page.dart'; import 'package:talawa/views/after_auth_screens/app_settings/app_settings_page.dart'; import 'package:talawa/views/after_auth_screens/chat/chat_message_screen.dart'; import 'package:talawa/views/after_auth_screens/chat/select_contact.dart'; +import 'package:talawa/views/after_auth_screens/events/create_custom_recurring_event.dart'; import 'package:talawa/views/after_auth_screens/events/create_event_page.dart'; import 'package:talawa/views/after_auth_screens/events/edit_event_page.dart'; import 'package:talawa/views/after_auth_screens/events/event_calendar.dart'; @@ -185,6 +186,18 @@ void main() { } }); + testWidgets('Test createCustomRecurringEvent route', + (WidgetTester tester) async { + final route = + generateRoute(const RouteSettings(name: Routes.customRecurrencePage)); + expect(route, isA()); + if (route is MaterialPageRoute) { + final builder = route.builder; + final widget = builder(MockBuildContext()); + expect(widget, isA()); + } + }); + testWidgets('Test ProfilePage route', (WidgetTester tester) async { final route = generateRoute(const RouteSettings(name: Routes.profilePage)); diff --git a/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart b/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart new file mode 100644 index 000000000..4509c13d9 --- /dev/null +++ b/test/views/after_auth_screens/events/create_custom_recurring_event_test.dart @@ -0,0 +1,233 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:talawa/constants/custom_theme.dart'; +import 'package:talawa/router.dart' as router; +import 'package:talawa/services/size_config.dart'; +import 'package:talawa/utils/app_localization.dart'; +import 'package:talawa/view_model/lang_view_model.dart'; +import 'package:talawa/views/after_auth_screens/events/create_custom_recurring_event.dart'; +import 'package:talawa/views/base_view.dart'; +import 'package:talawa/widgets/create_recurring_event_helper_widgets.dart'; +import 'package:talawa/widgets/custom_weekday_selector.dart'; + +import '../../../helpers/test_locator.dart'; + +/// Creates a EventScreen for tests. +/// +/// **params**: +/// * `themeMode`: ThemeMode +/// * `theme`: ThemeData of App +/// +/// **returns**: +/// * `Widget`: Event Screen Widget +Widget createCustomRecurrenceScreen({ + ThemeMode themeMode = ThemeMode.light, + required ThemeData theme, +}) => + BaseView( + onModelReady: (model) => model.initialize(), + builder: (context, langModel, child) { + return MaterialApp( + locale: const Locale('en'), + localizationsDelegates: [ + const AppLocalizationsDelegate(isTest: true), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + key: const Key('Root'), + themeMode: themeMode, + theme: theme, + home: Scaffold( + body: CustomRecurrencePage(), + ), + navigatorKey: navigationService.navigatorKey, + onGenerateRoute: router.generateRoute, + ); + }, + ); + +void main() { + SizeConfig().test(); + testSetupLocator(); + + // setUp(() { + // registerServices(); + // }); + + // tearDown(() { + // unregisterServices(); + // }); + group('Test custom recurrence page.', () { + testWidgets('Appbar is being rendered as expected.', (tester) async { + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pump(); + + final appBarFinder = find.byType(AppBar); + + // verify if AppBar renders. + expect(appBarFinder, findsOne); + + // Verify if the AppBar renders with the correct title + expect(find.text('Custom recurrence'), findsOneWidget); + + await tester.tap(find.text('Done')); + await tester.pumpAndSettle(); + + // Verify if the Done button is present + expect(find.text('Done'), findsOneWidget); + }); + + testWidgets('Custom divider widget.', (tester) async { + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pump(); + + final customDividerFinder = find.byType(Divider); + + expect(customDividerFinder, findsNWidgets(2)); + }); + + testWidgets('Custom inpurFieldHeading widgets.', (tester) async { + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pump(); + + expect(find.text("Repeats every"), findsOne); + expect(find.text("Repeats on"), findsOne); + expect(find.text("Ends"), findsOne); + }); + + testWidgets('CustomRectangle & CustomTextField widgets.', (tester) async { + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pump(); + + final customRectangleFinder = find.byType(CustomRectangle); + final customTextFieldFinder = find.byType(CustomTextField); + + expect(customRectangleFinder, findsNWidgets(2)); + expect(customTextFieldFinder, findsNWidgets(2)); + }); + + testWidgets('section1InputFields', (tester) async { + await tester.pumpWidget( + createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pump(); + + // Test text field. + final inputsection1TextField = + find.byKey(const Key('inputsection1TextField')); + + await tester.enterText(inputsection1TextField, "23"); + await tester.pumpAndSettle(); + + expect(find.text("23"), findsOne); + + await tester.enterText(inputsection1TextField, "235"); + await tester.pumpAndSettle(); + + // Testing the limit is set to 2. + expect(find.text("23"), findsOne); + + // Test RecurrenceFrequencyDropdown + expect(find.text("week"), findsOne); + + final popupMenuButton = find.byType(PopupMenuButton); + + await tester.tap(popupMenuButton); + await tester.pumpAndSettle(); + + expect(find.text("day"), findsOne); + expect(find.text("month"), findsOne); + expect(find.text("year"), findsOne); + + // Test dropdown selection working. + await tester.tap(find.text("month")); + await tester.pumpAndSettle(); + + expect(find.text("month"), findsOne); + }); + + testWidgets('CustomWeekDaySelector', (tester) async { + final widget = createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ); + await tester.pumpWidget(widget); + await tester.pump(); + + // Test widget rendering. + expect(find.byType(CustomWeekDaySelector), findsOne); + + // Test widget functionality. + await tester.tap(find.text("M")); + await tester.pumpAndSettle(); + await tester.tap(find.text("W")); + await tester.pumpAndSettle(); + + expect(find.text("S"), findsNWidgets(2)); + expect(find.text("M"), findsNWidgets(1)); + expect(find.text("T"), findsNWidgets(2)); + expect(find.text("W"), findsNWidgets(1)); + expect(find.text("F"), findsNWidgets(1)); + }); + + testWidgets('EventEndOptions', (tester) async { + final widget = createCustomRecurrenceScreen( + theme: TalawaTheme.darkTheme, + ); + await tester.pumpWidget(widget); + await tester.pump(); + + expect( + find.byKey( + const Key('neverRadioButton'), + ), + findsOne, + ); + expect( + find.byKey( + const Key('onRadioButton'), + ), + findsOne, + ); + expect(find.byKey(const Key('afterRadioButton')), findsOne); + + final afterFinder = find.byKey(const Key('afterRadioButton')); + + await tester.tap(afterFinder); + await tester.pumpAndSettle(); + + final dateSelectorCalendar = + find.byKey(const Key('dateSelectorCalendar')); + + await tester.tap(dateSelectorCalendar); + await tester.pumpAndSettle(); + + expect(find.byType(DatePickerDialog), findsOne); + + await tester.tap(find.text('15').last); + await tester.pumpAndSettle(); + await tester.tap(find.text('OK')); + await tester.pumpAndSettle(); + + expect(find.byType(DatePickerDialog), findsNothing); + }); + }); +} 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 1020f44d0..444835317 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 @@ -88,7 +88,7 @@ void main() { ); await tester.pump(); final inkwellFinder = find.byType(InkWell); - expect(inkwellFinder, findsNWidgets(7)); + expect(inkwellFinder, findsNWidgets(8)); // tester.allElements.forEach((element) { // print(element); // }); @@ -123,7 +123,7 @@ void main() { ); await tester.pump(); final inkwellFinder = find.byType(InkWell); - expect(inkwellFinder, findsNWidgets(7)); + expect(inkwellFinder, findsNWidgets(8)); ///returning the file variable to the ///result of function multimediaPickerService.getPhotoFromGallery @@ -163,6 +163,38 @@ void main() { expect(createEventViewModel.validate, AutovalidateMode.disabled); }); + + testWidgets('recurrence button', (tester) async { + await tester.pumpWidget( + createEventScreen( + themeMode: ThemeMode.dark, + theme: TalawaTheme.darkTheme, + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.restore)); + await tester.pumpAndSettle(); + + const List frequencies = [ + "Does not repeat", + "Every day", + "Every week", + "Every month", + "Every year", + "Custom...", + ]; + + final customButtonFinder = find.text("Custom..."); + + await tester.tap(customButtonFinder); + await tester.pumpAndSettle(); + + for (final frequency in frequencies) { + expect(find.text(frequency), findsAny); + } + }); + testWidgets("Checking tap Inkwell for setDate 2 datetime", (tester) async { await tester.pumpWidget( createEventScreen( @@ -172,7 +204,7 @@ void main() { ); await tester.pump(); final inkwellFinder = find.byType(InkWell); - expect(inkwellFinder, findsNWidgets(7)); + expect(inkwellFinder, findsNWidgets(8)); ///returning the file variable to the ///result of function multimediaPickerService.getPhotoFromGallery @@ -204,7 +236,7 @@ void main() { ); await tester.pump(); final inkwellFinder = find.byType(InkWell); - expect(inkwellFinder, findsNWidgets(7)); + expect(inkwellFinder, findsNWidgets(8)); // tester.allElements.forEach((element) { // print(element); // }); @@ -437,7 +469,7 @@ void main() { ); await tester.pump(); final inkwellFinder = find.byType(InkWell); - expect(inkwellFinder, findsNWidgets(7)); + expect(inkwellFinder, findsNWidgets(8)); await tester.ensureVisible(find.byKey(const Key('inwell_cep2'))); await tester.pump(); diff --git a/test/widget_tests/after_auth_screens/events/event_calendar_test.dart b/test/widget_tests/after_auth_screens/events/event_calendar_test.dart index 985c21dd5..219184f35 100644 --- a/test/widget_tests/after_auth_screens/events/event_calendar_test.dart +++ b/test/widget_tests/after_auth_screens/events/event_calendar_test.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; +import 'package:syncfusion_flutter_datepicker/datepicker.dart'; import 'package:talawa/locator.dart'; import 'package:talawa/models/events/event_model.dart'; +import 'package:talawa/view_model/after_auth_view_models/event_view_models/event_calendar_view_model.dart'; import 'package:talawa/views/after_auth_screens/events/event_calendar.dart'; import '../../../helpers/test_helpers.dart'; @@ -51,6 +53,8 @@ void main() { }); }); group('Tests for EventCalendar', () { + setUp(() => locator.registerSingleton(EventCalendarViewModel())); + tearDown(() => locator.unregister()); testWidgets('Testing if EventCalendar shows up', (tester) async { await tester.pumpWidget(createEventCalendar()); await tester.pump(); @@ -71,5 +75,46 @@ void main() { await tester.tap(find.text('OK')); await tester.pump(); }); + + testWidgets('calendarViewSelection', (tester) async { + await tester.pumpWidget( + createEventCalendar(), + ); + + await tester.pump(); + + final popupButtonFinder = find.byType(PopupMenuButton); + + await tester.tap(popupButtonFinder); + await tester.pumpAndSettle(); + + await tester.tap(find.text("Day")); + await tester.pumpAndSettle(); + + await tester.tap(popupButtonFinder); + await tester.pumpAndSettle(); + + await tester.tap(find.text("Month")); + await tester.pumpAndSettle(); + + await tester.tap(popupButtonFinder); + await tester.pumpAndSettle(); + + await tester.tap(find.text("Schedule")); + await tester.pumpAndSettle(); + + await tester.tap(popupButtonFinder); + await tester.pumpAndSettle(); + + expect(find.text("Day"), findsOne); + expect(find.text("Month"), findsOne); + expect(find.text("Schedule"), findsOne); + }); + + test("dateRangePickerController getter", () async { + final EventCalendarViewModel model = EventCalendarViewModel(); + expect(model.dateRangePickerController, isA()); + expect(model.eventList, isA>()); + }); }); } From 9c14819eb9bfa0f51f73c7dab9c4ab747aab7d35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:34:20 -0800 Subject: [PATCH 5/8] Bump url_launcher from 6.2.4 to 6.2.5 (#2396) Bumps [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) from 6.2.4 to 6.2.5. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/url_launcher-v6.2.5/packages/url_launcher) --- updated-dependencies: - dependency-name: url_launcher dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 44 ++++++++++++++++++++++++++++++++++---------- pubspec.yaml | 2 +- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index a3637f7ce..91aa8ed80 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -837,6 +837,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lint: dependency: "direct dev" description: @@ -857,26 +881,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -961,10 +985,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1493,10 +1517,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.2.4" + version: "6.2.5" url_launcher_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3799fb579..8fd45a410 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,7 +72,7 @@ dependencies: tutorial_coach_mark: ^1.2.11 uni_links: ^0.5.1 uni_links_platform_interface: ^1.0.0 - url_launcher: ^6.2.4 + url_launcher: ^6.2.5 vibration: ^1.8.4 video_player: ^2.8.2 visibility_detector: ^0.4.0+2 From 56ac507fd5ad901e0754ccd64ea16670e32db882 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:34:50 -0800 Subject: [PATCH 6/8] Bump flutter_svg from 2.0.9 to 2.0.10+1 (#2397) Bumps [flutter_svg](https://github.com/dnfield/flutter_svg/tree/master/packages) from 2.0.9 to 2.0.10+1. - [Release notes](https://github.com/dnfield/flutter_svg/releases) - [Commits](https://github.com/dnfield/flutter_svg/commits/HEAD/packages) --- updated-dependencies: - dependency-name: flutter_svg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pubspec.lock | 16 ++++++++-------- pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 91aa8ed80..e878470c1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -487,10 +487,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -1589,26 +1589,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8fd45a410..6208e151d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: sdk: flutter flutter_reaction_button: ^3.0.0+3 flutter_speed_dial: ^7.0.0 - flutter_svg: ^2.0.9 + flutter_svg: ^2.0.10+1 font_awesome_flutter: ^10.7.0 geocoding: ^2.1.1 get_it: ^7.6.6 From 400b65b1fee69b2a35f61ba3b97e461598f42236 Mon Sep 17 00:00:00 2001 From: Arin Nigam <99138286+ArinNigam@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:02:33 +0530 Subject: [PATCH 7/8] removed geocoding (#2403) --- pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6208e151d..61829441c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,6 @@ dependencies: flutter_speed_dial: ^7.0.0 flutter_svg: ^2.0.10+1 font_awesome_flutter: ^10.7.0 - geocoding: ^2.1.1 get_it: ^7.6.6 graphql_flutter: ^5.1.2 hive: ^2.2.3 From 85fddfe3ea8874bb56a6d48a17b62e98b6d64cc8 Mon Sep 17 00:00:00 2001 From: Soaham <113767354+soaham-pimparkar@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:57:11 +0530 Subject: [PATCH 8/8] Fix Navigation: On clicking change language it navigates to home screen. (#2402) * fixed select language button * updated select language bug fix --- lib/views/pre_auth_screens/set_url.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/views/pre_auth_screens/set_url.dart b/lib/views/pre_auth_screens/set_url.dart index a223c519c..3e123aaa6 100644 --- a/lib/views/pre_auth_screens/set_url.dart +++ b/lib/views/pre_auth_screens/set_url.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:talawa/constants/routing_constants.dart'; import 'package:talawa/custom_painters/language_icon.dart'; import 'package:talawa/custom_painters/talawa_logo.dart'; import 'package:talawa/locator.dart'; @@ -186,7 +187,8 @@ class _SetUrlState extends State { GestureDetector( key: const Key('ChangeLanguage'), onTap: () { - navigationService.pop(); + navigationService + .pushScreen(Routes.languageSelectionRoute); }, child: Padding( padding: EdgeInsets.only(