From e8b21955f80a6d806cb0d6d67e1b659c3d4cc060 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Sun, 8 Jan 2023 20:51:19 +0800 Subject: [PATCH] chore: add calendar view plugin along with backend data (#1611) * chore: create build-in calendar data * feat: add new calendar view to plugins * chore: add create calendar page test * chore: disable for creation for now * fix: rebase regression Co-authored-by: nathan --- .../app_flowy/assets/translations/en.json | 3 + .../lib/plugins/calendar/calendar.dart | 111 ++++++++++++++++++ .../app_flowy/lib/startup/plugin/plugin.dart | 1 + .../lib/startup/tasks/load_plugin.dart | 3 + .../workspace/application/view/view_ext.dart | 2 + .../bloc_test/home_test/create_page_test.dart | 15 +++ .../src/deps_resolve/folder_deps.rs | 4 +- .../flowy-folder/src/entities/view.rs | 3 + .../flowy-grid/src/entities/field_entities.rs | 2 +- .../src/entities/setting_entities.rs | 3 + .../src/services/group/group_util.rs | 19 ++- frontend/rust-lib/flowy-grid/src/util.rs | 28 +++++ .../flowy-grid/tests/grid/grid_editor.rs | 13 +- frontend/rust-lib/flowy-test/src/helper.rs | 4 + shared-lib/folder-rev-model/src/view_rev.rs | 1 + shared-lib/grid-rev-model/src/grid_view.rs | 1 + 16 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 frontend/app_flowy/lib/plugins/calendar/calendar.dart diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index f4a2845157..ed2c1ff2a4 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -313,5 +313,8 @@ "column": { "create_new_card": "New" } + }, + "calendar": { + "menuName": "Calendar" } } \ No newline at end of file diff --git a/frontend/app_flowy/lib/plugins/calendar/calendar.dart b/frontend/app_flowy/lib/plugins/calendar/calendar.dart new file mode 100644 index 0000000000..70f8467b5a --- /dev/null +++ b/frontend/app_flowy/lib/plugins/calendar/calendar.dart @@ -0,0 +1,111 @@ +import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/startup/plugin/plugin.dart'; +import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; +import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; +import 'package:flutter/material.dart'; + +import '../util.dart'; + +class CalendarPluginBuilder extends PluginBuilder { + @override + Plugin build(dynamic data) { + if (data is ViewPB) { + return CalendarPlugin(pluginType: pluginType, view: data); + } else { + throw FlowyPluginException.invalidData; + } + } + + @override + String get menuName => LocaleKeys.calendar_menuName.tr(); + + @override + String get menuIcon => "editor/date"; + + @override + PluginType get pluginType => PluginType.calendar; + + @override + ViewDataFormatPB get dataFormatType => ViewDataFormatPB.DatabaseFormat; + + @override + ViewLayoutTypePB? get layoutType => ViewLayoutTypePB.Calendar; +} + +class CalendarPluginConfig implements PluginConfig { + @override + bool get creatable => false; +} + +class CalendarPlugin extends Plugin { + @override + final ViewPluginNotifier notifier; + final PluginType _pluginType; + + CalendarPlugin({ + required ViewPB view, + required PluginType pluginType, + }) : _pluginType = pluginType, + notifier = ViewPluginNotifier(view: view); + + @override + PluginDisplay get display => CalendarPluginDisplay(notifier: notifier); + + @override + PluginId get id => notifier.view.id; + + @override + PluginType get ty => _pluginType; +} + +class CalendarPluginDisplay extends PluginDisplay { + final ViewPluginNotifier notifier; + CalendarPluginDisplay({required this.notifier, Key? key}); + + ViewPB get view => notifier.view; + + @override + Widget get leftBarItem => ViewLeftBarItem(view: view); + + @override + Widget buildWidget(PluginContext context) { + notifier.isDeleted.addListener(() { + notifier.isDeleted.value.fold(() => null, (deletedView) { + if (deletedView.hasIndex()) { + context.onDeleted(view, deletedView.index); + } + }); + }); + + return BlankPage(key: ValueKey(view.id)); + // return BoardPage(key: ValueKey(view.id), view: view); + } + + @override + List get navigationItems => [this]; +} + +// mark for removal +class BlankPage extends StatefulWidget { + const BlankPage({Key? key}) : super(key: key); + + @override + State createState() => _BlankPageState(); +} + +class _BlankPageState extends State { + @override + Widget build(BuildContext context) { + return SizedBox.expand( + child: Container( + color: Theme.of(context).colorScheme.surface, + child: Padding( + padding: const EdgeInsets.all(10), + child: Container(), + ), + ), + ); + } +} diff --git a/frontend/app_flowy/lib/startup/plugin/plugin.dart b/frontend/app_flowy/lib/startup/plugin/plugin.dart index 5412ada941..d7ae8f378f 100644 --- a/frontend/app_flowy/lib/startup/plugin/plugin.dart +++ b/frontend/app_flowy/lib/startup/plugin/plugin.dart @@ -14,6 +14,7 @@ enum PluginType { trash, grid, board, + calendar, } typedef PluginId = String; diff --git a/frontend/app_flowy/lib/startup/tasks/load_plugin.dart b/frontend/app_flowy/lib/startup/tasks/load_plugin.dart index 5ee25b2286..55cdbf4ef5 100644 --- a/frontend/app_flowy/lib/startup/tasks/load_plugin.dart +++ b/frontend/app_flowy/lib/startup/tasks/load_plugin.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/plugins/calendar/calendar.dart'; import 'package:app_flowy/startup/plugin/plugin.dart'; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/plugins/blank/blank.dart'; @@ -17,5 +18,7 @@ class PluginLoadTask extends LaunchTask { registerPlugin(builder: DocumentPluginBuilder()); registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig()); registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig()); + registerPlugin( + builder: CalendarPluginBuilder(), config: CalendarPluginConfig()); } } diff --git a/frontend/app_flowy/lib/workspace/application/view/view_ext.dart b/frontend/app_flowy/lib/workspace/application/view/view_ext.dart index edc702830e..149682b6f0 100644 --- a/frontend/app_flowy/lib/workspace/application/view/view_ext.dart +++ b/frontend/app_flowy/lib/workspace/application/view/view_ext.dart @@ -44,6 +44,8 @@ extension ViewExtension on ViewPB { switch (layout) { case ViewLayoutTypePB.Board: return PluginType.board; + case ViewLayoutTypePB.Calendar: + return PluginType.calendar; case ViewLayoutTypePB.Document: return PluginType.editor; case ViewLayoutTypePB.Grid: diff --git a/frontend/app_flowy/test/bloc_test/home_test/create_page_test.dart b/frontend/app_flowy/test/bloc_test/home_test/create_page_test.dart index 58b3f15d63..0a66b4df86 100644 --- a/frontend/app_flowy/test/bloc_test/home_test/create_page_test.dart +++ b/frontend/app_flowy/test/bloc_test/home_test/create_page_test.dart @@ -1,4 +1,5 @@ import 'package:app_flowy/plugins/board/board.dart'; +import 'package:app_flowy/plugins/calendar/calendar.dart'; import 'package:app_flowy/plugins/document/document.dart'; import 'package:app_flowy/plugins/grid/grid.dart'; import 'package:app_flowy/workspace/application/app/app_bloc.dart'; @@ -37,6 +38,7 @@ void main() { assert(bloc.state.views.last.name == "Test grid"); assert(bloc.state.views.last.layout == ViewLayoutTypePB.Grid); }); + test('create a kanban', () async { final app = await testContext.createTestApp(); final bloc = AppBloc(app: app)..add(const AppEvent.initial()); @@ -49,4 +51,17 @@ void main() { assert(bloc.state.views.last.name == "Test board"); assert(bloc.state.views.last.layout == ViewLayoutTypePB.Board); }); + + test('create a calendar', () async { + final app = await testContext.createTestApp(); + final bloc = AppBloc(app: app)..add(const AppEvent.initial()); + await blocResponseFuture(); + + bloc.add(AppEvent.createView("Test calendar", CalendarPluginBuilder())); + await blocResponseFuture(); + + assert(bloc.state.views.length == 1); + assert(bloc.state.views.last.name == "Test calendar"); + assert(bloc.state.views.last.layout == ViewLayoutTypePB.Calendar); + }); } diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index d0eeef8ea1..9ec27cf0ca 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -11,7 +11,7 @@ use flowy_folder::{ }; use flowy_grid::entities::GridLayout; use flowy_grid::manager::{make_grid_view_data, GridManager}; -use flowy_grid::util::{make_default_board, make_default_grid}; +use flowy_grid::util::{make_default_board, make_default_calendar, make_default_grid}; use flowy_http_model::revision::Revision; use flowy_http_model::ws_data::ClientRevisionWSData; use flowy_net::ClientServerConfiguration; @@ -264,6 +264,7 @@ impl ViewDataProcessor for GridViewDataProcessor { let (build_context, layout) = match layout { ViewLayoutTypePB::Grid => (make_default_grid(), GridLayout::Table), ViewLayoutTypePB::Board => (make_default_board(), GridLayout::Board), + ViewLayoutTypePB::Calendar => (make_default_calendar(), GridLayout::Calendar), ViewLayoutTypePB::Document => { return FutureResult::new(async move { Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout))) @@ -293,6 +294,7 @@ impl ViewDataProcessor for GridViewDataProcessor { let layout = match layout { ViewLayoutTypePB::Grid => GridLayout::Table, ViewLayoutTypePB::Board => GridLayout::Board, + ViewLayoutTypePB::Calendar => GridLayout::Calendar, ViewLayoutTypePB::Document => { return FutureResult::new(async move { Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout))) diff --git a/frontend/rust-lib/flowy-folder/src/entities/view.rs b/frontend/rust-lib/flowy-folder/src/entities/view.rs index 1b7ff0fe52..510bfb0925 100644 --- a/frontend/rust-lib/flowy-folder/src/entities/view.rs +++ b/frontend/rust-lib/flowy-folder/src/entities/view.rs @@ -86,6 +86,7 @@ pub enum ViewLayoutTypePB { Document = 0, Grid = 3, Board = 4, + Calendar = 5, } impl std::default::Default for ViewLayoutTypePB { @@ -100,6 +101,7 @@ impl std::convert::From for ViewLayoutTypePB { ViewLayoutTypeRevision::Grid => ViewLayoutTypePB::Grid, ViewLayoutTypeRevision::Board => ViewLayoutTypePB::Board, ViewLayoutTypeRevision::Document => ViewLayoutTypePB::Document, + ViewLayoutTypeRevision::Calendar => ViewLayoutTypePB::Calendar, } } } @@ -110,6 +112,7 @@ impl std::convert::From for ViewLayoutTypeRevision { ViewLayoutTypePB::Grid => ViewLayoutTypeRevision::Grid, ViewLayoutTypePB::Board => ViewLayoutTypeRevision::Board, ViewLayoutTypePB::Document => ViewLayoutTypeRevision::Document, + ViewLayoutTypePB::Calendar => ViewLayoutTypeRevision::Calendar, } } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs index ea432586b7..5e54701551 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs @@ -570,7 +570,7 @@ impl FieldType { } pub fn can_be_group(&self) -> bool { - self.is_select_option() + self.is_select_option() || self.is_checkbox() } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs index 6f7b73b5a8..ed0222b11e 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs @@ -49,6 +49,7 @@ impl GridLayoutPB { pub enum GridLayout { Table = 0, Board = 1, + Calendar = 2, } impl std::default::Default for GridLayout { @@ -62,6 +63,7 @@ impl std::convert::From for GridLayout { match rev { LayoutRevision::Table => GridLayout::Table, LayoutRevision::Board => GridLayout::Board, + LayoutRevision::Calendar => GridLayout::Calendar, } } } @@ -71,6 +73,7 @@ impl std::convert::From for LayoutRevision { match layout { GridLayout::Table => LayoutRevision::Table, GridLayout::Board => LayoutRevision::Board, + GridLayout::Calendar => LayoutRevision::Calendar, } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs index 58ccf510c4..2be5c33583 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs @@ -74,17 +74,14 @@ where Ok(group_controller) } -pub fn find_group_field(field_revs: &[Arc], layout: &LayoutRevision) -> Option> { - match layout { - LayoutRevision::Table => field_revs.iter().find(|field_rev| field_rev.is_primary).cloned(), - LayoutRevision::Board => field_revs - .iter() - .find(|field_rev| { - let field_type: FieldType = field_rev.ty.into(); - field_type.can_be_group() - }) - .cloned(), - } +pub fn find_group_field(field_revs: &[Arc], _layout: &LayoutRevision) -> Option> { + field_revs + .iter() + .find(|field_rev| { + let field_type: FieldType = field_rev.ty.into(); + field_type.can_be_group() + }) + .cloned() } /// Returns a `default` group configuration for the [FieldRevision] diff --git a/frontend/rust-lib/flowy-grid/src/util.rs b/frontend/rust-lib/flowy-grid/src/util.rs index d7b8495531..7b4e246b26 100644 --- a/frontend/rust-lib/flowy-grid/src/util.rs +++ b/frontend/rust-lib/flowy-grid/src/util.rs @@ -70,6 +70,34 @@ pub fn make_default_board() -> BuildGridContext { grid_builder.build() } +pub fn make_default_calendar() -> BuildGridContext { + let mut grid_builder = GridBuilder::new(); + // text + let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) + .name("Description") + .visibility(true) + .primary(true) + .build(); + grid_builder.add_field(text_field); + + // date + let date_type_option = DateTypeOptionBuilder::default(); + let date_field = FieldBuilder::new(date_type_option) + .name("Date") + .visibility(true) + .build(); + grid_builder.add_field(date_field); + + // single select + let multi_select_type_option = MultiSelectTypeOptionBuilder::default(); + let multi_select_field = FieldBuilder::new(multi_select_type_option) + .name("Tags") + .visibility(true) + .build(); + grid_builder.add_field(multi_select_field); + grid_builder.build() +} + #[allow(dead_code)] pub fn make_default_board_2() -> BuildGridContext { let mut grid_builder = GridBuilder::new(); diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 6f7bdcca5a..b5e3b0b668 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -57,6 +57,11 @@ impl GridEditorTest { let view_data: Bytes = build_context.into(); ViewTest::new_board_view(&sdk, view_data.to_vec()).await } + GridLayout::Calendar => { + let build_context = make_test_calendar(); + let view_data: Bytes = build_context.into(); + ViewTest::new_calendar_view(&sdk, view_data.to_vec()).await + } }; let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap(); @@ -103,7 +108,7 @@ impl GridEditorTest { } /// returns the first `FieldRevision` in the build-in test grid. - /// Not support duplicate `FieldType` in test grid yet. + /// Not support duplicate `FieldType` in test grid yet. pub fn get_first_field_rev(&self, field_type: FieldType) -> &Arc { self.field_revs .iter() @@ -388,6 +393,7 @@ fn make_test_grid() -> BuildGridContext { grid_builder.build() } +// Kanban board unit test mock data fn make_test_board() -> BuildGridContext { let mut grid_builder = GridBuilder::new(); // Iterate through the FieldType to create the corresponding Field. @@ -557,3 +563,8 @@ fn make_test_board() -> BuildGridContext { } grid_builder.build() } + +// Calendar unit test mock data +fn make_test_calendar() -> BuildGridContext { + todo!() +} diff --git a/frontend/rust-lib/flowy-test/src/helper.rs b/frontend/rust-lib/flowy-test/src/helper.rs index aa1d812509..69d4cf34ef 100644 --- a/frontend/rust-lib/flowy-test/src/helper.rs +++ b/frontend/rust-lib/flowy-test/src/helper.rs @@ -51,6 +51,10 @@ impl ViewTest { Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Board, data).await } + pub async fn new_calendar_view(sdk: &FlowySDKTest, data: Vec) -> Self { + Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Calendar, data).await + } + pub async fn new_document_view(sdk: &FlowySDKTest) -> Self { let view_data_format = match sdk.document_version() { DocumentVersionPB::V0 => ViewDataFormatPB::DeltaFormat, diff --git a/shared-lib/folder-rev-model/src/view_rev.rs b/shared-lib/folder-rev-model/src/view_rev.rs index 55ed802bed..7906be39cd 100644 --- a/shared-lib/folder-rev-model/src/view_rev.rs +++ b/shared-lib/folder-rev-model/src/view_rev.rs @@ -75,6 +75,7 @@ pub enum ViewLayoutTypeRevision { // The for historical reasons, the value of Grid is not 1. Grid = 3, Board = 4, + Calendar = 5, } impl std::default::Default for ViewLayoutTypeRevision { diff --git a/shared-lib/grid-rev-model/src/grid_view.rs b/shared-lib/grid-rev-model/src/grid_view.rs index 8f5c841d38..d404a5f08e 100644 --- a/shared-lib/grid-rev-model/src/grid_view.rs +++ b/shared-lib/grid-rev-model/src/grid_view.rs @@ -13,6 +13,7 @@ pub fn gen_grid_view_id() -> String { pub enum LayoutRevision { Table = 0, Board = 1, + Calendar = 2, } impl ToString for LayoutRevision {