From 107e3cea4fc51e922ed40e6538a8cab7891aecda Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:42:00 +0100 Subject: [PATCH] feat: duplicate calendar event (#4816) * feat: duplicate calendar event * test: add simple test --- .../database/database_calendar_test.dart | 45 +++++++++++++++++++ .../calendar/application/calendar_bloc.dart | 20 +++++++++ .../presentation/calendar_event_card.dart | 17 ++++--- .../presentation/calendar_event_editor.dart | 45 +++++++++++-------- 4 files changed, 103 insertions(+), 24 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/database/database_calendar_test.dart b/frontend/appflowy_flutter/integration_test/database/database_calendar_test.dart index f882983712..33f06557be 100644 --- a/frontend/appflowy_flutter/integration_test/database/database_calendar_test.dart +++ b/frontend/appflowy_flutter/integration_test/database/database_calendar_test.dart @@ -1,5 +1,7 @@ +import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_editor.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -147,6 +149,49 @@ void main() { tester.assertNumberOfEventsOnSpecificDay(1, DateTime.now()); }); + testWidgets('create and duplicate calendar event', (tester) async { + const customTitle = "EventTitleCustom"; + + await tester.initializeAppFlowy(); + await tester.tapGoButton(); + + // Create the calendar view + await tester.createNewPageWithNameUnderParent( + layout: ViewLayoutPB.Calendar, + ); + + // Scroll until today's date cell is visible + await tester.scrollToToday(); + + // Hover over today's calendar cell + await tester.hoverOnTodayCalendarCell( + // Tap on create new event button + onHover: () async => tester.tapAddCalendarEventButton(), + ); + + // Make sure that the event editor popup is shown + tester.assertEventEditorOpen(); + + tester.assertNumberOfEventsInCalendar(1); + + // Change the title of the event + await tester.editEventTitle(customTitle); + + // Duplicate event + final duplicateBtnFinder = find + .descendant( + of: find.byType(CalendarEventEditor), + matching: find.byType( + FlowyIconButton, + ), + ) + .first; + await tester.tap(duplicateBtnFinder); + await tester.pumpAndSettle(); + + tester.assertNumberOfEventsInCalendar(2, title: customTitle); + }); + testWidgets('rescheduling events', (tester) async { await tester.initializeAppFlowy(); await tester.tapGoButton(); diff --git a/frontend/appflowy_flutter/lib/plugins/database/calendar/application/calendar_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/calendar/application/calendar_bloc.dart index b8edc4229f..4a2674f1c5 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/calendar/application/calendar_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/calendar/application/calendar_bloc.dart @@ -64,6 +64,20 @@ class CalendarBloc extends Bloc { createEvent: (DateTime date) async { await _createEvent(date); }, + duplicateEvent: (String viewId, String rowId) async { + final result = await RowBackendService.duplicateRow(viewId, rowId); + result.fold( + (_) => null, + (e) => Log.error('Failed to duplicate event: $e', e), + ); + }, + deleteEvent: (String viewId, String rowId) async { + final result = await RowBackendService.deleteRow(viewId, rowId); + result.fold( + (_) => null, + (e) => Log.error('Failed to delete event: $e', e), + ); + }, newEventPopupDisplayed: () { emit(state.copyWith(editingEvent: null)); }, @@ -407,6 +421,12 @@ class CalendarEvent with _$CalendarEvent { const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) = _ReceiveDatabaseUpdate; + + const factory CalendarEvent.duplicateEvent(String viewId, String rowId) = + _DuplicateEvent; + + const factory CalendarEvent.deleteEvent(String viewId, String rowId) = + _DeleteEvent; } @freezed diff --git a/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_card.dart b/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_card.dart index 40769d2303..9542cafe98 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_card.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_card.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/row/row_cache.dart'; @@ -9,11 +11,11 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../application/calendar_bloc.dart'; + import 'calendar_event_editor.dart'; class EventCard extends StatefulWidget { @@ -144,15 +146,18 @@ class _EventCardState extends State { asBarrier: true, margin: EdgeInsets.zero, offset: const Offset(10.0, 0), - popupBuilder: (BuildContext popoverContext) { + popupBuilder: (_) { final settings = context.watch().state.settings; if (settings == null) { return const SizedBox.shrink(); } - return CalendarEventEditor( - databaseController: widget.databaseController, - rowMeta: widget.event.event.rowMeta, - layoutSettings: settings, + return BlocProvider.value( + value: context.read(), + child: CalendarEventEditor( + databaseController: widget.databaseController, + rowMeta: widget.event.event.rowMeta, + layoutSettings: settings, + ), ); }, child: Container( diff --git a/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_editor.dart b/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_editor.dart index 5258e32d62..b701763a4a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/calendar/presentation/calendar_event_editor.dart @@ -1,25 +1,25 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/application/row/row_controller.dart'; -import 'package:appflowy/plugins/database/application/row/row_service.dart'; +import 'package:appflowy/plugins/database/calendar/application/calendar_bloc.dart'; import 'package:appflowy/plugins/database/calendar/application/calendar_event_editor_bloc.dart'; +import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart'; import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart'; import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart'; -import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; -import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart'; import 'package:appflowy/plugins/database/widgets/row/row_detail.dart'; import 'package:appflowy/util/field_type_extension.dart'; -import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class CalendarEventEditor extends StatelessWidget { @@ -86,17 +86,28 @@ class EventEditorControls extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + FlowyIconButton( + width: 20, + icon: const FlowySvg(FlowySvgs.m_duplicate_s), + iconColorOnHover: Theme.of(context).colorScheme.onSecondary, + onPressed: () => context.read().add( + CalendarEvent.duplicateEvent( + rowController.viewId, + rowController.rowId, + ), + ), + ), + const HSpace(8.0), FlowyIconButton( width: 20, icon: const FlowySvg(FlowySvgs.delete_s), iconColorOnHover: Theme.of(context).colorScheme.onSecondary, - onPressed: () async { - final result = await RowBackendService.deleteRow( - rowController.viewId, - rowController.rowId, - ); - result.fold((l) => null, (err) => Log.error(err)); - }, + onPressed: () => context.read().add( + CalendarEvent.deleteEvent( + rowController.viewId, + rowController.rowId, + ), + ), ), const HSpace(8.0), FlowyIconButton( @@ -107,12 +118,10 @@ class EventEditorControls extends StatelessWidget { PopoverContainer.of(context).close(); FlowyOverlay.show( context: context, - builder: (BuildContext context) { - return RowDetailPage( - databaseController: databaseController, - rowController: rowController, - ); - }, + builder: (_) => RowDetailPage( + databaseController: databaseController, + rowController: rowController, + ), ); }, ),