Explorar el Código

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 <[email protected]>
Richard Shiue hace 2 años
padre
commit
e8b21955f8

+ 3 - 0
frontend/app_flowy/assets/translations/en.json

@@ -313,5 +313,8 @@
     "column": {
     "column": {
       "create_new_card": "New"
       "create_new_card": "New"
     }
     }
+  },
+  "calendar": {
+    "menuName": "Calendar"
   }
   }
 }
 }

+ 111 - 0
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<NavigationItem> get navigationItems => [this];
+}
+
+// mark for removal
+class BlankPage extends StatefulWidget {
+  const BlankPage({Key? key}) : super(key: key);
+
+  @override
+  State<BlankPage> createState() => _BlankPageState();
+}
+
+class _BlankPageState extends State<BlankPage> {
+  @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(),
+        ),
+      ),
+    );
+  }
+}

+ 1 - 0
frontend/app_flowy/lib/startup/plugin/plugin.dart

@@ -14,6 +14,7 @@ enum PluginType {
   trash,
   trash,
   grid,
   grid,
   board,
   board,
+  calendar,
 }
 }
 
 
 typedef PluginId = String;
 typedef PluginId = String;

+ 3 - 0
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/plugin/plugin.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/blank/blank.dart';
 import 'package:app_flowy/plugins/blank/blank.dart';
@@ -17,5 +18,7 @@ class PluginLoadTask extends LaunchTask {
     registerPlugin(builder: DocumentPluginBuilder());
     registerPlugin(builder: DocumentPluginBuilder());
     registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
     registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
     registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig());
     registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig());
+    registerPlugin(
+        builder: CalendarPluginBuilder(), config: CalendarPluginConfig());
   }
   }
 }
 }

+ 2 - 0
frontend/app_flowy/lib/workspace/application/view/view_ext.dart

@@ -44,6 +44,8 @@ extension ViewExtension on ViewPB {
     switch (layout) {
     switch (layout) {
       case ViewLayoutTypePB.Board:
       case ViewLayoutTypePB.Board:
         return PluginType.board;
         return PluginType.board;
+      case ViewLayoutTypePB.Calendar:
+        return PluginType.calendar;
       case ViewLayoutTypePB.Document:
       case ViewLayoutTypePB.Document:
         return PluginType.editor;
         return PluginType.editor;
       case ViewLayoutTypePB.Grid:
       case ViewLayoutTypePB.Grid:

+ 15 - 0
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/board/board.dart';
+import 'package:app_flowy/plugins/calendar/calendar.dart';
 import 'package:app_flowy/plugins/document/document.dart';
 import 'package:app_flowy/plugins/document/document.dart';
 import 'package:app_flowy/plugins/grid/grid.dart';
 import 'package:app_flowy/plugins/grid/grid.dart';
 import 'package:app_flowy/workspace/application/app/app_bloc.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.name == "Test grid");
     assert(bloc.state.views.last.layout == ViewLayoutTypePB.Grid);
     assert(bloc.state.views.last.layout == ViewLayoutTypePB.Grid);
   });
   });
+
   test('create a kanban', () async {
   test('create a kanban', () async {
     final app = await testContext.createTestApp();
     final app = await testContext.createTestApp();
     final bloc = AppBloc(app: app)..add(const AppEvent.initial());
     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.name == "Test board");
     assert(bloc.state.views.last.layout == ViewLayoutTypePB.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);
+  });
 }
 }

+ 3 - 1
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::entities::GridLayout;
 use flowy_grid::manager::{make_grid_view_data, GridManager};
 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::revision::Revision;
 use flowy_http_model::ws_data::ClientRevisionWSData;
 use flowy_http_model::ws_data::ClientRevisionWSData;
 use flowy_net::ClientServerConfiguration;
 use flowy_net::ClientServerConfiguration;
