chore: reload after delete or insert row

This commit is contained in:
appflowy 2022-04-10 14:24:12 +08:00
parent 420b8ca05d
commit f01d3250ae
17 changed files with 533 additions and 117 deletions

View File

@ -34,8 +34,6 @@ class GridBloc extends Bloc<GridEvent, GridState> {
createRow: (_CreateRow value) {
_gridService.createRow(gridId: view.id);
},
delete: (_Delete value) {},
rename: (_Rename value) {},
updateDesc: (_Desc value) {},
didReceiveRowUpdate: (_DidReceiveRowUpdate value) {
emit(state.copyWith(rows: value.rows));
@ -71,14 +69,18 @@ class GridBloc extends Bloc<GridEvent, GridState> {
_gridListener.rowsUpdateNotifier.addPublishListener((result) {
result.fold((gridBlockChangeset) {
for (final changeset in gridBlockChangeset) {
if (changeset.insertedRows.isNotEmpty) {}
if (changeset.deletedRows.isNotEmpty) {}
if (changeset.updatedRows.isNotEmpty) {}
if (changeset.insertedRows.isNotEmpty) {
_insertRows(changeset.insertedRows);
}
// add(GridEvent.didReceiveRowUpdate(_buildRows(blockOrders)));
if (changeset.deletedRows.isNotEmpty) {
_deleteRows(changeset.deletedRows);
}
if (changeset.updatedRows.isNotEmpty) {
_updateRows(changeset.updatedRows);
}
}
}, (err) => Log.error(err));
});
_gridListener.start();
@ -111,29 +113,50 @@ class GridBloc extends Bloc<GridEvent, GridState> {
);
}
List<GridBlockRow> _buildRows(List<GridBlockOrder> blockOrders) {
List<GridBlockRow> rows = [];
for (final blockOrder in blockOrders) {
rows.addAll(blockOrder.rowOrders.map(
(rowOrder) => GridBlockRow(
gridId: view.id,
rowId: rowOrder.rowId,
height: rowOrder.height.toDouble(),
),
));
void _deleteRows(List<RowOrder> deletedRows) {
final List<RowOrder> rows = List.from(state.rows);
rows.retainWhere(
(row) => deletedRows.where((deletedRow) => deletedRow.rowId == row.rowId).isEmpty,
);
add(GridEvent.didReceiveRowUpdate(rows));
}
return rows;
void _insertRows(List<IndexRowOrder> createdRows) {
final List<RowOrder> rows = List.from(state.rows);
for (final newRow in createdRows) {
if (newRow.hasIndex()) {
rows.insert(newRow.index, newRow.rowOrder);
} else {
rows.add(newRow.rowOrder);
}
}
add(GridEvent.didReceiveRowUpdate(rows));
}
void _updateRows(List<RowOrder> updatedRows) {
final List<RowOrder> rows = List.from(state.rows);
for (final updatedRow in updatedRows) {
final index = rows.indexWhere((row) => row.rowId == updatedRow.rowId);
if (index != -1) {
rows.removeAt(index);
rows.insert(index, updatedRow);
}
}
add(GridEvent.didReceiveRowUpdate(rows));
}
List<RowOrder> _buildRows(List<GridBlockOrder> blockOrders) {
return blockOrders.expand((blockOrder) => blockOrder.rowOrders).toList();
}
}
@freezed
class GridEvent with _$GridEvent {
const factory GridEvent.initial() = InitialGrid;
const factory GridEvent.rename(String gridId, String name) = _Rename;
const factory GridEvent.updateDesc(String gridId, String desc) = _Desc;
const factory GridEvent.delete(String gridId) = _Delete;
const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.didReceiveRowUpdate(List<GridBlockRow> rows) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveRowUpdate(List<RowOrder> rows) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
}
@ -142,7 +165,7 @@ class GridState with _$GridState {
const factory GridState({
required GridLoadingState loadingState,
required List<Field> fields,
required List<GridBlockRow> rows,
required List<RowOrder> rows,
required Option<Grid> grid,
}) = _GridState;

View File

@ -65,21 +65,12 @@ class RowData with _$RowData {
required double height,
}) = _RowData;
factory RowData.fromBlockRow(GridBlockRow row, List<Field> fields) {
factory RowData.fromBlockRow(String gridId, RowOrder row, List<Field> fields) {
return RowData(
gridId: row.gridId,
gridId: gridId,
rowId: row.rowId,
fields: fields,
height: row.height,
height: row.height.toDouble(),
);
}
}
@freezed
class GridBlockRow with _$GridBlockRow {
const factory GridBlockRow({
required String gridId,
required String rowId,
required double height,
}) = _GridBlockRow;
}

View File

@ -73,7 +73,7 @@ class FlowyGrid extends StatefulWidget {
class _FlowyGridState extends State<FlowyGrid> {
final _scrollController = GridScrollController();
final _key = GlobalKey<SliverAnimatedListState>();
// final _key = GlobalKey<SliverAnimatedListState>();
@override
void dispose() {
@ -105,7 +105,7 @@ class _FlowyGridState extends State<FlowyGrid> {
slivers: [
_renderToolbar(gridId),
GridHeader(gridId: gridId, fields: List.from(state.fields)),
_renderRows(context),
_renderRows(gridId: gridId, context: context),
const GridFooter(),
],
),
@ -147,7 +147,7 @@ class _FlowyGridState extends State<FlowyGrid> {
);
}
Widget _renderRows(BuildContext context) {
Widget _renderRows({required String gridId, required BuildContext context}) {
return BlocBuilder<GridBloc, GridState>(
buildWhen: (previous, current) {
final rowChanged = previous.rows.length != current.rows.length;
@ -160,7 +160,7 @@ class _FlowyGridState extends State<FlowyGrid> {
(context, index) {
final blockRow = context.read<GridBloc>().state.rows[index];
final fields = context.read<GridBloc>().state.fields;
final rowData = RowData.fromBlockRow(blockRow, fields);
final rowData = RowData.fromBlockRow(gridId, blockRow, fields);
return GridRowWidget(data: rowData, key: ValueKey(rowData.rowId));
},
childCount: context.read<GridBloc>().state.rows.length,

View File

@ -84,10 +84,16 @@ class _RowActionCell extends StatelessWidget {
return SizedBox(
height: GridSize.typeOptionItemHeight,
child: FlowyButton(
text: FlowyText.medium(action.title(), fontSize: 12),
text: FlowyText.medium(
action.title(),
fontSize: 12,
color: action.enable() ? theme.textColor : theme.shader3,
),
hoverColor: theme.hover,
onTap: () {
if (action.enable()) {
action.performAction(context);
}
onDismissed();
},
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
@ -120,13 +126,22 @@ extension _RowActionExtension on _RowAction {
}
}
bool enable() {
switch (this) {
case _RowAction.duplicate:
return false;
case _RowAction.delete:
return true;
}
}
void performAction(BuildContext context) {
switch (this) {
case _RowAction.duplicate:
// context.read<RowActionSheetBloc>().add(const RowActionSheetEvent.duplicateRow());
context.read<RowActionSheetBloc>().add(const RowActionSheetEvent.duplicateRow());
break;
case _RowAction.delete:
// context.read<RowActionSheetBloc>().add(const RowActionSheetEvent.deleteRow());
context.read<RowActionSheetBloc>().add(const RowActionSheetEvent.deleteRow());
break;
}
}

View File

@ -858,7 +858,7 @@ class GridBlockOrder extends $pb.GeneratedMessage {
class GridBlockOrderChangeset extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockOrderChangeset', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
..pc<RowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
..pc<IndexRowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create)
..pc<RowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
..pc<RowOrder>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
..hasRequiredFields = false
@ -867,7 +867,7 @@ class GridBlockOrderChangeset extends $pb.GeneratedMessage {
GridBlockOrderChangeset._() : super();
factory GridBlockOrderChangeset({
$core.String? blockId,
$core.Iterable<RowOrder>? insertedRows,
$core.Iterable<IndexRowOrder>? insertedRows,
$core.Iterable<RowOrder>? deletedRows,
$core.Iterable<RowOrder>? updatedRows,
}) {
@ -917,7 +917,7 @@ class GridBlockOrderChangeset extends $pb.GeneratedMessage {
void clearBlockId() => clearField(1);
@$pb.TagNumber(2)
$core.List<RowOrder> get insertedRows => $_getList(1);
$core.List<IndexRowOrder> get insertedRows => $_getList(1);
@$pb.TagNumber(3)
$core.List<RowOrder> get deletedRows => $_getList(2);
@ -926,6 +926,82 @@ class GridBlockOrderChangeset extends $pb.GeneratedMessage {
$core.List<RowOrder> get updatedRows => $_getList(3);
}
enum IndexRowOrder_OneOfIndex {
index_,
notSet
}
class IndexRowOrder extends $pb.GeneratedMessage {
static const $core.Map<$core.int, IndexRowOrder_OneOfIndex> _IndexRowOrder_OneOfIndexByTag = {
2 : IndexRowOrder_OneOfIndex.index_,
0 : IndexRowOrder_OneOfIndex.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IndexRowOrder', createEmptyInstance: create)
..oo(0, [2])
..aOM<RowOrder>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrder', subBuilder: RowOrder.create)
..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'index', $pb.PbFieldType.O3)
..hasRequiredFields = false
;
IndexRowOrder._() : super();
factory IndexRowOrder({
RowOrder? rowOrder,
$core.int? index,
}) {
final _result = create();
if (rowOrder != null) {
_result.rowOrder = rowOrder;
}
if (index != null) {
_result.index = index;
}
return _result;
}
factory IndexRowOrder.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory IndexRowOrder.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
IndexRowOrder clone() => IndexRowOrder()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
IndexRowOrder copyWith(void Function(IndexRowOrder) updates) => super.copyWith((message) => updates(message as IndexRowOrder)) as IndexRowOrder; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static IndexRowOrder create() => IndexRowOrder._();
IndexRowOrder createEmptyInstance() => create();
static $pb.PbList<IndexRowOrder> createRepeated() => $pb.PbList<IndexRowOrder>();
@$core.pragma('dart2js:noInline')
static IndexRowOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<IndexRowOrder>(create);
static IndexRowOrder? _defaultInstance;
IndexRowOrder_OneOfIndex whichOneOfIndex() => _IndexRowOrder_OneOfIndexByTag[$_whichOneof(0)]!;
void clearOneOfIndex() => clearField($_whichOneof(0));
@$pb.TagNumber(1)
RowOrder get rowOrder => $_getN(0);
@$pb.TagNumber(1)
set rowOrder(RowOrder v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasRowOrder() => $_has(0);
@$pb.TagNumber(1)
void clearRowOrder() => clearField(1);
@$pb.TagNumber(1)
RowOrder ensureRowOrder() => $_ensure(0);
@$pb.TagNumber(2)
$core.int get index => $_getIZ(1);
@$pb.TagNumber(2)
set index($core.int v) { $_setSignedInt32(1, v); }
@$pb.TagNumber(2)
$core.bool hasIndex() => $_has(1);
@$pb.TagNumber(2)
void clearIndex() => clearField(2);
}
class GridBlock extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlock', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')

View File

@ -176,14 +176,28 @@ const GridBlockOrderChangeset$json = const {
'1': 'GridBlockOrderChangeset',
'2': const [
const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.RowOrder', '10': 'insertedRows'},
const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.IndexRowOrder', '10': 'insertedRows'},
const {'1': 'deleted_rows', '3': 3, '4': 3, '5': 11, '6': '.RowOrder', '10': 'deletedRows'},
const {'1': 'updated_rows', '3': 4, '4': 3, '5': 11, '6': '.RowOrder', '10': 'updatedRows'},
],
};
/// Descriptor for `GridBlockOrderChangeset`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List gridBlockOrderChangesetDescriptor = $convert.base64Decode('ChdHcmlkQmxvY2tPcmRlckNoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIuCg1pbnNlcnRlZF9yb3dzGAIgAygLMgkuUm93T3JkZXJSDGluc2VydGVkUm93cxIsCgxkZWxldGVkX3Jvd3MYAyADKAsyCS5Sb3dPcmRlclILZGVsZXRlZFJvd3MSLAoMdXBkYXRlZF9yb3dzGAQgAygLMgkuUm93T3JkZXJSC3VwZGF0ZWRSb3dz');
final $typed_data.Uint8List gridBlockOrderChangesetDescriptor = $convert.base64Decode('ChdHcmlkQmxvY2tPcmRlckNoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M=');
@$core.Deprecated('Use indexRowOrderDescriptor instead')
const IndexRowOrder$json = const {
'1': 'IndexRowOrder',
'2': const [
const {'1': 'row_order', '3': 1, '4': 1, '5': 11, '6': '.RowOrder', '10': 'rowOrder'},
const {'1': 'index', '3': 2, '4': 1, '5': 5, '9': 0, '10': 'index'},
],
'8': const [
const {'1': 'one_of_index'},
],
};
/// Descriptor for `IndexRowOrder`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List indexRowOrderDescriptor = $convert.base64Decode('Cg1JbmRleFJvd09yZGVyEiYKCXJvd19vcmRlchgBIAEoCzIJLlJvd09yZGVyUghyb3dPcmRlchIWCgVpbmRleBgCIAEoBUgAUgVpbmRleEIOCgxvbmVfb2ZfaW5kZXg=');
@$core.Deprecated('Use gridBlockDescriptor instead')
const GridBlock$json = const {
'1': 'GridBlock',

View File

@ -42,17 +42,30 @@ impl ClientGridBlockMetaEditor {
})
}
pub(crate) async fn create_row(&self, row: RowMeta, start_row_id: Option<String>) -> FlowyResult<i32> {
/// return current number of rows and the inserted index. The inserted index will be None if the start_row_id is None
pub(crate) async fn create_row(
&self,
row: RowMeta,
start_row_id: Option<String>,
) -> FlowyResult<(i32, Option<i32>)> {
let mut row_count = 0;
let mut row_index = None;
let _ = self
.modify(|pad| {
if let Some(start_row_id) = start_row_id.as_ref() {
match pad.index_of_row(start_row_id) {
None => {}
Some(index) => row_index = Some(index + 1),
}
}
let change = pad.add_row_meta(row, start_row_id)?;
row_count = pad.number_of_rows();
Ok(change)
})
.await?;
Ok(row_count)
Ok((row_count, row_index))
}
pub async fn delete_rows(&self, ids: Vec<Cow<'_, String>>) -> FlowyResult<i32> {
@ -89,7 +102,10 @@ impl ClientGridBlockMetaEditor {
Ok(cell_metas)
}
pub async fn get_row_orders(&self, row_ids: Option<Vec<Cow<'_, String>>>) -> FlowyResult<Vec<RowOrder>> {
pub async fn get_row_orders<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<RowOrder>>
where
T: AsRef<str> + ToOwned + ?Sized,
{
let row_orders = self
.pad
.read()

View File

@ -9,7 +9,7 @@ use dashmap::DashMap;
use flowy_error::FlowyResult;
use flowy_grid_data_model::entities::{
CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
GridBlockOrder, GridBlockOrderChangeset, RowMeta, RowMetaChangeset, RowOrder,
GridBlockOrderChangeset, IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
};
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_revision::{RevisionManager, RevisionPersistence};
@ -70,11 +70,13 @@ impl GridBlockMetaEditorManager {
) -> FlowyResult<i32> {
let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
let editor = self.get_editor(&row_meta.block_id).await?;
let row_order = RowOrder::from(&row_meta);
let row_count = editor.create_row(row_meta, start_row_id).await?;
let mut index_row_order = IndexRowOrder::from(&row_meta);
let (row_count, row_index) = editor.create_row(row_meta, start_row_id).await?;
index_row_order.index = row_index;
let _ = self
.notify_block_did_update_row(GridBlockOrderChangeset::from_update(block_id, vec![row_order]))
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(block_id, vec![index_row_order]))
.await?;
Ok(row_count)
}
@ -90,12 +92,13 @@ impl GridBlockMetaEditorManager {
let mut row_count = 0;
for row in row_metas {
let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
inserted_row_orders.push(RowOrder::from(&row));
row_count = editor.create_row(row, None).await?;
inserted_row_orders.push(IndexRowOrder::from(&row));
row_count = editor.create_row(row, None).await?.0;
}
changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
let _ = self
.notify_block_did_update_row(GridBlockOrderChangeset::from_insert(&block_id, inserted_row_orders))
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(&block_id, inserted_row_orders))
.await?;
}
@ -114,13 +117,26 @@ impl GridBlockMetaEditorManager {
None => {}
Some(row_order) => {
let block_order_changeset = GridBlockOrderChangeset::from_update(&editor.block_id, vec![row_order]);
let _ = self.notify_block_did_update_row(block_order_changeset).await?;
let _ = self.notify_did_update_grid_rows(block_order_changeset).await?;
}
}
Ok(())
}
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let row_id = row_id.to_owned();
let block_id = self.persistence.get_block_id(&row_id)?;
let editor = self.get_editor(&block_id).await?;
let row_orders = editor.get_row_orders(Some(vec![Cow::Borrowed(&row_id)])).await?;
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
let _ = self
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_delete(&block_id, row_orders))
.await?;
Ok(())
}
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockMetaChangeset>> {
let mut changesets = vec![];
for block_order in group_row_orders(row_orders) {
@ -167,7 +183,7 @@ impl GridBlockMetaEditorManager {
pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult<Vec<RowOrder>> {
let editor = self.get_editor(block_id).await?;
editor.get_row_orders(None).await
editor.get_row_orders::<&str>(None).await
}
pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
@ -197,7 +213,7 @@ impl GridBlockMetaEditorManager {
Ok(block_cell_metas)
}
async fn notify_block_did_update_row(&self, changeset: GridBlockOrderChangeset) -> FlowyResult<()> {
async fn notify_did_update_grid_rows(&self, changeset: GridBlockOrderChangeset) -> FlowyResult<()> {
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridBlock)
.payload(changeset)
.send();

View File

@ -70,7 +70,7 @@ impl CellDataOperation for DateTypeOption {
) -> Result<String, FlowyError> {
let changeset = changeset.into();
if changeset.parse::<f64>().is_err() || changeset.parse::<i64>().is_err() {
return Err(FlowyError::internal().context(format!("Parse {} failed", changeset.to_string())));
return Err(FlowyError::internal().context(format!("Parse {} failed", changeset)));
};
Ok(TypeOptionCellData::new(changeset, self.field_type()).json())

View File

@ -469,7 +469,7 @@ mod tests {
let single_select = SingleSelectTypeOptionBuilder::default()
.option(google_option.clone())
.option(facebook_option.clone())
.option(twitter_option.clone());
.option(twitter_option);
let field_meta = FieldBuilder::new(single_select)
.name("Platform")
@ -478,7 +478,7 @@ mod tests {
let type_option = SingleSelectTypeOption::from(&field_meta);
let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR);
let data = SelectOptionCellChangeset::from_insert(&option_ids).cell_data();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(type_option.decode_cell_data(cell_data, &field_meta), google_option.name,);
@ -511,7 +511,7 @@ mod tests {
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(google_option.clone())
.option(facebook_option.clone())
.option(twitter_option.clone());
.option(twitter_option);
let field_meta = FieldBuilder::new(multi_select)
.name("Platform")
@ -525,7 +525,7 @@ mod tests {
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option.decode_cell_data(cell_data, &field_meta),
vec![google_option.name.clone(), facebook_option.name.clone()].join(SELECTION_IDS_SEPARATOR),
vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR),
);
let data = SelectOptionCellChangeset::from_insert(&google_option.id).cell_data();

View File

@ -268,11 +268,12 @@ impl ClientGridEditor {
}
}
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
todo!()
let _ = self.block_meta_manager.delete_row(row_id).await?;
Ok(())
}
pub async fn duplicate_row(&self, row_id: &str) -> FlowyResult<()> {
todo!()
pub async fn duplicate_row(&self, _row_id: &str) -> FlowyResult<()> {
Ok(())
}
pub async fn get_cell(&self, params: &CellIdentifier) -> Option<Cell> {
@ -429,7 +430,7 @@ impl ClientGridEditor {
debug_assert!(field_metas.len() == 1);
if let Some(field_meta) = field_metas.pop() {
send_dart_notification(&field_id, GridNotification::DidUpdateField)
send_dart_notification(field_id, GridNotification::DidUpdateField)
.payload(field_meta)
.send();
}

View File

@ -4,7 +4,7 @@ use flowy_grid_data_model::entities::{
Cell, CellMeta, FieldMeta, GridBlock, GridBlockOrder, RepeatedGridBlock, Row, RowMeta, RowOrder,
};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;

View File

@ -4,7 +4,7 @@ use chrono::NaiveDateTime;
use flowy_grid::services::field::{
MultiSelectTypeOption, SelectOption, SelectOptionCellChangeset, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
};
use flowy_grid::services::row::{apply_cell_data_changeset, decode_cell_data, CellDataOperation, CreateRowMetaBuilder};
use flowy_grid::services::row::{decode_cell_data, CreateRowMetaBuilder};
use flowy_grid_data_model::entities::{
CellMetaChangeset, FieldChangesetParams, FieldType, GridBlockMeta, GridBlockMetaChangeset, RowMetaChangeset,
TypeOptionDataEntry,

View File

@ -280,7 +280,7 @@ pub struct GridBlockOrderChangeset {
pub block_id: String,
#[pb(index = 2)]
pub inserted_rows: Vec<RowOrder>,
pub inserted_rows: Vec<IndexRowOrder>,
#[pb(index = 3)]
pub deleted_rows: Vec<RowOrder>,
@ -289,8 +289,30 @@ pub struct GridBlockOrderChangeset {
pub updated_rows: Vec<RowOrder>,
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct IndexRowOrder {
#[pb(index = 1)]
pub row_order: RowOrder,
#[pb(index = 2, one_of)]
pub index: Option<i32>,
}
impl std::convert::From<RowOrder> for IndexRowOrder {
fn from(row_order: RowOrder) -> Self {
Self { row_order, index: None }
}
}
impl std::convert::From<&RowMeta> for IndexRowOrder {
fn from(row: &RowMeta) -> Self {
let row_order = RowOrder::from(row);
Self::from(row_order)
}
}
impl GridBlockOrderChangeset {
pub fn from_insert(block_id: &str, inserted_rows: Vec<RowOrder>) -> Self {
pub fn from_insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
Self {
block_id: block_id.to_owned(),
inserted_rows,

View File

@ -2923,7 +2923,7 @@ impl ::protobuf::reflect::ProtobufValue for GridBlockOrder {
pub struct GridBlockOrderChangeset {
// message fields
pub block_id: ::std::string::String,
pub inserted_rows: ::protobuf::RepeatedField<RowOrder>,
pub inserted_rows: ::protobuf::RepeatedField<IndexRowOrder>,
pub deleted_rows: ::protobuf::RepeatedField<RowOrder>,
pub updated_rows: ::protobuf::RepeatedField<RowOrder>,
// special fields
@ -2968,10 +2968,10 @@ impl GridBlockOrderChangeset {
::std::mem::replace(&mut self.block_id, ::std::string::String::new())
}
// repeated .RowOrder inserted_rows = 2;
// repeated .IndexRowOrder inserted_rows = 2;
pub fn get_inserted_rows(&self) -> &[RowOrder] {
pub fn get_inserted_rows(&self) -> &[IndexRowOrder] {
&self.inserted_rows
}
pub fn clear_inserted_rows(&mut self) {
@ -2979,17 +2979,17 @@ impl GridBlockOrderChangeset {
}
// Param is passed by value, moved
pub fn set_inserted_rows(&mut self, v: ::protobuf::RepeatedField<RowOrder>) {
pub fn set_inserted_rows(&mut self, v: ::protobuf::RepeatedField<IndexRowOrder>) {
self.inserted_rows = v;
}
// Mutable pointer to the field.
pub fn mut_inserted_rows(&mut self) -> &mut ::protobuf::RepeatedField<RowOrder> {
pub fn mut_inserted_rows(&mut self) -> &mut ::protobuf::RepeatedField<IndexRowOrder> {
&mut self.inserted_rows
}
// Take field
pub fn take_inserted_rows(&mut self) -> ::protobuf::RepeatedField<RowOrder> {
pub fn take_inserted_rows(&mut self) -> ::protobuf::RepeatedField<IndexRowOrder> {
::std::mem::replace(&mut self.inserted_rows, ::protobuf::RepeatedField::new())
}
@ -3174,7 +3174,7 @@ impl ::protobuf::Message for GridBlockOrderChangeset {
|m: &GridBlockOrderChangeset| { &m.block_id },
|m: &mut GridBlockOrderChangeset| { &mut m.block_id },
));
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowOrder>>(
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<IndexRowOrder>>(
"inserted_rows",
|m: &GridBlockOrderChangeset| { &m.inserted_rows },
|m: &mut GridBlockOrderChangeset| { &mut m.inserted_rows },
@ -3225,6 +3225,238 @@ impl ::protobuf::reflect::ProtobufValue for GridBlockOrderChangeset {
}
}
#[derive(PartialEq,Clone,Default)]
pub struct IndexRowOrder {
// message fields
pub row_order: ::protobuf::SingularPtrField<RowOrder>,
// message oneof groups
pub one_of_index: ::std::option::Option<IndexRowOrder_oneof_one_of_index>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a IndexRowOrder {
fn default() -> &'a IndexRowOrder {
<IndexRowOrder as ::protobuf::Message>::default_instance()
}
}
#[derive(Clone,PartialEq,Debug)]
pub enum IndexRowOrder_oneof_one_of_index {
index(i32),
}
impl IndexRowOrder {
pub fn new() -> IndexRowOrder {
::std::default::Default::default()
}
// .RowOrder row_order = 1;
pub fn get_row_order(&self) -> &RowOrder {
self.row_order.as_ref().unwrap_or_else(|| <RowOrder as ::protobuf::Message>::default_instance())
}
pub fn clear_row_order(&mut self) {
self.row_order.clear();
}
pub fn has_row_order(&self) -> bool {
self.row_order.is_some()
}
// Param is passed by value, moved
pub fn set_row_order(&mut self, v: RowOrder) {
self.row_order = ::protobuf::SingularPtrField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_row_order(&mut self) -> &mut RowOrder {
if self.row_order.is_none() {
self.row_order.set_default();
}
self.row_order.as_mut().unwrap()
}
// Take field
pub fn take_row_order(&mut self) -> RowOrder {
self.row_order.take().unwrap_or_else(|| RowOrder::new())
}
// int32 index = 2;
pub fn get_index(&self) -> i32 {
match self.one_of_index {
::std::option::Option::Some(IndexRowOrder_oneof_one_of_index::index(v)) => v,
_ => 0,
}
}
pub fn clear_index(&mut self) {
self.one_of_index = ::std::option::Option::None;
}
pub fn has_index(&self) -> bool {
match self.one_of_index {
::std::option::Option::Some(IndexRowOrder_oneof_one_of_index::index(..)) => true,
_ => false,
}
}
// Param is passed by value, moved
pub fn set_index(&mut self, v: i32) {
self.one_of_index = ::std::option::Option::Some(IndexRowOrder_oneof_one_of_index::index(v))
}
}
impl ::protobuf::Message for IndexRowOrder {
fn is_initialized(&self) -> bool {
for v in &self.row_order {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.row_order)?;
},
2 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
self.one_of_index = ::std::option::Option::Some(IndexRowOrder_oneof_one_of_index::index(is.read_int32()?));
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if let Some(ref v) = self.row_order.as_ref() {
let len = v.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
}
if let ::std::option::Option::Some(ref v) = self.one_of_index {
match v {
&IndexRowOrder_oneof_one_of_index::index(v) => {
my_size += ::protobuf::rt::value_size(2, v, ::protobuf::wire_format::WireTypeVarint);
},
};
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if let Some(ref v) = self.row_order.as_ref() {
os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
os.write_raw_varint32(v.get_cached_size())?;
v.write_to_with_cached_sizes(os)?;
}
if let ::std::option::Option::Some(ref v) = self.one_of_index {
match v {
&IndexRowOrder_oneof_one_of_index::index(v) => {
os.write_int32(2, v)?;
},
};
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> IndexRowOrder {
IndexRowOrder::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowOrder>>(
"row_order",
|m: &IndexRowOrder| { &m.row_order },
|m: &mut IndexRowOrder| { &mut m.row_order },
));
fields.push(::protobuf::reflect::accessor::make_singular_i32_accessor::<_>(
"index",
IndexRowOrder::has_index,
IndexRowOrder::get_index,
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<IndexRowOrder>(
"IndexRowOrder",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static IndexRowOrder {
static instance: ::protobuf::rt::LazyV2<IndexRowOrder> = ::protobuf::rt::LazyV2::INIT;
instance.get(IndexRowOrder::new)
}
}
impl ::protobuf::Clear for IndexRowOrder {
fn clear(&mut self) {
self.row_order.clear();
self.one_of_index = ::std::option::Option::None;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for IndexRowOrder {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for IndexRowOrder {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct GridBlock {
// message fields
@ -5640,34 +5872,36 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\x05\
items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"U\n\x0eGridBlockOrder\
\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12(\n\nrow_orders\
\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\"\xc0\x01\n\x17GridBlockOr\
derChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12.\n\
\rinserted_rows\x18\x02\x20\x03(\x0b2\t.RowOrderR\x0cinsertedRows\x12,\n\
\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x12,\n\
\x0cupdated_rows\x18\x04\x20\x03(\x0b2\t.RowOrderR\x0bupdatedRows\"E\n\t\
GridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\
\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08\
field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\
\x01(\tR\x07content\"\x8f\x01\n\x14CellNotificationData\x12\x17\n\x07gri\
d_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01\
(\tR\x07fieldId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\x12\x1a\
\n\x07content\x18\x04\x20\x01(\tH\0R\x07contentB\x10\n\x0eone_of_content\
\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\
\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\t\
R\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05va\
lue\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\
\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\
idId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\
\x13one_of_start_row_id\"\xb6\x01\n\x12CreateFieldPayload\x12\x17\n\x07g\
rid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\
\x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\
\x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\
\x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\x11QueryFieldPaylo\
ad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_or\
ders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\
\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06g\
ridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\
\x0bblockOrdersb\x06proto3\
\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\"\xc5\x01\n\x17GridBlockOr\
derChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x123\n\
\rinserted_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cinsertedRows\
\x12,\n\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\
\x12,\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\t.RowOrderR\x0bupdatedRows\
\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\
\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\
\x0cone_of_index\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02\
id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\
\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\
\x07content\x18\x02\x20\x01(\tR\x07content\"\x8f\x01\n\x14CellNotificati\
onData\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08f\
ield_id\x18\x02\x20\x01(\tR\x07fieldId\x12\x15\n\x06row_id\x18\x03\x20\
\x01(\tR\x05rowId\x12\x1a\n\x07content\x18\x04\x20\x01(\tH\0R\x07content\
B\x10\n\x0eone_of_content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\
\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\
\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05va\
lue\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\
\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid\
_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\
\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12Cre\
ateFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\
\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type\
_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_fie\
ld_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_fiel\
d_id\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFiel\
dOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_\
id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\
\x0b2\x0f.GridBlockOrderR\x0bblockOrdersb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -61,10 +61,14 @@ message GridBlockOrder {
}
message GridBlockOrderChangeset {
string block_id = 1;
repeated RowOrder inserted_rows = 2;
repeated IndexRowOrder inserted_rows = 2;
repeated RowOrder deleted_rows = 3;
repeated RowOrder updated_rows = 4;
}
message IndexRowOrder {
RowOrder row_order = 1;
oneof one_of_index { int32 index = 2; };
}
message GridBlock {
string id = 1;
repeated RowOrder row_orders = 2;

View File

@ -48,16 +48,13 @@ impl GridBlockMetaPad {
) -> CollaborateResult<Option<GridBlockMetaChange>> {
self.modify(|rows| {
if let Some(start_row_id) = start_row_id {
if start_row_id.is_empty() {
rows.insert(0, Arc::new(row));
return Ok(Some(()));
}
if !start_row_id.is_empty() {
if let Some(index) = rows.iter().position(|row| row.id == start_row_id) {
rows.insert(index + 1, Arc::new(row));
return Ok(Some(()));
}
}
}
rows.push(Arc::new(row));
Ok(Some(()))
@ -121,6 +118,13 @@ impl GridBlockMetaPad {
self.rows.len() as i32
}
pub fn index_of_row(&self, row_id: &str) -> Option<i32> {
self.rows
.iter()
.position(|row| row.id == row_id)
.map(|index| index as i32)
}
pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockMetaChange>> {
let row_id = changeset.row_id.clone();
self.modify_row(&row_id, |row| {