From fe4e28b57623840182bb742d8326d126808c88b4 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Thu, 12 Jan 2023 07:56:46 +0800 Subject: [PATCH] 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 Co-authored-by: nathan --- frontend/app_flowy/pubspec.lock | 2 +- .../bloc_test/home_test/view_bloc_test.dart | 106 +++++++++--------- .../flowy-grid/src/services/block_manager.rs | 1 + .../flowy-grid/src/services/grid_editor.rs | 32 +----- .../src/services/grid_editor_trait_impl.rs | 19 +++- .../src/services/group/configuration.rs | 8 ++ .../src/services/view_editor/editor.rs | 63 ++++++++++- .../src/services/view_editor/trait_impl.rs | 16 ++- .../flowy-grid/tests/grid/cell_test/test.rs | 12 +- 9 files changed, 172 insertions(+), 87 deletions(-) diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index ac9558330b..b557a5af14 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/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: diff --git a/frontend/app_flowy/test/bloc_test/home_test/view_bloc_test.dart b/frontend/app_flowy/test/bloc_test/home_test/view_bloc_test.dart index a702d653cf..f5c37d986e 100644 --- a/frontend/app_flowy/test/bloc_test/home_test/view_bloc_test.dart +++ b/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( - "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"); + }); - blocTest( - "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); - }, - ); + test('duplicate view test', () async { + final app = await testContext.createTestApp(); + final appBloc = AppBloc(app: app)..add(const AppEvent.initial()); + await blocResponseFuture(); - blocTest( - "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); - }, - ); + 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(); + + viewBloc.add(const ViewEvent.delete()); + await blocResponseFuture(); + + assert(appBloc.state.views.isEmpty); }); } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index 0adec1f3f3..65bca33d03 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/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>> { let mut row_revs = vec![]; for iter in self.block_editors.iter() { diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index e4a23a607e..226b6921d0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/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> { - 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> { + 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)] diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs index 3a56b0c5d0..0a6902c243 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor_trait_impl.rs +++ b/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>, pub(crate) block_manager: Arc, pub(crate) task_scheduler: Arc>, + 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>> { 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> { + // } + fn get_blocks(&self) -> Fut> { 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> { self.task_scheduler.clone() } + + fn get_type_option_cell_handler( + &self, + field_rev: &FieldRevision, + field_type: &FieldType, + ) -> Option> { + TypeOptionCellExt::new_with_cell_data_cache(field_rev, Some(self.cell_data_cache.clone())) + .get_type_option_cell_data_handler(field_type) + } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs index 22cfc2e3f7..767fa76a96 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs +++ b/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>>; + fn get_configuration_cells(&self, field_id: &str) -> Fut>>; } pub trait GroupConfigurationWriter: Send + Sync + 'static { @@ -53,6 +55,11 @@ pub struct GroupContext { /// Cache all the groups groups_map: IndexMap, + /// A reader that implement the [GroupConfigurationReader] trait + /// + #[allow(dead_code)] + reader: Arc, + /// 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, diff --git a/frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs b/frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs index 97a57f5b5a..9dfa0557ff 100644 --- a/frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs +++ b/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>; + + fn get_type_option_cell_handler( + &self, + field_rev: &FieldRevision, + field_type: &FieldType, + ) -> Option>; } 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> { + 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, + field_id: &str, +) -> FlowyResult> { + 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>>, delegate: Arc, ) -> FlowyResult> { - 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>>, field_rev: Arc, row_revs: Vec>, + configuration_reader: GroupConfigurationReaderImpl, ) -> FlowyResult> { - let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone()); let configuration_writer = GroupConfigurationWriterImpl { user_id, rev_manager, diff --git a/frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs b/frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs index 14bf0f0558..661e8e4597 100644 --- a/frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs +++ b/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>); +pub(crate) struct GroupConfigurationReaderImpl { + pub(crate) pad: Arc>, + pub(crate) view_editor_delegate: Arc, +} impl GroupConfigurationReader for GroupConfigurationReaderImpl { fn get_configuration(&self) -> Fut>> { - 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>> { + 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 { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs index dca4159470..43e16c71ee 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs +++ b/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();