mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: duplicate calendar event (#4816)
* feat: duplicate calendar event * test: add simple test
This commit is contained in:
parent
ba965caa8f
commit
107e3cea4f
@ -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-database2/setting_entities.pbenum.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.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:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
@ -147,6 +149,49 @@ void main() {
|
|||||||
tester.assertNumberOfEventsOnSpecificDay(1, DateTime.now());
|
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 {
|
testWidgets('rescheduling events', (tester) async {
|
||||||
await tester.initializeAppFlowy();
|
await tester.initializeAppFlowy();
|
||||||
await tester.tapGoButton();
|
await tester.tapGoButton();
|
||||||
|
@ -64,6 +64,20 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
|||||||
createEvent: (DateTime date) async {
|
createEvent: (DateTime date) async {
|
||||||
await _createEvent(date);
|
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: () {
|
newEventPopupDisplayed: () {
|
||||||
emit(state.copyWith(editingEvent: null));
|
emit(state.copyWith(editingEvent: null));
|
||||||
},
|
},
|
||||||
@ -407,6 +421,12 @@ class CalendarEvent with _$CalendarEvent {
|
|||||||
|
|
||||||
const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
|
const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
|
||||||
_ReceiveDatabaseUpdate;
|
_ReceiveDatabaseUpdate;
|
||||||
|
|
||||||
|
const factory CalendarEvent.duplicateEvent(String viewId, String rowId) =
|
||||||
|
_DuplicateEvent;
|
||||||
|
|
||||||
|
const factory CalendarEvent.deleteEvent(String viewId, String rowId) =
|
||||||
|
_DeleteEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
@ -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/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/database_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/row/row_cache.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/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../application/calendar_bloc.dart';
|
import '../application/calendar_bloc.dart';
|
||||||
|
|
||||||
import 'calendar_event_editor.dart';
|
import 'calendar_event_editor.dart';
|
||||||
|
|
||||||
class EventCard extends StatefulWidget {
|
class EventCard extends StatefulWidget {
|
||||||
@ -144,15 +146,18 @@ class _EventCardState extends State<EventCard> {
|
|||||||
asBarrier: true,
|
asBarrier: true,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
offset: const Offset(10.0, 0),
|
offset: const Offset(10.0, 0),
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
popupBuilder: (_) {
|
||||||
final settings = context.watch<CalendarBloc>().state.settings;
|
final settings = context.watch<CalendarBloc>().state.settings;
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return CalendarEventEditor(
|
return BlocProvider.value(
|
||||||
databaseController: widget.databaseController,
|
value: context.read<CalendarBloc>(),
|
||||||
rowMeta: widget.event.event.rowMeta,
|
child: CalendarEventEditor(
|
||||||
layoutSettings: settings,
|
databaseController: widget.databaseController,
|
||||||
|
rowMeta: widget.event.event.rowMeta,
|
||||||
|
layoutSettings: settings,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.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/cell/cell_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/database_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/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database/application/row/row_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/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/cell/editable_cell_skeleton/text.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.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/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/plugins/database/widgets/row/row_detail.dart';
|
||||||
import 'package:appflowy/util/field_type_extension.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_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class CalendarEventEditor extends StatelessWidget {
|
class CalendarEventEditor extends StatelessWidget {
|
||||||
@ -86,17 +86,28 @@ class EventEditorControls extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
FlowyIconButton(
|
||||||
|
width: 20,
|
||||||
|
icon: const FlowySvg(FlowySvgs.m_duplicate_s),
|
||||||
|
iconColorOnHover: Theme.of(context).colorScheme.onSecondary,
|
||||||
|
onPressed: () => context.read<CalendarBloc>().add(
|
||||||
|
CalendarEvent.duplicateEvent(
|
||||||
|
rowController.viewId,
|
||||||
|
rowController.rowId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(8.0),
|
||||||
FlowyIconButton(
|
FlowyIconButton(
|
||||||
width: 20,
|
width: 20,
|
||||||
icon: const FlowySvg(FlowySvgs.delete_s),
|
icon: const FlowySvg(FlowySvgs.delete_s),
|
||||||
iconColorOnHover: Theme.of(context).colorScheme.onSecondary,
|
iconColorOnHover: Theme.of(context).colorScheme.onSecondary,
|
||||||
onPressed: () async {
|
onPressed: () => context.read<CalendarBloc>().add(
|
||||||
final result = await RowBackendService.deleteRow(
|
CalendarEvent.deleteEvent(
|
||||||
rowController.viewId,
|
rowController.viewId,
|
||||||
rowController.rowId,
|
rowController.rowId,
|
||||||
);
|
),
|
||||||
result.fold((l) => null, (err) => Log.error(err));
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const HSpace(8.0),
|
const HSpace(8.0),
|
||||||
FlowyIconButton(
|
FlowyIconButton(
|
||||||
@ -107,12 +118,10 @@ class EventEditorControls extends StatelessWidget {
|
|||||||
PopoverContainer.of(context).close();
|
PopoverContainer.of(context).close();
|
||||||
FlowyOverlay.show(
|
FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (_) => RowDetailPage(
|
||||||
return RowDetailPage(
|
databaseController: databaseController,
|
||||||
databaseController: databaseController,
|
rowController: rowController,
|
||||||
rowController: rowController,
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user