Преглед изворни кода

Feat/read cell data for field (#1695)

* chore: read cells for field

* feat: enable read cells for specific field

* ci: fix tests

Co-authored-by: vedon <[email protected]>
Co-authored-by: nathan <[email protected]>
Nathan.fooo пре 2 година
родитељ
комит
fe4e28b576

+ 1 - 1
frontend/app_flowy/pubspec.lock

@@ -42,7 +42,7 @@ packages:
       path: "packages/appflowy_editor"
       relative: true
     source: path
-    version: "0.0.7"
+    version: "0.0.9"
   appflowy_editor_plugins:
     dependency: "direct main"
     description:

+ 56 - 50
frontend/app_flowy/test/bloc_test/home_test/view_bloc_test.dart

@@ -1,67 +1,73 @@
 import 'package:app_flowy/plugins/document/document.dart';
 import 'package:app_flowy/workspace/application/app/app_bloc.dart';
 import 'package:app_flowy/workspace/application/view/view_bloc.dart';
-import 'package:bloc_test/bloc_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 
 import '../../util.dart';
 
 void main() {
-  late AppFlowyUnitTest test;
+  late AppFlowyUnitTest testContext;
   setUpAll(() async {
-    test = await AppFlowyUnitTest.ensureInitialized();
+    testContext = await AppFlowyUnitTest.ensureInitialized();
   });
 
-  group('$ViewBloc', () {
-    late AppBloc appBloc;
+  test('rename view test', () async {
+    final app = await testContext.createTestApp();
+    final appBloc = AppBloc(app: app)..add(const AppEvent.initial());
+    appBloc.add(AppEvent.createView(
+      "Test document",
+      DocumentPluginBuilder(),
+    ));
+    await blocResponseFuture();
 
-    setUpAll(() async {
-      final app = await test.createTestApp();
-      appBloc = AppBloc(app: app)..add(const AppEvent.initial());
-      appBloc.add(AppEvent.createView(
-        "Test document",
-        DocumentPluginBuilder(),
-      ));
-      await blocResponseFuture();
-    });
+    final viewBloc = ViewBloc(view: appBloc.state.views.first)
+      ..add(const ViewEvent.initial());
+    viewBloc.add(const ViewEvent.rename('Hello world'));
+    await blocResponseFuture();
 
-    blocTest<ViewBloc, ViewState>(
-      "rename view",
-      build: () => ViewBloc(view: appBloc.state.views.first)
-        ..add(const ViewEvent.initial()),
-      act: (bloc) {
-        bloc.add(const ViewEvent.rename('Hello world'));
-      },
-      wait: blocResponseDuration(),
-      verify: (bloc) {
-        assert(bloc.state.view.name == "Hello world");
-      },
-    );
+    assert(viewBloc.state.view.name == "Hello world");
+  });
+
+  test('duplicate view test', () async {
+    final app = await testContext.createTestApp();
+    final appBloc = AppBloc(app: app)..add(const AppEvent.initial());
+    await blocResponseFuture();
+
+    appBloc.add(AppEvent.createView(
+      "Test document",
+      DocumentPluginBuilder(),
+    ));
+    await blocResponseFuture();
+
+    final viewBloc = ViewBloc(view: appBloc.state.views.first)
+      ..add(const ViewEvent.initial());
+    await blocResponseFuture();
+
+    viewBloc.add(const ViewEvent.duplicate());
+    await blocResponseFuture();
+
+    assert(appBloc.state.views.length == 2);
+  });
+
+  test('delete view test', () async {
+    final app = await testContext.createTestApp();
+    final appBloc = AppBloc(app: app)..add(const AppEvent.initial());
+    await blocResponseFuture();
+
+    appBloc.add(AppEvent.createView(
+      "Test document",
+      DocumentPluginBuilder(),
+    ));
+    await blocResponseFuture();
+    assert(appBloc.state.views.length == 1);
+
+    final viewBloc = ViewBloc(view: appBloc.state.views.first)
+      ..add(const ViewEvent.initial());
+    await blocResponseFuture();
 
-    blocTest<ViewBloc, ViewState>(
-      "duplicate view",
-      build: () => ViewBloc(view: appBloc.state.views.first)
-        ..add(const ViewEvent.initial()),
-      act: (bloc) {
-        bloc.add(const ViewEvent.duplicate());
-      },
-      wait: blocResponseDuration(),
-      verify: (bloc) {
-        assert(appBloc.state.views.length == 2);
-      },
-    );
+    viewBloc.add(const ViewEvent.delete());
+    await blocResponseFuture();
 
-    blocTest<ViewBloc, ViewState>(
-      "delete view",
-      build: () => ViewBloc(view: appBloc.state.views.first)
-        ..add(const ViewEvent.initial()),
-      act: (bloc) {
-        bloc.add(const ViewEvent.delete());
-      },
-      wait: blocResponseDuration(),
-      verify: (bloc) {
-        assert(appBloc.state.views.length == 1);
-      },
-    );
+    assert(appBloc.state.views.isEmpty);
   });
 }

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/block_manager.rs

@@ -228,6 +228,7 @@ impl GridBlockManager {
         editor.get_row_rev(row_id).await
     }
 
+    #[allow(dead_code)]
     pub async fn get_row_revs(&self) -> FlowyResult<Vec<Arc<RowRevision>>> {
         let mut row_revs = vec![];
         for iter in self.block_editors.iter() {

+ 6 - 26
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -9,7 +9,7 @@ use crate::services::cell::{
 };
 use crate::services::field::{
     default_type_option_builder_from_type, transform_type_option, type_option_builder_from_bytes, FieldBuilder,
-    RowSingleCellData, TypeOptionCellExt,
+    RowSingleCellData,
 };
 
 use crate::services::filter::FilterType;
@@ -76,6 +76,7 @@ impl GridRevisionEditor {
             pad: grid_pad.clone(),
             block_manager: block_manager.clone(),
             task_scheduler,
+            cell_data_cache: cell_data_cache.clone(),
         });
 
         // View manager
@@ -496,31 +497,10 @@ impl GridRevisionEditor {
         }
     }
 
-    pub async fn get_cell_data_for_field(&self, field_id: &str) -> FlowyResult<Vec<RowSingleCellData>> {
-        let row_revs = self.block_manager.get_row_revs().await?;
-        let field_rev = self.get_field_rev(field_id).await.unwrap();
-        let field_type: FieldType = field_rev.ty.into();
-        let mut cells = vec![];
-        if let Some(handler) =
-            TypeOptionCellExt::new_with_cell_data_cache(&field_rev, Some(self.cell_data_cache.clone()))
-                .get_type_option_cell_data_handler(&field_type)
-        {
-            for row_rev in row_revs {
-                if let Some(cell_rev) = row_rev.cells.get(field_id) {
-                    if let Ok(type_cell_data) = TypeCellData::try_from(cell_rev) {
-                        if let Ok(cell_data) = handler.get_cell_data(type_cell_data.cell_str, &field_type, &field_rev) {
-                            cells.push(RowSingleCellData {
-                                row_id: row_rev.id.clone(),
-                                field_id: field_rev.id.clone(),
-                                field_type: field_type.clone(),
-                                cell_data,
-                            })
-                        }
-                    }
-                }
-            }
-        }
-        Ok(cells)
+    /// Returns the list of cells corresponding to the given field.
+    pub async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> FlowyResult<Vec<RowSingleCellData>> {
+        let view_editor = self.view_manager.get_view_editor(view_id).await?;
+        view_editor.get_cells_for_field(field_id).await
     }
 
     #[tracing::instrument(level = "trace", skip_all, err)]

+ 18 - 1
frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs

@@ -1,6 +1,10 @@
+use crate::entities::FieldType;
 use crate::services::block_manager::GridBlockManager;
+use crate::services::cell::AtomicCellDataCache;
+use crate::services::field::{TypeOptionCellDataHandler, TypeOptionCellExt};
 use crate::services::row::GridBlockRowRevision;
 use crate::services::view_editor::GridViewEditorDelegate;
+
 use flowy_sync::client_grid::GridRevisionPad;
 use flowy_task::TaskDispatcher;
 use grid_rev_model::{FieldRevision, RowRevision};
@@ -12,6 +16,7 @@ pub(crate) struct GridViewEditorDelegateImpl {
     pub(crate) pad: Arc<RwLock<GridRevisionPad>>,
     pub(crate) block_manager: Arc<GridBlockManager>,
     pub(crate) task_scheduler: Arc<RwLock<TaskDispatcher>>,
+    pub(crate) cell_data_cache: AtomicCellDataCache,
 }
 
 impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
@@ -27,7 +32,6 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
             }
         })
     }