@@ -264,6 +264,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
         let (build_context, layout) = match layout {
         let (build_context, layout) = match layout {
             ViewLayoutTypePB::Grid => (make_default_grid(), GridLayout::Table),
             ViewLayoutTypePB::Grid => (make_default_grid(), GridLayout::Table),
             ViewLayoutTypePB::Board => (make_default_board(), GridLayout::Board),
             ViewLayoutTypePB::Board => (make_default_board(), GridLayout::Board),
+            ViewLayoutTypePB::Calendar => (make_default_calendar(), GridLayout::Calendar),
             ViewLayoutTypePB::Document => {
             ViewLayoutTypePB::Document => {
                 return FutureResult::new(async move {
                 return FutureResult::new(async move {
                     Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
                     Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
@@ -293,6 +294,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
         let layout = match layout {
         let layout = match layout {
             ViewLayoutTypePB::Grid => GridLayout::Table,
             ViewLayoutTypePB::Grid => GridLayout::Table,
             ViewLayoutTypePB::Board => GridLayout::Board,
             ViewLayoutTypePB::Board => GridLayout::Board,
+            ViewLayoutTypePB::Calendar => GridLayout::Calendar,
             ViewLayoutTypePB::Document => {
             ViewLayoutTypePB::Document => {
                 return FutureResult::new(async move {
                 return FutureResult::new(async move {
                     Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
                     Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))

+ 3 - 0
frontend/rust-lib/flowy-folder/src/entities/view.rs

@@ -86,6 +86,7 @@ pub enum ViewLayoutTypePB {
     Document = 0,
     Document = 0,
     Grid = 3,
     Grid = 3,
     Board = 4,
     Board = 4,
+    Calendar = 5,
 }
 }
 
 
 impl std::default::Default for ViewLayoutTypePB {
 impl std::default::Default for ViewLayoutTypePB {
@@ -100,6 +101,7 @@ impl std::convert::From<ViewLayoutTypeRevision> for ViewLayoutTypePB {
             ViewLayoutTypeRevision::Grid => ViewLayoutTypePB::Grid,
             ViewLayoutTypeRevision::Grid => ViewLayoutTypePB::Grid,
             ViewLayoutTypeRevision::Board => ViewLayoutTypePB::Board,
             ViewLayoutTypeRevision::Board => ViewLayoutTypePB::Board,
             ViewLayoutTypeRevision::Document => ViewLayoutTypePB::Document,
             ViewLayoutTypeRevision::Document => ViewLayoutTypePB::Document,
+            ViewLayoutTypeRevision::Calendar => ViewLayoutTypePB::Calendar,
         }
         }
     }
     }
 }
 }
@@ -110,6 +112,7 @@ impl std::convert::From<ViewLayoutTypePB> for ViewLayoutTypeRevision {
             ViewLayoutTypePB::Grid => ViewLayoutTypeRevision::Grid,
             ViewLayoutTypePB::Grid => ViewLayoutTypeRevision::Grid,
             ViewLayoutTypePB::Board => ViewLayoutTypeRevision::Board,
             ViewLayoutTypePB::Board => ViewLayoutTypeRevision::Board,
             ViewLayoutTypePB::Document => ViewLayoutTypeRevision::Document,
             ViewLayoutTypePB::Document => ViewLayoutTypeRevision::Document,
+            ViewLayoutTypePB::Calendar => ViewLayoutTypeRevision::Calendar,
         }
         }
     }
     }
 }
 }

+ 1 - 1
frontend/rust-lib/flowy-grid/src/entities/field_entities.rs

@@ -570,7 +570,7 @@ impl FieldType {
     }
     }
 
 
     pub fn can_be_group(&self) -> bool {
     pub fn can_be_group(&self) -> bool {
-        self.is_select_option()
+        self.is_select_option() || self.is_checkbox()
     }
     }
 }
 }
 
 

+ 3 - 0
frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs

@@ -49,6 +49,7 @@ impl GridLayoutPB {
 pub enum GridLayout {
 pub enum GridLayout {
     Table = 0,
     Table = 0,
     Board = 1,
     Board = 1,
+    Calendar = 2,
 }
 }
 
 
 impl std::default::Default for GridLayout {
 impl std::default::Default for GridLayout {
@@ -62,6 +63,7 @@ impl std::convert::From<LayoutRevision> for GridLayout {
         match rev {
         match rev {
             LayoutRevision::Table => GridLayout::Table,
             LayoutRevision::Table => GridLayout::Table,
             LayoutRevision::Board => GridLayout::Board,
             LayoutRevision::Board => GridLayout::Board,
+            LayoutRevision::Calendar => GridLayout::Calendar,
         }
         }
     }
     }
 }
 }
@@ -71,6 +73,7 @@ impl std::convert::From<GridLayout> for LayoutRevision {
         match layout {
         match layout {
             GridLayout::Table => LayoutRevision::Table,
             GridLayout::Table => LayoutRevision::Table,
             GridLayout::Board => LayoutRevision::Board,
             GridLayout::Board => LayoutRevision::Board,
+            GridLayout::Calendar => LayoutRevision::Calendar,
         }
         }
     }
     }
 }
 }

