mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #882 from AppFlowy-IO/feat/board_configuration
Feat/board configuration
This commit is contained in:
commit
5e6b9496fc
2
frontend/.vscode/launch.json
vendored
2
frontend/.vscode/launch.json
vendored
@ -29,7 +29,7 @@
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
"RUST_LOG": "debug"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
@ -3,7 +3,6 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/board_card.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ impl FlowyError {
|
||||
static_flowy_error!(user_not_exist, ErrorCode::UserNotExist);
|
||||
static_flowy_error!(text_too_long, ErrorCode::TextTooLong);
|
||||
static_flowy_error!(invalid_data, ErrorCode::InvalidData);
|
||||
static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds);
|
||||
}
|
||||
|
||||
impl std::convert::From<ErrorCode> for FlowyError {
|
||||
|
@ -11,7 +11,8 @@ pub enum GridNotification {
|
||||
DidUpdateRow = 30,
|
||||
DidUpdateCell = 40,
|
||||
DidUpdateField = 50,
|
||||
DidUpdateGroup = 60,
|
||||
DidUpdateGroupView = 60,
|
||||
DidUpdateGroup = 61,
|
||||
}
|
||||
|
||||
impl std::default::Default for GridNotification {
|
||||
|
@ -2,7 +2,6 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
|
||||
use flowy_sync::entities::grid::FieldChangesetParams;
|
||||
use serde_repr::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -491,6 +490,27 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayloadPB {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FieldChangesetParams {
|
||||
pub field_id: String,
|
||||
|
||||
pub grid_id: String,
|
||||
|
||||
pub name: Option<String>,
|
||||
|
||||
pub desc: Option<String>,
|
||||
|
||||
pub field_type: Option<FieldTypeRevision>,
|
||||
|
||||
pub frozen: Option<bool>,
|
||||
|
||||
pub visibility: Option<bool>,
|
||||
|
||||
pub width: Option<i32>,
|
||||
|
||||
pub type_option_data: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
@ -5,8 +5,7 @@ use crate::entities::{
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, FilterConfigurationRevision};
|
||||
use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision, FilterConfigurationRevision};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -72,6 +71,12 @@ impl TryInto<DeleteFilterParams> for DeleteFilterPayloadPB {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeleteFilterParams {
|
||||
pub field_id: String,
|
||||
pub filter_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CreateGridFilterPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
@ -99,10 +104,10 @@ impl CreateGridFilterPayloadPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<CreateGridFilterParams> for CreateGridFilterPayloadPB {
|
||||
impl TryInto<CreateFilterParams> for CreateGridFilterPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateGridFilterParams, Self::Error> {
|
||||
fn try_into(self) -> Result<CreateFilterParams, Self::Error> {
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
@ -125,7 +130,7 @@ impl TryInto<CreateGridFilterParams> for CreateGridFilterPayloadPB {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CreateGridFilterParams {
|
||||
Ok(CreateFilterParams {
|
||||
field_id,
|
||||
field_type_rev: self.field_type.into(),
|
||||
condition,
|
||||
@ -133,3 +138,10 @@ impl TryInto<CreateGridFilterParams> for CreateGridFilterPayloadPB {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreateFilterParams {
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
pub condition: u8,
|
||||
pub content: Option<String>,
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
use crate::entities::{CreateRowParams, GridLayout};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CreateBoardCardPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub group_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateRowParams, Self::Error> {
|
||||
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
||||
let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?;
|
||||
Ok(CreateRowParams {
|
||||
grid_id: grid_id.0,
|
||||
start_row_id: None,
|
||||
group_id: Some(group_id.0),
|
||||
layout: GridLayout::Board,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_grid_data_model::revision::{GroupRecordRevision, SelectOptionGroupConfigurationRevision};
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct UrlGroupConfigurationPB {
|
||||
@ -18,6 +19,32 @@ pub struct SelectOptionGroupConfigurationPB {
|
||||
hide_empty: bool,
|
||||
}
|
||||
|
||||
impl std::convert::From<SelectOptionGroupConfigurationRevision> for SelectOptionGroupConfigurationPB {
|
||||
fn from(rev: SelectOptionGroupConfigurationRevision) -> Self {
|
||||
Self {
|
||||
hide_empty: rev.hide_empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct GroupRecordPB {
|
||||
#[pb(index = 1)]
|
||||
group_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
visible: bool,
|
||||
}
|
||||
|
||||
impl std::convert::From<GroupRecordRevision> for GroupRecordPB {
|
||||
fn from(rev: GroupRecordRevision) -> Self {
|
||||
Self {
|
||||
group_id: rev.group_id,
|
||||
visible: rev.visible,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct NumberGroupConfigurationPB {
|
||||
#[pb(index = 1)]
|
||||
|
@ -1,12 +1,35 @@
|
||||
use crate::entities::{FieldType, RowPB};
|
||||
use crate::entities::{CreateRowParams, FieldType, GridLayout, RowPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::GroupConfigurationRevision;
|
||||
use flowy_sync::entities::grid::{CreateGridGroupParams, DeleteGroupParams};
|
||||
use flowy_grid_data_model::revision::{FieldTypeRevision, GroupConfigurationRevision};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CreateBoardCardPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub group_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateRowParams, Self::Error> {
|
||||
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
||||
let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?;
|
||||
Ok(CreateRowParams {
|
||||
grid_id: grid_id.0,
|
||||
start_row_id: None,
|
||||
group_id: Some(group_id.0),
|
||||
layout: GridLayout::Board,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct GridGroupConfigurationPB {
|
||||
#[pb(index = 1)]
|
||||
@ -86,27 +109,28 @@ pub struct CreateGridGroupPayloadPB {
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
#[pb(index = 3, one_of)]
|
||||
pub content: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl TryInto<CreateGridGroupParams> for CreateGridGroupPayloadPB {
|
||||
impl TryInto<CreatGroupParams> for CreateGridGroupPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateGridGroupParams, Self::Error> {
|
||||
fn try_into(self) -> Result<CreatGroupParams, Self::Error> {
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
|
||||
Ok(CreateGridGroupParams {
|
||||
Ok(CreatGroupParams {
|
||||
field_id,
|
||||
field_type_rev: self.field_type.into(),
|
||||
content: self.content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CreatGroupParams {
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct DeleteGroupPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
@ -137,3 +161,9 @@ impl TryInto<DeleteGroupParams> for DeleteGroupPayloadPB {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeleteGroupParams {
|
||||
pub field_id: String,
|
||||
pub group_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::entities::{InsertedRowPB, RowPB};
|
||||
use crate::entities::{GroupPB, InsertedRowPB, RowPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
@ -62,3 +64,64 @@ impl GroupRowsChangesetPB {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct MoveGroupPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub from_group_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub to_group_id: String,
|
||||
}
|
||||
|
||||
pub struct MoveGroupParams {
|
||||
pub view_id: String,
|
||||
pub from_group_id: String,
|
||||
pub to_group_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<MoveGroupParams> for MoveGroupPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<MoveGroupParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id)
|
||||
.map_err(|_| ErrorCode::GridViewIdIsEmpty)?
|
||||
.0;
|
||||
let from_group_id = NotEmptyStr::parse(self.from_group_id)
|
||||
.map_err(|_| ErrorCode::GroupIdIsEmpty)?
|
||||
.0;
|
||||
let to_group_id = NotEmptyStr::parse(self.to_group_id)
|
||||
.map_err(|_| ErrorCode::GroupIdIsEmpty)?
|
||||
.0;
|
||||
Ok(MoveGroupParams {
|
||||
view_id,
|
||||
from_group_id,
|
||||
to_group_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct GroupViewChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub inserted_groups: Vec<InsertedGroupPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub deleted_groups: Vec<String>,
|
||||
}
|
||||
|
||||
impl GroupViewChangesetPB {}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct InsertedGroupPB {
|
||||
#[pb(index = 1)]
|
||||
pub group: GroupPB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub index: i32,
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
mod board_card;
|
||||
mod configuration;
|
||||
mod group;
|
||||
mod group_changeset;
|
||||
|
||||
pub use board_card::*;
|
||||
pub use configuration::*;
|
||||
pub use group::*;
|
||||
pub use group_changeset::*;
|
||||
|
@ -6,7 +6,6 @@ mod grid_entities;
|
||||
mod group_entities;
|
||||
mod row_entities;
|
||||
mod setting_entities;
|
||||
mod sort_entities;
|
||||
|
||||
pub use block_entities::*;
|
||||
pub use cell_entities::*;
|
||||
@ -16,4 +15,3 @@ pub use grid_entities::*;
|
||||
pub use group_entities::*;
|
||||
pub use row_entities::*;
|
||||
pub use setting_entities::*;
|
||||
pub use sort_entities::*;
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::entities::{
|
||||
CreateGridFilterPayloadPB, CreateGridGroupPayloadPB, CreateGridSortPayloadPB, DeleteFilterPayloadPB,
|
||||
DeleteGroupPayloadPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RepeatedGridSortPB,
|
||||
CreatGroupParams, CreateFilterParams, CreateGridFilterPayloadPB, CreateGridGroupPayloadPB, DeleteFilterParams,
|
||||
DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, RepeatedGridConfigurationFilterPB,
|
||||
RepeatedGridGroupConfigurationPB,
|
||||
};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::LayoutRevision;
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use strum::IntoEnumIterator;
|
||||
@ -26,9 +26,6 @@ pub struct GridSettingPB {
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub group_configuration_by_field_id: HashMap<String, RepeatedGridGroupConfigurationPB>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub sorts_by_field_id: HashMap<String, RepeatedGridSortPB>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
@ -98,12 +95,6 @@ pub struct GridSettingChangesetPayloadPB {
|
||||
|
||||
#[pb(index = 6, one_of)]
|
||||
pub delete_group: Option<DeleteGroupPayloadPB>,
|
||||
|
||||
#[pb(index = 7, one_of)]
|
||||
pub insert_sort: Option<CreateGridSortPayloadPB>,
|
||||
|
||||
#[pb(index = 8, one_of)]
|
||||
pub delete_sort: Option<String>,
|
||||
}
|
||||
|
||||
impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
|
||||
@ -134,16 +125,6 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let insert_sort = match self.insert_sort {
|
||||
None => None,
|
||||
Some(payload) => Some(payload.try_into()?),
|
||||
};
|
||||
|
||||
let delete_sort = match self.delete_sort {
|
||||
None => None,
|
||||
Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
|
||||
};
|
||||
|
||||
Ok(GridSettingChangesetParams {
|
||||
grid_id: view_id,
|
||||
layout_type: self.layout_type.into(),
|
||||
@ -151,8 +132,21 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
|
||||
delete_filter,
|
||||
insert_group,
|
||||
delete_group,
|
||||
insert_sort,
|
||||
delete_sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GridSettingChangesetParams {
|
||||
pub grid_id: String,
|
||||
pub layout_type: LayoutRevision,
|
||||
pub insert_filter: Option<CreateFilterParams>,
|
||||
pub delete_filter: Option<DeleteFilterParams>,
|
||||
pub insert_group: Option<CreatGroupParams>,
|
||||
pub delete_group: Option<DeleteGroupParams>,
|
||||
}
|
||||
|
||||
impl GridSettingChangesetParams {
|
||||
pub fn is_filter_changed(&self) -> bool {
|
||||
self.insert_filter.is_some() || self.delete_filter.is_some()
|
||||
}
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::SortConfigurationRevision;
|
||||
use flowy_sync::entities::grid::CreateGridSortParams;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct GridSort {
|
||||
#[pb(index = 1)]
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub field_id: Option<String>,
|
||||
}
|
||||
|
||||
impl std::convert::From<&SortConfigurationRevision> for GridSort {
|
||||
fn from(rev: &SortConfigurationRevision) -> Self {
|
||||
GridSort {
|
||||
id: rev.id.clone(),
|
||||
|
||||
field_id: rev.field_id.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct RepeatedGridSortPB {
|
||||
#[pb(index = 1)]
|
||||
pub items: Vec<GridSort>,
|
||||
}
|
||||
|
||||
impl std::convert::From<Vec<Arc<SortConfigurationRevision>>> for RepeatedGridSortPB {
|
||||
fn from(revs: Vec<Arc<SortConfigurationRevision>>) -> Self {
|
||||
RepeatedGridSortPB {
|
||||
items: revs.into_iter().map(|rev| rev.as_ref().into()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<Vec<GridSort>> for RepeatedGridSortPB {
|
||||
fn from(items: Vec<GridSort>) -> Self {
|
||||
Self { items }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CreateGridSortPayloadPB {
|
||||
#[pb(index = 1, one_of)]
|
||||
pub field_id: Option<String>,
|
||||
}
|
||||
|
||||
impl TryInto<CreateGridSortParams> for CreateGridSortPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateGridSortParams, Self::Error> {
|
||||
let field_id = match self.field_id {
|
||||
None => None,
|
||||
Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
|
||||
};
|
||||
|
||||
Ok(CreateGridSortParams { field_id })
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ use crate::services::field::{
|
||||
use crate::services::row::make_row_from_row_rev;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::FieldRevision;
|
||||
use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
|
||||
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -36,17 +35,6 @@ pub(crate) async fn get_grid_setting_handler(
|
||||
data_result(grid_setting)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn update_grid_setting_handler(
|
||||
data: Data<GridSettingChangesetPayloadPB>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: GridSettingChangesetParams = data.into_inner().try_into()?;
|
||||
let editor = manager.open_grid(¶ms.grid_id).await?;
|
||||
let _ = editor.update_grid_setting(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn get_grid_blocks_handler(
|
||||
data: Data<QueryBlocksPayloadPB>,
|
||||
@ -437,3 +425,25 @@ pub(crate) async fn create_board_card_handler(
|
||||
let row = editor.create_row(params).await?;
|
||||
data_result(row)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn move_group_handler(
|
||||
data: Data<MoveGroupPayloadPB>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> FlowyResult<()> {
|
||||
let params: MoveGroupParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(params.view_id.as_ref())?;
|
||||
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(())
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
.event(GridEvent::GetGrid, get_grid_handler)
|
||||
.event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
|
||||
.event(GridEvent::GetGridSetting, get_grid_setting_handler)
|
||||
.event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
|
||||
// .event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
|
||||
// Field
|
||||
.event(GridEvent::GetFields, get_fields_handler)
|
||||
.event(GridEvent::UpdateField, update_field_handler)
|
||||
@ -41,6 +41,8 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
.event(GridEvent::UpdateDateCell, update_date_cell_handler)
|
||||
// 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
|
||||
@ -217,4 +219,10 @@ pub enum GridEvent {
|
||||
|
||||
#[event(input = "CreateBoardCardPayloadPB", output = "RowPB")]
|
||||
CreateBoardCard = 110,
|
||||
|
||||
#[event(input = "MoveGroupPayloadPB")]
|
||||
MoveGroup = 111,
|
||||
|
||||
#[event(input = "MoveGroupRowPayloadPB")]
|
||||
MoveGroupRow = 112,
|
||||
}
|
||||
|
@ -131,7 +131,6 @@ impl GridManager {
|
||||
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
|
||||
match self.grid_editors.get(grid_id) {
|
||||
None => {
|
||||
tracing::trace!("Create grid editor with id: {}", grid_id);
|
||||
let db_pool = self.grid_user.db_pool()?;
|
||||
let editor = self.make_grid_rev_editor(grid_id, db_pool).await?;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_results)]
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::entities::{FieldType, GridBlockChangesetPB};
|
||||
use crate::entities::{FieldType, GridBlockChangesetPB, GridSettingChangesetParams};
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::cell::{AnyCellData, CellFilterOperation};
|
||||
use crate::services::field::{
|
||||
@ -20,7 +20,6 @@ use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
|
||||
use flowy_sync::client_grid::GridRevisionPad;
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
@ -15,7 +15,6 @@ use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::*;
|
||||
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
|
||||
use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeserializer};
|
||||
use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
use flowy_sync::errors::CollaborateResult;
|
||||
use flowy_sync::util::make_text_delta_from_revisions;
|
||||
@ -96,23 +95,19 @@ impl GridRevisionEditor {
|
||||
} = params;
|
||||
let field_id = field.id.clone();
|
||||
if self.contain_field(&field_id).await {
|
||||
let _ = self
|
||||
.modify(|grid| {
|
||||
let deserializer = TypeOptionJsonDeserializer(field.field_type.clone());
|
||||
let changeset = FieldChangesetParams {
|
||||
field_id: field.id,
|
||||
grid_id,
|
||||
name: Some(field.name),
|
||||
desc: Some(field.desc),
|
||||
field_type: Some(field.field_type.into()),
|
||||
frozen: Some(field.frozen),
|
||||
visibility: Some(field.visibility),
|
||||
width: Some(field.width),
|
||||
type_option_data: Some(type_option_data),
|
||||
};
|
||||
Ok(grid.update_field_rev(changeset, deserializer)?)
|
||||
})
|
||||
.await?;
|
||||
let changeset = FieldChangesetParams {
|
||||
field_id: field.id,
|
||||
grid_id,
|
||||
name: Some(field.name),
|
||||
desc: Some(field.desc),
|
||||
field_type: Some(field.field_type.clone().into()),
|
||||
frozen: Some(field.frozen),
|
||||
visibility: Some(field.visibility),
|
||||
width: Some(field.width),
|
||||
type_option_data: Some(type_option_data),
|
||||
};
|
||||
|
||||
let _ = self.update_field_rev(changeset, field.field_type).await?;
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
} else {
|
||||
let _ = self
|
||||
@ -140,19 +135,13 @@ impl GridRevisionEditor {
|
||||
return Ok(());
|
||||
}
|
||||
let field_rev = result.unwrap();
|
||||
let _ = self
|
||||
.modify(|grid| {
|
||||
let field_type = field_rev.ty.into();
|
||||
let deserializer = TypeOptionJsonDeserializer(field_type);
|
||||
let changeset = FieldChangesetParams {
|
||||
field_id: field_id.to_owned(),
|
||||
grid_id: grid_id.to_owned(),
|
||||
type_option_data: Some(type_option_data),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(grid.update_field_rev(changeset, deserializer)?)
|
||||
})
|
||||
.await?;
|
||||
let changeset = FieldChangesetParams {
|
||||
field_id: field_id.to_owned(),
|
||||
grid_id: grid_id.to_owned(),
|
||||
type_option_data: Some(type_option_data),
|
||||
..Default::default()
|
||||
};
|
||||
let _ = self.update_field_rev(changeset, field_rev.ty.into()).await?;
|
||||
let _ = self.notify_did_update_grid_field(field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -179,17 +168,21 @@ impl GridRevisionEditor {
|
||||
|
||||
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
|
||||
let field_id = params.field_id.clone();
|
||||
let json_deserializer = match self.grid_pad.read().await.get_field_rev(params.field_id.as_str()) {
|
||||
None => return Err(ErrorCode::FieldDoesNotExist.into()),
|
||||
Some((_, field_rev)) => TypeOptionJsonDeserializer(field_rev.ty.into()),
|
||||
};
|
||||
let field_type: Option<FieldType> = self
|
||||
.grid_pad
|
||||
.read()
|
||||
.await
|
||||
.get_field_rev(params.field_id.as_str())
|
||||
.map(|(_, field_rev)| field_rev.ty.into());
|
||||
|
||||
let _ = self
|
||||
.modify(|grid| Ok(grid.update_field_rev(params, json_deserializer)?))
|
||||
.await?;
|
||||
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
Ok(())
|
||||
match field_type {
|
||||
None => Err(ErrorCode::FieldDoesNotExist.into()),
|
||||
Some(field_type) => {
|
||||
let _ = self.update_field_rev(params, field_type).await?;
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn replace_field(&self, field_rev: Arc<FieldRevision>) -> FlowyResult<()> {
|
||||
@ -269,6 +262,62 @@ impl GridRevisionEditor {
|
||||
Ok(field_revs)
|
||||
}
|
||||
|
||||
async fn update_field_rev(&self, params: FieldChangesetParams, field_type: FieldType) -> FlowyResult<()> {
|
||||
self.modify(|grid| {
|
||||
let deserializer = TypeOptionJsonDeserializer(field_type);
|
||||
|
||||
let changeset = grid.modify_field(¶ms.field_id, |field| {
|
||||
let mut is_changed = None;
|
||||
if let Some(name) = params.name {
|
||||
field.name = name;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(desc) = params.desc {
|
||||
field.desc = desc;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(field_type) = params.field_type {
|
||||
field.ty = field_type;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(frozen) = params.frozen {
|
||||
field.frozen = frozen;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(visibility) = params.visibility {
|
||||
field.visibility = visibility;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(width) = params.width {
|
||||
field.width = width;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(type_option_data) = params.type_option_data {
|
||||
match deserializer.deserialize(type_option_data) {
|
||||
Ok(json_str) => {
|
||||
let field_type = field.ty;
|
||||
field.insert_type_option_str(&field_type, json_str);
|
||||
is_changed = Some(())
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Deserialize data to type option json failed: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(is_changed)
|
||||
})?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_block(&self, block_meta_rev: GridBlockMetaRevision) -> FlowyResult<()> {
|
||||
let _ = self
|
||||
.modify(|grid_pad| Ok(grid_pad.create_block_meta_rev(block_meta_rev)?))
|
||||
@ -294,6 +343,11 @@ impl GridRevisionEditor {
|
||||
Ok(row_pb)
|
||||
}
|
||||
|
||||
pub async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.move_group(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
|
||||
let block_id = self.block_id().await?;
|
||||
let mut rows_by_block_id: HashMap<String, Vec<RowRevision>> = HashMap::new();
|
||||
@ -460,8 +514,13 @@ impl GridRevisionEditor {
|
||||
self.view_manager.get_filters().await
|
||||
}
|
||||
|
||||
pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.update_setting(params).await?;
|
||||
pub async fn update_filter(&self, params: CreateFilterParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.update_filter(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.delete_filter(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -501,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),
|
||||
@ -520,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: _,
|
||||
|
@ -1,18 +1,23 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::entities::{
|
||||
CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB, RowPB,
|
||||
CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridLayout, GridLayoutPB,
|
||||
GridSettingPB, GroupPB, GroupRowsChangesetPB, GroupViewChangesetPB, InsertedGroupPB, InsertedRowPB,
|
||||
MoveGroupParams, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RowPB,
|
||||
};
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
|
||||
use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService};
|
||||
use crate::services::setting::make_grid_setting;
|
||||
use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter, GroupService};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision};
|
||||
use flowy_grid_data_model::revision::{
|
||||
gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision,
|
||||
RowChangeset, RowRevision,
|
||||
};
|
||||
use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder};
|
||||
use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
use lib_infra::future::{wrap_future, AFFuture, FutureResult};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
@ -31,6 +36,7 @@ pub struct GridViewRevisionEditor {
|
||||
}
|
||||
|
||||
impl GridViewRevisionEditor {
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn new(
|
||||
user_id: &str,
|
||||
token: &str,
|
||||
@ -46,7 +52,14 @@ impl GridViewRevisionEditor {
|
||||
let view_revision_pad = rev_manager.load::<GridViewRevisionPadBuilder>(Some(cloud)).await?;
|
||||
let pad = Arc::new(RwLock::new(view_revision_pad));
|
||||
let rev_manager = Arc::new(rev_manager);
|
||||
let group_service = GroupService::new(Box::new(pad.clone())).await;
|
||||
|
||||
let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
|
||||
let configuration_writer = GroupConfigurationWriterImpl {
|
||||
user_id: user_id.to_owned(),
|
||||
rev_manager: rev_manager.clone(),
|
||||
view_pad: pad.clone(),
|
||||
};
|
||||
let group_service = GroupService::new(configuration_reader, configuration_writer).await;
|
||||
let user_id = user_id.to_owned();
|
||||
let did_load_group = AtomicBool::new(false);
|
||||
Ok(Self {
|
||||
@ -67,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)
|
||||
@ -87,7 +100,7 @@ impl GridViewRevisionEditor {
|
||||
index: None,
|
||||
};
|
||||
let changeset = GroupRowsChangesetPB::insert(group_id.clone(), vec![inserted_row]);
|
||||
self.notify_did_update_group(changeset).await;
|
||||
self.notify_did_update_group_rows(changeset).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +115,7 @@ impl GridViewRevisionEditor {
|
||||
.await
|
||||
{
|
||||
for changeset in changesets {
|
||||
self.notify_did_update_group(changeset).await;
|
||||
self.notify_did_update_group_rows(changeset).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,38 +129,40 @@ impl GridViewRevisionEditor {
|
||||
.await
|
||||
{
|
||||
for changeset in changesets {
|
||||
self.notify_did_update_group(changeset).await;
|
||||
self.notify_did_update_group_rows(changeset).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
.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(changeset).await;
|
||||
self.notify_did_update_group_rows(changeset).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
|
||||
let groups = if !self.did_load_group.load(Ordering::SeqCst) {
|
||||
self.did_load_group.store(true, Ordering::SeqCst);
|
||||
let field_revs = self.field_delegate.get_field_revs().await;
|
||||
let row_revs = self.row_delegate.gv_row_revs().await;
|
||||
|
||||
match self
|
||||
.group_service
|
||||
.write()
|
||||
@ -162,23 +177,47 @@ impl GridViewRevisionEditor {
|
||||
self.group_service.read().await.groups().await
|
||||
};
|
||||
|
||||
tracing::trace!("Number of groups: {}", groups.len());
|
||||
Ok(groups.into_iter().map(GroupPB::from).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
|
||||
let _ = self
|
||||
.group_service
|
||||
.write()
|
||||
.await
|
||||
.move_group(¶ms.from_group_id, ¶ms.to_group_id)
|
||||
.await?;
|
||||
|
||||
match self.group_service.read().await.get_group(¶ms.from_group_id).await {
|
||||
None => {}
|
||||
Some((index, group)) => {
|
||||
let inserted_group = InsertedGroupPB {
|
||||
group: GroupPB::from(group),
|
||||
index: index as i32,
|
||||
};
|
||||
|
||||
let changeset = GroupViewChangesetPB {
|
||||
view_id: "".to_string(),
|
||||
inserted_groups: vec![inserted_group],
|
||||
deleted_groups: vec![params.from_group_id.clone()],
|
||||
};
|
||||
|
||||
self.notify_did_update_view(changeset).await;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_setting(&self) -> GridSettingPB {
|
||||
let field_revs = self.field_delegate.get_field_revs().await;
|
||||
let grid_setting = make_grid_setting(self.pad.read().await.get_setting_rev(), &field_revs);
|
||||
let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs);
|
||||
grid_setting
|
||||
}
|
||||
|
||||
pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> {
|
||||
let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_filters(&self) -> Vec<GridFilterConfiguration> {
|
||||
let field_revs = self.field_delegate.get_field_revs().await;
|
||||
match self.pad.read().await.get_setting_rev().get_all_filters(&field_revs) {
|
||||
match self.pad.read().await.get_all_filters(&field_revs) {
|
||||
None => vec![],
|
||||
Some(filters) => filters
|
||||
.into_values()
|
||||
@ -188,12 +227,45 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
}
|
||||
|
||||
async fn notify_did_update_group(&self, changeset: GroupRowsChangesetPB) {
|
||||
pub(crate) async fn insert_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> {
|
||||
self.modify(|pad| {
|
||||
let filter_rev = FilterConfigurationRevision {
|
||||
id: gen_grid_filter_id(),
|
||||
field_id: insert_filter.field_id.clone(),
|
||||
condition: insert_filter.condition,
|
||||
content: insert_filter.content,
|
||||
};
|
||||
let changeset = pad.insert_filter(&insert_filter.field_id, &insert_filter.field_type_rev, filter_rev)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> {
|
||||
self.modify(|pad| {
|
||||
let changeset = pad.delete_filter(
|
||||
&delete_filter.field_id,
|
||||
&delete_filter.field_type_rev,
|
||||
&delete_filter.filter_id,
|
||||
)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn notify_did_update_group_rows(&self, changeset: GroupRowsChangesetPB) {
|
||||
send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
|
||||
send_dart_notification(&self.view_id, GridNotification::DidUpdateGroupView)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn modify<F>(&self, f: F) -> FlowyResult<()>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,
|
||||
@ -202,28 +274,24 @@ impl GridViewRevisionEditor {
|
||||
match f(&mut *write_guard)? {
|
||||
None => {}
|
||||
Some(change) => {
|
||||
let _ = self.apply_change(change).await?;
|
||||
let _ = apply_change(&self.user_id, self.rev_manager.clone(), change).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_change(&self, change: GridViewRevisionChangeset) -> FlowyResult<()> {
|
||||
let GridViewRevisionChangeset { delta, md5 } = change;
|
||||
let user_id = self.user_id.clone();
|
||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||
let delta_data = delta.json_bytes();
|
||||
let revision = Revision::new(
|
||||
&self.rev_manager.object_id,
|
||||
base_rev_id,
|
||||
rev_id,
|
||||
delta_data,
|
||||
&user_id,
|
||||
md5,
|
||||
);
|
||||
let _ = self.rev_manager.add_local_revision(&revision).await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn apply_change(
|
||||
user_id: &str,
|
||||
rev_manager: Arc<RevisionManager>,
|
||||
change: GridViewRevisionChangeset,
|
||||
) -> FlowyResult<()> {
|
||||
let GridViewRevisionChangeset { delta, md5 } = change;
|
||||
let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair();
|
||||
let delta_data = delta.json_bytes();
|
||||
let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5);
|
||||
let _ = rev_manager.add_local_revision(&revision).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct GridViewRevisionCloudService {
|
||||
@ -248,19 +316,97 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupConfigurationDelegate for Arc<RwLock<GridViewRevisionPad>> {
|
||||
fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision> {
|
||||
let view_pad = self.clone();
|
||||
struct GroupConfigurationReaderImpl(Arc<RwLock<GridViewRevisionPad>>);
|
||||
|
||||
impl GroupConfigurationReader for GroupConfigurationReaderImpl {
|
||||
fn get_group_configuration(
|
||||
&self,
|
||||
field_rev: Arc<FieldRevision>,
|
||||
) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
|
||||
let view_pad = self.0.clone();
|
||||
wrap_future(async move {
|
||||
let grid_pad = view_pad.read().await;
|
||||
let configurations = grid_pad.get_groups(&field_rev.id, &field_rev.ty);
|
||||
match configurations {
|
||||
None => default_group_configuration(&field_rev),
|
||||
Some(mut configurations) => {
|
||||
assert_eq!(configurations.len(), 1);
|
||||
(&*configurations.pop().unwrap()).clone()
|
||||
}
|
||||
let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?;
|
||||
if groups.is_empty() {
|
||||
None
|
||||
} else {
|
||||
debug_assert_eq!(groups.len(), 1);
|
||||
Some(groups.pop().unwrap())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct GroupConfigurationWriterImpl {
|
||||
user_id: String,
|
||||
rev_manager: Arc<RevisionManager>,
|
||||
view_pad: Arc<RwLock<GridViewRevisionPad>>,
|
||||
}
|
||||
|
||||
impl GroupConfigurationWriter for GroupConfigurationWriterImpl {
|
||||
fn save_group_configuration(
|
||||
&self,
|
||||
field_id: &str,
|
||||
field_type: FieldTypeRevision,
|
||||
group_configuration: GroupConfigurationRevision,
|
||||
) -> AFFuture<FlowyResult<()>> {
|
||||
let user_id = self.user_id.clone();
|
||||
let rev_manager = self.rev_manager.clone();
|
||||
let view_pad = self.view_pad.clone();
|
||||
let field_id = field_id.to_owned();
|
||||
|
||||
wrap_future(async move {
|
||||
let changeset = view_pad
|
||||
.write()
|
||||
.await
|
||||
.insert_group(&field_id, &field_type, group_configuration)?;
|
||||
|
||||
if let Some(changeset) = changeset {
|
||||
let _ = apply_change(&user_id, rev_manager, changeset).await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB {
|
||||
let current_layout_type: GridLayout = view_pad.layout.clone().into();
|
||||
let filters_by_field_id = view_pad
|
||||
.get_all_filters(field_revs)
|
||||
.map(|filters_by_field_id| {
|
||||
filters_by_field_id
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect::<HashMap<String, RepeatedGridConfigurationFilterPB>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let groups_by_field_id = view_pad
|
||||
.get_all_groups(field_revs)
|
||||
.map(|groups_by_field_id| {
|
||||
groups_by_field_id
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect::<HashMap<String, RepeatedGridGroupConfigurationPB>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
GridSettingPB {
|
||||
layouts: GridLayoutPB::all(),
|
||||
current_layout_type,
|
||||
filter_configuration_by_field_id: filters_by_field_id,
|
||||
group_configuration_by_field_id: groups_by_field_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lib_ot::core::TextDelta;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let s1 = r#"[{"insert":"{\"view_id\":\"fTURELffPr\",\"grid_id\":\"fTURELffPr\",\"layout\":0,\"filters\":[],\"groups\":[]}"}]"#;
|
||||
let _delta_1 = TextDelta::from_json(s1).unwrap();
|
||||
|
||||
let s2 = r#"[{"retain":195},{"insert":"{\\\"group_id\\\":\\\"wD9i\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"xZtv\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"tFV2\\\",\\\"visible\\\":true}"},{"retain":10}]"#;
|
||||
let _delta_2 = TextDelta::from_json(s2).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB};
|
||||
use crate::entities::{
|
||||
CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridSettingPB, MoveGroupParams,
|
||||
RepeatedGridGroupPB, RowPB,
|
||||
};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::grid_view_editor::GridViewRevisionEditor;
|
||||
@ -8,7 +11,6 @@ use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
|
||||
use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
use flowy_sync::util::make_text_delta_from_revisions;
|
||||
use lib_infra::future::AFFuture;
|
||||
@ -97,30 +99,47 @@ impl GridViewManager {
|
||||
Ok(view_editor.get_setting().await)
|
||||
}
|
||||
|
||||
pub(crate) async fn update_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let _ = view_editor.update_setting(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_filters(&self) -> FlowyResult<Vec<GridFilterConfiguration>> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
Ok(view_editor.get_filters().await)
|
||||
}
|
||||
|
||||
pub(crate) async fn update_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
view_editor.insert_filter(insert_filter).await
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
view_editor.delete_filter(delete_filter).await
|
||||
}
|
||||
|
||||
pub(crate) async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let groups = view_editor.load_groups().await?;
|
||||
Ok(RepeatedGridGroupPB { items: groups })
|
||||
}
|
||||
|
||||
pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let _ = view_editor.move_group(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
@ -163,12 +182,11 @@ async fn make_view_editor(
|
||||
row_delegate: Arc<dyn GridViewRowDelegate>,
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
) -> FlowyResult<GridViewRevisionEditor> {
|
||||
tracing::trace!("Open view:{} editor", view_id);
|
||||
|
||||
let rev_manager = make_grid_view_rev_manager(user, view_id).await?;
|
||||
let user_id = user.user_id()?;
|
||||
let token = user.token()?;
|
||||
let view_id = view_id.to_owned();
|
||||
|
||||
GridViewRevisionEditor::new(
|
||||
&user_id,
|
||||
&token,
|
||||
@ -182,7 +200,6 @@ async fn make_view_editor(
|
||||
}
|
||||
|
||||
pub async fn make_grid_view_rev_manager(user: &Arc<dyn GridUser>, view_id: &str) -> FlowyResult<RevisionManager> {
|
||||
tracing::trace!("Open view:{} editor", view_id);
|
||||
let user_id = user.user_id()?;
|
||||
let pool = user.db_pool()?;
|
||||
|
||||
|
17
frontend/rust-lib/flowy-grid/src/services/group/action.rs
Normal file
17
frontend/rust-lib/flowy-grid/src/services/group/action.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::entities::GroupRowsChangesetPB;
|
||||
|
||||
use crate::services::group::controller::MoveGroupRowContext;
|
||||
use flowy_grid_data_model::revision::RowRevision;
|
||||
|
||||
pub trait GroupAction: Send + Sync {
|
||||
type CellDataType;
|
||||
fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB>;
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB>;
|
||||
|
||||
fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec<GroupRowsChangesetPB>;
|
||||
}
|
221
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
Normal file
221
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
Normal file
@ -0,0 +1,221 @@
|
||||
use crate::services::group::{default_group_configuration, Group};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::{
|
||||
FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRecordRevision,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use lib_infra::future::AFFuture;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait GroupConfigurationReader: Send + Sync + 'static {
|
||||
fn get_group_configuration(
|
||||
&self,
|
||||
field_rev: Arc<FieldRevision>,
|
||||
) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
|
||||
}
|
||||
|
||||
pub trait GroupConfigurationWriter: Send + Sync + 'static {
|
||||
fn save_group_configuration(
|
||||
&self,
|
||||
field_id: &str,
|
||||
field_type: FieldTypeRevision,
|
||||
group_configuration: GroupConfigurationRevision,
|
||||
) -> AFFuture<FlowyResult<()>>;
|
||||
}
|
||||
|
||||
pub struct GenericGroupConfiguration<C> {
|
||||
pub configuration: Arc<GroupConfigurationRevision>,
|
||||
configuration_content: PhantomData<C>,
|
||||
field_rev: Arc<FieldRevision>,
|
||||
groups_map: IndexMap<String, Group>,
|
||||
writer: Arc<dyn GroupConfigurationWriter>,
|
||||
}
|
||||
|
||||
impl<C> GenericGroupConfiguration<C>
|
||||
where
|
||||
C: GroupConfigurationContentSerde,
|
||||
{
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn new(
|
||||
field_rev: Arc<FieldRevision>,
|
||||
reader: Arc<dyn GroupConfigurationReader>,
|
||||
writer: Arc<dyn GroupConfigurationWriter>,
|
||||
) -> FlowyResult<Self> {
|
||||
let configuration = match reader.get_group_configuration(field_rev.clone()).await {
|
||||
None => {
|
||||
let default_group_configuration = default_group_configuration(&field_rev);
|
||||
writer
|
||||
.save_group_configuration(&field_rev.id, field_rev.ty, default_group_configuration.clone())
|
||||
.await?;
|
||||
Arc::new(default_group_configuration)
|
||||
}
|
||||
Some(configuration) => configuration,
|
||||
};
|
||||
|
||||
// let configuration = C::from_configuration_content(&configuration_rev.content)?;
|
||||
Ok(Self {
|
||||
field_rev,
|
||||
groups_map: IndexMap::new(),
|
||||
writer,
|
||||
configuration,
|
||||
configuration_content: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn groups(&self) -> Vec<&Group> {
|
||||
self.groups_map.values().collect()
|
||||
}
|
||||
|
||||
pub(crate) fn clone_groups(&self) -> Vec<Group> {
|
||||
self.groups_map.values().cloned().collect()
|
||||
}
|
||||
|
||||
pub(crate) async fn merge_groups(&mut self, groups: Vec<Group>) -> FlowyResult<()> {
|
||||
let (group_revs, groups) = merge_groups(&self.configuration.groups, groups);
|
||||
self.mut_configuration(move |configuration| {
|
||||
configuration.groups = group_revs;
|
||||
true
|
||||
})?;
|
||||
|
||||
groups.into_iter().for_each(|group| {
|
||||
self.groups_map.insert(group.id.clone(), group);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn hide_group(&mut self, group_id: &str) -> FlowyResult<()> {
|
||||
self.mut_configuration_group(group_id, |group_rev| {
|
||||
group_rev.visible = false;
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn show_group(&mut self, group_id: &str) -> FlowyResult<()> {
|
||||
self.mut_configuration_group(group_id, |group_rev| {
|
||||
group_rev.visible = true;
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn with_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) {
|
||||
self.groups_map.iter_mut().for_each(|(_, group)| {
|
||||
each(group);
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> {
|
||||
self.groups_map.get_mut(group_id)
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
true
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(FlowyError::out_of_bounds()),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the index and group specified by the group_id
|
||||
pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> {
|
||||
match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) {
|
||||
(Some(index), Some(group)) => Some((index, group)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_configuration(&self) -> FlowyResult<()> {
|
||||
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;
|
||||
tokio::spawn(async move {
|
||||
match writer
|
||||
.save_group_configuration(&field_id, field_type, configuration)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("Save group configuration failed: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mut_configuration_group(
|
||||
&mut self,
|
||||
group_id: &str,
|
||||
mut_groups_fn: impl Fn(&mut GroupRecordRevision),
|
||||
) -> FlowyResult<()> {
|
||||
self.mut_configuration(|configuration| {
|
||||
match configuration.groups.iter_mut().find(|group| group.group_id == group_id) {
|
||||
None => false,
|
||||
Some(group_rev) => {
|
||||
mut_groups_fn(group_rev);
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn mut_configuration(
|
||||
&mut self,
|
||||
mut_configuration_fn: impl FnOnce(&mut GroupConfigurationRevision) -> bool,
|
||||
) -> FlowyResult<()> {
|
||||
let configuration = Arc::make_mut(&mut self.configuration);
|
||||
let is_changed = mut_configuration_fn(configuration);
|
||||
if is_changed {
|
||||
let _ = self.save_configuration()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_groups(old_group_revs: &[GroupRecordRevision], groups: Vec<Group>) -> (Vec<GroupRecordRevision>, Vec<Group>) {
|
||||
if old_group_revs.is_empty() {
|
||||
let new_groups = groups
|
||||
.iter()
|
||||
.map(|group| GroupRecordRevision::new(group.id.clone()))
|
||||
.collect();
|
||||
return (new_groups, groups);
|
||||
}
|
||||
|
||||
let mut group_map: IndexMap<String, Group> = IndexMap::new();
|
||||
groups.into_iter().for_each(|group| {
|
||||
group_map.insert(group.id.clone(), group);
|
||||
});
|
||||
|
||||
// Inert
|
||||
let mut sorted_groups: Vec<Group> = vec![];
|
||||
for group_rev in old_group_revs {
|
||||
if let Some(group) = group_map.remove(&group_rev.group_id) {
|
||||
sorted_groups.push(group);
|
||||
}
|
||||
}
|
||||
sorted_groups.extend(group_map.into_values().collect::<Vec<Group>>());
|
||||
let new_group_revs = sorted_groups
|
||||
.iter()
|
||||
.map(|group| GroupRecordRevision::new(group.id.clone()))
|
||||
.collect::<Vec<GroupRecordRevision>>();
|
||||
|
||||
tracing::trace!("group revs: {}, groups: {}", new_group_revs.len(), sorted_groups.len());
|
||||
(new_group_revs, sorted_groups)
|
||||
}
|
214
frontend/rust-lib/flowy-grid/src/services/group/controller.rs
Normal file
214
frontend/rust-lib/flowy-grid/src/services/group/controller.rs
Normal file
@ -0,0 +1,214 @@
|
||||
use crate::entities::{GroupRowsChangesetPB, RowPB};
|
||||
use crate::services::cell::{decode_any_cell_data, CellBytesParser};
|
||||
use crate::services::group::action::GroupAction;
|
||||
use crate::services::group::configuration::GenericGroupConfiguration;
|
||||
use crate::services::group::entities::Group;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
FieldRevision, GroupConfigurationContentSerde, RowChangeset, RowRevision, TypeOptionDataDeserializer,
|
||||
};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
const DEFAULT_GROUP_ID: &str = "default_group";
|
||||
|
||||
// Each kind of group must implement this trait to provide custom group
|
||||
// operations. For example, insert cell data to the row_rev when creating
|
||||
// a new row.
|
||||
pub trait GroupController: GroupControllerSharedOperation + Send + Sync {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
}
|
||||
|
||||
pub trait GroupGenerator {
|
||||
type ConfigurationType;
|
||||
type TypeOptionType;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
configuration: &Self::ConfigurationType,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> 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
|
||||
fn field_id(&self) -> &str;
|
||||
fn groups(&self) -> Vec<Group>;
|
||||
fn get_group(&self, group_id: &str) -> Option<(usize, Group)>;
|
||||
fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>>;
|
||||
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
|
||||
fn did_update_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
|
||||
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
}
|
||||
|
||||
/// C: represents the group configuration that impl [GroupConfigurationSerde]
|
||||
/// T: the type option data deserializer that impl [TypeOptionDataDeserializer]
|
||||
/// G: the group generator, [GroupGenerator]
|
||||
/// P: the parser that impl [CellBytesParser] for the CellBytes
|
||||
pub struct GenericGroupController<C, T, G, P> {
|
||||
pub field_id: String,
|
||||
pub type_option: Option<T>,
|
||||
pub configuration: GenericGroupConfiguration<C>,
|
||||
/// default_group is used to store the rows that don't belong to any groups.
|
||||
default_group: Group,
|
||||
group_action_phantom: PhantomData<G>,
|
||||
cell_parser_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<C, T, G, P> GenericGroupController<C, T, G, P>
|
||||
where
|
||||
C: GroupConfigurationContentSerde,
|
||||
T: TypeOptionDataDeserializer,
|
||||
G: GroupGenerator<ConfigurationType = GenericGroupConfiguration<C>, TypeOptionType = T>,
|
||||
{
|
||||
pub async fn new(
|
||||
field_rev: &Arc<FieldRevision>,
|
||||
mut configuration: GenericGroupConfiguration<C>,
|
||||
) -> FlowyResult<Self> {
|
||||
let field_type_rev = field_rev.ty;
|
||||
let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
|
||||
let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
|
||||
let _ = configuration.merge_groups(groups).await?;
|
||||
let default_group = Group::new(
|
||||
DEFAULT_GROUP_ID.to_owned(),
|
||||
field_rev.id.clone(),
|
||||
format!("No {}", field_rev.name),
|
||||
"".to_string(),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
field_id: field_rev.id.clone(),
|
||||
default_group,
|
||||
type_option,
|
||||
configuration,
|
||||
group_action_phantom: PhantomData,
|
||||
cell_parser_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, P> GroupControllerSharedOperation for GenericGroupController<C, T, G, P>
|
||||
where
|
||||
P: CellBytesParser,
|
||||
C: GroupConfigurationContentSerde,
|
||||
Self: GroupAction<CellDataType = P::Object>,
|
||||
{
|
||||
fn field_id(&self) -> &str {
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn groups(&self) -> Vec<Group> {
|
||||
self.configuration.clone_groups()
|
||||
}
|
||||
|
||||
fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
|
||||
let group = self.configuration.get_group(group_id)?;
|
||||
Some((group.0, group.1.clone()))
|
||||
}
|
||||
|
||||
fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
|
||||
for row_rev in row_revs {
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let mut group_rows: Vec<GroupRow> = vec![];
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
for group in self.configuration.groups() {
|
||||
if self.can_group(&group.content, &cell_data) {
|
||||
group_rows.push(GroupRow {
|
||||
row: row_rev.into(),
|
||||
group_id: group.id.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if group_rows.is_empty() {
|
||||
self.default_group.add_row(row_rev.into());
|
||||
} else {
|
||||
for group_row in group_rows {
|
||||
if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) {
|
||||
group.add_row(group_row.row);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.default_group.add_row(row_rev.into());
|
||||
}
|
||||
}
|
||||
|
||||
let default_group = self.default_group.clone();
|
||||
let mut groups: Vec<Group> = self.configuration.clone_groups();
|
||||
if !default_group.number_of_row() == 0 {
|
||||
groups.push(default_group);
|
||||
}
|
||||
|
||||
Ok(groups)
|
||||
}
|
||||
|
||||
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
|
||||
self.configuration.move_group(from_group_id, to_group_id)
|
||||
}
|
||||
|
||||
fn did_update_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> 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);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.add_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> 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);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.remove_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
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>()?;
|
||||
Ok(self.move_row(&cell_data, context))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GroupRow {
|
||||
row: RowPB,
|
||||
group_id: String,
|
||||
}
|
@ -1,20 +1,25 @@
|
||||
use crate::entities::{CheckboxGroupConfigurationPB, GroupRowsChangesetPB};
|
||||
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
|
||||
use crate::entities::GroupRowsChangesetPB;
|
||||
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
|
||||
use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable};
|
||||
use crate::services::group::action::GroupAction;
|
||||
use crate::services::group::configuration::GenericGroupConfiguration;
|
||||
use crate::services::group::controller::{
|
||||
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
||||
};
|
||||
use crate::services::group::entities::Group;
|
||||
|
||||
use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowRevision};
|
||||
|
||||
pub type CheckboxGroupController = GenericGroupController<
|
||||
CheckboxGroupConfigurationPB,
|
||||
CheckboxGroupConfigurationRevision,
|
||||
CheckboxTypeOptionPB,
|
||||
CheckboxGroupGenerator,
|
||||
CheckboxCellDataParser,
|
||||
>;
|
||||
|
||||
impl Groupable for CheckboxGroupController {
|
||||
type CellDataType = CheckboxCellData;
|
||||
pub type CheckboxGroupConfiguration = GenericGroupConfiguration<CheckboxGroupConfigurationRevision>;
|
||||
|
||||
impl GroupAction for CheckboxGroupController {
|
||||
type CellDataType = CheckboxCellData;
|
||||
fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
|
||||
false
|
||||
}
|
||||
@ -35,13 +40,10 @@ impl Groupable 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!()
|
||||
}
|
||||
@ -55,12 +57,12 @@ impl GroupController for CheckboxGroupController {
|
||||
|
||||
pub struct CheckboxGroupGenerator();
|
||||
impl GroupGenerator for CheckboxGroupGenerator {
|
||||
type ConfigurationType = CheckboxGroupConfigurationPB;
|
||||
type ConfigurationType = CheckboxGroupConfiguration;
|
||||
type TypeOptionType = CheckboxTypeOptionPB;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
_configuration: &Self::ConfigurationType,
|
||||
_type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
let check_group = Group::new(
|
@ -0,0 +1,5 @@
|
||||
mod checkbox_controller;
|
||||
mod select_option_controller;
|
||||
|
||||
pub use checkbox_controller::*;
|
||||
pub use select_option_controller::*;
|
@ -0,0 +1,7 @@
|
||||
mod multi_select_controller;
|
||||
mod single_select_controller;
|
||||
mod util;
|
||||
|
||||
pub use multi_select_controller::*;
|
||||
pub use single_select_controller::*;
|
||||
pub use util::*;
|
@ -0,0 +1,98 @@
|
||||
use crate::entities::GroupRowsChangesetPB;
|
||||
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, MoveGroupRowContext,
|
||||
};
|
||||
use crate::services::group::controller_impls::select_option_controller::util::*;
|
||||
use crate::services::group::entities::Group;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
|
||||
|
||||
// MultiSelect
|
||||
pub type MultiSelectGroupController = GenericGroupController<
|
||||
SelectOptionGroupConfigurationRevision,
|
||||
MultiSelectTypeOptionPB,
|
||||
MultiSelectGroupGenerator,
|
||||
SelectOptionCellDataParser,
|
||||
>;
|
||||
|
||||
impl GroupAction for MultiSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
|
||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn move_row(
|
||||
&mut self,
|
||||
cell_data: &Self::CellDataType,
|
||||
mut context: MoveGroupRowContext,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut group_changeset = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
move_select_option_row(group, &mut group_changeset, cell_data, &mut context);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for MultiSelectGroupController {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
match self.configuration.get_group(group_id) {
|
||||
None => tracing::warn!("Can not find the group: {}", group_id),
|
||||
Some((_, group)) => {
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_rev.cells.insert(field_rev.id.clone(), cell_rev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiSelectGroupGenerator();
|
||||
impl GroupGenerator for MultiSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfiguration;
|
||||
type TypeOptionType = MultiSelectTypeOptionPB;
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Self::ConfigurationType,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
match type_option {
|
||||
None => vec![],
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
use crate::entities::{GroupRowsChangesetPB, RowPB};
|
||||
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, MoveGroupRowContext,
|
||||
};
|
||||
use crate::services::group::controller_impls::select_option_controller::util::*;
|
||||
use crate::services::group::entities::Group;
|
||||
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
|
||||
|
||||
// SingleSelect
|
||||
pub type SingleSelectGroupController = GenericGroupController<
|
||||
SelectOptionGroupConfigurationRevision,
|
||||
SingleSelectTypeOptionPB,
|
||||
SingleSelectGroupGenerator,
|
||||
SelectOptionCellDataParser,
|
||||
>;
|
||||
|
||||
impl GroupAction for SingleSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn move_row(
|
||||
&mut self,
|
||||
cell_data: &Self::CellDataType,
|
||||
mut context: MoveGroupRowContext,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut group_changeset = vec![];
|
||||
self.configuration.with_mut_groups(|group| {
|
||||
move_select_option_row(group, &mut group_changeset, cell_data, &mut context);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for SingleSelectGroupController {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&mut Group> = self.configuration.get_mut_group(group_id);
|
||||
match group {
|
||||
None => {}
|
||||
Some(group) => {
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_rev.cells.insert(field_rev.id.clone(), cell_rev);
|
||||
group.add_row(RowPB::from(row_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SingleSelectGroupGenerator();
|
||||
impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfiguration;
|
||||
type TypeOptionType = SingleSelectTypeOptionPB;
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Self::ConfigurationType,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
match type_option {
|
||||
None => vec![],
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
use crate::entities::{GroupRowsChangesetPB, InsertedRowPB, RowPB};
|
||||
use crate::services::cell::insert_select_option_cell;
|
||||
use crate::services::field::SelectOptionCellDataPB;
|
||||
use crate::services::group::configuration::GenericGroupConfiguration;
|
||||
use crate::services::group::Group;
|
||||
|
||||
use crate::services::group::controller::MoveGroupRowContext;
|
||||
use flowy_grid_data_model::revision::{RowRevision, SelectOptionGroupConfigurationRevision};
|
||||
|
||||
pub type SelectOptionGroupConfiguration = GenericGroupConfiguration<SelectOptionGroupConfigurationRevision>;
|
||||
|
||||
pub fn add_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id {
|
||||
if !group.contains_row(&row_rev.id) {
|
||||
let row_pb = RowPB::from(row_rev);
|
||||
changesets.push(GroupRowsChangesetPB::insert(
|
||||
group.id.clone(),
|
||||
vec![InsertedRowPB::new(row_pb.clone())],
|
||||
));
|
||||
group.add_row(row_pb);
|
||||
}
|
||||
} else if group.contains_row(&row_rev.id) {
|
||||
changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn remove_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id && group.contains_row(&row_rev.id) {
|
||||
changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn move_select_option_row(
|
||||
group: &mut Group,
|
||||
group_changeset: &mut Vec<GroupRowsChangesetPB>,
|
||||
_cell_data: &SelectOptionCellDataPB,
|
||||
context: &mut MoveGroupRowContext,
|
||||
) {
|
||||
let MoveGroupRowContext {
|
||||
row_rev,
|
||||
row_changeset,
|
||||
field_rev,
|
||||
to_group_id,
|
||||
to_row_id,
|
||||
} = context;
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
73
frontend/rust-lib/flowy-grid/src/services/group/entities.rs
Normal file
73
frontend/rust-lib/flowy-grid/src/services/group/entities.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crate::entities::{GroupPB, RowPB};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub desc: String,
|
||||
rows: Vec<RowPB>,
|
||||
|
||||
/// [content] is used to determine which group the cell belongs to.
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl std::convert::From<Group> for GroupPB {
|
||||
fn from(group: Group) -> Self {
|
||||
Self {
|
||||
field_id: group.field_id,
|
||||
group_id: group.id,
|
||||
desc: group.desc,
|
||||
rows: group.rows,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn new(id: String, field_id: String, desc: String, content: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
field_id,
|
||||
desc,
|
||||
rows: vec![],
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_row(&self, row_id: &str) -> bool {
|
||||
self.rows.iter().any(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn remove_row(&mut self, row_id: &str) {
|
||||
match self.rows.iter().position(|row| row.id == row_id) {
|
||||
None => {}
|
||||
Some(pos) => {
|
||||
self.rows.remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_row(&mut self, row_pb: RowPB) {
|
||||
match self.rows.iter().find(|row| row.id == row_pb.id) {
|
||||
None => {
|
||||
self.rows.push(row_pb);
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_row(&mut self, index: usize, row_pb: RowPB) {
|
||||
if index < self.rows.len() {
|
||||
self.rows.insert(index, row_pb);
|
||||
} else {
|
||||
tracing::error!("Insert row index:{} beyond the bounds:{},", index, self.rows.len());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_of_row(&self, row_id: &str) -> Option<usize> {
|
||||
self.rows.iter().position(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn number_of_row(&self) -> usize {
|
||||
self.rows.len()
|
||||
}
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
use crate::entities::{GroupPB, GroupRowsChangesetPB, RowPB};
|
||||
use crate::services::cell::{decode_any_cell_data, CellBytesParser};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait GroupGenerator {
|
||||
type ConfigurationType;
|
||||
type TypeOptionType;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group>;
|
||||
}
|
||||
|
||||
pub trait Groupable: Send + Sync {
|
||||
type CellDataType;
|
||||
fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB>;
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
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>;
|
||||
}
|
||||
|
||||
pub trait GroupController: GroupControllerSharedAction + Send + Sync {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
}
|
||||
|
||||
pub trait GroupControllerSharedAction: Send + Sync {
|
||||
// The field that is used for grouping the rows
|
||||
fn field_id(&self) -> &str;
|
||||
fn groups(&self) -> Vec<Group>;
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
|
||||
fn did_update_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
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>>;
|
||||
}
|
||||
|
||||
const DEFAULT_GROUP_ID: &str = "default_group";
|
||||
|
||||
/// C: represents the group configuration structure
|
||||
/// T: the type option data deserializer that impl [TypeOptionDataDeserializer]
|
||||
/// G: the group generator, [GroupGenerator]
|
||||
/// P: the parser that impl [CellBytesParser] for the CellBytes
|
||||
pub struct GenericGroupController<C, T, G, P> {
|
||||
pub field_id: String,
|
||||
pub groups_map: IndexMap<String, Group>,
|
||||
default_group: Group,
|
||||
pub type_option: Option<T>,
|
||||
pub configuration: Option<C>,
|
||||
group_action_phantom: PhantomData<G>,
|
||||
cell_parser_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub desc: String,
|
||||
rows: Vec<RowPB>,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl std::convert::From<Group> for GroupPB {
|
||||
fn from(group: Group) -> Self {
|
||||
Self {
|
||||
field_id: group.field_id,
|
||||
group_id: group.id,
|
||||
desc: group.desc,
|
||||
rows: group.rows,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn new(id: String, field_id: String, desc: String, content: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
field_id,
|
||||
desc,
|
||||
rows: vec![],
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_row(&self, row_id: &str) -> bool {
|
||||
self.rows.iter().any(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn remove_row(&mut self, row_id: &str) {
|
||||
match self.rows.iter().position(|row| row.id == row_id) {
|
||||
None => {}
|
||||
Some(pos) => {
|
||||
self.rows.remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_row(&mut self, row_pb: RowPB) {
|
||||
match self.rows.iter().find(|row| row.id == row_pb.id) {
|
||||
None => {
|
||||
self.rows.push(row_pb);
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_row(&mut self, index: usize, row_pb: RowPB) {
|
||||
if index < self.rows.len() {
|
||||
self.rows.insert(index, row_pb);
|
||||
} else {
|
||||
tracing::error!("Insert row index:{} beyond the bounds:{},", index, self.rows.len());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_of_row(&self, row_id: &str) -> Option<usize> {
|
||||
self.rows.iter().position(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn number_of_row(&self) -> usize {
|
||||
self.rows.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, P> GenericGroupController<C, T, G, P>
|
||||
where
|
||||
C: TryFrom<Bytes, Error = protobuf::ProtobufError>,
|
||||
T: TypeOptionDataDeserializer,
|
||||
G: GroupGenerator<ConfigurationType = C, TypeOptionType = T>,
|
||||
{
|
||||
pub fn new(field_rev: &Arc<FieldRevision>, configuration: GroupConfigurationRevision) -> FlowyResult<Self> {
|
||||
let configuration = match configuration.content {
|
||||
None => None,
|
||||
Some(content) => Some(C::try_from(Bytes::from(content))?),
|
||||
};
|
||||
let field_type_rev = field_rev.ty;
|
||||
let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
|
||||
let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
|
||||
|
||||
let default_group = Group::new(
|
||||
DEFAULT_GROUP_ID.to_owned(),
|
||||
field_rev.id.clone(),
|
||||
format!("No {}", field_rev.name),
|
||||
"".to_string(),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
field_id: field_rev.id.clone(),
|
||||
groups_map: groups.into_iter().map(|group| (group.id.clone(), group)).collect(),
|
||||
default_group,
|
||||
type_option,
|
||||
configuration,
|
||||
group_action_phantom: PhantomData,
|
||||
cell_parser_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, P> GroupControllerSharedAction for GenericGroupController<C, T, G, P>
|
||||
where
|
||||
P: CellBytesParser,
|
||||
Self: Groupable<CellDataType = P::Object>,
|
||||
{
|
||||
fn field_id(&self) -> &str {
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn groups(&self) -> Vec<Group> {
|
||||
let default_group = self.default_group.clone();
|
||||
let mut groups: Vec<Group> = self.groups_map.values().cloned().collect();
|
||||
if !default_group.rows.is_empty() {
|
||||
groups.push(default_group);
|
||||
}
|
||||
groups
|
||||
}
|
||||
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||
if self.configuration.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for row_rev in row_revs {
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let mut records: Vec<GroupRecord> = vec![];
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
for group in self.groups_map.values() {
|
||||
if self.can_group(&group.content, &cell_data) {
|
||||
records.push(GroupRecord {
|
||||
row: row_rev.into(),
|
||||
group_id: group.id.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if records.is_empty() {
|
||||
self.default_group.rows.push(row_rev.into());
|
||||
} else {
|
||||
for record in records {
|
||||
if let Some(group) = self.groups_map.get_mut(&record.group_id) {
|
||||
group.rows.push(record.row);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.default_group.rows.push(row_rev.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn did_update_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> 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);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.add_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> 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);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.remove_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GroupRecord {
|
||||
row: RowPB,
|
||||
group_id: String,
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
mod checkbox_group;
|
||||
mod group_controller;
|
||||
mod select_option_group;
|
||||
|
||||
pub use checkbox_group::*;
|
||||
pub use group_controller::*;
|
||||
pub use select_option_group::*;
|
@ -1,287 +0,0 @@
|
||||
use crate::entities::{GroupRowsChangesetPB, InsertedRowPB, RowPB, SelectOptionGroupConfigurationPB};
|
||||
use crate::services::cell::insert_select_option_cell;
|
||||
use crate::services::field::{
|
||||
MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
|
||||
};
|
||||
use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable};
|
||||
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
|
||||
// SingleSelect
|
||||
pub type SingleSelectGroupController = GenericGroupController<
|
||||
SelectOptionGroupConfigurationPB,
|
||||
SingleSelectTypeOptionPB,
|
||||
SingleSelectGroupGenerator,
|
||||
SelectOptionCellDataParser,
|
||||
>;
|
||||
|
||||
impl Groupable for SingleSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
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> {
|
||||
let mut group_changeset = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
move_row(
|
||||
group,
|
||||
&mut group_changeset,
|
||||
field_rev,
|
||||
row_rev,
|
||||
row_changeset,
|
||||
cell_data,
|
||||
to_row_id,
|
||||
);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for SingleSelectGroupController {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&mut Group> = self.groups_map.get_mut(group_id);
|
||||
match group {
|
||||
None => {}
|
||||
Some(group) => {
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_rev.cells.insert(field_rev.id.clone(), cell_rev);
|
||||
group.add_row(RowPB::from(row_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SingleSelectGroupGenerator();
|
||||
impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfigurationPB;
|
||||
type TypeOptionType = SingleSelectTypeOptionPB;
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
match type_option {
|
||||
None => vec![],
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MultiSelect
|
||||
pub type MultiSelectGroupController = GenericGroupController<
|
||||
SelectOptionGroupConfigurationPB,
|
||||
MultiSelectTypeOptionPB,
|
||||
MultiSelectGroupGenerator,
|
||||
SelectOptionCellDataParser,
|
||||
>;
|
||||
|
||||
impl Groupable for MultiSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
|
||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
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> {
|
||||
let mut group_changeset = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
move_row(
|
||||
group,
|
||||
&mut group_changeset,
|
||||
field_rev,
|
||||
row_rev,
|
||||
row_changeset,
|
||||
cell_data,
|
||||
to_row_id,
|
||||
);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for MultiSelectGroupController {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&Group> = self.groups_map.get(group_id);
|
||||
match group {
|
||||
None => tracing::warn!("Can not find the group: {}", group_id),
|
||||
Some(group) => {
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_rev.cells.insert(field_rev.id.clone(), cell_rev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiSelectGroupGenerator();
|
||||
impl GroupGenerator for MultiSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfigurationPB;
|
||||
type TypeOptionType = MultiSelectTypeOptionPB;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
match type_option {
|
||||
None => vec![],
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id {
|
||||
if !group.contains_row(&row_rev.id) {
|
||||
let row_pb = RowPB::from(row_rev);
|
||||
changesets.push(GroupRowsChangesetPB::insert(
|
||||
group.id.clone(),
|
||||
vec![InsertedRowPB::new(row_pb.clone())],
|
||||
));
|
||||
group.add_row(row_pb);
|
||||
}
|
||||
} else if group.contains_row(&row_rev.id) {
|
||||
changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id && group.contains_row(&row_rev.id) {
|
||||
changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn move_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.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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
group.add_row(row_pb);
|
||||
} else {
|
||||
group.insert_row(to_index, row_pb);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
@ -1,43 +1,49 @@
|
||||
use crate::entities::{
|
||||
CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupRowsChangesetPB,
|
||||
NumberGroupConfigurationPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB,
|
||||
};
|
||||
use crate::entities::{FieldType, GroupRowsChangesetPB};
|
||||
use crate::services::group::configuration::GroupConfigurationReader;
|
||||
use crate::services::group::controller::{GroupController, MoveGroupRowContext};
|
||||
use crate::services::group::{
|
||||
CheckboxGroupController, Group, GroupController, MultiSelectGroupController, SingleSelectGroupController,
|
||||
CheckboxGroupConfiguration, CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController,
|
||||
SelectOptionGroupConfiguration, SingleSelectGroupController,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision,
|
||||
CheckboxGroupConfigurationRevision, DateGroupConfigurationRevision, FieldRevision, GroupConfigurationRevision,
|
||||
NumberGroupConfigurationRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
|
||||
TextGroupConfigurationRevision, UrlGroupConfigurationRevision,
|
||||
};
|
||||
use lib_infra::future::AFFuture;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub trait GroupConfigurationDelegate: Send + Sync + 'static {
|
||||
fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision>;
|
||||
}
|
||||
|
||||
pub(crate) struct GroupService {
|
||||
delegate: Box<dyn GroupConfigurationDelegate>,
|
||||
group_controller: Option<Arc<RwLock<dyn GroupController>>>,
|
||||
configuration_reader: Arc<dyn GroupConfigurationReader>,
|
||||
configuration_writer: Arc<dyn GroupConfigurationWriter>,
|
||||
group_controller: Option<Box<dyn GroupController>>,
|
||||
}
|
||||
|
||||
impl GroupService {
|
||||
pub(crate) async fn new(delegate: Box<dyn GroupConfigurationDelegate>) -> Self {
|
||||
pub(crate) async fn new<R, W>(configuration_reader: R, configuration_writer: W) -> Self
|
||||
where
|
||||
R: GroupConfigurationReader,
|
||||
W: GroupConfigurationWriter,
|
||||
{
|
||||
Self {
|
||||
delegate,
|
||||
configuration_reader: Arc::new(configuration_reader),
|
||||
configuration_writer: Arc::new(configuration_writer),
|
||||
group_controller: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn groups(&self) -> Vec<Group> {
|
||||
if let Some(group_action_handler) = self.group_controller.as_ref() {
|
||||
group_action_handler.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)> {
|
||||
self.group_controller
|
||||
.as_ref()
|
||||
.and_then(|group_controller| group_controller.get_group(group_id))
|
||||
}
|
||||
|
||||
pub(crate) async fn load_groups(
|
||||
@ -47,37 +53,37 @@ impl GroupService {
|
||||
) -> Option<Vec<Group>> {
|
||||
let field_rev = find_group_field(field_revs)?;
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
let configuration = self.delegate.get_group_configuration(field_rev.clone()).await;
|
||||
match self
|
||||
.build_groups(&field_type, &field_rev, row_revs, configuration)
|
||||
.await
|
||||
{
|
||||
Ok(groups) => Some(groups),
|
||||
Err(_) => None,
|
||||
}
|
||||
|
||||
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![]
|
||||
}
|
||||
};
|
||||
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>>
|
||||
@ -85,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);
|
||||
@ -98,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);
|
||||
@ -128,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>>
|
||||
@ -136,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);
|
||||
@ -149,14 +159,24 @@ impl GroupService {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
async fn build_groups(
|
||||
&mut self,
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
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.move_group(from_group_id, to_group_id)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self, field_rev), err)]
|
||||
async fn make_group_controller(
|
||||
&self,
|
||||
field_type: &FieldType,
|
||||
field_rev: &Arc<FieldRevision>,
|
||||
row_revs: Vec<Arc<RowRevision>>,
|
||||
configuration: GroupConfigurationRevision,
|
||||
) -> FlowyResult<Vec<Group>> {
|
||||
) -> 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);
|
||||
@ -168,31 +188,40 @@ impl GroupService {
|
||||
// let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
|
||||
}
|
||||
FieldType::SingleSelect => {
|
||||
let controller = SingleSelectGroupController::new(field_rev, configuration)?;
|
||||
self.group_controller = Some(Arc::new(RwLock::new(controller)));
|
||||
let configuration = SelectOptionGroupConfiguration::new(
|
||||
field_rev.clone(),
|
||||
self.configuration_reader.clone(),
|
||||
self.configuration_writer.clone(),
|
||||
)
|
||||
.await?;
|
||||
let controller = SingleSelectGroupController::new(field_rev, configuration).await?;
|
||||
group_controller = Some(Box::new(controller));
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let controller = MultiSelectGroupController::new(field_rev, configuration)?;
|
||||
self.group_controller = Some(Arc::new(RwLock::new(controller)));
|
||||
let configuration = SelectOptionGroupConfiguration::new(
|
||||
field_rev.clone(),
|
||||
self.configuration_reader.clone(),
|
||||
self.configuration_writer.clone(),
|
||||
)
|
||||
.await?;
|
||||
let controller = MultiSelectGroupController::new(field_rev, configuration).await?;
|
||||
group_controller = Some(Box::new(controller));
|
||||
}
|
||||
FieldType::Checkbox => {
|
||||
let controller = CheckboxGroupController::new(field_rev, configuration)?;
|
||||
self.group_controller = Some(Arc::new(RwLock::new(controller)));
|
||||
let configuration = CheckboxGroupConfiguration::new(
|
||||
field_rev.clone(),
|
||||
self.configuration_reader.clone(),
|
||||
self.configuration_writer.clone(),
|
||||
)
|
||||
.await?;
|
||||
let controller = CheckboxGroupController::new(field_rev, configuration).await?;
|
||||
group_controller = Some(Box::new(controller));
|
||||
}
|
||||
FieldType::URL => {
|
||||
// let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
|
||||
}
|
||||
};
|
||||
|
||||
let mut groups = vec![];
|
||||
if let Some(group_action_handler) = self.group_controller.as_ref() {
|
||||
let mut write_guard = group_action_handler.write().await;
|
||||
let _ = write_guard.group_rows(&row_revs, field_rev)?;
|
||||
groups = write_guard.groups();
|
||||
drop(write_guard);
|
||||
}
|
||||
|
||||
Ok(groups)
|
||||
Ok(group_controller)
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,20 +237,41 @@ fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevisi
|
||||
}
|
||||
|
||||
pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
|
||||
let field_id = field_rev.id.clone();
|
||||
let field_type_rev = field_rev.ty;
|
||||
let field_type: FieldType = field_rev.ty.into();
|
||||
let bytes: Bytes = match field_type {
|
||||
FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::Number => NumberGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::DateTime => DateGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::SingleSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::MultiSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::Checkbox => CheckboxGroupConfigurationPB::default().try_into().unwrap(),
|
||||
FieldType::URL => UrlGroupConfigurationPB::default().try_into().unwrap(),
|
||||
};
|
||||
GroupConfigurationRevision {
|
||||
id: gen_grid_group_id(),
|
||||
field_id: field_rev.id.clone(),
|
||||
field_type_rev: field_rev.ty,
|
||||
content: Some(bytes.to_vec()),
|
||||
match field_type {
|
||||
FieldType::RichText => {
|
||||
GroupConfigurationRevision::new(field_id, field_type_rev, TextGroupConfigurationRevision::default())
|
||||
.unwrap()
|
||||
}
|
||||
FieldType::Number => {
|
||||
GroupConfigurationRevision::new(field_id, field_type_rev, NumberGroupConfigurationRevision::default())
|
||||
.unwrap()
|
||||
}
|
||||
FieldType::DateTime => {
|
||||
GroupConfigurationRevision::new(field_id, field_type_rev, DateGroupConfigurationRevision::default())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
FieldType::SingleSelect => GroupConfigurationRevision::new(
|
||||
field_id,
|
||||
field_type_rev,
|
||||
SelectOptionGroupConfigurationRevision::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
FieldType::MultiSelect => GroupConfigurationRevision::new(
|
||||
field_id,
|
||||
field_type_rev,
|
||||
SelectOptionGroupConfigurationRevision::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
FieldType::Checkbox => {
|
||||
GroupConfigurationRevision::new(field_id, field_type_rev, CheckboxGroupConfigurationRevision::default())
|
||||
.unwrap()
|
||||
}
|
||||
FieldType::URL => {
|
||||
GroupConfigurationRevision::new(field_id, field_type_rev, UrlGroupConfigurationRevision::default()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
mod group_generator;
|
||||
mod action;
|
||||
mod configuration;
|
||||
mod controller;
|
||||
mod controller_impls;
|
||||
mod entities;
|
||||
mod group_service;
|
||||
|
||||
pub(crate) use group_generator::*;
|
||||
pub(crate) use configuration::*;
|
||||
pub(crate) use controller_impls::*;
|
||||
pub(crate) use entities::*;
|
||||
pub(crate) use group_service::*;
|
||||
|
@ -1,11 +1,4 @@
|
||||
use crate::entities::{
|
||||
GridLayout, GridLayoutPB, GridSettingPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB,
|
||||
RepeatedGridSortPB,
|
||||
};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, SettingRevision};
|
||||
use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use crate::entities::{CreateFilterParams, DeleteFilterParams, GridLayout, GridSettingChangesetParams};
|
||||
|
||||
pub struct GridSettingChangesetBuilder {
|
||||
params: GridSettingChangesetParams,
|
||||
@ -20,13 +13,11 @@ impl GridSettingChangesetBuilder {
|
||||
delete_filter: None,
|
||||
insert_group: None,
|
||||
delete_group: None,
|
||||
insert_sort: None,
|
||||
delete_sort: None,
|
||||
};
|
||||
Self { params }
|
||||
}
|
||||
|
||||
pub fn insert_filter(mut self, params: CreateGridFilterParams) -> Self {
|
||||
pub fn insert_filter(mut self, params: CreateFilterParams) -> Self {
|
||||
self.params.insert_filter = Some(params);
|
||||
self
|
||||
}
|
||||
@ -40,42 +31,3 @@ impl GridSettingChangesetBuilder {
|
||||
self.params
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_grid_setting(grid_setting_rev: &SettingRevision, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB {
|
||||
let current_layout_type: GridLayout = grid_setting_rev.layout.clone().into();
|
||||
let filters_by_field_id = grid_setting_rev
|
||||
.get_all_filters(field_revs)
|
||||
.map(|filters_by_field_id| {
|
||||
filters_by_field_id
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect::<HashMap<String, RepeatedGridConfigurationFilterPB>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let groups_by_field_id = grid_setting_rev
|
||||
.get_all_groups(field_revs)
|
||||
.map(|groups_by_field_id| {
|
||||
groups_by_field_id
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect::<HashMap<String, RepeatedGridGroupConfigurationPB>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let sorts_by_field_id = grid_setting_rev
|
||||
.get_all_sort()
|
||||
.map(|sorts_by_field_id| {
|
||||
sorts_by_field_id
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect::<HashMap<String, RepeatedGridSortPB>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
GridSettingPB {
|
||||
layouts: GridLayoutPB::all(),
|
||||
current_layout_type,
|
||||
filter_configuration_by_field_id: filters_by_field_id,
|
||||
group_configuration_by_field_id: groups_by_field_id,
|
||||
sorts_by_field_id,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
use flowy_grid::entities::InsertFieldParams;
|
||||
use flowy_grid::entities::{FieldChangesetParams, InsertFieldParams};
|
||||
use flowy_grid_data_model::revision::FieldRevision;
|
||||
use flowy_sync::entities::grid::FieldChangesetParams;
|
||||
|
||||
pub enum FieldScript {
|
||||
CreateField {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::grid::field_test::script::FieldScript::*;
|
||||
use crate::grid::field_test::script::GridFieldTest;
|
||||
use crate::grid::field_test::util::*;
|
||||
use flowy_grid::entities::FieldChangesetParams;
|
||||
use flowy_grid::services::field::selection_type_option::SelectOptionPB;
|
||||
use flowy_grid::services::field::SingleSelectTypeOptionPB;
|
||||
use flowy_grid_data_model::revision::TypeOptionDataEntry;
|
||||
use flowy_sync::entities::grid::FieldChangesetParams;
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_create_field() {
|
||||
|
@ -3,17 +3,12 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use flowy_grid::entities::{CreateGridFilterPayloadPB, GridLayout, GridSettingPB};
|
||||
use flowy_grid::entities::{CreateFilterParams, CreateGridFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB};
|
||||
use flowy_grid::services::setting::GridSettingChangesetBuilder;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
|
||||
use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
|
||||
pub enum FilterScript {
|
||||
#[allow(dead_code)]
|
||||
UpdateGridSetting {
|
||||
params: GridSettingChangesetParams,
|
||||
},
|
||||
InsertGridTableFilter {
|
||||
payload: CreateGridFilterPayloadPB,
|
||||
},
|
||||
@ -50,27 +45,18 @@ impl GridFilterTest {
|
||||
|
||||
pub async fn run_script(&mut self, script: FilterScript) {
|
||||
match script {
|
||||
FilterScript::UpdateGridSetting { params } => {
|
||||
let _ = self.editor.update_grid_setting(params).await.unwrap();
|
||||
}
|
||||
|
||||
FilterScript::InsertGridTableFilter { payload } => {
|
||||
let params: CreateGridFilterParams = payload.try_into().unwrap();
|
||||
let layout_type = GridLayout::Table;
|
||||
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
|
||||
.insert_filter(params)
|
||||
.build();
|
||||
let _ = self.editor.update_grid_setting(params).await.unwrap();
|
||||
let params: CreateFilterParams = payload.try_into().unwrap();
|
||||
let _ = self.editor.update_filter(params).await.unwrap();
|
||||
}
|
||||
FilterScript::AssertTableFilterCount { count } => {
|
||||
let filters = self.editor.get_grid_filter().await.unwrap();
|
||||
assert_eq!(count as usize, filters.len());
|
||||
}
|
||||
FilterScript::DeleteGridTableFilter { filter_id, field_rev} => {
|
||||
let layout_type = GridLayout::Table;
|
||||
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
|
||||
.delete_filter(DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.ty })
|
||||
.build();
|
||||
let _ = self.editor.update_grid_setting(params).await.unwrap();
|
||||
let params = DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.ty };
|
||||
let _ = self.editor.delete_filter(params).await.unwrap();
|
||||
}
|
||||
FilterScript::AssertGridSetting { expected_setting } => {
|
||||
let setting = self.editor.get_grid_setting().await.unwrap();
|
||||
|
@ -12,9 +12,6 @@ use flowy_grid::services::setting::GridSettingChangesetBuilder;
|
||||
use flowy_grid_data_model::revision::*;
|
||||
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
|
||||
use flowy_sync::client_grid::GridBuilder;
|
||||
use flowy_sync::entities::grid::{
|
||||
CreateGridFilterParams, DeleteFilterParams, FieldChangesetParams, GridSettingChangesetParams,
|
||||
};
|
||||
use flowy_test::helper::ViewTest;
|
||||
use flowy_test::FlowySDKTest;
|
||||
use std::collections::HashMap;
|
||||
|
@ -1,14 +1,20 @@
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
use flowy_grid::entities::{CreateRowParams, FieldType, GridLayout, GroupPB, 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;
|
||||
|
||||
pub enum GroupScript {
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: usize,
|
||||
row_count: usize,
|
||||
},
|
||||
AssertGroupCount(usize),
|
||||
AssertGroup {
|
||||
group_index: usize,
|
||||
expected_group: GroupPB,
|
||||
},
|
||||
AssertRow {
|
||||
group_index: usize,
|
||||
row_index: usize,
|
||||
@ -32,6 +38,10 @@ pub enum GroupScript {
|
||||
row_index: usize,
|
||||
to_group_index: usize,
|
||||
},
|
||||
MoveGroup {
|
||||
from_group_index: usize,
|
||||
to_group_index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct GridGroupTest {
|
||||
@ -52,7 +62,7 @@ impl GridGroupTest {
|
||||
|
||||
pub async fn run_script(&mut self, script: GroupScript) {
|
||||
match script {
|
||||
GroupScript::AssertGroup { group_index, row_count } => {
|
||||
GroupScript::AssertGroupRowCount { group_index, row_count } => {
|
||||
assert_eq!(row_count, self.group_at_index(group_index).await.rows.len());
|
||||
}
|
||||
GroupScript::AssertGroupCount(count) => {
|
||||
@ -67,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,
|
||||
@ -84,7 +96,6 @@ impl GridGroupTest {
|
||||
//
|
||||
let group = self.group_at_index(group_index).await;
|
||||
let compare_row = group.rows.get(row_index).unwrap().clone();
|
||||
|
||||
assert_eq!(row.id, compare_row.id);
|
||||
}
|
||||
GroupScript::CreateRow { group_index } => {
|
||||
@ -125,6 +136,27 @@ impl GridGroupTest {
|
||||
row_changeset.cell_by_field_id.insert(field_id, cell_rev);
|
||||
self.editor.update_row(row_changeset).await.unwrap();
|
||||
}
|
||||
GroupScript::MoveGroup {
|
||||
from_group_index,
|
||||
to_group_index,
|
||||
} => {
|
||||
let from_group = self.group_at_index(from_group_index).await;
|
||||
let to_group = self.group_at_index(to_group_index).await;
|
||||
let params = MoveGroupParams {
|
||||
view_id: self.editor.grid_id.clone(),
|
||||
from_group_id: from_group.group_id,
|
||||
to_group_id: to_group.group_id,
|
||||
};
|
||||
self.editor.move_group(params).await.unwrap();
|
||||
//
|
||||
}
|
||||
GroupScript::AssertGroup {
|
||||
group_index,
|
||||
expected_group: group_pb,
|
||||
} => {
|
||||
let group = self.group_at_index(group_index).await;
|
||||
assert_eq!(group.group_id, group_pb.group_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,19 +2,19 @@ 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),
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 2,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 1,
|
||||
row_count: 2,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 2,
|
||||
row_count: 1,
|
||||
},
|
||||
@ -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![
|
||||
@ -34,7 +34,7 @@ async fn board_move_row_test() {
|
||||
to_group_index: 0,
|
||||
to_row_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 2,
|
||||
},
|
||||
@ -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![
|
||||
@ -58,11 +58,11 @@ async fn board_move_row_to_other_group_test() {
|
||||
to_group_index: 1,
|
||||
to_row_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 1,
|
||||
row_count: 3,
|
||||
},
|
||||
@ -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,17 +128,84 @@ 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 },
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 3,
|
||||
},
|
||||
CreateRow { group_index: 1 },
|
||||
CreateRow { group_index: 1 },
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 1,
|
||||
row_count: 4,
|
||||
},
|
||||
@ -121,14 +214,14 @@ 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 {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 1,
|
||||
},
|
||||
@ -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 {
|
||||
@ -148,7 +241,7 @@ async fn board_delete_all_row_test() {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 0,
|
||||
},
|
||||
@ -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
|
||||
@ -166,11 +259,11 @@ async fn board_update_row_test() {
|
||||
row_index: 0,
|
||||
to_group_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 1,
|
||||
row_count: 3,
|
||||
},
|
||||
@ -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
|
||||
@ -188,14 +281,36 @@ async fn board_reorder_group_test() {
|
||||
row_index: 0,
|
||||
to_group_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 0,
|
||||
row_count: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
AssertGroupRowCount {
|
||||
group_index: 1,
|
||||
row_count: 3,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::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;
|
||||
let scripts = vec![
|
||||
MoveGroup {
|
||||
from_group_index: 0,
|
||||
to_group_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 0,
|
||||
expected_group: group_1,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 1,
|
||||
expected_group: group_0,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String {
|
||||
filters.push(format!("lib_ot={}", level));
|
||||
filters.push(format!("lib_ws={}", level));
|
||||
filters.push(format!("lib_infra={}", level));
|
||||
// filters.push(format!("flowy_sync={}", level));
|
||||
filters.push(format!("flowy_sync={}", level));
|
||||
// filters.push(format!("flowy_revision={}", level));
|
||||
// filters.push(format!("lib_dispatch={}", level));
|
||||
|
||||
|
@ -125,6 +125,9 @@ pub enum ErrorCode {
|
||||
|
||||
#[display(fmt = "Invalid data")]
|
||||
InvalidData = 1000,
|
||||
|
||||
#[display(fmt = "Out of bounds")]
|
||||
OutOfBounds = 10001,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
|
@ -0,0 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
|
||||
pub struct FilterConfigurationRevision {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub condition: u8,
|
||||
pub content: Option<String>,
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
use crate::revision::{FieldRevision, FieldTypeRevision};
|
||||
use crate::revision::{FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision};
|
||||
use indexmap::IndexMap;
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
@ -15,6 +14,7 @@ pub fn gen_grid_group_id() -> String {
|
||||
nanoid!(6)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn gen_grid_sort_id() -> String {
|
||||
nanoid!(6)
|
||||
}
|
||||
@ -24,120 +24,12 @@ pub type FilterConfigurationsByFieldId = HashMap<String, Vec<Arc<FilterConfigura
|
||||
//
|
||||
pub type GroupConfiguration = Configuration<GroupConfigurationRevision>;
|
||||
pub type GroupConfigurationsByFieldId = HashMap<String, Vec<Arc<GroupConfigurationRevision>>>;
|
||||
//
|
||||
pub type SortConfiguration = Configuration<SortConfigurationRevision>;
|
||||
pub type SortConfigurationsByFieldId = HashMap<String, Vec<Arc<SortConfigurationRevision>>>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
|
||||
pub struct SettingRevision {
|
||||
pub layout: LayoutRevision,
|
||||
|
||||
pub filters: FilterConfiguration,
|
||||
|
||||
#[serde(default)]
|
||||
pub groups: GroupConfiguration,
|
||||
|
||||
#[serde(skip)]
|
||||
pub sorts: SortConfiguration,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum LayoutRevision {
|
||||
Table = 0,
|
||||
Board = 1,
|
||||
}
|
||||
|
||||
impl ToString for LayoutRevision {
|
||||
fn to_string(&self) -> String {
|
||||
let layout_rev = self.clone() as u8;
|
||||
layout_rev.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for LayoutRevision {
|
||||
fn default() -> Self {
|
||||
LayoutRevision::Table
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingRevision {
|
||||
pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
||||
self.groups.get_all_objects(field_revs)
|
||||
}
|
||||
|
||||
pub fn get_groups(
|
||||
&self,
|
||||
field_id: &str,
|
||||
field_type_rev: &FieldTypeRevision,
|
||||
) -> Option<Vec<Arc<GroupConfigurationRevision>>> {
|
||||
self.groups.get_objects(field_id, field_type_rev)
|
||||
}
|
||||
|
||||
pub fn get_mut_groups(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
field_type: &FieldTypeRevision,
|
||||
) -> Option<&mut Vec<Arc<GroupConfigurationRevision>>> {
|
||||
self.groups.get_mut_objects(field_id, field_type)
|
||||
}
|
||||
|
||||
pub fn insert_group(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
field_type: &FieldTypeRevision,
|
||||
group_rev: GroupConfigurationRevision,
|
||||
) {
|
||||
// only one group can be set
|
||||
self.groups.remove_all();
|
||||
self.groups.insert_object(field_id, field_type, group_rev);
|
||||
}
|
||||
|
||||
pub fn get_all_filters(&self, field_revs: &[Arc<FieldRevision>]) -> Option<FilterConfigurationsByFieldId> {
|
||||
self.filters.get_all_objects(field_revs)
|
||||
}
|
||||
|
||||
pub fn get_filters(
|
||||
&self,
|
||||
field_id: &str,
|
||||
field_type_rev: &FieldTypeRevision,
|
||||
) -> Option<Vec<Arc<FilterConfigurationRevision>>> {
|
||||
self.filters.get_objects(field_id, field_type_rev)
|
||||
}
|
||||
|
||||
pub fn get_mut_filters(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
field_type: &FieldTypeRevision,
|
||||
) -> Option<&mut Vec<Arc<FilterConfigurationRevision>>> {
|
||||
self.filters.get_mut_objects(field_id, field_type)
|
||||
}
|
||||
|
||||
pub fn insert_filter(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
field_type: &FieldTypeRevision,
|
||||
filter_rev: FilterConfigurationRevision,
|
||||
) {
|
||||
self.filters.insert_object(field_id, field_type, filter_rev);
|
||||
}
|
||||
|
||||
pub fn get_all_sort(&self) -> Option<SortConfigurationsByFieldId> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub struct SortConfigurationRevision {
|
||||
pub id: String,
|
||||
pub field_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(transparent)]
|
||||
pub struct Configuration<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
/// Key: field_id
|
||||
/// Value: this value contains key/value.
|
||||
@ -149,7 +41,7 @@ where
|
||||
|
||||
impl<T> Configuration<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
pub fn get_mut_objects(&mut self, field_id: &str, field_type: &FieldTypeRevision) -> Option<&mut Vec<Arc<T>>> {
|
||||
let value = self
|
||||
@ -157,7 +49,7 @@ where
|
||||
.get_mut(field_id)
|
||||
.and_then(|object_rev_map| object_rev_map.get_mut(field_type));
|
||||
if value.is_none() {
|
||||
tracing::warn!("Can't find the {:?} with", std::any::type_name::<T>());
|
||||
tracing::warn!("[Configuration] Can't find the {:?} with", std::any::type_name::<T>());
|
||||
}
|
||||
value
|
||||
}
|
||||
@ -184,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())
|
||||
@ -196,16 +89,16 @@ where
|
||||
.push(Arc::new(object))
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) {
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(transparent)]
|
||||
pub struct ObjectIndexMap<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
#[serde(with = "indexmap::serde_seq")]
|
||||
pub object_by_field_type: IndexMap<FieldTypeRevision, Vec<Arc<T>>>,
|
||||
@ -213,7 +106,7 @@ where
|
||||
|
||||
impl<T> ObjectIndexMap<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
ObjectIndexMap::default()
|
||||
@ -222,7 +115,7 @@ where
|
||||
|
||||
impl<T> std::ops::Deref for ObjectIndexMap<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
type Target = IndexMap<FieldTypeRevision, Vec<Arc<T>>>;
|
||||
|
||||
@ -233,25 +126,9 @@ where
|
||||
|
||||
impl<T> std::ops::DerefMut for ObjectIndexMap<T>
|
||||
where
|
||||
T: Debug + Clone + Default + Eq + PartialEq + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
T: Debug + Clone + Default + serde::Serialize + serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.object_by_field_type
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
pub struct GroupConfigurationRevision {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
pub content: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
|
||||
pub struct FilterConfigurationRevision {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub condition: u8,
|
||||
pub content: Option<String>,
|
||||
}
|
||||
|
@ -1,24 +1,50 @@
|
||||
use crate::revision::SettingRevision;
|
||||
use crate::revision::{FilterConfiguration, GroupConfiguration};
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::*;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn gen_grid_view_id() -> String {
|
||||
nanoid!(6)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum LayoutRevision {
|
||||
Table = 0,
|
||||
Board = 1,
|
||||
}
|
||||
|
||||
impl ToString for LayoutRevision {
|
||||
fn to_string(&self) -> String {
|
||||
let layout_rev = self.clone() as u8;
|
||||
layout_rev.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for LayoutRevision {
|
||||
fn default() -> Self {
|
||||
LayoutRevision::Table
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct GridViewRevision {
|
||||
pub view_id: String,
|
||||
|
||||
pub grid_id: String,
|
||||
|
||||
pub setting: SettingRevision,
|
||||
pub layout: LayoutRevision,
|
||||
|
||||
// For the moment, we just use the order returned from the GridRevision
|
||||
#[allow(dead_code)]
|
||||
#[serde(skip, rename = "row")]
|
||||
pub row_orders: Vec<RowOrderRevision>,
|
||||
#[serde(default)]
|
||||
pub filters: FilterConfiguration,
|
||||
|
||||
#[serde(default)]
|
||||
pub groups: GroupConfiguration,
|
||||
// // For the moment, we just use the order returned from the GridRevision
|
||||
// #[allow(dead_code)]
|
||||
// #[serde(skip, rename = "rows")]
|
||||
// pub row_orders: Vec<RowOrderRevision>,
|
||||
}
|
||||
|
||||
impl GridViewRevision {
|
||||
@ -26,8 +52,10 @@ impl GridViewRevision {
|
||||
GridViewRevision {
|
||||
view_id,
|
||||
grid_id,
|
||||
setting: Default::default(),
|
||||
row_orders: vec![],
|
||||
layout: Default::default(),
|
||||
filters: Default::default(),
|
||||
groups: Default::default(),
|
||||
// row_orders: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,3 +64,24 @@ impl GridViewRevision {
|
||||
pub struct RowOrderRevision {
|
||||
pub row_id: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::revision::GridViewRevision;
|
||||
|
||||
#[test]
|
||||
fn grid_view_revision_serde_test() {
|
||||
let grid_view_revision = GridViewRevision {
|
||||
view_id: "1".to_string(),
|
||||
grid_id: "1".to_string(),
|
||||
layout: Default::default(),
|
||||
filters: Default::default(),
|
||||
groups: Default::default(),
|
||||
};
|
||||
let s = serde_json::to_string(&grid_view_revision).unwrap();
|
||||
assert_eq!(
|
||||
s,
|
||||
r#"{"view_id":"1","grid_id":"1","layout":0,"filters":[],"groups":[]}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
181
shared-lib/flowy-grid-data-model/src/revision/group_rev.rs
Normal file
181
shared-lib/flowy-grid-data-model/src/revision/group_rev.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use crate::revision::{gen_grid_group_id, FieldTypeRevision};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Error;
|
||||
use serde_repr::*;
|
||||
|
||||
pub trait GroupConfigurationContentSerde: Sized + Send + Sync {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, serde_json::Error>;
|
||||
|
||||
fn to_configuration_content(&self) -> Result<String, serde_json::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct GroupConfigurationRevision {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
pub groups: Vec<GroupRecordRevision>,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl GroupConfigurationRevision {
|
||||
pub fn new<T>(field_id: String, field_type: FieldTypeRevision, content: T) -> Result<Self, serde_json::Error>
|
||||
where
|
||||
T: GroupConfigurationContentSerde,
|
||||
{
|
||||
let content = content.to_configuration_content()?;
|
||||
Ok(Self {
|
||||
id: gen_grid_group_id(),
|
||||
field_id,
|
||||
field_type_rev: field_type,
|
||||
groups: vec![],
|
||||
content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct TextGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for TextGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct NumberGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for NumberGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct UrlGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for UrlGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct CheckboxGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for CheckboxGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct SelectOptionGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for SelectOptionGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct GroupRecordRevision {
|
||||
pub group_id: String,
|
||||
|
||||
#[serde(default = "DEFAULT_GROUP_RECORD_VISIBILITY")]
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
const DEFAULT_GROUP_RECORD_VISIBILITY: fn() -> bool = || true;
|
||||
|
||||
impl GroupRecordRevision {
|
||||
pub fn new(group_id: String) -> Self {
|
||||
Self {
|
||||
group_id,
|
||||
visible: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct DateGroupConfigurationRevision {
|
||||
pub hide_empty: bool,
|
||||
pub condition: DateCondition,
|
||||
}
|
||||
|
||||
impl GroupConfigurationContentSerde for DateGroupConfigurationRevision {
|
||||
fn from_configuration_content(s: &str) -> Result<Self, Error> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
fn to_configuration_content(&self) -> Result<String, Error> {
|
||||
serde_json::to_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum DateCondition {
|
||||
Relative = 0,
|
||||
Day = 1,
|
||||
Week = 2,
|
||||
Month = 3,
|
||||
Year = 4,
|
||||
}
|
||||
|
||||
impl std::default::Default for DateCondition {
|
||||
fn default() -> Self {
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
mod filter_rev;
|
||||
mod grid_block;
|
||||
mod grid_rev;
|
||||
mod grid_setting_rev;
|
||||
mod grid_view;
|
||||
mod group_rev;
|
||||
|
||||
pub use filter_rev::*;
|
||||
pub use grid_block::*;
|
||||
pub use grid_rev::*;
|
||||
pub use grid_setting_rev::*;
|
||||
pub use grid_view::*;
|
||||
pub use group_rev::*;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::entities::grid::FieldChangesetParams;
|
||||
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
||||
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
||||
@ -162,61 +161,6 @@ impl GridRevisionPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_field_rev<T: JsonDeserializer>(
|
||||
&mut self,
|
||||
changeset: FieldChangesetParams,
|
||||
deserializer: T,
|
||||
) -> CollaborateResult<Option<GridRevisionChangeset>> {
|
||||
let field_id = changeset.field_id.clone();
|
||||
self.modify_field(&field_id, |field| {
|
||||
let mut is_changed = None;
|
||||
if let Some(name) = changeset.name {
|
||||
field.name = name;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(desc) = changeset.desc {
|
||||
field.desc = desc;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(field_type) = changeset.field_type {
|
||||
field.ty = field_type;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(frozen) = changeset.frozen {
|
||||
field.frozen = frozen;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(visibility) = changeset.visibility {
|
||||
field.visibility = visibility;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(width) = changeset.width {
|
||||
field.width = width;
|
||||
is_changed = Some(())
|
||||
}
|
||||
|
||||
if let Some(type_option_data) = changeset.type_option_data {
|
||||
match deserializer.deserialize(type_option_data) {
|
||||
Ok(json_str) => {
|
||||
let field_type = field.ty;
|
||||
field.insert_type_option_str(&field_type, json_str);
|
||||
is_changed = Some(())
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Deserialize data to type option json failed: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(is_changed)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_field_rev(&self, field_id: &str) -> Option<(usize, &Arc<FieldRevision>)> {
|
||||
self.grid_rev
|
||||
.fields
|
||||
@ -399,7 +343,7 @@ impl GridRevisionPad {
|
||||
)
|
||||
}
|
||||
|
||||
fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridRevisionChangeset>>
|
||||
pub fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridRevisionChangeset>>
|
||||
where
|
||||
F: FnOnce(&mut FieldRevision) -> CollaborateResult<Option<()>>,
|
||||
{
|
||||
|
@ -1,11 +1,9 @@
|
||||
use crate::entities::grid::{CreateGridFilterParams, CreateGridGroupParams, GridSettingChangesetParams};
|
||||
use crate::entities::revision::{md5, Revision};
|
||||
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
||||
use flowy_grid_data_model::revision::{
|
||||
gen_grid_filter_id, gen_grid_group_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision,
|
||||
FilterConfigurationsByFieldId, GridViewRevision, GroupConfigurationRevision, GroupConfigurationsByFieldId,
|
||||
SettingRevision, SortConfigurationsByFieldId,
|
||||
FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision,
|
||||
GroupConfigurationRevision, GroupConfigurationsByFieldId,
|
||||
};
|
||||
use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
|
||||
use std::sync::Arc;
|
||||
@ -50,71 +48,11 @@ impl GridViewRevisionPad {
|
||||
Self::from_delta(delta)
|
||||
}
|
||||
|
||||
pub fn get_setting_rev(&self) -> &SettingRevision {
|
||||
&self.view.setting
|
||||
}
|
||||
|
||||
pub fn update_setting(
|
||||
&mut self,
|
||||
changeset: GridSettingChangesetParams,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| {
|
||||
let mut is_changed = None;
|
||||
if let Some(params) = changeset.insert_filter {
|
||||
view.setting.filters.insert_object(
|
||||
¶ms.field_id,
|
||||
¶ms.field_type_rev,
|
||||
make_filter_revision(¶ms),
|
||||
);
|
||||
is_changed = Some(())
|
||||
}
|
||||
if let Some(params) = changeset.delete_filter {
|
||||
if let Some(filters) = view
|
||||
.setting
|
||||
.filters
|
||||
.get_mut_objects(¶ms.field_id, ¶ms.field_type_rev)
|
||||
{
|
||||
filters.retain(|filter| filter.id != params.filter_id);
|
||||
is_changed = Some(())
|
||||
}
|
||||
}
|
||||
if let Some(params) = changeset.insert_group {
|
||||
view.setting.groups.remove_all();
|
||||
view.setting.groups.insert_object(
|
||||
¶ms.field_id,
|
||||
¶ms.field_type_rev,
|
||||
make_group_revision(¶ms),
|
||||
);
|
||||
|
||||
is_changed = Some(());
|
||||
}
|
||||
if let Some(params) = changeset.delete_group {
|
||||
if let Some(groups) = view
|
||||
.setting
|
||||
.groups
|
||||
.get_mut_objects(¶ms.field_id, ¶ms.field_type_rev)
|
||||
{
|
||||
groups.retain(|group| group.id != params.group_id);
|
||||
is_changed = Some(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(is_changed)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
||||
self.setting.groups.get_all_objects(field_revs)
|
||||
}
|
||||
|
||||
pub fn get_groups(
|
||||
&self,
|
||||
field_id: &str,
|
||||
field_type_rev: &FieldTypeRevision,
|
||||
) -> Option<Vec<Arc<GroupConfigurationRevision>>> {
|
||||
self.setting.groups.get_objects(field_id, field_type_rev)
|
||||
self.groups.get_all_objects(field_revs)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub fn insert_group(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
@ -122,13 +60,40 @@ impl GridViewRevisionPad {
|
||||
group_rev: GroupConfigurationRevision,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| {
|
||||
// only one group can be set
|
||||
view.setting.groups.remove_all();
|
||||
view.setting.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(()))
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub fn contains_group(&self, field_id: &str, field_type: &FieldTypeRevision) -> bool {
|
||||
self.view.groups.get_objects(field_id, field_type).is_some()
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub fn with_mut_group<F: FnOnce(&mut GroupConfigurationRevision)>(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
field_type: &FieldTypeRevision,
|
||||
configuration_id: &str,
|
||||
mut_configuration_fn: F,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| match view.groups.get_mut_objects(field_id, field_type) {
|
||||
None => Ok(None),
|
||||
Some(configurations_revs) => {
|
||||
for configuration_rev in configurations_revs {
|
||||
if configuration_rev.id == configuration_id {
|
||||
mut_configuration_fn(Arc::make_mut(configuration_rev));
|
||||
return Ok(Some(()));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_group(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
@ -136,7 +101,7 @@ impl GridViewRevisionPad {
|
||||
group_id: &str,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| {
|
||||
if let Some(groups) = view.setting.groups.get_mut_objects(field_id, field_type) {
|
||||
if let Some(groups) = view.groups.get_mut_objects(field_id, field_type) {
|
||||
groups.retain(|group| group.id != group_id);
|
||||
Ok(Some(()))
|
||||
} else {
|
||||
@ -146,7 +111,7 @@ impl GridViewRevisionPad {
|
||||
}
|
||||
|
||||
pub fn get_all_filters(&self, field_revs: &[Arc<FieldRevision>]) -> Option<FilterConfigurationsByFieldId> {
|
||||
self.setting.filters.get_all_objects(field_revs)
|
||||
self.filters.get_all_objects(field_revs)
|
||||
}
|
||||
|
||||
pub fn get_filters(
|
||||
@ -154,7 +119,7 @@ impl GridViewRevisionPad {
|
||||
field_id: &str,
|
||||
field_type_rev: &FieldTypeRevision,
|
||||
) -> Option<Vec<Arc<FilterConfigurationRevision>>> {
|
||||
self.setting.filters.get_objects(field_id, field_type_rev)
|
||||
self.filters.get_objects(field_id, field_type_rev)
|
||||
}
|
||||
|
||||
pub fn insert_filter(
|
||||
@ -164,7 +129,7 @@ impl GridViewRevisionPad {
|
||||
filter_rev: FilterConfigurationRevision,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| {
|
||||
view.setting.filters.insert_object(field_id, field_type, filter_rev);
|
||||
view.filters.add_object(field_id, field_type, filter_rev);
|
||||
Ok(Some(()))
|
||||
})
|
||||
}
|
||||
@ -176,7 +141,7 @@ impl GridViewRevisionPad {
|
||||
filter_id: &str,
|
||||
) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
|
||||
self.modify(|view| {
|
||||
if let Some(filters) = view.setting.filters.get_mut_objects(field_id, field_type) {
|
||||
if let Some(filters) = view.filters.get_mut_objects(field_id, field_type) {
|
||||
filters.retain(|filter| filter.id != filter_id);
|
||||
Ok(Some(()))
|
||||
} else {
|
||||
@ -185,10 +150,6 @@ impl GridViewRevisionPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_all_sort(&self) -> Option<SortConfigurationsByFieldId> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn json_str(&self) -> CollaborateResult<String> {
|
||||
make_grid_view_rev_json_str(&self.view)
|
||||
}
|
||||
@ -216,24 +177,7 @@ impl GridViewRevisionPad {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision {
|
||||
FilterConfigurationRevision {
|
||||
id: gen_grid_filter_id(),
|
||||
field_id: params.field_id.clone(),
|
||||
condition: params.condition,
|
||||
content: params.content.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision {
|
||||
GroupConfigurationRevision {
|
||||
id: gen_grid_group_id(),
|
||||
field_id: params.field_id.clone(),
|
||||
field_type_rev: params.field_type_rev,
|
||||
content: params.content.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridViewRevisionChangeset {
|
||||
pub delta: TextDelta,
|
||||
pub md5: String,
|
||||
|
@ -1,67 +0,0 @@
|
||||
use flowy_grid_data_model::revision::{FieldTypeRevision, LayoutRevision};
|
||||
|
||||
pub struct GridSettingChangesetParams {
|
||||
pub grid_id: String,
|
||||
pub layout_type: LayoutRevision,
|
||||
pub insert_filter: Option<CreateGridFilterParams>,
|
||||
pub delete_filter: Option<DeleteFilterParams>,
|
||||
pub insert_group: Option<CreateGridGroupParams>,
|
||||
pub delete_group: Option<DeleteGroupParams>,
|
||||
pub insert_sort: Option<CreateGridSortParams>,
|
||||
pub delete_sort: Option<String>,
|
||||
}
|
||||
|
||||
impl GridSettingChangesetParams {
|
||||
pub fn is_filter_changed(&self) -> bool {
|
||||
self.insert_filter.is_some() || self.delete_filter.is_some()
|
||||
}
|
||||
}
|
||||
pub struct CreateGridFilterParams {
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
pub condition: u8,
|
||||
pub content: Option<String>,
|
||||
}
|
||||
|
||||
pub struct DeleteFilterParams {
|
||||
pub field_id: String,
|
||||
pub filter_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
}
|
||||
|
||||
pub struct CreateGridGroupParams {
|
||||
pub field_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
pub content: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub struct DeleteGroupParams {
|
||||
pub field_id: String,
|
||||
pub group_id: String,
|
||||
pub field_type_rev: FieldTypeRevision,
|
||||
}
|
||||
|
||||
pub struct CreateGridSortParams {
|
||||
pub field_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FieldChangesetParams {
|
||||
pub field_id: String,
|
||||
|
||||
pub grid_id: String,
|
||||
|
||||
pub name: Option<String>,
|
||||
|
||||
pub desc: Option<String>,
|
||||
|
||||
pub field_type: Option<FieldTypeRevision>,
|
||||
|
||||
pub frozen: Option<bool>,
|
||||
|
||||
pub visibility: Option<bool>,
|
||||
|
||||
pub width: Option<i32>,
|
||||
|
||||
pub type_option_data: Option<Vec<u8>>,
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
pub mod folder;
|
||||
pub mod grid;
|
||||
pub mod parser;
|
||||
pub mod revision;
|
||||
pub mod text_block;
|
||||
|
Loading…
Reference in New Issue
Block a user