mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #846 from AppFlowy-IO/feat/board_create_card
Feat/board create card
This commit is contained in:
commit
2623974def
@ -52,8 +52,21 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
_startListening();
|
||||
await _loadGrid(emit);
|
||||
},
|
||||
createRow: () {
|
||||
_dataController.createRow();
|
||||
createRow: (groupId) async {
|
||||
final result = await _dataController.createBoardCard(groupId);
|
||||
result.fold(
|
||||
(rowPB) {
|
||||
emit(state.copyWith(editingRow: some(rowPB)));
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
endEditRow: (rowId) {
|
||||
assert(state.editingRow.isSome());
|
||||
state.editingRow.fold(() => null, (row) {
|
||||
assert(row.id == rowId);
|
||||
emit(state.copyWith(editingRow: none()));
|
||||
});
|
||||
},
|
||||
didReceiveGridUpdate: (GridPB grid) {
|
||||
emit(state.copyWith(grid: Some(grid)));
|
||||
@ -99,7 +112,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
|
||||
boardDataController.addColumns(columns);
|
||||
},
|
||||
onRowsChanged: (List<RowInfo> rowInfos, RowChangeReason reason) {
|
||||
onRowsChanged: (List<RowInfo> rowInfos, RowsChangedReason reason) {
|
||||
add(BoardEvent.didReceiveRows(rowInfos));
|
||||
},
|
||||
onError: (err) {
|
||||
@ -140,7 +153,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
@freezed
|
||||
class BoardEvent with _$BoardEvent {
|
||||
const factory BoardEvent.initial() = InitialGrid;
|
||||
const factory BoardEvent.createRow() = _CreateRow;
|
||||
const factory BoardEvent.createRow(String groupId) = _CreateRow;
|
||||
const factory BoardEvent.endEditRow(String rowId) = _EndEditRow;
|
||||
const factory BoardEvent.didReceiveGroups(List<GroupPB> groups) =
|
||||
_DidReceiveGroup;
|
||||
const factory BoardEvent.didReceiveRows(List<RowInfo> rowInfos) =
|
||||
@ -156,6 +170,7 @@ class BoardState with _$BoardState {
|
||||
required String gridId,
|
||||
required Option<GridPB> grid,
|
||||
required List<GroupPB> groups,
|
||||
required Option<RowPB> editingRow,
|
||||
required List<RowInfo> rowInfos,
|
||||
required GridLoadingState loadingState,
|
||||
}) = _BoardState;
|
||||
@ -165,6 +180,7 @@ class BoardState with _$BoardState {
|
||||
groups: [],
|
||||
grid: none(),
|
||||
gridId: gridId,
|
||||
editingRow: none(),
|
||||
loadingState: const _Loading(),
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'dart:async';
|
||||
@ -15,14 +14,14 @@ typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldPB>);
|
||||
typedef OnGridChanged = void Function(GridPB);
|
||||
typedef OnGroupChanged = void Function(List<GroupPB>);
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo> rowInfos,
|
||||
RowChangeReason,
|
||||
List<RowInfo>,
|
||||
RowsChangedReason,
|
||||
);
|
||||
typedef OnError = void Function(FlowyError);
|
||||
|
||||
class BoardDataController {
|
||||
final String gridId;
|
||||
final GridService _gridFFIService;
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldCache fieldCache;
|
||||
|
||||
// key: the block id
|
||||
@ -46,7 +45,7 @@ class BoardDataController {
|
||||
BoardDataController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
_blocks = LinkedHashMap.new(),
|
||||
_gridFFIService = GridService(gridId: view.id),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldCache = GridFieldCache(gridId: view.id);
|
||||
|
||||
void addListener({
|
||||
@ -73,11 +72,11 @@ class BoardDataController {
|
||||
() => result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
_initialBlocks(grid.blocks);
|
||||
|
||||
return await _loadFields(grid).then((result) {
|
||||
return result.fold(
|
||||
(l) {
|
||||
_loadGroups();
|
||||
_loadGroups(grid.blocks);
|
||||
return left(l);
|
||||
},
|
||||
(err) => right(err),
|
||||
@ -89,8 +88,8 @@ class BoardDataController {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow() {
|
||||
return _gridFFIService.createRow();
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) {
|
||||
return _gridFFIService.createBoardCard(groupId);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
@ -102,29 +101,6 @@ class BoardDataController {
|
||||
}
|
||||
}
|
||||
|
||||
void _initialBlocks(List<BlockPB> blocks) {
|
||||
for (final block in blocks) {
|
||||
if (_blocks[block.id] != null) {
|
||||
Log.warn("Initial duplicate block's cache: ${block.id}");
|
||||
return;
|
||||
}
|
||||
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
|
||||
cache.addListener(
|
||||
onChangeReason: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
},
|
||||
);
|
||||
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> _loadFields(GridPB grid) async {
|
||||
final result = await _gridFFIService.getFields(fieldIds: grid.fields);
|
||||
return Future(
|
||||
@ -139,7 +115,20 @@ class BoardDataController {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadGroups() async {
|
||||
Future<void> _loadGroups(List<BlockPB> blocks) async {
|
||||
for (final block in blocks) {
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
|
||||
cache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
|
||||
final result = await _gridFFIService.loadGroups();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
|
@ -74,7 +74,7 @@ class BoardCardEvent with _$BoardCardEvent {
|
||||
const factory BoardCardEvent.initial() = _InitialRow;
|
||||
const factory BoardCardEvent.createRow() = _CreateRow;
|
||||
const factory BoardCardEvent.didReceiveCells(
|
||||
GridCellMap gridCellMap, RowChangeReason reason) = _DidReceiveCells;
|
||||
GridCellMap gridCellMap, RowsChangedReason reason) = _DidReceiveCells;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -83,7 +83,7 @@ class BoardCardState with _$BoardCardState {
|
||||
required RowPB rowPB,
|
||||
required GridCellMap gridCellMap,
|
||||
required UnmodifiableListView<GridCellEquatable> cells,
|
||||
RowChangeReason? changeReason,
|
||||
RowsChangedReason? changeReason,
|
||||
}) = _BoardCardState;
|
||||
|
||||
factory BoardCardState.initial(RowPB rowPB, GridCellMap cellDataMap) =>
|
||||
|
@ -6,7 +6,7 @@ import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
typedef OnCardChanged = void Function(GridCellMap, RowChangeReason);
|
||||
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
||||
class CardDataController extends BoardCellBuilderDelegate {
|
||||
final RowPB rowPB;
|
||||
|
@ -53,7 +53,7 @@ class BoardContent extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
child: AFBoard(
|
||||
key: UniqueKey(),
|
||||
// key: UniqueKey(),
|
||||
scrollController: ScrollController(),
|
||||
dataController: context.read<BoardBloc>().boardDataController,
|
||||
headerBuilder: _buildHeader,
|
||||
@ -83,11 +83,13 @@ class BoardContent extends StatelessWidget {
|
||||
|
||||
Widget _buildFooter(BuildContext context, AFBoardColumnData columnData) {
|
||||
return AppFlowyColumnFooter(
|
||||
icon: const Icon(Icons.add, size: 20),
|
||||
title: const Text('New'),
|
||||
height: 50,
|
||||
margin: config.columnItemPadding,
|
||||
);
|
||||
icon: const Icon(Icons.add, size: 20),
|
||||
title: const Text('New'),
|
||||
height: 50,
|
||||
margin: config.columnItemPadding,
|
||||
onAddButtonClick: () {
|
||||
context.read<BoardBloc>().add(BoardEvent.createRow(columnData.id));
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildCard(BuildContext context, AFColumnItem item) {
|
||||
@ -106,13 +108,21 @@ class BoardContent extends StatelessWidget {
|
||||
);
|
||||
|
||||
final cellBuilder = BoardCellBuilder(cardController);
|
||||
final isEditing = context.read<BoardBloc>().state.editingRow.fold(
|
||||
() => false,
|
||||
(editingRow) => editingRow.id == rowPB.id,
|
||||
);
|
||||
|
||||
return AppFlowyColumnItemCard(
|
||||
key: ObjectKey(item),
|
||||
child: BoardCard(
|
||||
gridId: gridId,
|
||||
isEditing: isEditing,
|
||||
cellBuilder: cellBuilder,
|
||||
dataController: cardController,
|
||||
gridId: gridId,
|
||||
onEditEditing: (rowId) {
|
||||
context.read<BoardBloc>().add(BoardEvent.endEditRow(rowId));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -9,15 +9,21 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'card_cell_builder.dart';
|
||||
import 'card_container.dart';
|
||||
|
||||
typedef OnEndEditing = void Function(String rowId);
|
||||
|
||||
class BoardCard extends StatefulWidget {
|
||||
final String gridId;
|
||||
final bool isEditing;
|
||||
final CardDataController dataController;
|
||||
final BoardCellBuilder cellBuilder;
|
||||
final OnEndEditing onEditEditing;
|
||||
|
||||
const BoardCard({
|
||||
required this.gridId,
|
||||
required this.isEditing,
|
||||
required this.dataController,
|
||||
required this.cellBuilder,
|
||||
required this.onEditEditing,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -60,7 +66,6 @@ class _BoardCardState extends State<BoardCard> {
|
||||
return cellMap.values.map(
|
||||
(cellId) {
|
||||
final child = widget.cellBuilder.buildCell(cellId);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
child: child,
|
||||
|
@ -29,9 +29,13 @@ class BoardCardContainer extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: container,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 30),
|
||||
child: container,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -42,7 +42,7 @@ class GridBlockCache {
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required void Function(RowChangeReason) onChangeReason,
|
||||
required void Function(RowsChangedReason) onRowsChanged,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
_rowCache.onRowsChanged((reason) {
|
||||
@ -50,7 +50,7 @@ class GridBlockCache {
|
||||
return;
|
||||
}
|
||||
|
||||
onChangeReason(reason);
|
||||
onRowsChanged(reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class GridEvent with _$GridEvent {
|
||||
const factory GridEvent.createRow() = _CreateRow;
|
||||
const factory GridEvent.didReceiveRowUpdate(
|
||||
List<RowInfo> rows,
|
||||
RowChangeReason listState,
|
||||
RowsChangedReason listState,
|
||||
) = _DidReceiveRowUpdate;
|
||||
const factory GridEvent.didReceiveFieldUpdate(
|
||||
UnmodifiableListView<FieldPB> fields,
|
||||
@ -117,7 +117,7 @@ class GridState with _$GridState {
|
||||
required GridFieldEquatable fields,
|
||||
required List<RowInfo> rowInfos,
|
||||
required GridLoadingState loadingState,
|
||||
required RowChangeReason reason,
|
||||
required RowsChangedReason reason,
|
||||
}) = _GridState;
|
||||
|
||||
factory GridState.initial(String gridId) => GridState(
|
||||
|
@ -18,13 +18,13 @@ typedef OnGridChanged = void Function(GridPB);
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo> rowInfos,
|
||||
RowChangeReason,
|
||||
RowsChangedReason,
|
||||
);
|
||||
typedef ListenOnRowChangedCondition = bool Function();
|
||||
|
||||
class GridDataController {
|
||||
final String gridId;
|
||||
final GridService _gridFFIService;
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldCache fieldCache;
|
||||
|
||||
// key: the block id
|
||||
@ -47,7 +47,7 @@ class GridDataController {
|
||||
GridDataController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
_blocks = LinkedHashMap.new(),
|
||||
_gridFFIService = GridService(gridId: view.id),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldCache = GridFieldCache(gridId: view.id);
|
||||
|
||||
void addListener({
|
||||
@ -105,7 +105,7 @@ class GridDataController {
|
||||
);
|
||||
|
||||
cache.addListener(
|
||||
onChangeReason: (reason) {
|
||||
onRowsChanged: (reason) {
|
||||
_onRowChanged?.call(rowInfos, reason);
|
||||
},
|
||||
);
|
||||
|
@ -3,14 +3,15 @@ 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';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
class GridService {
|
||||
class GridFFIService {
|
||||
final String gridId;
|
||||
GridService({
|
||||
GridFFIService({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
@ -27,6 +28,13 @@ class GridService {
|
||||
return GridEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) {
|
||||
CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
|
||||
..gridId = gridId
|
||||
..groupId = groupId;
|
||||
return GridEventCreateBoardCard(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RepeatedFieldPB, FlowyError>> getFields(
|
||||
{required List<FieldIdPB> fieldIds}) {
|
||||
final payload = QueryFieldPayloadPB.create()
|
||||
|
@ -14,13 +14,13 @@ class RowActionSheetBloc
|
||||
extends Bloc<RowActionSheetEvent, RowActionSheetState> {
|
||||
final RowFFIService _rowService;
|
||||
|
||||
RowActionSheetBloc({required RowInfo rowData})
|
||||
RowActionSheetBloc({required RowInfo rowInfo})
|
||||
: _rowService = RowFFIService(
|
||||
gridId: rowData.gridId,
|
||||
blockId: rowData.blockId,
|
||||
rowId: rowData.id,
|
||||
gridId: rowInfo.gridId,
|
||||
blockId: rowInfo.blockId,
|
||||
rowId: rowInfo.rowPB.id,
|
||||
),
|
||||
super(RowActionSheetState.initial(rowData)) {
|
||||
super(RowActionSheetState.initial(rowInfo)) {
|
||||
on<RowActionSheetEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
|
@ -21,7 +21,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
}) : _rowService = RowFFIService(
|
||||
gridId: rowInfo.gridId,
|
||||
blockId: rowInfo.blockId,
|
||||
rowId: rowInfo.id,
|
||||
rowId: rowInfo.rowPB.id,
|
||||
),
|
||||
_dataController = dataController,
|
||||
super(RowState.initial(rowInfo, dataController.loadData())) {
|
||||
@ -71,7 +71,7 @@ class RowEvent with _$RowEvent {
|
||||
const factory RowEvent.initial() = _InitialRow;
|
||||
const factory RowEvent.createRow() = _CreateRow;
|
||||
const factory RowEvent.didReceiveCells(
|
||||
GridCellMap gridCellMap, RowChangeReason reason) = _DidReceiveCells;
|
||||
GridCellMap gridCellMap, RowsChangedReason reason) = _DidReceiveCells;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -80,7 +80,7 @@ class RowState with _$RowState {
|
||||
required RowInfo rowInfo,
|
||||
required GridCellMap gridCellMap,
|
||||
required UnmodifiableListView<GridCellEquatable> cells,
|
||||
RowChangeReason? changeReason,
|
||||
RowsChangedReason? changeReason,
|
||||
}) = _RowState;
|
||||
|
||||
factory RowState.initial(RowInfo rowInfo, GridCellMap cellDataMap) =>
|
||||
|
@ -51,11 +51,9 @@ class GridRowCache {
|
||||
_fieldNotifier = notifier {
|
||||
//
|
||||
notifier.onRowFieldsChanged(() => _rowChangeReasonNotifier
|
||||
.receive(const RowChangeReason.fieldDidChange()));
|
||||
.receive(const RowsChangedReason.fieldDidChange()));
|
||||
notifier.onRowFieldChanged((field) => _cellCache.remove(field.id));
|
||||
_rowInfos = block.rows
|
||||
.map((rowInfo) => buildGridRow(rowInfo.id, rowInfo.height.toDouble()))
|
||||
.toList();
|
||||
_rowInfos = block.rows.map((rowPB) => buildGridRow(rowPB)).toList();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
@ -85,16 +83,16 @@ class GridRowCache {
|
||||
for (var rowId in deletedRows) rowId: rowId
|
||||
};
|
||||
|
||||
_rowInfos.asMap().forEach((index, row) {
|
||||
if (deletedRowByRowId[row.id] == null) {
|
||||
newRows.add(row);
|
||||
_rowInfos.asMap().forEach((index, RowInfo rowInfo) {
|
||||
if (deletedRowByRowId[rowInfo.rowPB.id] == null) {
|
||||
newRows.add(rowInfo);
|
||||
} else {
|
||||
_rowByRowId.remove(row.id);
|
||||
deletedIndex.add(DeletedIndex(index: index, row: row));
|
||||
_rowByRowId.remove(rowInfo.rowPB.id);
|
||||
deletedIndex.add(DeletedIndex(index: index, row: rowInfo));
|
||||
}
|
||||
});
|
||||
_rowInfos = newRows;
|
||||
_rowChangeReasonNotifier.receive(RowChangeReason.delete(deletedIndex));
|
||||
_rowChangeReasonNotifier.receive(RowsChangedReason.delete(deletedIndex));
|
||||
}
|
||||
|
||||
void _insertRows(List<InsertedRowPB> insertRows) {
|
||||
@ -103,39 +101,42 @@ class GridRowCache {
|
||||
}
|
||||
|
||||
InsertedIndexs insertIndexs = [];
|
||||
for (final insertRow in insertRows) {
|
||||
for (final InsertedRowPB insertRow in insertRows) {
|
||||
final insertIndex = InsertedIndex(
|
||||
index: insertRow.index,
|
||||
rowId: insertRow.rowId,
|
||||
rowId: insertRow.row.id,
|
||||
);
|
||||
insertIndexs.add(insertIndex);
|
||||
_rowInfos.insert(insertRow.index,
|
||||
(buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
|
||||
_rowInfos.insert(
|
||||
insertRow.index,
|
||||
(buildGridRow(insertRow.row)),
|
||||
);
|
||||
}
|
||||
|
||||
_rowChangeReasonNotifier.receive(RowChangeReason.insert(insertIndexs));
|
||||
_rowChangeReasonNotifier.receive(RowsChangedReason.insert(insertIndexs));
|
||||
}
|
||||
|
||||
void _updateRows(List<UpdatedRowPB> updatedRows) {
|
||||
void _updateRows(List<RowPB> updatedRows) {
|
||||
if (updatedRows.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||
for (final updatedRow in updatedRows) {
|
||||
final rowId = updatedRow.rowId;
|
||||
final index = _rowInfos.indexWhere((row) => row.id == rowId);
|
||||
for (final RowPB updatedRow in updatedRows) {
|
||||
final rowId = updatedRow.id;
|
||||
final index = _rowInfos.indexWhere(
|
||||
(rowInfo) => rowInfo.rowPB.id == rowId,
|
||||
);
|
||||
if (index != -1) {
|
||||
_rowByRowId[rowId] = updatedRow.row;
|
||||
_rowByRowId[rowId] = updatedRow;
|
||||
|
||||
_rowInfos.removeAt(index);
|
||||
_rowInfos.insert(
|
||||
index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
|
||||
_rowInfos.insert(index, buildGridRow(updatedRow));
|
||||
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
|
||||
}
|
||||
}
|
||||
|
||||
_rowChangeReasonNotifier.receive(RowChangeReason.update(updatedIndexs));
|
||||
_rowChangeReasonNotifier.receive(RowsChangedReason.update(updatedIndexs));
|
||||
}
|
||||
|
||||
void _hideRows(List<String> hideRows) {}
|
||||
@ -143,7 +144,7 @@ class GridRowCache {
|
||||
void _showRows(List<String> visibleRows) {}
|
||||
|
||||
void onRowsChanged(
|
||||
void Function(RowChangeReason) onRowChanged,
|
||||
void Function(RowsChangedReason) onRowChanged,
|
||||
) {
|
||||
_rowChangeReasonNotifier.addListener(() {
|
||||
onRowChanged(_rowChangeReasonNotifier.reason);
|
||||
@ -152,7 +153,7 @@ class GridRowCache {
|
||||
|
||||
RowUpdateCallback addListener({
|
||||
required String rowId,
|
||||
void Function(GridCellMap, RowChangeReason)? onCellUpdated,
|
||||
void Function(GridCellMap, RowsChangedReason)? onCellUpdated,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
listenerHandler() async {
|
||||
@ -230,40 +231,43 @@ class GridRowCache {
|
||||
|
||||
_rowByRowId[updatedRow.id] = updatedRow;
|
||||
final index =
|
||||
_rowInfos.indexWhere((gridRow) => gridRow.id == updatedRow.id);
|
||||
_rowInfos.indexWhere((rowInfo) => rowInfo.rowPB.id == updatedRow.id);
|
||||
if (index != -1) {
|
||||
// update the corresponding row in _rows if they are not the same
|
||||
if (_rowInfos[index].rawRow != updatedRow) {
|
||||
final row = _rowInfos.removeAt(index).copyWith(rawRow: updatedRow);
|
||||
_rowInfos.insert(index, row);
|
||||
if (_rowInfos[index].rowPB != updatedRow) {
|
||||
final rowInfo = _rowInfos.removeAt(index).copyWith(rowPB: updatedRow);
|
||||
_rowInfos.insert(index, rowInfo);
|
||||
|
||||
// Calculate the update index
|
||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||
updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id);
|
||||
updatedIndexs[rowInfo.rowPB.id] = UpdatedIndex(
|
||||
index: index,
|
||||
rowId: rowInfo.rowPB.id,
|
||||
);
|
||||
|
||||
//
|
||||
_rowChangeReasonNotifier.receive(RowChangeReason.update(updatedIndexs));
|
||||
_rowChangeReasonNotifier
|
||||
.receive(RowsChangedReason.update(updatedIndexs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowInfo buildGridRow(String rowId, double rowHeight) {
|
||||
RowInfo buildGridRow(RowPB rowPB) {
|
||||
return RowInfo(
|
||||
gridId: gridId,
|
||||
blockId: block.id,
|
||||
fields: _fieldNotifier.fields,
|
||||
id: rowId,
|
||||
height: rowHeight,
|
||||
rowPB: rowPB,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RowChangesetNotifier extends ChangeNotifier {
|
||||
RowChangeReason reason = const InitialListState();
|
||||
RowsChangedReason reason = const InitialListState();
|
||||
|
||||
_RowChangesetNotifier();
|
||||
|
||||
void receive(RowChangeReason newReason) {
|
||||
void receive(RowsChangedReason newReason) {
|
||||
reason = newReason;
|
||||
reason.map(
|
||||
insert: (_) => notifyListeners(),
|
||||
@ -280,10 +284,8 @@ class RowInfo with _$RowInfo {
|
||||
const factory RowInfo({
|
||||
required String gridId,
|
||||
required String blockId,
|
||||
required String id,
|
||||
required UnmodifiableListView<FieldPB> fields,
|
||||
required double height,
|
||||
RowPB? rawRow,
|
||||
required RowPB rowPB,
|
||||
}) = _RowInfo;
|
||||
}
|
||||
|
||||
@ -292,12 +294,12 @@ typedef DeletedIndexs = List<DeletedIndex>;
|
||||
typedef UpdatedIndexs = LinkedHashMap<String, UpdatedIndex>;
|
||||
|
||||
@freezed
|
||||
class RowChangeReason with _$RowChangeReason {
|
||||
const factory RowChangeReason.insert(InsertedIndexs items) = _Insert;
|
||||
const factory RowChangeReason.delete(DeletedIndexs items) = _Delete;
|
||||
const factory RowChangeReason.update(UpdatedIndexs indexs) = _Update;
|
||||
const factory RowChangeReason.fieldDidChange() = _FieldDidChange;
|
||||
const factory RowChangeReason.initial() = InitialListState;
|
||||
class RowsChangedReason with _$RowsChangedReason {
|
||||
const factory RowsChangedReason.insert(InsertedIndexs items) = _Insert;
|
||||
const factory RowsChangedReason.delete(DeletedIndexs items) = _Delete;
|
||||
const factory RowsChangedReason.update(UpdatedIndexs indexs) = _Update;
|
||||
const factory RowsChangedReason.fieldDidChange() = _FieldDidChange;
|
||||
const factory RowsChangedReason.initial() = InitialListState;
|
||||
}
|
||||
|
||||
class InsertedIndex {
|
||||
|
@ -5,7 +5,7 @@ import '../cell/cell_service/cell_service.dart';
|
||||
import '../field/field_cache.dart';
|
||||
import 'row_cache.dart';
|
||||
|
||||
typedef OnRowChanged = void Function(GridCellMap, RowChangeReason);
|
||||
typedef OnRowChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
||||
class GridRowDataController extends GridCellBuilderDelegate {
|
||||
final RowInfo rowInfo;
|
||||
@ -21,12 +21,12 @@ class GridRowDataController extends GridCellBuilderDelegate {
|
||||
_rowCache = rowCache;
|
||||
|
||||
GridCellMap loadData() {
|
||||
return _rowCache.loadGridCells(rowInfo.id);
|
||||
return _rowCache.loadGridCells(rowInfo.rowPB.id);
|
||||
}
|
||||
|
||||
void addListener({OnRowChanged? onRowChanged}) {
|
||||
_onRowChangedListeners.add(_rowCache.addListener(
|
||||
rowId: rowInfo.id,
|
||||
rowId: rowInfo.rowPB.id,
|
||||
onCellUpdated: onRowChanged,
|
||||
));
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
Animation<double> animation,
|
||||
) {
|
||||
final rowCache =
|
||||
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id);
|
||||
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.rowPB.id);
|
||||
|
||||
/// Return placeholder widget if the rowCache is null.
|
||||
if (rowCache == null) return const SizedBox();
|
||||
@ -267,7 +267,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
cellBuilder,
|
||||
);
|
||||
},
|
||||
key: ValueKey(rowInfo.id),
|
||||
key: ValueKey(rowInfo.rowPB.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
value: _rowBloc,
|
||||
child: _RowEnterRegion(
|
||||
child: BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (p, c) => p.rowInfo.height != c.rowInfo.height,
|
||||
buildWhen: (p, c) => p.rowInfo.rowPB.height != c.rowInfo.rowPB.height,
|
||||
builder: (context, state) {
|
||||
final children = [
|
||||
const _RowLeading(),
|
||||
|
@ -21,7 +21,7 @@ class GridRowActionSheet extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => RowActionSheetBloc(rowData: rowData),
|
||||
create: (context) => RowActionSheetBloc(rowInfo: rowData),
|
||||
child: BlocBuilder<RowActionSheetBloc, RowActionSheetState>(
|
||||
builder: (context, state) {
|
||||
final cells = _RowAction.values
|
||||
|
@ -11,6 +11,7 @@ pub enum GridNotification {
|
||||
DidUpdateRow = 30,
|
||||
DidUpdateCell = 40,
|
||||
DidUpdateField = 50,
|
||||
DidUpdateBoard = 60,
|
||||
}
|
||||
|
||||
impl std::default::Default for GridNotification {
|
||||
|
@ -106,48 +106,15 @@ impl std::convert::From<Vec<BlockPB>> for RepeatedBlockPB {
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct InsertedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
pub row: RowPB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub row_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub height: i32,
|
||||
|
||||
#[pb(index = 4, one_of)]
|
||||
#[pb(index = 2, one_of)]
|
||||
pub index: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct UpdatedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub row_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub row: RowPB,
|
||||
}
|
||||
|
||||
impl UpdatedRowPB {
|
||||
pub fn new(row_rev: &RowRevision, row: RowPB) -> Self {
|
||||
Self {
|
||||
row_id: row_rev.id.clone(),
|
||||
block_id: row_rev.block_id.clone(),
|
||||
row,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<RowPB> for InsertedRowPB {
|
||||
fn from(row_info: RowPB) -> Self {
|
||||
Self {
|
||||
row_id: row_info.id,
|
||||
block_id: row_info.block_id,
|
||||
height: row_info.height,
|
||||
index: None,
|
||||
}
|
||||
fn from(row: RowPB) -> Self {
|
||||
Self { row, index: None }
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +137,7 @@ pub struct GridBlockChangesetPB {
|
||||
pub deleted_rows: Vec<String>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub updated_rows: Vec<UpdatedRowPB>,
|
||||
pub updated_rows: Vec<RowPB>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub visible_rows: Vec<String>,
|
||||
@ -179,9 +146,9 @@ pub struct GridBlockChangesetPB {
|
||||
pub hide_rows: Vec<String>,
|
||||
}
|
||||
impl GridBlockChangesetPB {
|
||||
pub fn insert(block_id: &str, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
||||
pub fn insert(block_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
block_id,
|
||||
inserted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
@ -195,7 +162,7 @@ impl GridBlockChangesetPB {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowPB>) -> Self {
|
||||
pub fn update(block_id: &str, updated_rows: Vec<RowPB>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
updated_rows,
|
||||
|
@ -0,0 +1,70 @@
|
||||
use crate::entities::RowPB;
|
||||
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,
|
||||
}
|
||||
pub struct CreateBoardCardParams {
|
||||
pub grid_id: String,
|
||||
pub group_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<CreateBoardCardParams> for CreateBoardCardPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateBoardCardParams, 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(CreateBoardCardParams {
|
||||
grid_id: grid_id.0,
|
||||
group_id: group_id.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct BoardCardChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub group_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub inserted_cards: Vec<RowPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub deleted_cards: Vec<String>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub updated_cards: Vec<RowPB>,
|
||||
}
|
||||
impl BoardCardChangesetPB {
|
||||
pub fn insert(group_id: String, inserted_cards: Vec<RowPB>) -> Self {
|
||||
Self {
|
||||
group_id,
|
||||
inserted_cards,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(group_id: String, deleted_cards: Vec<String>) -> Self {
|
||||
Self {
|
||||
group_id,
|
||||
deleted_cards,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(group_id: String, updated_cards: Vec<RowPB>) -> Self {
|
||||
Self {
|
||||
group_id,
|
||||
updated_cards,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
mod board_card;
|
||||
mod configuration;
|
||||
mod group;
|
||||
|
||||
pub use board_card::*;
|
||||
pub use configuration::*;
|
||||
pub use group::*;
|
||||
|
@ -232,10 +232,7 @@ pub(crate) async fn get_row_handler(
|
||||
) -> DataResult<OptionalRowPB, FlowyError> {
|
||||
let params: RowIdParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(¶ms.grid_id)?;
|
||||
let row = editor
|
||||
.get_row_rev(¶ms.row_id)
|
||||
.await?
|
||||
.and_then(make_row_from_row_rev);
|
||||
let row = editor.get_row_rev(¶ms.row_id).await?.map(make_row_from_row_rev);
|
||||
|
||||
data_result(OptionalRowPB { row })
|
||||
}
|
||||
@ -266,11 +263,11 @@ pub(crate) async fn duplicate_row_handler(
|
||||
pub(crate) async fn create_row_handler(
|
||||
data: Data<CreateRowPayloadPB>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
) -> DataResult<RowPB, FlowyError> {
|
||||
let params: CreateRowParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
|
||||
let _ = editor.create_row(params.start_row_id).await?;
|
||||
Ok(())
|
||||
let row = editor.create_row(params.start_row_id).await?;
|
||||
data_result(row)
|
||||
}
|
||||
|
||||
// #[tracing::instrument(level = "debug", skip_all, err)]
|
||||
@ -416,3 +413,14 @@ pub(crate) async fn get_groups_handler(
|
||||
let group = editor.load_groups().await?;
|
||||
data_result(group)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn create_board_card_handler(
|
||||
data: Data<CreateBoardCardPayloadPB>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> DataResult<RowPB, FlowyError> {
|
||||
let params: CreateBoardCardParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
|
||||
let row = editor.create_board_card(¶ms.group_id).await?;
|
||||
data_result(row)
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
// Date
|
||||
.event(GridEvent::UpdateDateCell, update_date_cell_handler)
|
||||
// Group
|
||||
.event(GridEvent::CreateBoardCard, create_board_card_handler)
|
||||
.event(GridEvent::GetGroup, get_groups_handler);
|
||||
|
||||
module
|
||||
@ -209,4 +210,7 @@ pub enum GridEvent {
|
||||
|
||||
#[event(input = "GridIdPB", output = "RepeatedGridGroupPB")]
|
||||
GetGroup = 100,
|
||||
|
||||
#[event(input = "CreateBoardCardPayloadPB", output = "RowPB")]
|
||||
CreateBoardCard = 110,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB, UpdatedRowPB};
|
||||
use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_revision_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor};
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::row::{block_from_row_orders, GridBlockSnapshot};
|
||||
use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlockSnapshot};
|
||||
use dashmap::DashMap;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
@ -62,22 +62,16 @@ impl GridBlockManager {
|
||||
Ok(self.get_editor(&block_id).await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn create_row(
|
||||
&self,
|
||||
block_id: &str,
|
||||
row_rev: RowRevision,
|
||||
start_row_id: Option<String>,
|
||||
) -> FlowyResult<i32> {
|
||||
pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<i32> {
|
||||
let block_id = row_rev.block_id.clone();
|
||||
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
||||
let editor = self.get_editor(&row_rev.block_id).await?;
|
||||
|
||||
let mut index_row_order = InsertedRowPB::from(&row_rev);
|
||||
let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
|
||||
index_row_order.index = row_index;
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_block(block_id, GridBlockChangesetPB::insert(block_id, vec![index_row_order]))
|
||||
.await?;
|
||||
let changeset = GridBlockChangesetPB::insert(block_id.clone(), vec![index_row_order]);
|
||||
let _ = self.notify_did_update_block(&block_id, changeset).await?;
|
||||
Ok(row_count)
|
||||
}
|
||||
|
||||
@ -98,10 +92,16 @@ impl GridBlockManager {
|
||||
row_order.index = index;
|
||||
inserted_row_orders.push(row_order);
|
||||
}
|
||||
changesets.push(GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count));
|
||||
changesets.push(GridBlockMetaRevisionChangeset::from_row_count(
|
||||
block_id.clone(),
|
||||
row_count,
|
||||
));
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_block(&block_id, GridBlockChangesetPB::insert(&block_id, inserted_row_orders))
|
||||
.notify_did_update_block(
|
||||
&block_id,
|
||||
GridBlockChangesetPB::insert(block_id.clone(), inserted_row_orders),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@ -110,20 +110,18 @@ impl GridBlockManager {
|
||||
|
||||
pub async fn update_row<F>(&self, changeset: RowMetaChangeset, row_builder: F) -> FlowyResult<()>
|
||||
where
|
||||
F: FnOnce(Arc<RowRevision>) -> Option<RowPB>,
|
||||
F: FnOnce(Arc<RowRevision>) -> RowPB,
|
||||
{
|
||||
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
|
||||
let _ = editor.update_row(changeset.clone()).await?;
|
||||
match editor.get_row_rev(&changeset.row_id).await? {
|
||||
None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id),
|
||||
Some(row_rev) => {
|
||||
if let Some(row) = row_builder(row_rev.clone()) {
|
||||
let row_order = UpdatedRowPB::new(&row_rev, row);
|
||||
let block_order_changeset = GridBlockChangesetPB::update(&editor.block_id, vec![row_order]);
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
||||
.await?;
|
||||
}
|
||||
let block_order_changeset =
|
||||
GridBlockChangesetPB::update(&editor.block_id, vec![row_builder(row_rev.clone())]);
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -156,46 +154,40 @@ impl GridBlockManager {
|
||||
.map(|row_info| Cow::Owned(row_info.row_id().to_owned()))
|
||||
.collect::<Vec<Cow<String>>>();
|
||||
let row_count = editor.delete_rows(row_ids).await?;
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(&grid_block.id, row_count);
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(grid_block.id.clone(), row_count);
|
||||
changesets.push(changeset);
|
||||
}
|
||||
|
||||
Ok(changesets)
|
||||
}
|
||||
|
||||
pub(crate) async fn move_row(&self, row_id: &str, from: usize, to: usize) -> FlowyResult<()> {
|
||||
let editor = self.get_editor_from_row_id(row_id).await?;
|
||||
let _ = editor.move_row(row_id, from, to).await?;
|
||||
pub(crate) async fn move_row(&self, row_rev: Arc<RowRevision>, from: usize, to: usize) -> FlowyResult<()> {
|
||||
let editor = self.get_editor_from_row_id(&row_rev.id).await?;
|
||||
let _ = editor.move_row(&row_rev.id, from, to).await?;
|
||||
|
||||
match editor.get_row_revs(Some(vec![Cow::Borrowed(row_id)])).await?.pop() {
|
||||
None => {}
|
||||
Some(row_rev) => {
|
||||
let insert_row = InsertedRowPB {
|
||||
block_id: row_rev.block_id.clone(),
|
||||
row_id: row_rev.id.clone(),
|
||||
index: Some(to as i32),
|
||||
height: row_rev.height,
|
||||
};
|
||||
let delete_row_id = row_rev.id.clone();
|
||||
let insert_row = InsertedRowPB {
|
||||
index: Some(to as i32),
|
||||
row: make_row_from_row_rev(row_rev),
|
||||
};
|
||||
|
||||
let notified_changeset = GridBlockChangesetPB {
|
||||
block_id: editor.block_id.clone(),
|
||||
inserted_rows: vec![insert_row],
|
||||
deleted_rows: vec![row_rev.id.clone()],
|
||||
..Default::default()
|
||||
};
|
||||
let notified_changeset = GridBlockChangesetPB {
|
||||
block_id: editor.block_id.clone(),
|
||||
inserted_rows: vec![insert_row],
|
||||
deleted_rows: vec![delete_row_id],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, notified_changeset)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, notified_changeset)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_cell<F>(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()>
|
||||
where
|
||||
F: FnOnce(Arc<RowRevision>) -> Option<RowPB>,
|
||||
F: FnOnce(Arc<RowRevision>) -> RowPB,
|
||||
{
|
||||
let row_changeset: RowMetaChangeset = changeset.clone().into();
|
||||
let _ = self.update_row(row_changeset, row_builder).await?;
|
||||
|
@ -135,6 +135,47 @@ pub fn try_decode_cell_data(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_text_cell(s: String, field_rev: &FieldRevision) -> CellRevision {
|
||||
let data = apply_cell_data_changeset(s, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn insert_number_cell(num: i64, field_rev: &FieldRevision) -> CellRevision {
|
||||
let data = apply_cell_data_changeset(num, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn insert_url_cell(url: String, field_rev: &FieldRevision) -> CellRevision {
|
||||
let data = apply_cell_data_changeset(url, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn insert_checkbox_cell(is_check: bool, field_rev: &FieldRevision) -> CellRevision {
|
||||
let s = if is_check {
|
||||
CHECK.to_string()
|
||||
} else {
|
||||
UNCHECK.to_string()
|
||||
};
|
||||
let data = apply_cell_data_changeset(s, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn insert_date_cell(timestamp: i64, field_rev: &FieldRevision) -> CellRevision {
|
||||
let cell_data = serde_json::to_string(&DateCellChangesetPB {
|
||||
date: Some(timestamp.to_string()),
|
||||
time: None,
|
||||
})
|
||||
.unwrap();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
pub fn insert_select_option_cell(option_id: String, field_rev: &FieldRevision) -> CellRevision {
|
||||
let cell_data = SelectOptionCellChangeset::from_insert(&option_id).to_str();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
|
||||
CellRevision::new(data)
|
||||
}
|
||||
|
||||
/// If the cell data is not String type, it should impl this trait.
|
||||
/// Deserialize the String into cell specific data type.
|
||||
pub trait FromCellString {
|
||||
|
@ -37,7 +37,7 @@ pub struct GridRevisionEditor {
|
||||
pub(crate) filter_service: Arc<GridFilterService>,
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) group_service: Arc<GridGroupService>,
|
||||
pub(crate) group_service: Arc<RwLock<GridGroupService>>,
|
||||
}
|
||||
|
||||
impl Drop for GridRevisionEditor {
|
||||
@ -62,17 +62,17 @@ impl GridRevisionEditor {
|
||||
let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
|
||||
let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
|
||||
let filter_service =
|
||||
Arc::new(GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
|
||||
GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
|
||||
let group_service =
|
||||
Arc::new(GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
|
||||
GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
|
||||
let editor = Arc::new(Self {
|
||||
grid_id: grid_id.to_owned(),
|
||||
user,
|
||||
grid_pad,
|
||||
rev_manager,
|
||||
block_manager,
|
||||
filter_service,
|
||||
group_service,
|
||||
filter_service: Arc::new(filter_service),
|
||||
group_service: Arc::new(RwLock::new(group_service)),
|
||||
});
|
||||
|
||||
Ok(editor)
|
||||
@ -275,20 +275,8 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowPB> {
|
||||
let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
|
||||
let block_id = self.block_id().await?;
|
||||
|
||||
// insert empty row below the row whose id is upper_row_id
|
||||
let row_rev = RowRevisionBuilder::new(&block_id, &field_revs).build();
|
||||
let row_order = RowPB::from(&row_rev);
|
||||
|
||||
// insert the row
|
||||
let row_count = self.block_manager.create_row(&block_id, row_rev, start_row_id).await?;
|
||||
|
||||
// update block row count
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count);
|
||||
let _ = self.update_block(changeset).await?;
|
||||
Ok(row_order)
|
||||
let row_rev = self.create_row_rev().await?;
|
||||
self.create_row_pb(row_rev, start_row_id).await
|
||||
}
|
||||
|
||||
pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
|
||||
@ -338,6 +326,7 @@ impl GridRevisionEditor {
|
||||
|
||||
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.block_manager.delete_row(row_id).await?;
|
||||
self.group_service.read().await.did_delete_card(row_id.to_owned()).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -529,10 +518,22 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
|
||||
let _ = self.block_manager.move_row(row_id, from as usize, to as usize).await?;
|
||||
match self.block_manager.get_row_rev(row_id).await? {
|
||||
None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
|
||||
Some(row_rev) => {
|
||||
let _ = self
|
||||
.block_manager
|
||||
.move_row(row_rev.clone(), from as usize, to as usize)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_board_card(&self, group_id: &str, from: i32, to: i32) -> FlowyResult<()> {
|
||||
self.group_service.write().await.move_card(group_id, from, to).await;
|
||||
Ok(())
|
||||
}
|
||||
pub async fn delta_bytes(&self) -> Bytes {
|
||||
self.grid_pad.read().await.delta_bytes()
|
||||
}
|
||||
@ -564,12 +565,48 @@ impl GridRevisionEditor {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_board_card(&self, group_id: &str) -> FlowyResult<RowPB> {
|
||||
let mut row_rev = self.create_row_rev().await?;
|
||||
let _ = self
|
||||
.group_service
|
||||
.write()
|
||||
.await
|
||||
.update_board_card(&mut row_rev, group_id)
|
||||
.await;
|
||||
|
||||
let row_pb = self.create_row_pb(row_rev, None).await?;
|
||||
self.group_service.read().await.did_create_card(group_id, &row_pb).await;
|
||||
Ok(row_pb)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
|
||||
let groups = self.group_service.load_groups().await.unwrap_or_default();
|
||||
let groups = self.group_service.write().await.load_groups().await.unwrap_or_default();
|
||||
Ok(RepeatedGridGroupPB { items: groups })
|
||||
}
|
||||
|
||||
async fn create_row_rev(&self) -> FlowyResult<RowRevision> {
|
||||
let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
|
||||
let block_id = self.block_id().await?;
|
||||
|
||||
// insert empty row below the row whose id is upper_row_id
|
||||
let row_rev = RowRevisionBuilder::new(&block_id, &field_revs).build();
|
||||
Ok(row_rev)
|
||||
}
|
||||
|
||||
async fn create_row_pb(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<RowPB> {
|
||||
let row_pb = RowPB::from(&row_rev);
|
||||
let block_id = row_rev.block_id.clone();
|
||||
|
||||
// insert the row
|
||||
let row_count = self.block_manager.create_row(row_rev, start_row_id).await?;
|
||||
|
||||
// update block row count
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(block_id, row_count);
|
||||
let _ = self.update_block(changeset).await?;
|
||||
Ok(row_pb)
|
||||
}
|
||||
|
||||
async fn modify<F>(&self, f: F) -> FlowyResult<()>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut GridRevisionPad) -> FlowyResult<Option<GridChangeset>>,
|
||||
|
@ -1,17 +1,48 @@
|
||||
use crate::entities::CheckboxGroupConfigurationPB;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
|
||||
use crate::services::group::{Group, GroupAction, GroupCellContentProvider, GroupController, GroupGenerator};
|
||||
use crate::services::group::{
|
||||
Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
|
||||
};
|
||||
|
||||
pub type CheckboxGroupController =
|
||||
GroupController<CheckboxGroupConfigurationPB, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser>;
|
||||
|
||||
impl Groupable for CheckboxGroupController {
|
||||
type CellDataType = CheckboxCellData;
|
||||
|
||||
fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupActionHandler for CheckboxGroupController {
|
||||
fn field_id(&self) -> &str {
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn get_groups(&self) -> Vec<Group> {
|
||||
self.make_groups()
|
||||
}
|
||||
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||
self.handle_rows(row_revs, field_rev)
|
||||
}
|
||||
|
||||
fn update_card(&self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CheckboxGroupGenerator();
|
||||
impl GroupGenerator for CheckboxGroupGenerator {
|
||||
type ConfigurationType = CheckboxGroupConfigurationPB;
|
||||
type TypeOptionType = CheckboxTypeOptionPB;
|
||||
|
||||
fn gen_groups(
|
||||
fn generate_groups(
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
_type_option: &Option<Self::TypeOptionType>,
|
||||
_cell_content_provider: &dyn GroupCellContentProvider,
|
||||
@ -33,11 +64,3 @@ impl GroupGenerator for CheckboxGroupGenerator {
|
||||
vec![check_group, uncheck_group]
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupAction for CheckboxGroupController {
|
||||
type CellDataType = CheckboxCellData;
|
||||
|
||||
fn should_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -5,16 +5,12 @@ use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
FieldRevision, GroupConfigurationRevision, RowRevision, TypeOptionDataDeserializer,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait GroupAction {
|
||||
type CellDataType;
|
||||
|
||||
fn should_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
|
||||
}
|
||||
|
||||
pub trait GroupCellContentProvider {
|
||||
/// We need to group the rows base on the deduplication cell content when the field type is
|
||||
/// RichText.
|
||||
@ -27,22 +23,46 @@ pub trait GroupGenerator {
|
||||
type ConfigurationType;
|
||||
type TypeOptionType;
|
||||
|
||||
fn gen_groups(
|
||||
fn generate_groups(
|
||||
configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
cell_content_provider: &dyn GroupCellContentProvider,
|
||||
) -> Vec<Group>;
|
||||
}
|
||||
|
||||
pub struct GroupController<C, T, G, CP> {
|
||||
pub field_rev: Arc<FieldRevision>,
|
||||
pub groups: IndexMap<String, Group>,
|
||||
pub trait Groupable {
|
||||
type CellDataType;
|
||||
fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
|
||||
}
|
||||
|
||||
pub trait GroupActionHandler: Send + Sync {
|
||||
fn field_id(&self) -> &str;
|
||||
fn get_groups(&self) -> Vec<Group>;
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
|
||||
fn update_card(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
}
|
||||
|
||||
pub trait GroupActionHandler2: Send + Sync {
|
||||
fn create_card(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
}
|
||||
|
||||
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 container generator
|
||||
/// P: the parser that impl [CellBytesParser] for the CellBytes
|
||||
pub struct GroupController<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<CP>,
|
||||
cell_parser_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
pub id: String,
|
||||
pub desc: String,
|
||||
@ -60,14 +80,14 @@ impl std::convert::From<Group> for GroupPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, CP> GroupController<C, T, G, CP>
|
||||
impl<C, T, G, P> GroupController<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>,
|
||||
field_rev: &Arc<FieldRevision>,
|
||||
configuration: GroupConfigurationRevision,
|
||||
cell_content_provider: &dyn GroupCellContentProvider,
|
||||
) -> FlowyResult<Self> {
|
||||
@ -77,10 +97,19 @@ where
|
||||
};
|
||||
let field_type_rev = field_rev.field_type_rev;
|
||||
let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
|
||||
let groups = G::gen_groups(&configuration, &type_option, cell_content_provider);
|
||||
let groups = G::generate_groups(&configuration, &type_option, cell_content_provider);
|
||||
|
||||
let default_group = Group {
|
||||
id: DEFAULT_GROUP_ID.to_owned(),
|
||||
desc: format!("No {}", field_rev.name),
|
||||
rows: vec![],
|
||||
content: "".to_string(),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
field_rev,
|
||||
groups: groups.into_iter().map(|group| (group.id.clone(), group)).collect(),
|
||||
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,
|
||||
@ -88,42 +117,52 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub fn take_groups(self) -> Vec<Group> {
|
||||
self.groups.into_values().collect()
|
||||
pub fn make_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
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, CP> GroupController<C, T, G, CP>
|
||||
impl<C, T, G, P> GroupController<C, T, G, P>
|
||||
where
|
||||
CP: CellBytesParser,
|
||||
Self: GroupAction<CellDataType = CP::Object>,
|
||||
P: CellBytesParser,
|
||||
Self: Groupable<CellDataType = P::Object>,
|
||||
{
|
||||
pub fn group_rows(&mut self, rows: &[Arc<RowRevision>]) -> FlowyResult<()> {
|
||||
pub fn handle_rows(&mut self, rows: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||
// The field_rev might be None if corresponding field_rev is deleted.
|
||||
if self.configuration.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for row in rows {
|
||||
if let Some(cell_rev) = row.cells.get(&self.field_rev.id) {
|
||||
if let Some(cell_rev) = row.cells.get(&self.field_id) {
|
||||
let mut records: Vec<GroupRecord> = vec![];
|
||||
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), &self.field_rev);
|
||||
let cell_data = cell_bytes.parser::<CP>()?;
|
||||
for group in self.groups.values() {
|
||||
if self.should_group(&group.content, &cell_data) {
|
||||
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.into(),
|
||||
group_id: group.id.clone(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for record in records {
|
||||
if let Some(group) = self.groups.get_mut(&record.group_id) {
|
||||
group.rows.push(record.row);
|
||||
if records.is_empty() {
|
||||
self.default_group.rows.push(row.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.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,16 @@
|
||||
use crate::entities::SelectOptionGroupConfigurationPB;
|
||||
use crate::services::cell::insert_select_option_cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::services::field::{
|
||||
MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
|
||||
};
|
||||
use crate::services::group::{Group, GroupAction, GroupCellContentProvider, GroupController, GroupGenerator};
|
||||
use crate::services::group::{
|
||||
Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
|
||||
};
|
||||
|
||||
// SingleSelect
|
||||
pub type SingleSelectGroupController = GroupController<
|
||||
@ -13,11 +20,43 @@ pub type SingleSelectGroupController = GroupController<
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupActionHandler for SingleSelectGroupController {
|
||||
fn field_id(&self) -> &str {
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn get_groups(&self) -> Vec<Group> {
|
||||
self.make_groups()
|
||||
}
|
||||
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||
self.handle_rows(row_revs, field_rev)
|
||||
}
|
||||
|
||||
fn update_card(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&Group> = self.groups_map.get(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SingleSelectGroupGenerator();
|
||||
impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfigurationPB;
|
||||
type TypeOptionType = SingleSelectTypeOptionPB;
|
||||
fn gen_groups(
|
||||
fn generate_groups(
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
_cell_content_provider: &dyn GroupCellContentProvider,
|
||||
@ -38,13 +77,6 @@ impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupAction for SingleSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
fn should_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
}
|
||||
|
||||
// MultiSelect
|
||||
pub type MultiSelectGroupController = GroupController<
|
||||
SelectOptionGroupConfigurationPB,
|
||||
@ -53,12 +85,44 @@ pub type MultiSelectGroupController = GroupController<
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupActionHandler for MultiSelectGroupController {
|
||||
fn field_id(&self) -> &str {
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn get_groups(&self) -> Vec<Group> {
|
||||
self.make_groups()
|
||||
}
|
||||
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||
self.handle_rows(row_revs, field_rev)
|
||||
}
|
||||
|
||||
fn update_card(&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 gen_groups(
|
||||
fn generate_groups(
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
_cell_content_provider: &dyn GroupCellContentProvider,
|
||||
@ -78,10 +142,3 @@ impl GroupGenerator for MultiSelectGroupGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupAction for MultiSelectGroupController {
|
||||
type CellDataType = SelectOptionCellDataPB;
|
||||
fn should_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,30 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::entities::{
|
||||
BoardCardChangesetPB, CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupPB,
|
||||
NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB,
|
||||
UrlGroupConfigurationPB,
|
||||
};
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::group::{
|
||||
CheckboxGroupController, Group, GroupCellContentProvider, MultiSelectGroupController, SingleSelectGroupController,
|
||||
CheckboxGroupController, GroupActionHandler, GroupCellContentProvider, MultiSelectGroupController,
|
||||
SingleSelectGroupController,
|
||||
};
|
||||
|
||||
use crate::entities::{
|
||||
CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupPB, NumberGroupConfigurationPB,
|
||||
SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision};
|
||||
use flowy_sync::client_grid::GridRevisionPad;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub(crate) struct GridGroupService {
|
||||
#[allow(dead_code)]
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
#[allow(dead_code)]
|
||||
grid_pad: Arc<RwLock<GridRevisionPad>>,
|
||||
#[allow(dead_code)]
|
||||
block_manager: Arc<GridBlockManager>,
|
||||
group_action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
|
||||
}
|
||||
|
||||
impl GridGroupService {
|
||||
@ -35,14 +38,14 @@ impl GridGroupService {
|
||||
scheduler,
|
||||
grid_pad,
|
||||
block_manager,
|
||||
group_action_handler: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn load_groups(&self) -> Option<Vec<GroupPB>> {
|
||||
let grid_pad = self.grid_pad.read().await;
|
||||
let field_rev = find_group_field(grid_pad.fields()).unwrap();
|
||||
pub(crate) async fn load_groups(&mut self) -> Option<Vec<GroupPB>> {
|
||||
let field_rev = find_group_field(self.grid_pad.read().await.fields()).unwrap();
|
||||
let field_type: FieldType = field_rev.field_type_rev.into();
|
||||
let configuration = self.get_group_configuration(field_rev).await;
|
||||
let configuration = self.get_group_configuration(&field_rev).await;
|
||||
|
||||
let blocks = self.block_manager.get_block_snapshots(None).await.unwrap();
|
||||
let row_revs = blocks
|
||||
@ -51,19 +54,39 @@ impl GridGroupService {
|
||||
.flatten()
|
||||
.collect::<Vec<Arc<RowRevision>>>();
|
||||
|
||||
match self.build_groups(&field_type, field_rev, row_revs, configuration) {
|
||||
match self
|
||||
.build_groups(&field_type, &field_rev, row_revs, configuration)
|
||||
.await
|
||||
{
|
||||
Ok(groups) => Some(groups),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
|
||||
#[tracing::instrument(level = "debug", skip(self, row_rev))]
|
||||
pub(crate) async fn update_board_card(&self, row_rev: &mut RowRevision, group_id: &str) {
|
||||
if let Some(group_action_handler) = self.group_action_handler.as_ref() {
|
||||
let field_id = group_action_handler.read().await.field_id().to_owned();
|
||||
|
||||
match self.grid_pad.read().await.get_field_rev(&field_id) {
|
||||
None => tracing::warn!("Fail to create card because the field does not exist"),
|
||||
Some((_, field_rev)) => {
|
||||
group_action_handler
|
||||
.write()
|
||||
.await
|
||||
.update_card(row_rev, field_rev, group_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
|
||||
let grid_pad = self.grid_pad.read().await;
|
||||
let setting = grid_pad.get_setting_rev();
|
||||
let layout = &setting.layout;
|
||||
let configurations = setting.get_groups(layout, &field_rev.id, &field_rev.field_type_rev);
|
||||
match configurations {
|
||||
None => self.default_group_configuration(field_rev),
|
||||
None => default_group_configuration(field_rev),
|
||||
Some(mut configurations) => {
|
||||
assert_eq!(configurations.len(), 1);
|
||||
(&*configurations.pop().unwrap()).clone()
|
||||
@ -71,79 +94,112 @@ impl GridGroupService {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
|
||||
let field_type: FieldType = field_rev.field_type_rev.clone().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.field_type_rev.clone(),
|
||||
content: Some(bytes.to_vec()),
|
||||
pub async fn move_card(&self, _group_id: &str, _from: i32, _to: i32) {
|
||||
// BoardCardChangesetPB {
|
||||
// group_id: "".to_string(),
|
||||
// inserted_cards: vec![],
|
||||
// deleted_cards: vec![],
|
||||
// updated_cards: vec![]
|
||||
// }
|
||||
// let row_pb = make_row_from_row_rev(row_rev);
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn did_delete_card(&self, _row_id: String) {
|
||||
// let changeset = BoardCardChangesetPB::delete(group_id.to_owned(), vec![row_id]);
|
||||
// self.notify_did_update_board(changeset).await;
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn did_create_card(&self, group_id: &str, row_pb: &RowPB) {
|
||||
let changeset = BoardCardChangesetPB::insert(group_id.to_owned(), vec![row_pb.clone()]);
|
||||
self.notify_did_update_board(changeset).await;
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_board(&self, changeset: BoardCardChangesetPB) {
|
||||
if self.group_action_handler.is_none() {
|
||||
return;
|
||||
}
|
||||
send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
fn build_groups(
|
||||
&self,
|
||||
async fn build_groups(
|
||||
&mut self,
|
||||
field_type: &FieldType,
|
||||
field_rev: &Arc<FieldRevision>,
|
||||
row_revs: Vec<Arc<RowRevision>>,
|
||||
configuration: GroupConfigurationRevision,
|
||||
) -> FlowyResult<Vec<GroupPB>> {
|
||||
let groups: Vec<Group> = match field_type {
|
||||
match field_type {
|
||||
FieldType::RichText => {
|
||||
// let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
|
||||
vec![]
|
||||
}
|
||||
FieldType::Number => {
|
||||
// let generator = GroupGenerator::<NumberGroupConfigurationPB>::from_configuration(configuration);
|
||||
vec![]
|
||||
}
|
||||
FieldType::DateTime => {
|
||||
// let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
|
||||
vec![]
|
||||
}
|
||||
FieldType::SingleSelect => {
|
||||
let mut group_controller =
|
||||
SingleSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
|
||||
let _ = group_controller.group_rows(&row_revs)?;
|
||||
group_controller.take_groups()
|
||||
let controller = SingleSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
|
||||
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let mut group_controller =
|
||||
MultiSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
|
||||
let _ = group_controller.group_rows(&row_revs)?;
|
||||
group_controller.take_groups()
|
||||
let controller = MultiSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
|
||||
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
|
||||
}
|
||||
FieldType::Checkbox => {
|
||||
let mut group_controller =
|
||||
CheckboxGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
|
||||
let _ = group_controller.group_rows(&row_revs)?;
|
||||
group_controller.take_groups()
|
||||
let controller = CheckboxGroupController::new(field_rev, configuration, &self.grid_pad)?;
|
||||
self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
|
||||
}
|
||||
FieldType::URL => {
|
||||
// let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut groups = vec![];
|
||||
if let Some(group_action_handler) = self.group_action_handler.as_ref() {
|
||||
let mut write_guard = group_action_handler.write().await;
|
||||
let _ = write_guard.group_rows(&row_revs, field_rev)?;
|
||||
groups = write_guard.get_groups();
|
||||
drop(write_guard);
|
||||
}
|
||||
|
||||
Ok(groups.into_iter().map(GroupPB::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<&Arc<FieldRevision>> {
|
||||
field_revs.iter().find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.field_type_rev.into();
|
||||
field_type.can_be_group()
|
||||
})
|
||||
fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevision>> {
|
||||
let field_rev = field_revs
|
||||
.iter()
|
||||
.find(|field_rev| {
|
||||
let field_type: FieldType = field_rev.field_type_rev.into();
|
||||
field_type.can_be_group()
|
||||
})
|
||||
.cloned();
|
||||
field_rev
|
||||
}
|
||||
|
||||
impl GroupCellContentProvider for Arc<RwLock<GridRevisionPad>> {}
|
||||
|
||||
fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
|
||||
let field_type: FieldType = field_rev.field_type_rev.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.field_type_rev,
|
||||
content: Some(bytes.to_vec()),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::services::cell::apply_cell_data_changeset;
|
||||
use crate::services::field::{DateCellChangesetPB, SelectOptionCellChangeset};
|
||||
use crate::services::cell::{
|
||||
insert_checkbox_cell, insert_date_cell, insert_number_cell, insert_select_option_cell, insert_text_cell,
|
||||
insert_url_cell,
|
||||
};
|
||||
|
||||
use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT};
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
@ -34,47 +37,68 @@ impl<'a> RowRevisionBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_cell(&mut self, field_id: &str, data: String) {
|
||||
pub fn insert_text_cell(&mut self, field_id: &str, data: String) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => {
|
||||
tracing::warn!("Can't find the field with id: {}", field_id);
|
||||
}
|
||||
None => tracing::warn!("Can't find the text field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
let data = apply_cell_data_changeset(data, None, field_rev).unwrap();
|
||||
let cell = CellRevision::new(data);
|
||||
self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_text_cell(data, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_url_cell(&mut self, field_id: &str, data: String) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => tracing::warn!("Can't find the url field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_url_cell(data, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_number_cell(&mut self, field_id: &str, num: i64) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => tracing::warn!("Can't find the number field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_number_cell(num, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_checkbox_cell(&mut self, field_id: &str, is_check: bool) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => tracing::warn!("Can't find the checkbox field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_checkbox_cell(is_check, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_date_cell(&mut self, field_id: &str, timestamp: i64) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => {
|
||||
tracing::warn!("Invalid field_id: {}", field_id);
|
||||
}
|
||||
None => tracing::warn!("Can't find the date field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
let cell_data = serde_json::to_string(&DateCellChangesetPB {
|
||||
date: Some(timestamp.to_string()),
|
||||
time: None,
|
||||
})
|
||||
.unwrap();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
|
||||
let cell = CellRevision::new(data);
|
||||
self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_date_cell(timestamp, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_select_option_cell(&mut self, field_id: &str, data: String) {
|
||||
match self.field_rev_map.get(&field_id.to_owned()) {
|
||||
None => {
|
||||
tracing::warn!("Invalid field_id: {}", field_id);
|
||||
}
|
||||
None => tracing::warn!("Can't find the select option field with id: {}", field_id),
|
||||
Some(field_rev) => {
|
||||
let cell_data = SelectOptionCellChangeset::from_insert(&data).to_str();
|
||||
let data = apply_cell_data_changeset(cell_data, None, field_rev).unwrap();
|
||||
let cell = CellRevision::new(data);
|
||||
self.payload.cell_by_field_id.insert(field_id.to_owned(), cell);
|
||||
self.payload
|
||||
.cell_by_field_id
|
||||
.insert(field_id.to_owned(), insert_select_option_cell(data, field_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Ve
|
||||
row_revs.iter().map(RowPB::from).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn make_row_from_row_rev(row_rev: Arc<RowRevision>) -> Option<RowPB> {
|
||||
make_rows_from_row_revs(&[row_rev]).pop()
|
||||
pub(crate) fn make_row_from_row_rev(row_rev: Arc<RowRevision>) -> RowPB {
|
||||
make_rows_from_row_revs(&[row_rev]).pop().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn make_rows_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<RowPB> {
|
||||
|
@ -76,7 +76,7 @@ pub fn make_default_board() -> BuildGridContext {
|
||||
let multi_select_type_option = MultiSelectTypeOptionBuilder::default()
|
||||
.add_option(banana_option.clone())
|
||||
.add_option(apple_option.clone())
|
||||
.add_option(pear_option.clone());
|
||||
.add_option(pear_option);
|
||||
let multi_select_field = FieldBuilder::new(multi_select_type_option)
|
||||
.name("Fruit")
|
||||
.visibility(true)
|
||||
@ -114,20 +114,15 @@ pub fn make_default_board() -> BuildGridContext {
|
||||
row_builder.insert_select_option_cell(&multi_select_field_id, apple_option.id.clone());
|
||||
row_builder.insert_select_option_cell(&multi_select_field_id, banana_option.id.clone());
|
||||
// insert text
|
||||
row_builder.insert_cell(&text_field_id, format!("Card {}", i));
|
||||
row_builder.insert_text_cell(&text_field_id, format!("Card {}", i));
|
||||
// insert date
|
||||
row_builder.insert_date_cell(&date_field_id, timestamp);
|
||||
// number
|
||||
row_builder.insert_cell(&number_field_id, format!("{}", i));
|
||||
row_builder.insert_number_cell(&number_field_id, i);
|
||||
// checkbox
|
||||
let is_check = if i % 2 == 0 {
|
||||
CHECK.to_string()
|
||||
} else {
|
||||
UNCHECK.to_string()
|
||||
};
|
||||
row_builder.insert_cell(&checkbox_field_id, is_check);
|
||||
row_builder.insert_checkbox_cell(&checkbox_field_id, i % 2 == 0);
|
||||
// url
|
||||
row_builder.insert_cell(&url_field_id, "https://appflowy.io".to_string());
|
||||
row_builder.insert_url_cell(&url_field_id, "https://appflowy.io".to_string());
|
||||
|
||||
let row = row_builder.build();
|
||||
grid_builder.add_row(row);
|
||||
|
@ -26,14 +26,14 @@ impl<'a> GridRowTestBuilder<'a> {
|
||||
|
||||
pub fn insert_text_cell(&mut self, data: &str) -> String {
|
||||
let text_field = self.field_rev_with_type(&FieldType::RichText);
|
||||
self.inner_builder.insert_cell(&text_field.id, data.to_string());
|
||||
self.inner_builder.insert_text_cell(&text_field.id, data.to_string());
|
||||
|
||||
text_field.id.clone()
|
||||
}
|
||||
|
||||
pub fn insert_number_cell(&mut self, data: &str) -> String {
|
||||
let number_field = self.field_rev_with_type(&FieldType::Number);
|
||||
self.inner_builder.insert_cell(&number_field.id, data.to_string());
|
||||
self.inner_builder.insert_text_cell(&number_field.id, data.to_string());
|
||||
number_field.id.clone()
|
||||
}
|
||||
|
||||
@ -44,20 +44,21 @@ impl<'a> GridRowTestBuilder<'a> {
|
||||
})
|
||||
.unwrap();
|
||||
let date_field = self.field_rev_with_type(&FieldType::DateTime);
|
||||
self.inner_builder.insert_cell(&date_field.id, value);
|
||||
self.inner_builder.insert_text_cell(&date_field.id, value);
|
||||
date_field.id.clone()
|
||||
}
|
||||
|
||||
pub fn insert_checkbox_cell(&mut self, data: &str) -> String {
|
||||
let checkbox_field = self.field_rev_with_type(&FieldType::Checkbox);
|
||||
self.inner_builder.insert_cell(&checkbox_field.id, data.to_string());
|
||||
self.inner_builder
|
||||
.insert_text_cell(&checkbox_field.id, data.to_string());
|
||||
|
||||
checkbox_field.id.clone()
|
||||
}
|
||||
|
||||
pub fn insert_url_cell(&mut self, data: &str) -> String {
|
||||
let url_field = self.field_rev_with_type(&FieldType::URL);
|
||||
self.inner_builder.insert_cell(&url_field.id, data.to_string());
|
||||
self.inner_builder.insert_text_cell(&url_field.id, data.to_string());
|
||||
url_field.id.clone()
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,9 @@ pub enum ErrorCode {
|
||||
#[display(fmt = "Field's type option data should not be empty")]
|
||||
TypeOptionDataIsEmpty = 450,
|
||||
|
||||
#[display(fmt = "Group id is empty")]
|
||||
GroupIdIsEmpty = 460,
|
||||
|
||||
#[display(fmt = "Invalid date time format")]
|
||||
InvalidDateTimeFormat = 500,
|
||||
|
||||
|
@ -88,9 +88,9 @@ pub struct GridBlockMetaRevisionChangeset {
|
||||
}
|
||||
|
||||
impl GridBlockMetaRevisionChangeset {
|
||||
pub fn from_row_count(block_id: &str, row_count: i32) -> Self {
|
||||
pub fn from_row_count(block_id: String, row_count: i32) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_string(),
|
||||
block_id,
|
||||
start_row_index: None,
|
||||
row_count: Some(row_count),
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ impl GridRevisionPad {
|
||||
.blocks
|
||||
.iter()
|
||||
.map(|block| {
|
||||
let mut duplicated_block = (&*block.clone()).clone();
|
||||
let mut duplicated_block = (&**block).clone();
|
||||
duplicated_block.block_id = gen_block_id();
|
||||
duplicated_block
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user