+ 8 - 11
frontend/rust-lib/flowy-grid/src/services/group/group_util.rs

@@ -74,17 +74,14 @@ where
     Ok(group_controller)
     Ok(group_controller)
 }
 }
 
 
-pub fn find_group_field(field_revs: &[Arc<FieldRevision>], layout: &LayoutRevision) -> Option<Arc<FieldRevision>> {
-    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<FieldRevision>], _layout: &LayoutRevision) -> Option<Arc<FieldRevision>> {
+    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]
 /// Returns a `default` group configuration for the [FieldRevision]

+ 28 - 0
frontend/rust-lib/flowy-grid/src/util.rs

@@ -70,6 +70,34 @@ pub fn make_default_board() -> BuildGridContext {
     grid_builder.build()
     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)]
 #[allow(dead_code)]
 pub fn make_default_board_2() -> BuildGridContext {
 pub fn make_default_board_2() -> BuildGridContext {
     let mut grid_builder = GridBuilder::new();
     let mut grid_builder = GridBuilder::new();

+ 12 - 1
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -57,6 +57,11 @@ impl GridEditorTest {
                 let view_data: Bytes = build_context.into();
                 let view_data: Bytes = build_context.into();
                 ViewTest::new_board_view(&sdk, view_data.to_vec()).await
                 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();
         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.
     /// 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<FieldRevision> {
     pub fn get_first_field_rev(&self, field_type: FieldType) -> &Arc<FieldRevision> {
         self.field_revs
         self.field_revs
             .iter()
             .iter()
@@ -388,6 +393,7 @@ fn make_test_grid() -> BuildGridContext {
     grid_builder.build()
     grid_builder.build()
 }
 }
 
 
+// Kanban board unit test mock data
 fn make_test_board() -> BuildGridContext {
 fn make_test_board() -> BuildGridContext {
     let mut grid_builder = GridBuilder::new();
     let mut grid_builder = GridBuilder::new();
     // Iterate through the FieldType to create the corresponding Field.
     // Iterate through the FieldType to create the corresponding Field.
@@ -557,3 +563,8 @@ fn make_test_board() -> BuildGridContext {
     }
     }
     grid_builder.build()
     grid_builder.build()
 }
 }
+
+// Calendar unit test mock data
+fn make_test_calendar() -> BuildGridContext {
+    todo!()
+}

+ 4 - 0
frontend/rust-lib/flowy-test/src/helper.rs

@@ -51,6 +51,10 @@ impl ViewTest {
         Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Board, data).await
         Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Board, data).await
     }
     }
 
 
+    pub async fn new_calendar_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
+        Self::new(sdk, ViewDataFormatPB::DatabaseFormat, ViewLayoutTypePB::Calendar, data).await
+    }
+
     pub async fn new_document_view(sdk: &FlowySDKTest) -> Self {
     pub async fn new_document_view(sdk: &FlowySDKTest) -> Self {
         let view_data_format = match sdk.document_version() {
         let view_data_format = match sdk.document_version() {
             DocumentVersionPB::V0 => ViewDataFormatPB::DeltaFormat,
             DocumentVersionPB::V0 => ViewDataFormatPB::DeltaFormat,

+ 1 - 0
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.
     // The for historical reasons, the value of Grid is not 1.
     Grid = 3,
     Grid = 3,
     Board = 4,
     Board = 4,
+    Calendar = 5,
 }
 }
 
 
 impl std::default::Default for ViewLayoutTypeRevision {
 impl std::default::Default for ViewLayoutTypeRevision {

+ 1 - 0
shared-lib/grid-rev-model/src/grid_view.rs

@@ -13,6 +13,7 @@ pub fn gen_grid_view_id() -> String {
 pub enum LayoutRevision {
 pub enum LayoutRevision {
     Table = 0,
     Table = 0,
     Board = 1,
     Board = 1,
+    Calendar = 2,
 }
 }
 
 
 impl ToString for LayoutRevision {
 impl ToString for LayoutRevision {