mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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 <vedon.fu@gmail.com> Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
b7ba189642
commit
fe4e28b576
@ -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:
|
||||
|
@ -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");
|
||||
});
|
||||
|
||||
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);
|
||||
},
|
||||
);
|
||||
test('duplicate view test', () async {
|
||||
final app = await testContext.createTestApp();
|
||||
final appBloc = AppBloc(app: app)..add(const AppEvent.initial());
|
||||
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);
|
||||
},
|
||||
);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user