-
     fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>> {
         let pad = self.pad.clone();
         let field_id = field_id.to_owned();
@@ -63,6 +67,10 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
         })
     }
 
+    // /// Returns the list of cells corresponding to the given field.
+    // pub async fn get_cells_for_field(&self, field_id: &str) -> FlowyResult<Vec<RowSingleCellData>> {
+    // }
+
     fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>> {
         let block_manager = self.block_manager.clone();
         to_fut(async move { block_manager.get_blocks(None).await.unwrap_or_default() })
@@ -71,4 +79,13 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
     fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>> {
         self.task_scheduler.clone()
     }
+
+    fn get_type_option_cell_handler(
+        &self,
+        field_rev: &FieldRevision,
+        field_type: &FieldType,
+    ) -> Option<Box<dyn TypeOptionCellDataHandler>> {
+        TypeOptionCellExt::new_with_cell_data_cache(field_rev, Some(self.cell_data_cache.clone()))
+            .get_type_option_cell_data_handler(field_type)
+    }
 }

+ 8 - 0
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs

@@ -1,4 +1,5 @@
 use crate::entities::{GroupPB, GroupViewChangesetPB};
+use crate::services::field::RowSingleCellData;
 use crate::services::group::{default_group_configuration, GeneratedGroupContext, Group};
 use flowy_error::{FlowyError, FlowyResult};
 use grid_rev_model::{
@@ -13,6 +14,7 @@ use std::sync::Arc;
 
 pub trait GroupConfigurationReader: Send + Sync + 'static {
     fn get_configuration(&self) -> Fut<Option<Arc<GroupConfigurationRevision>>>;
+    fn get_configuration_cells(&self, field_id: &str) -> Fut<FlowyResult<Vec<RowSingleCellData>>>;
 }
 
 pub trait GroupConfigurationWriter: Send + Sync + 'static {
