fix: fix some bugs

This commit is contained in:
appflowy 2022-08-22 16:16:15 +08:00
parent ffc6f141fa
commit 074c497d57
27 changed files with 486 additions and 252 deletions

View File

@ -29,7 +29,7 @@
"program": "./lib/main.dart",
"type": "dart",
"env": {
"RUST_LOG": "trace"
"RUST_LOG": "debug"
},
"cwd": "${workspaceRoot}/app_flowy"
},

View File

@ -23,7 +23,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
final BoardDataController _dataController;
late final AFBoardDataController afBoardDataController;
final MoveRowFFIService _rowService;
Map<String, GroupController> groupControllers = {};
LinkedHashMap<String, GroupController> groupControllers = LinkedHashMap.new();
GridFieldCache get fieldCache => _dataController.fieldCache;
String get gridId => _dataController.gridId;
@ -34,9 +34,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
super(BoardState.initial(view.id)) {
afBoardDataController = AFBoardDataController(
onMoveColumn: (
fromColumnId,
fromIndex,
toColumnId,
toIndex,
) {},
) {
_moveGroup(fromColumnId, toColumnId);
},
onMoveColumnItem: (
columnId,
fromIndex,
@ -44,7 +48,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
) {
final fromRow = groupControllers[columnId]?.rowAtIndex(fromIndex);
final toRow = groupControllers[columnId]?.rowAtIndex(toIndex);
_moveRow(fromRow, toRow);
_moveRow(fromRow, columnId, toRow);
},
onMoveColumnItemToColumn: (
fromColumnId,
@ -54,7 +58,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
) {
final fromRow = groupControllers[fromColumnId]?.rowAtIndex(fromIndex);
final toRow = groupControllers[toColumnId]?.rowAtIndex(toIndex);
_moveRow(fromRow, toRow);
_moveRow(fromRow, toColumnId, toRow);
},
);
@ -95,12 +99,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
);
}
void _moveRow(RowPB? fromRow, RowPB? toRow) {
if (fromRow != null && toRow != null) {
void _moveRow(RowPB? fromRow, String columnId, RowPB? toRow) {
if (fromRow != null) {
_rowService
.moveRow(
.moveGroupRow(
fromRowId: fromRow.id,
toRowId: toRow.id,
toGroupId: columnId,
toRowId: toRow?.id,
)
.then((result) {
result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
@ -108,6 +113,17 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
}
}
void _moveGroup(String fromColumnId, String toColumnId) {
_rowService
.moveGroup(
fromGroupId: fromColumnId,
toGroupId: toColumnId,
)
.then((result) {
result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
});
}
@override
Future<void> close() async {
await _dataController.dispose();

View File

@ -37,6 +37,14 @@ class GroupController {
(GroupRowsChangesetPB changeset) {
for (final insertedRow in changeset.insertedRows) {
final index = insertedRow.hasIndex() ? insertedRow.index : null;
if (insertedRow.hasIndex() &&
group.rows.length > insertedRow.index) {
group.rows.insert(insertedRow.index, insertedRow.row);
} else {
group.rows.add(insertedRow.row);
}
delegate.insertRow(
group.groupId,
insertedRow.row,
@ -45,10 +53,19 @@ class GroupController {
}
for (final deletedRow in changeset.deletedRows) {
group.rows.removeWhere((rowPB) => rowPB.id == deletedRow);
delegate.removeRow(group.groupId, deletedRow);
}
for (final updatedRow in changeset.updatedRows) {
final index = group.rows.indexWhere(
(rowPB) => rowPB.id == updatedRow.id,
);
if (index != -1) {
group.rows[index] = updatedRow;
}
delegate.updateRow(group.groupId, updatedRow);
}
},

View File

@ -31,7 +31,7 @@ class BoardPluginBuilder implements PluginBuilder {
class BoardPluginConfig implements PluginConfig {
@override
bool get creatable => true;
bool get creatable => false;
}
class BoardPlugin extends Plugin {

View File

@ -3,6 +3,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
class RowFFIService {
@ -68,4 +69,33 @@ class MoveRowFFIService {
return GridEventMoveRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroupRow({
required String fromRowId,
required String toGroupId,
required String? toRowId,
}) {
var payload = MoveGroupRowPayloadPB.create()
..viewId = gridId
..fromRowId = fromRowId
..toGroupId = toGroupId;
if (toRowId != null) {
payload.toRowId = toRowId;
}
return GridEventMoveGroupRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroup({
required String fromGroupId,
required String toGroupId,
}) {
final payload = MoveGroupPayloadPB.create()
..viewId = gridId
..fromGroupId = fromGroupId
..toGroupId = toGroupId;
return GridEventMoveGroup(payload).send();
}
}

View File

@ -10,7 +10,7 @@ class MultiBoardListExample extends StatefulWidget {
class _MultiBoardListExampleState extends State<MultiBoardListExample> {
final AFBoardDataController boardDataController = AFBoardDataController(
onMoveColumn: (fromIndex, toIndex) {
onMoveColumn: (fromColumnId, fromIndex, toColumnId, toIndex) {
debugPrint('Move column from $fromIndex to $toIndex');
},
onMoveColumnItem: (columnId, fromIndex, toIndex) {

View File

@ -145,7 +145,8 @@ class AFBoardColumnData<CustomData> extends ReoderFlexItem with EquatableMixin {
}) : _items = items;
/// Returns the readonly List<ColumnItem>
UnmodifiableListView<AFColumnItem> get items => UnmodifiableListView(_items);
UnmodifiableListView<AFColumnItem> get items =>
UnmodifiableListView([..._items]);
@override
List<Object?> get props => [id, ..._items];

View File

@ -8,7 +8,12 @@ import 'reorder_flex/reorder_flex.dart';
import 'package:flutter/material.dart';
import 'reorder_phantom/phantom_controller.dart';
typedef OnMoveColumn = void Function(int fromIndex, int toIndex);
typedef OnMoveColumn = void Function(
String fromColumnId,
int fromIndex,
String toColumnId,
int toIndex,
);
typedef OnMoveColumnItem = void Function(
String columnId,
@ -98,9 +103,11 @@ class AFBoardDataController extends ChangeNotifier
}
void moveColumn(int fromIndex, int toIndex, {bool notify = true}) {
final columnData = _columnDatas.removeAt(fromIndex);
_columnDatas.insert(toIndex, columnData);
onMoveColumn?.call(fromIndex, toIndex);
final toColumnData = _columnDatas[toIndex];
final fromColumnData = _columnDatas.removeAt(fromIndex);
_columnDatas.insert(toIndex, fromColumnData);
onMoveColumn?.call(fromColumnData.id, fromIndex, toColumnData.id, toIndex);
if (notify) notifyListeners();
}

View File

@ -123,3 +123,46 @@ impl TryInto<MoveRowParams> for MoveRowPayloadPB {
})
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct MoveGroupRowPayloadPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub from_row_id: String,
#[pb(index = 3)]
pub to_group_id: String,
#[pb(index = 4, one_of)]
pub to_row_id: Option<String>,
}
pub struct MoveGroupRowParams {
pub view_id: String,
pub from_row_id: String,
pub to_group_id: String,
pub to_row_id: Option<String>,
}
impl TryInto<MoveGroupRowParams> for MoveGroupRowPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<MoveGroupRowParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::GridViewIdIsEmpty)?;
let from_row_id = NotEmptyStr::parse(self.from_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
let to_group_id = NotEmptyStr::parse(self.to_group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?;
let to_row_id = match self.to_row_id {
None => None,
Some(to_row_id) => Some(NotEmptyStr::parse(to_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0),
};
Ok(MoveGroupRowParams {
view_id: view_id.0,
from_row_id: from_row_id.0,
to_group_id: to_group_id.0,
to_row_id,
})
}
}

View File

@ -436,3 +436,14 @@ pub(crate) async fn move_group_handler(
let _ = editor.move_group(params).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn move_group_row_handler(
data: Data<MoveGroupRowPayloadPB>,
manager: AppData<Arc<GridManager>>,
) -> FlowyResult<()> {
let params: MoveGroupRowParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(params.view_id.as_ref())?;
let _ = editor.move_group_row(params).await?;
Ok(())
}

View File

@ -42,6 +42,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
// Group
.event(GridEvent::CreateBoardCard, create_board_card_handler)
.event(GridEvent::MoveGroup, move_group_handler)
.event(GridEvent::MoveGroupRow, move_group_row_handler)
.event(GridEvent::GetGroup, get_groups_handler);
module
@ -221,4 +222,7 @@ pub enum GridEvent {
#[event(input = "MoveGroupPayloadPB")]
MoveGroup = 111,
#[event(input = "MoveGroupRowPayloadPB")]
MoveGroupRow = 112,
}

View File

@ -560,16 +560,6 @@ impl GridRevisionEditor {
.block_manager
.move_row(row_rev.clone(), from_index, to_index)
.await?;
if let Some(row_changeset) = self.view_manager.move_row(row_rev, to_row_id.clone()).await {
tracing::trace!("Receive row changeset after moving the row");
match self.block_manager.update_row(row_changeset).await {
Ok(_) => {}
Err(e) => {
tracing::error!("Apply row changeset error:{:?}", e);
}
}
}
}
(_, None) => tracing::warn!("Can not find the from row id: {}", from_row_id),
(None, _) => tracing::warn!("Can not find the to row id: {}", to_row_id),
@ -579,6 +569,35 @@ impl GridRevisionEditor {
Ok(())
}
pub async fn move_group_row(&self, params: MoveGroupRowParams) -> FlowyResult<()> {
let MoveGroupRowParams {
view_id: _,
from_row_id,
to_group_id,
to_row_id,
} = params;
match self.block_manager.get_row_rev(&from_row_id).await? {
None => tracing::warn!("Move row failed, can not find the row:{}", from_row_id),
Some(row_rev) => {
if let Some(row_changeset) = self
.view_manager
.move_group_row(row_rev, to_group_id, to_row_id.clone())
.await
{
match self.block_manager.update_row(row_changeset).await {
Ok(_) => {}
Err(e) => {
tracing::error!("Apply row changeset error:{:?}", e);
}
}
}
}
}
Ok(())
}
pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> {
let MoveFieldParams {
grid_id: _,

View File

@ -80,7 +80,7 @@ impl GridViewRevisionEditor {
None => {}
Some(group_id) => {
self.group_service
.read()
.write()
.await
.will_create_row(row_rev, group_id, |field_id| {
self.field_delegate.get_field_rev(&field_id)
@ -109,7 +109,7 @@ impl GridViewRevisionEditor {
// Send the group notification if the current view has groups;
if let Some(changesets) = self
.group_service
.read()
.write()
.await
.did_delete_row(row_rev, |field_id| self.field_delegate.get_field_rev(&field_id))
.await
@ -123,7 +123,7 @@ impl GridViewRevisionEditor {
pub(crate) async fn did_update_row(&self, row_rev: &RowRevision) {
if let Some(changesets) = self
.group_service
.read()
.write()
.await
.did_update_row(row_rev, |field_id| self.field_delegate.get_field_rev(&field_id))
.await
@ -134,23 +134,23 @@ impl GridViewRevisionEditor {
}
}
pub(crate) async fn did_move_row(
pub(crate) async fn move_group_row(
&self,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
upper_row_id: &str,
to_group_id: &str,
to_row_id: Option<String>,
) {
if let Some(changesets) = self
.group_service
.read()
.write()
.await
.did_move_row(row_rev, row_changeset, upper_row_id, |field_id| {
.move_group_row(row_rev, row_changeset, to_group_id, to_row_id, |field_id| {
self.field_delegate.get_field_rev(&field_id)
})
.await
{
for changeset in changesets {
tracing::trace!("Group: {} changeset: {}", changeset.group_id, changeset);
self.notify_did_update_group_rows(changeset).await;
}
}
@ -184,7 +184,7 @@ impl GridViewRevisionEditor {
pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
let _ = self
.group_service
.read()
.write()
.await
.move_group(&params.from_group_id, &params.to_group_id)
.await?;
@ -326,7 +326,6 @@ impl GroupConfigurationReader for GroupConfigurationReaderImpl {
let view_pad = self.0.clone();
wrap_future(async move {
let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?;
if groups.is_empty() {
None
} else {

View File

@ -129,10 +129,17 @@ impl GridViewManager {
/// It may generate a RowChangeset when the Row was moved from one group to another.
/// The return value, [RowChangeset], contains the changes made by the groups.
///
pub(crate) async fn move_row(&self, row_rev: Arc<RowRevision>, to_row_id: String) -> Option<RowChangeset> {
pub(crate) async fn move_group_row(
&self,
row_rev: Arc<RowRevision>,
to_group_id: String,
to_row_id: Option<String>,
) -> Option<RowChangeset> {
let mut row_changeset = RowChangeset::new(row_rev.id.clone());
for view_editor in self.view_editors.iter() {
view_editor.did_move_row(&row_rev, &mut row_changeset, &to_row_id).await;
view_editor
.move_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone())
.await;
}
if row_changeset.has_changed() {

View File

@ -1,6 +1,7 @@
use crate::entities::GroupRowsChangesetPB;
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
use crate::services::group::controller::MoveGroupRowContext;
use flowy_grid_data_model::revision::RowRevision;
pub trait GroupAction: Send + Sync {
type CellDataType;
@ -12,12 +13,5 @@ pub trait GroupAction: Send + Sync {
cell_data: &Self::CellDataType,
) -> Vec<GroupRowsChangesetPB>;
fn move_row_if_match(
&mut self,
field_rev: &FieldRevision,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
cell_data: &Self::CellDataType,
to_row_id: &str,
) -> Vec<GroupRowsChangesetPB>;
fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec<GroupRowsChangesetPB>;
}

View File

@ -101,9 +101,9 @@ where
Ok(())
}
pub(crate) fn with_mut_groups(&mut self, mut mut_groups_fn: impl FnMut(&mut Group)) {
pub(crate) fn with_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) {
self.groups_map.iter_mut().for_each(|(_, group)| {
mut_groups_fn(group);
each(group);
})
}
@ -111,22 +111,16 @@ where
self.groups_map.get_mut(group_id)
}
pub(crate) fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
let from_group_index = self.groups_map.get_index_of(from_group_id);
let to_group_index = self.groups_map.get_index_of(to_group_id);
match (from_group_index, to_group_index) {
pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> {
let from_index = self.groups_map.get_index_of(from_id);
let to_index = self.groups_map.get_index_of(to_id);
match (from_index, to_index) {
(Some(from_index), Some(to_index)) => {
self.groups_map.swap_indices(from_index, to_index);
self.mut_configuration(|configuration| {
let from_index = configuration
.groups
.iter()
.position(|group| group.group_id == from_group_id);
let to_index = configuration
.groups
.iter()
.position(|group| group.group_id == to_group_id);
let from_index = configuration.groups.iter().position(|group| group.group_id == from_id);
let to_index = configuration.groups.iter().position(|group| group.group_id == to_id);
if let (Some(from), Some(to)) = (from_index, to_index) {
configuration.groups.swap(from, to);
}
@ -150,7 +144,7 @@ where
let configuration = (&*self.configuration).clone();
let writer = self.writer.clone();
let field_id = self.field_rev.id.clone();
let field_type = self.field_rev.ty.clone();
let field_type = self.field_rev.ty;
tokio::spawn(async move {
match writer
.save_group_configuration(&field_id, field_type, configuration)
@ -196,7 +190,6 @@ where
}
fn merge_groups(old_group_revs: &[GroupRecordRevision], groups: Vec<Group>) -> (Vec<GroupRecordRevision>, Vec<Group>) {
tracing::trace!("Merge group: old: {}, new: {}", old_group_revs.len(), groups.len());
if old_group_revs.is_empty() {
let new_groups = groups
.iter()

View File

@ -31,6 +31,14 @@ pub trait GroupGenerator {
) -> Vec<Group>;
}
pub struct MoveGroupRowContext<'a> {
pub row_rev: &'a RowRevision,
pub row_changeset: &'a mut RowChangeset,
pub field_rev: &'a FieldRevision,
pub to_group_id: &'a str,
pub to_row_id: Option<String>,
}
// Defines the shared actions each group controller can perform.
pub trait GroupControllerSharedOperation: Send + Sync {
// The field that is used for grouping the rows
@ -51,13 +59,7 @@ pub trait GroupControllerSharedOperation: Send + Sync {
field_rev: &FieldRevision,
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
fn did_move_row(
&mut self,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
field_rev: &FieldRevision,
to_row_id: &str,
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
}
/// C: represents the group configuration that impl [GroupConfigurationSerde]
@ -195,18 +197,11 @@ where
}
}
fn did_move_row(
&mut self,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
field_rev: &FieldRevision,
to_row_id: &str,
) -> FlowyResult<Vec<GroupRowsChangesetPB>> {
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsChangesetPB>> {
if let Some(cell_rev) = context.row_rev.cells.get(&self.field_id) {
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), context.field_rev);
let cell_data = cell_bytes.parser::<P>()?;
tracing::trace!("Move row:{} to row:{}", row_rev.id, to_row_id);
Ok(self.move_row_if_match(field_rev, row_rev, row_changeset, &cell_data, to_row_id))
Ok(self.move_row(&cell_data, context))
} else {
Ok(vec![])
}

View File

@ -2,10 +2,12 @@ use crate::entities::GroupRowsChangesetPB;
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
use crate::services::group::action::GroupAction;
use crate::services::group::configuration::GenericGroupConfiguration;
use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
use crate::services::group::controller::{
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
};
use crate::services::group::entities::Group;
use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowChangeset, RowRevision};
use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowRevision};
pub type CheckboxGroupController = GenericGroupController<
CheckboxGroupConfigurationRevision,
@ -38,13 +40,10 @@ impl GroupAction for CheckboxGroupController {
todo!()
}
fn move_row_if_match(
fn move_row(
&mut self,
_field_rev: &FieldRevision,
_row_rev: &RowRevision,
_row_changeset: &mut RowChangeset,
_cell_data: &Self::CellDataType,
_to_row_id: &str,
_context: MoveGroupRowContext,
) -> Vec<GroupRowsChangesetPB> {
todo!()
}

View File

@ -3,12 +3,12 @@ use crate::services::cell::insert_select_option_cell;
use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser};
use crate::services::group::action::GroupAction;
use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
use crate::services::group::controller::{
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
};
use crate::services::group::controller_impls::select_option_controller::util::*;
use crate::services::group::entities::Group;
use flowy_grid_data_model::revision::{
FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
};
use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
// MultiSelect
pub type MultiSelectGroupController = GenericGroupController<
@ -45,25 +45,14 @@ impl GroupAction for MultiSelectGroupController {
changesets
}
fn move_row_if_match(
fn move_row(
&mut self,
field_rev: &FieldRevision,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
cell_data: &Self::CellDataType,
to_row_id: &str,
mut context: MoveGroupRowContext,
) -> Vec<GroupRowsChangesetPB> {
let mut group_changeset = vec![];
self.configuration.with_mut_groups(|group| {
move_row(
group,
&mut group_changeset,
field_rev,
row_rev,
row_changeset,
cell_data,
to_row_id,
);
move_select_option_row(group, &mut group_changeset, cell_data, &mut context);
});
group_changeset
}

View File

@ -3,13 +3,13 @@ use crate::services::cell::insert_select_option_cell;
use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB};
use crate::services::group::action::GroupAction;
use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
use crate::services::group::controller::{
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
};
use crate::services::group::controller_impls::select_option_controller::util::*;
use crate::services::group::entities::Group;
use flowy_grid_data_model::revision::{
FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
};
use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
// SingleSelect
pub type SingleSelectGroupController = GenericGroupController<
@ -45,25 +45,14 @@ impl GroupAction for SingleSelectGroupController {
changesets
}
fn move_row_if_match(
fn move_row(
&mut self,
field_rev: &FieldRevision,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
cell_data: &Self::CellDataType,
to_row_id: &str,
mut context: MoveGroupRowContext,
) -> Vec<GroupRowsChangesetPB> {
let mut group_changeset = vec![];
self.configuration.with_mut_groups(|group| {
move_row(
group,
&mut group_changeset,
field_rev,
row_rev,
row_changeset,
cell_data,
to_row_id,
);
move_select_option_row(group, &mut group_changeset, cell_data, &mut context);
});
group_changeset
}

View File

@ -4,9 +4,8 @@ use crate::services::field::SelectOptionCellDataPB;
use crate::services::group::configuration::GenericGroupConfiguration;
use crate::services::group::Group;
use flowy_grid_data_model::revision::{
FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
};
use crate::services::group::controller::MoveGroupRowContext;
use flowy_grid_data_model::revision::{RowRevision, SelectOptionGroupConfigurationRevision};
pub type SelectOptionGroupConfiguration = GenericGroupConfiguration<SelectOptionGroupConfigurationRevision>;
@ -47,45 +46,60 @@ pub fn remove_row(
});
}
pub fn move_row(
pub fn move_select_option_row(
group: &mut Group,
group_changeset: &mut Vec<GroupRowsChangesetPB>,
field_rev: &FieldRevision,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
cell_data: &SelectOptionCellDataPB,
to_row_id: &str,
_cell_data: &SelectOptionCellDataPB,
context: &mut MoveGroupRowContext,
) {
cell_data.select_options.iter().for_each(|option| {
// Remove the row in which group contains the row
let is_group_contains = group.contains_row(&row_rev.id);
let to_index = group.index_of_row(to_row_id);
let MoveGroupRowContext {
row_rev,
row_changeset,
field_rev,
to_group_id,
to_row_id,
} = context;
if option.id == group.id && is_group_contains {
group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
group.remove_row(&row_rev.id);
}
let from_index = group.index_of_row(&row_rev.id);
let to_index = match to_row_id {
None => None,
Some(to_row_id) => group.index_of_row(to_row_id),
};
// Find the inserted group
if let Some(to_index) = to_index {
let row_pb = RowPB::from(row_rev);
let inserted_row = InsertedRowPB {
row: row_pb.clone(),
index: Some(to_index as i32),
};
group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row]));
if group.number_of_row() == to_index {
// Remove the row in which group contains it
if from_index.is_some() {
group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
tracing::debug!("Group:{} remove row:{}", group.id, row_rev.id);
group.remove_row(&row_rev.id);
}
if group.id == *to_group_id {
let row_pb = RowPB::from(*row_rev);
let mut inserted_row = InsertedRowPB::new(row_pb.clone());
match to_index {
None => {
group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row]));
tracing::debug!("Group:{} append row:{}", group.id, row_rev.id);
group.add_row(row_pb);
} else {
group.insert_row(to_index, row_pb);
}
Some(to_index) => {
if to_index < group.number_of_row() {
tracing::debug!("Group:{} insert row:{} at {} ", group.id, row_rev.id, to_index);
inserted_row.index = Some(to_index as i32);
group.insert_row(to_index, row_pb);
} else {
tracing::debug!("Group:{} append row:{}", group.id, row_rev.id);
group.add_row(row_pb);
}
group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row]));
}
}
// If the inserted row comes from other group, it needs to update the corresponding cell content.
if to_index.is_some() && option.id != group.id {
// Update the corresponding row's cell content.
// Update the corresponding row's cell content.
if from_index.is_none() {
tracing::debug!("Mark row:{} belong to group:{}", row_rev.id, group.id);
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev);
}
});
}
}

View File

@ -1,5 +1,6 @@
use crate::entities::{FieldType, GroupRowsChangesetPB};
use crate::services::group::configuration::GroupConfigurationReader;
use crate::services::group::controller::GroupController;
use crate::services::group::controller::{GroupController, MoveGroupRowContext};
use crate::services::group::{
CheckboxGroupConfiguration, CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController,
SelectOptionGroupConfiguration, SingleSelectGroupController,
@ -10,16 +11,13 @@ use flowy_grid_data_model::revision::{
NumberGroupConfigurationRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
TextGroupConfigurationRevision, UrlGroupConfigurationRevision,
};
use crate::entities::{FieldType, GroupRowsChangesetPB};
use std::future::Future;
use std::sync::Arc;
use tokio::sync::RwLock;
pub(crate) struct GroupService {
configuration_reader: Arc<dyn GroupConfigurationReader>,
configuration_writer: Arc<dyn GroupConfigurationWriter>,
group_controller: Option<Arc<RwLock<dyn GroupController>>>,
group_controller: Option<Box<dyn GroupController>>,
}
impl GroupService {
@ -36,19 +34,16 @@ impl GroupService {
}
pub(crate) async fn groups(&self) -> Vec<Group> {
if let Some(group_controller) = self.group_controller.as_ref() {
group_controller.read().await.groups()
} else {
vec![]
}
self.group_controller
.as_ref()
.and_then(|group_controller| Some(group_controller.groups()))
.unwrap_or(vec![])
}
pub(crate) async fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
if let Some(group_controller) = self.group_controller.as_ref() {
group_controller.read().await.get_group(group_id)
} else {
None
}
self.group_controller
.as_ref()
.and_then(|group_controller| group_controller.get_group(group_id))
}
pub(crate) async fn load_groups(
@ -58,51 +53,37 @@ impl GroupService {
) -> Option<Vec<Group>> {
let field_rev = find_group_field(field_revs)?;
let field_type: FieldType = field_rev.ty.into();
match self.make_group_controller(&field_type, &field_rev).await {
Ok(group_controller) => {
self.group_controller = group_controller;
let mut groups = vec![];
if let Some(group_action_handler) = self.group_controller.as_ref() {
let mut write_guard = group_action_handler.write().await;
groups = match write_guard.fill_groups(&row_revs, &field_rev) {
Ok(groups) => groups,
Err(e) => {
tracing::error!("Fill groups failed:{:?}", e);
vec![]
}
};
drop(write_guard);
}
Some(groups)
let mut group_controller = self.make_group_controller(&field_type, &field_rev).await.ok()??;
let groups = match group_controller.fill_groups(&row_revs, &field_rev) {
Ok(groups) => groups,
Err(e) => {
tracing::error!("Fill groups failed:{:?}", e);
vec![]
}
Err(err) => {
tracing::error!("Load group failed: {}", err);
Some(vec![])
}
}
};
self.group_controller = Some(group_controller);
Some(groups)
}
pub(crate) async fn will_create_row<F, O>(&self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F)
pub(crate) async fn will_create_row<F, O>(&mut self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F)
where
F: FnOnce(String) -> O,
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
{
if let Some(group_controller) = self.group_controller.as_ref() {
let field_id = group_controller.read().await.field_id().to_owned();
if let Some(group_controller) = self.group_controller.as_mut() {
let field_id = group_controller.field_id().to_owned();
match get_field_fn(field_id).await {
None => {}
Some(field_rev) => {
group_controller
.write()
.await
.will_create_row(row_rev, &field_rev, group_id);
group_controller.will_create_row(row_rev, &field_rev, group_id);
}
}
}
}
pub(crate) async fn did_delete_row<F, O>(
&self,
&mut self,
row_rev: &RowRevision,
get_field_fn: F,
) -> Option<Vec<GroupRowsChangesetPB>>
@ -110,11 +91,11 @@ impl GroupService {
F: FnOnce(String) -> O,
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
{
let group_controller = self.group_controller.as_ref()?;
let field_id = group_controller.read().await.field_id().to_owned();
let group_controller = self.group_controller.as_mut()?;
let field_id = group_controller.field_id().to_owned();
let field_rev = get_field_fn(field_id).await?;
match group_controller.write().await.did_delete_row(row_rev, &field_rev) {
match group_controller.did_delete_row(row_rev, &field_rev) {
Ok(changesets) => Some(changesets),
Err(e) => {
tracing::error!("Delete group data failed, {:?}", e);
@ -123,26 +104,30 @@ impl GroupService {
}
}
pub(crate) async fn did_move_row<F, O>(
&self,
pub(crate) async fn move_group_row<F, O>(
&mut self,
row_rev: &RowRevision,
row_changeset: &mut RowChangeset,
upper_row_id: &str,
to_group_id: &str,
to_row_id: Option<String>,
get_field_fn: F,
) -> Option<Vec<GroupRowsChangesetPB>>
where
F: FnOnce(String) -> O,
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
{
let group_controller = self.group_controller.as_ref()?;
let field_id = group_controller.read().await.field_id().to_owned();
let group_controller = self.group_controller.as_mut()?;
let field_id = group_controller.field_id().to_owned();
let field_rev = get_field_fn(field_id).await?;
let move_row_context = MoveGroupRowContext {
row_rev,
row_changeset,
field_rev: field_rev.as_ref(),
to_group_id,
to_row_id,
};
match group_controller
.write()
.await
.did_move_row(row_rev, row_changeset, &field_rev, upper_row_id)
{
match group_controller.move_group_row(move_row_context) {
Ok(changesets) => Some(changesets),
Err(e) => {
tracing::error!("Move group data failed, {:?}", e);
@ -153,7 +138,7 @@ impl GroupService {
#[tracing::instrument(level = "trace", skip_all)]
pub(crate) async fn did_update_row<F, O>(
&self,
&mut self,
row_rev: &RowRevision,
get_field_fn: F,
) -> Option<Vec<GroupRowsChangesetPB>>
@ -161,11 +146,11 @@ impl GroupService {
F: FnOnce(String) -> O,
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
{
let group_controller = self.group_controller.as_ref()?;
let field_id = group_controller.read().await.field_id().to_owned();
let group_controller = self.group_controller.as_mut()?;
let field_id = group_controller.field_id().to_owned();
let field_rev = get_field_fn(field_id).await?;
match group_controller.write().await.did_update_row(row_rev, &field_rev) {
match group_controller.did_update_row(row_rev, &field_rev) {
Ok(changeset) => Some(changeset),
Err(e) => {
tracing::error!("Update group data failed, {:?}", e);
@ -175,11 +160,11 @@ impl GroupService {
}
#[tracing::instrument(level = "trace", skip_all)]
pub(crate) async fn move_group(&self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
match self.group_controller.as_ref() {
pub(crate) async fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
match self.group_controller.as_mut() {
None => Ok(()),
Some(group_controller) => {
let _ = group_controller.write().await.move_group(from_group_id, to_group_id)?;
let _ = group_controller.move_group(from_group_id, to_group_id)?;
Ok(())
}
}
@ -190,8 +175,8 @@ impl GroupService {
&self,
field_type: &FieldType,
field_rev: &Arc<FieldRevision>,
) -> FlowyResult<Option<Arc<RwLock<dyn GroupController>>>> {
let mut group_controller: Option<Arc<RwLock<dyn GroupController>>> = None;
) -> FlowyResult<Option<Box<dyn GroupController>>> {
let mut group_controller: Option<Box<dyn GroupController>> = None;
match field_type {
FieldType::RichText => {
// let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
@ -210,7 +195,7 @@ impl GroupService {
)
.await?;
let controller = SingleSelectGroupController::new(field_rev, configuration).await?;
group_controller = Some(Arc::new(RwLock::new(controller)));
group_controller = Some(Box::new(controller));
}
FieldType::MultiSelect => {
let configuration = SelectOptionGroupConfiguration::new(
@ -220,7 +205,7 @@ impl GroupService {
)
.await?;
let controller = MultiSelectGroupController::new(field_rev, configuration).await?;
group_controller = Some(Arc::new(RwLock::new(controller)));
group_controller = Some(Box::new(controller));
}
FieldType::Checkbox => {
let configuration = CheckboxGroupConfiguration::new(
@ -230,7 +215,7 @@ impl GroupService {
)
.await?;
let controller = CheckboxGroupController::new(field_rev, configuration).await?;
group_controller = Some(Arc::new(RwLock::new(controller)))
group_controller = Some(Box::new(controller));
}
FieldType::URL => {
// let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);

View File

@ -1,5 +1,7 @@
use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{CreateRowParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveRowParams, RowPB};
use flowy_grid::entities::{
CreateRowParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB,
};
use flowy_grid::services::cell::insert_select_option_cell;
use flowy_grid_data_model::revision::RowChangeset;
@ -75,14 +77,16 @@ impl GridGroupTest {
} => {
let groups: Vec<GroupPB> = self.editor.load_groups().await.unwrap().items;
let from_row = groups.get(from_group_index).unwrap().rows.get(from_row_index).unwrap();
let to_row = groups.get(to_group_index).unwrap().rows.get(to_row_index).unwrap();
let params = MoveRowParams {
let to_group = groups.get(to_group_index).unwrap();
let to_row = to_group.rows.get(to_row_index).unwrap();
let params = MoveGroupRowParams {
view_id: self.inner.grid_id.clone(),
from_row_id: from_row.id.clone(),
to_row_id: to_row.id.clone(),
to_group_id: to_group.group_id.clone(),
to_row_id: Some(to_row.id.clone()),
};
self.editor.move_row(params).await.unwrap();
self.editor.move_group_row(params).await.unwrap();
}
GroupScript::AssertRow {
group_index,

View File

@ -2,7 +2,7 @@ use crate::grid::group_test::script::GridGroupTest;
use crate::grid::group_test::script::GroupScript::*;
#[tokio::test]
async fn board_init_test() {
async fn group_init_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
AssertGroupCount(3),
@ -23,7 +23,7 @@ async fn board_init_test() {
}
#[tokio::test]
async fn board_move_row_test() {
async fn group_move_row_test() {
let mut test = GridGroupTest::new().await;
let group = test.group_at_index(0).await;
let scripts = vec![
@ -48,7 +48,7 @@ async fn board_move_row_test() {
}
#[tokio::test]
async fn board_move_row_to_other_group_test() {
async fn group_move_row_to_other_group_test() {
let mut test = GridGroupTest::new().await;
let group = test.group_at_index(0).await;
let scripts = vec![
@ -76,7 +76,7 @@ async fn board_move_row_to_other_group_test() {
}
#[tokio::test]
async fn board_move_row_to_other_group_and_reorder_test() {
async fn group_move_two_row_to_other_group_test() {
let mut test = GridGroupTest::new().await;
let group = test.group_at_index(0).await;
let scripts = vec![
@ -86,15 +86,41 @@ async fn board_move_row_to_other_group_and_reorder_test() {
to_group_index: 1,
to_row_index: 1,
},
MoveRow {
from_group_index: 1,
from_row_index: 1,
to_group_index: 1,
to_row_index: 2,
AssertGroupRowCount {
group_index: 0,
row_count: 1,
},
AssertGroupRowCount {
group_index: 1,
row_count: 3,
},
AssertRow {
group_index: 1,
row_index: 2,
row_index: 1,
row: group.rows.get(0).unwrap().clone(),
},
];
test.run_scripts(scripts).await;
let group = test.group_at_index(0).await;
let scripts = vec![
MoveRow {
from_group_index: 0,
from_row_index: 0,
to_group_index: 1,
to_row_index: 1,
},
AssertGroupRowCount {
group_index: 0,
row_count: 0,
},
AssertGroupRowCount {
group_index: 1,
row_count: 4,
},
AssertRow {
group_index: 1,
row_index: 1,
row: group.rows.get(0).unwrap().clone(),
},
];
@ -102,7 +128,74 @@ async fn board_move_row_to_other_group_and_reorder_test() {
}
#[tokio::test]
async fn board_create_row_test() {
async fn group_move_row_to_other_group_and_reorder_from_up_to_down_test() {
let mut test = GridGroupTest::new().await;
let group_0 = test.group_at_index(0).await;
let group_1 = test.group_at_index(1).await;
let scripts = vec![
MoveRow {
from_group_index: 0,
from_row_index: 0,
to_group_index: 1,
to_row_index: 1,
},
AssertRow {
group_index: 1,
row_index: 1,
row: group_0.rows.get(0).unwrap().clone(),
},
];
test.run_scripts(scripts).await;
let scripts = vec![
MoveRow {
from_group_index: 1,
from_row_index: 0,
to_group_index: 1,
to_row_index: 2,
},
AssertRow {
group_index: 1,
row_index: 2,
row: group_1.rows.get(0).unwrap().clone(),
},
];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![MoveRow {
from_group_index: 0,
from_row_index: 0,
to_group_index: 1,
to_row_index: 1,
}];
test.run_scripts(scripts).await;
let group = test.group_at_index(1).await;
let scripts = vec![
AssertGroupRowCount {
group_index: 1,
row_count: 3,
},
MoveRow {
from_group_index: 1,
from_row_index: 2,
to_group_index: 1,
to_row_index: 0,
},
AssertRow {
group_index: 1,
row_index: 0,
row: group.rows.get(2).unwrap().clone(),
},
];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn group_create_row_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
CreateRow { group_index: 0 },
@ -121,7 +214,7 @@ async fn board_create_row_test() {
}
#[tokio::test]
async fn board_delete_row_test() {
async fn group_delete_row_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
DeleteRow {
@ -137,7 +230,7 @@ async fn board_delete_row_test() {
}
#[tokio::test]
async fn board_delete_all_row_test() {
async fn group_delete_all_row_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
DeleteRow {
@ -157,7 +250,7 @@ async fn board_delete_all_row_test() {
}
#[tokio::test]
async fn board_update_row_test() {
async fn group_update_row_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
// Update the row at 0 in group0 by setting the row's group field data
@ -179,7 +272,7 @@ async fn board_update_row_test() {
}
#[tokio::test]
async fn board_reorder_group_test() {
async fn group_reorder_group_test() {
let mut test = GridGroupTest::new().await;
let scripts = vec![
// Update the row at 0 in group0 by setting the row's group field data
@ -201,7 +294,7 @@ async fn board_reorder_group_test() {
}
#[tokio::test]
async fn board_move_group_test() {
async fn group_move_group_test() {
let mut test = GridGroupTest::new().await;
let group_0 = test.group_at_index(0).await;
let group_1 = test.group_at_index(1).await;

View File

@ -76,7 +76,8 @@ where
Some(objects_by_field_id)
}
pub fn insert_object(&mut self, field_id: &str, field_type: &FieldTypeRevision, object: T) {
/// add object to the end of the list
pub fn add_object(&mut self, field_id: &str, field_type: &FieldTypeRevision, object: T) {
let object_rev_map = self
.inner
.entry(field_id.to_string())
@ -88,7 +89,7 @@ where
.push(Arc::new(object))
}
pub fn remove_all(&mut self) {
pub fn clear(&mut self) {
self.inner.clear()
}
}

View File

@ -155,3 +155,27 @@ impl std::default::Default for DateCondition {
DateCondition::Relative
}
}
#[cfg(test)]
mod tests {
use crate::revision::{GroupConfigurationRevision, SelectOptionGroupConfigurationRevision};
#[test]
fn group_configuration_serde_test() {
let content = SelectOptionGroupConfigurationRevision { hide_empty: false };
let rev = GroupConfigurationRevision::new("1".to_owned(), 2, content).unwrap();
let json = serde_json::to_string(&rev).unwrap();
let rev: GroupConfigurationRevision = serde_json::from_str(&json).unwrap();
let _content: SelectOptionGroupConfigurationRevision = serde_json::from_str(&rev.content).unwrap();
}
#[test]
fn group_configuration_serde_test2() {
let content = SelectOptionGroupConfigurationRevision { hide_empty: false };
let content_json = serde_json::to_string(&content).unwrap();
let rev = GroupConfigurationRevision::new("1".to_owned(), 2, content).unwrap();
assert_eq!(rev.content, content_json);
}
}

View File

@ -60,7 +60,9 @@ impl GridViewRevisionPad {
group_rev: GroupConfigurationRevision,
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
self.modify(|view| {
view.groups.insert_object(field_id, field_type, group_rev);
// Only save one group
view.groups.clear();
view.groups.add_object(field_id, field_type, group_rev);
Ok(Some(()))
})
}
@ -127,7 +129,7 @@ impl GridViewRevisionPad {
filter_rev: FilterConfigurationRevision,
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
self.modify(|view| {
view.filters.insert_object(field_id, field_type, filter_rev);
view.filters.add_object(field_id, field_type, filter_rev);
Ok(Some(()))
})
}
@ -166,8 +168,6 @@ impl GridViewRevisionPad {
None => Ok(None),
Some(delta) => {
self.delta = self.delta.compose(&delta)?;
tracing::info!("GridView: {:?}", delta);
let md5 = md5(&self.delta.json_bytes());
Ok(Some(GridViewRevisionChangeset { delta, md5 }))
}