@@ -53,6 +55,11 @@ pub struct GroupContext<C> {
     /// Cache all the groups
     groups_map: IndexMap<String, Group>,
 
+    /// A reader that implement the [GroupConfigurationReader] trait
+    ///
+    #[allow(dead_code)]
+    reader: Arc<dyn GroupConfigurationReader>,
+
     /// A writer that implement the [GroupConfigurationWriter] trait is used to save the
     /// configuration to disk  
     ///
@@ -85,6 +92,7 @@ where
             view_id,
             field_rev,
             groups_map: IndexMap::new(),
+            reader,
             writer,
             configuration,
             configuration_phantom: PhantomData,

+ 59 - 4
frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs

@@ -1,7 +1,8 @@
 use crate::dart_notification::{send_dart_notification, GridDartNotification};
 use crate::entities::*;
 use crate::services::block_manager::GridBlockEvent;
-use crate::services::cell::AtomicCellDataCache;
+use crate::services::cell::{AtomicCellDataCache, TypeCellData};
+use crate::services::field::{RowSingleCellData, TypeOptionCellDataHandler};
 use crate::services::filter::{FilterChangeset, FilterController, FilterTaskHandler, FilterType, UpdatedFilterType};
 use crate::services::group::{
     default_group_configuration, find_group_field, make_group_controller, Group, GroupConfigurationReader,
@@ -58,6 +59,12 @@ pub trait GridViewEditorDelegate: Send + Sync + 'static {
 
     /// Returns a `TaskDispatcher` used to poll a `Task`
     fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
+
+    fn get_type_option_cell_handler(
+        &self,
+        field_rev: &FieldRevision,
+        field_type: &FieldType,
+    ) -> Option<Box<dyn TypeOptionCellDataHandler>>;
 }
 
 pub struct GridViewRevisionEditor {
@@ -581,6 +588,10 @@ impl GridViewRevisionEditor {
     pub async fn group_by_view_field(&self, field_id: &str) -> FlowyResult<()> {
         if let Some(field_rev) = self.delegate.get_field_rev(field_id).await {
             let row_revs = self.delegate.get_row_revs(None).await;
+            let configuration_reader = GroupConfigurationReaderImpl {
+                pad: self.pad.clone(),
+                view_editor_delegate: self.delegate.clone(),
+            };
             let new_group_controller = new_group_controller_with_field_rev(
                 self.user_id.clone(),
                 self.view_id.clone(),
@@ -588,6 +599,7 @@ impl GridViewRevisionEditor {
                 self.rev_manager.clone(),
                 field_rev,
                 row_revs,
+                configuration_reader,
             )
             .await?;
 
@@ -614,6 +626,10 @@ impl GridViewRevisionEditor {
         Ok(())
     }
 
+    pub(crate) async fn get_cells_for_field(&self, field_id: &str) -> FlowyResult<Vec<RowSingleCellData>> {
+        get_cells_for_field(self.delegate.clone(), field_id).await
+    }
+
     async fn notify_did_update_setting(&self) {
         let setting = self.get_view_setting().await;
         send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGridSetting)
@@ -691,6 +707,33 @@ impl GridViewRevisionEditor {
         }
     }
 }
+/// Returns the list of cells corresponding to the given field.
+pub(crate) async fn get_cells_for_field(
+    delegate: Arc<dyn GridViewEditorDelegate>,
+    field_id: &str,
+) -> FlowyResult<Vec<RowSingleCellData>> {
+    let row_revs = delegate.get_row_revs(None).await;
+    let field_rev = delegate.get_field_rev(field_id).await.unwrap();
+    let field_type: FieldType = field_rev.ty.into();
+    let mut cells = vec![];
+    if let Some(handler) = delegate.get_type_option_cell_handler(&field_rev, &field_type) {
+        for row_rev in row_revs {
+            if let Some(cell_rev) = row_rev.cells.get(field_id) {
+                if let Ok(type_cell_data) = TypeCellData::try_from(cell_rev) {
+                    if let Ok(cell_data) = handler.get_cell_data(type_cell_data.cell_str, &field_type, &field_rev) {
+                        cells.push(RowSingleCellData {
+                            row_id: row_rev.id.clone(),
+                            field_id: field_rev.id.clone(),
+                            field_type: field_type.clone(),
+                            cell_data,
+                        })
+                    }
+                }
+            }
+        }
+    }
+    Ok(cells)
+}
 
 #[async_trait]
 impl RefCountValue for GridViewRevisionEditor {
@@ -706,7 +749,10 @@ async fn new_group_controller(
     rev_manager: Arc<RevisionManager<Arc<ConnectionPool>>>,
     delegate: Arc<dyn GridViewEditorDelegate>,
 ) -> FlowyResult<Box<dyn GroupController>> {
-    let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
+    let configuration_reader = GroupConfigurationReaderImpl {
+        pad: view_rev_pad.clone(),
+        view_editor_delegate: delegate.clone(),
+    };
     let field_revs = delegate.get_field_revs(None).await;
     let row_revs = delegate.get_row_revs(None).await;
     let layout = view_rev_pad.read().await.layout();
@@ -722,7 +768,16 @@ async fn new_group_controller(
         })
         .unwrap_or_else(|| find_group_field(&field_revs, &layout).unwrap());
 
-    new_group_controller_with_field_rev(user_id, view_id, view_rev_pad, rev_manager, field_rev, row_revs).await
+    new_group_controller_with_field_rev(
+        user_id,
+        view_id,
+        view_rev_pad,
+        rev_manager,
+        field_rev,
+        row_revs,
+        configuration_reader,
+    )
+    .await
 }
 
 /// Returns a [GroupController]  
@@ -734,8 +789,8 @@ async fn new_group_controller_with_field_rev(
     rev_manager: Arc<RevisionManager<Arc<ConnectionPool>>>,
     field_rev: Arc<FieldRevision>,
     row_revs: Vec<Arc<RowRevision>>,
+    configuration_reader: GroupConfigurationReaderImpl,
 ) -> FlowyResult<Box<dyn GroupController>> {
-    let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
     let configuration_writer = GroupConfigurationWriterImpl {
         user_id,
         rev_manager,

+ 13 - 3
frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs

@@ -1,9 +1,10 @@
 use crate::entities::{GridLayout, GridLayoutPB, GridSettingPB};
+use crate::services::field::RowSingleCellData;
 use crate::services::filter::{FilterController, FilterDelegate, FilterType};
 use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter};
 use crate::services::row::GridBlockRowRevision;
 use crate::services::sort::{SortDelegate, SortType};
-use crate::services::view_editor::GridViewEditorDelegate;
+use crate::services::view_editor::{get_cells_for_field, GridViewEditorDelegate};
 use bytes::Bytes;
 use flowy_database::ConnectionPool;
 use flowy_error::{FlowyError, FlowyResult};
@@ -56,11 +57,14 @@ impl RevisionMergeable for GridViewRevisionMergeable {
     }
 }
 
-pub(crate) struct GroupConfigurationReaderImpl(pub(crate) Arc<RwLock<GridViewRevisionPad>>);
+pub(crate) struct GroupConfigurationReaderImpl {
+    pub(crate) pad: Arc<RwLock<GridViewRevisionPad>>,
+    pub(crate) view_editor_delegate: Arc<dyn GridViewEditorDelegate>,
+}
 
 impl GroupConfigurationReader for GroupConfigurationReaderImpl {
     fn get_configuration(&self) -> Fut<Option<Arc<GroupConfigurationRevision>>> {
-        let view_pad = self.0.clone();
+        let view_pad = self.pad.clone();
         to_fut(async move {
             let mut groups = view_pad.read().await.get_all_groups();
             if groups.is_empty() {
@@ -71,6 +75,12 @@ impl GroupConfigurationReader for GroupConfigurationReaderImpl {
             }
         })
     }
+
+    fn get_configuration_cells(&self, field_id: &str) -> Fut<FlowyResult<Vec<RowSingleCellData>>> {
+        let field_id = field_id.to_owned();
+        let view_editor_delegate = self.view_editor_delegate.clone();
+        to_fut(async move { get_cells_for_field(view_editor_delegate, &field_id).await })
+    }
 }
 
 pub(crate) struct GroupConfigurationWriterImpl {

+ 10 - 2
frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs

@@ -62,7 +62,11 @@ async fn grid_cell_update() {
 async fn text_cell_date_test() {
     let test = GridCellTest::new().await;
     let text_field = test.get_first_field_rev(FieldType::RichText);
-    let cells = test.editor.get_cell_data_for_field(&text_field.id).await.unwrap();
+    let cells = test
+        .editor
+        .get_cells_for_field(&test.view_id, &text_field.id)
+        .await
+        .unwrap();
 
     for (i, cell) in cells.iter().enumerate() {
         let text = cell.get_text_field_cell_data().unwrap();
@@ -82,7 +86,11 @@ async fn text_cell_date_test() {
 async fn url_cell_date_test() {
     let test = GridCellTest::new().await;
     let url_field = test.get_first_field_rev(FieldType::URL);
-    let cells = test.editor.get_cell_data_for_field(&url_field.id).await.unwrap();
+    let cells = test
+        .editor
+        .get_cells_for_field(&test.view_id, &url_field.id)
+        .await
+        .unwrap();
 
     for (i, cell) in cells.iter().enumerate() {
         let url_cell_data = cell.get_url_field_cell_data().unwrap();