Merge branch 'AppFlowy-IO:main' into documentation/flowy_editor

This commit is contained in:
Lucas.Xu 2022-08-16 19:46:47 +08:00 committed by GitHub
commit 713e6abaa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 612 additions and 298 deletions

View File

@ -14,12 +14,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:collection'; import 'dart:collection';
import 'board_data_controller.dart'; import 'board_data_controller.dart';
import 'group_controller.dart';
part 'board_bloc.freezed.dart'; part 'board_bloc.freezed.dart';
class BoardBloc extends Bloc<BoardEvent, BoardState> { class BoardBloc extends Bloc<BoardEvent, BoardState> {
final BoardDataController _dataController; final BoardDataController _dataController;
late final AFBoardDataController boardDataController; late final AFBoardDataController afBoardDataController;
List<GroupController> groupControllers = [];
GridFieldCache get fieldCache => _dataController.fieldCache; GridFieldCache get fieldCache => _dataController.fieldCache;
String get gridId => _dataController.gridId; String get gridId => _dataController.gridId;
@ -27,7 +29,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
BoardBloc({required ViewPB view}) BoardBloc({required ViewPB view})
: _dataController = BoardDataController(view: view), : _dataController = BoardDataController(view: view),
super(BoardState.initial(view.id)) { super(BoardState.initial(view.id)) {
boardDataController = AFBoardDataController( afBoardDataController = AFBoardDataController(
onMoveColumn: ( onMoveColumn: (
fromIndex, fromIndex,
toIndex, toIndex,
@ -71,9 +73,6 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
didReceiveGridUpdate: (GridPB grid) { didReceiveGridUpdate: (GridPB grid) {
emit(state.copyWith(grid: Some(grid))); emit(state.copyWith(grid: Some(grid)));
}, },
didReceiveGroups: (List<GroupPB> groups) {
emit(state.copyWith(groups: groups));
},
didReceiveRows: (List<RowInfo> rowInfos) { didReceiveRows: (List<RowInfo> rowInfos) {
emit(state.copyWith(rowInfos: rowInfos)); emit(state.copyWith(rowInfos: rowInfos));
}, },
@ -85,9 +84,24 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
@override @override
Future<void> close() async { Future<void> close() async {
await _dataController.dispose(); await _dataController.dispose();
for (final controller in groupControllers) {
controller.dispose();
}
return super.close(); return super.close();
} }
void initializeGroups(List<GroupPB> groups) {
for (final group in groups) {
final delegate = GroupControllerDelegateImpl(afBoardDataController);
final controller = GroupController(
group: group,
delegate: delegate,
);
controller.startListening();
groupControllers.add(controller);
}
}
GridRowCache? getRowCache(String blockId) { GridRowCache? getRowCache(String blockId) {
final GridBlockCache? blockCache = _dataController.blocks[blockId]; final GridBlockCache? blockCache = _dataController.blocks[blockId];
return blockCache?.rowCache; return blockCache?.rowCache;
@ -100,7 +114,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
add(BoardEvent.didReceiveGridUpdate(grid)); add(BoardEvent.didReceiveGridUpdate(grid));
} }
}, },
onGroupChanged: (groups) { didLoadGroups: (groups) {
List<AFBoardColumnData> columns = groups.map((group) { List<AFBoardColumnData> columns = groups.map((group) {
return AFBoardColumnData( return AFBoardColumnData(
id: group.groupId, id: group.groupId,
@ -110,7 +124,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
); );
}).toList(); }).toList();
boardDataController.addColumns(columns); afBoardDataController.addColumns(columns);
initializeGroups(groups);
}, },
onRowsChanged: (List<RowInfo> rowInfos, RowsChangedReason reason) { onRowsChanged: (List<RowInfo> rowInfos, RowsChangedReason reason) {
add(BoardEvent.didReceiveRows(rowInfos)); add(BoardEvent.didReceiveRows(rowInfos));
@ -123,14 +138,6 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
List<AFColumnItem> _buildRows(List<RowPB> rows) { List<AFColumnItem> _buildRows(List<RowPB> rows) {
final items = rows.map((row) { final items = rows.map((row) {
// final rowInfo = RowInfo(
// gridId: _dataController.gridId,
// blockId: row.blockId,
// id: row.id,
// fields: _dataController.fieldCache.unmodifiableFields,
// height: row.height.toDouble(),
// rawRow: row,
// );
return BoardColumnItem(row: row); return BoardColumnItem(row: row);
}).toList(); }).toList();
@ -155,8 +162,6 @@ class BoardEvent with _$BoardEvent {
const factory BoardEvent.initial() = InitialGrid; const factory BoardEvent.initial() = InitialGrid;
const factory BoardEvent.createRow(String groupId) = _CreateRow; const factory BoardEvent.createRow(String groupId) = _CreateRow;
const factory BoardEvent.endEditRow(String rowId) = _EndEditRow; const factory BoardEvent.endEditRow(String rowId) = _EndEditRow;
const factory BoardEvent.didReceiveGroups(List<GroupPB> groups) =
_DidReceiveGroup;
const factory BoardEvent.didReceiveRows(List<RowInfo> rowInfos) = const factory BoardEvent.didReceiveRows(List<RowInfo> rowInfos) =
_DidReceiveRows; _DidReceiveRows;
const factory BoardEvent.didReceiveGridUpdate( const factory BoardEvent.didReceiveGridUpdate(
@ -169,7 +174,6 @@ class BoardState with _$BoardState {
const factory BoardState({ const factory BoardState({
required String gridId, required String gridId,
required Option<GridPB> grid, required Option<GridPB> grid,
required List<GroupPB> groups,
required Option<RowPB> editingRow, required Option<RowPB> editingRow,
required List<RowInfo> rowInfos, required List<RowInfo> rowInfos,
required GridLoadingState loadingState, required GridLoadingState loadingState,
@ -177,7 +181,6 @@ class BoardState with _$BoardState {
factory BoardState.initial(String gridId) => BoardState( factory BoardState.initial(String gridId) => BoardState(
rowInfos: [], rowInfos: [],
groups: [],
grid: none(), grid: none(),
gridId: gridId, gridId: gridId,
editingRow: none(), editingRow: none(),
@ -228,3 +231,27 @@ class CreateCardItem extends AFColumnItem {
@override @override
String get id => '$CreateCardItem'; String get id => '$CreateCardItem';
} }
class GroupControllerDelegateImpl extends GroupControllerDelegate {
final AFBoardDataController controller;
GroupControllerDelegateImpl(this.controller);
@override
void insertRow(String groupId, RowPB row, int? index) {
final item = BoardColumnItem(row: row);
if (index != null) {
controller.insertColumnItem(groupId, index, item);
} else {
controller.addColumnItem(groupId, item);
}
}
@override
void removeRow(String groupId, String rowId) {
controller.removeColumnItem(groupId, rowId);
}
@override
void updateRow(String groupId, RowPB row) {}
}

View File

@ -12,7 +12,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldPB>); typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldPB>);
typedef OnGridChanged = void Function(GridPB); typedef OnGridChanged = void Function(GridPB);
typedef OnGroupChanged = void Function(List<GroupPB>); typedef DidLoadGroups = void Function(List<GroupPB>);
typedef OnRowsChanged = void Function( typedef OnRowsChanged = void Function(
List<RowInfo>, List<RowInfo>,
RowsChangedReason, RowsChangedReason,
@ -30,7 +30,7 @@ class BoardDataController {
OnFieldsChanged? _onFieldsChanged; OnFieldsChanged? _onFieldsChanged;
OnGridChanged? _onGridChanged; OnGridChanged? _onGridChanged;
OnGroupChanged? _onGroupChanged; DidLoadGroups? _didLoadGroup;
OnRowsChanged? _onRowsChanged; OnRowsChanged? _onRowsChanged;
OnError? _onError; OnError? _onError;
@ -51,13 +51,13 @@ class BoardDataController {
void addListener({ void addListener({
OnGridChanged? onGridChanged, OnGridChanged? onGridChanged,
OnFieldsChanged? onFieldsChanged, OnFieldsChanged? onFieldsChanged,
OnGroupChanged? onGroupChanged, DidLoadGroups? didLoadGroups,
OnRowsChanged? onRowsChanged, OnRowsChanged? onRowsChanged,
OnError? onError, OnError? onError,
}) { }) {
_onGridChanged = onGridChanged; _onGridChanged = onGridChanged;
_onFieldsChanged = onFieldsChanged; _onFieldsChanged = onFieldsChanged;
_onGroupChanged = onGroupChanged; _didLoadGroup = didLoadGroups;
_onRowsChanged = onRowsChanged; _onRowsChanged = onRowsChanged;
_onError = onError; _onError = onError;
@ -133,7 +133,7 @@ class BoardDataController {
return Future( return Future(
() => result.fold( () => result.fold(
(groups) { (groups) {
_onGroupChanged?.call(groups.items); _didLoadGroup?.call(groups.items);
}, },
(err) => _onError?.call(err), (err) => _onError?.call(err),
), ),

View File

@ -23,7 +23,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
}) : _rowService = RowFFIService( }) : _rowService = RowFFIService(
gridId: gridId, gridId: gridId,
blockId: dataController.rowPB.blockId, blockId: dataController.rowPB.blockId,
rowId: dataController.rowPB.id,
), ),
_dataController = dataController, _dataController = dataController,
super(BoardCardState.initial( super(BoardCardState.initial(
@ -34,9 +33,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
initial: (_InitialRow value) async { initial: (_InitialRow value) async {
await _startListening(); await _startListening();
}, },
createRow: (_CreateRow value) {
_rowService.createRow();
},
didReceiveCells: (_DidReceiveCells value) async { didReceiveCells: (_DidReceiveCells value) async {
final cells = value.gridCellMap.values final cells = value.gridCellMap.values
.map((e) => GridCellEquatable(e.field)) .map((e) => GridCellEquatable(e.field))
@ -58,6 +54,16 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
return super.close(); return super.close();
} }
RowInfo rowInfo() {
return RowInfo(
gridId: _rowService.gridId,
fields: UnmodifiableListView(
state.cells.map((cell) => cell._field).toList(),
),
rowPB: state.rowPB,
);
}
Future<void> _startListening() async { Future<void> _startListening() async {
_dataController.addListener( _dataController.addListener(
onRowChanged: (cells, reason) { onRowChanged: (cells, reason) {
@ -72,7 +78,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
@freezed @freezed
class BoardCardEvent with _$BoardCardEvent { class BoardCardEvent with _$BoardCardEvent {
const factory BoardCardEvent.initial() = _InitialRow; const factory BoardCardEvent.initial() = _InitialRow;
const factory BoardCardEvent.createRow() = _CreateRow;
const factory BoardCardEvent.didReceiveCells( const factory BoardCardEvent.didReceiveCells(
GridCellMap gridCellMap, RowsChangedReason reason) = _DidReceiveCells; GridCellMap gridCellMap, RowsChangedReason reason) = _DidReceiveCells;
} }

View File

@ -0,0 +1,49 @@
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'group_listener.dart';
abstract class GroupControllerDelegate {
void removeRow(String groupId, String rowId);
void insertRow(String groupId, RowPB row, int? index);
void updateRow(String groupId, RowPB row);
}
class GroupController {
final GroupPB group;
final GroupListener _listener;
final GroupControllerDelegate delegate;
GroupController({required this.group, required this.delegate})
: _listener = GroupListener(group);
void startListening() {
_listener.start(onGroupChanged: (result) {
result.fold(
(GroupRowsChangesetPB changeset) {
for (final insertedRow in changeset.insertedRows) {
final index = insertedRow.hasIndex() ? insertedRow.index : null;
delegate.insertRow(
group.groupId,
insertedRow.row,
index,
);
}
for (final deletedRow in changeset.deletedRows) {
delegate.removeRow(group.groupId, deletedRow);
}
for (final updatedRow in changeset.updatedRows) {
delegate.updateRow(group.groupId, updatedRow);
}
},
(err) => Log.error(err),
);
});
}
Future<void> dispose() async {
_listener.stop();
}
}

View File

@ -0,0 +1,51 @@
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
typedef UpdateGroupNotifiedValue = Either<GroupRowsChangesetPB, FlowyError>;
class GroupListener {
final GroupPB group;
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
GridNotificationListener? _listener;
GroupListener(this.group);
void start({
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
}) {
_groupNotifier?.addPublishListener(onGroupChanged);
_listener = GridNotificationListener(
objectId: group.groupId,
handler: _handler,
);
}
void _handler(
GridNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridNotification.DidUpdateGroup:
result.fold(
(payload) => _groupNotifier?.value =
left(GroupRowsChangesetPB.fromBuffer(payload)),
(error) => _groupNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_groupNotifier?.dispose();
_groupNotifier = null;
}
}

View File

@ -1,11 +1,20 @@
// ignore_for_file: unused_field // ignore_for_file: unused_field
import 'dart:collection';
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart'; import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_detail.dart';
import 'package:appflowy_board/appflowy_board.dart'; import 'package:appflowy_board/appflowy_board.dart';
import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.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:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../../grid/application/row/row_cache.dart';
import '../application/board_bloc.dart'; import '../application/board_bloc.dart';
import 'card/card.dart'; import 'card/card.dart';
import 'card/card_cell_builder.dart'; import 'card/card_cell_builder.dart';
@ -55,7 +64,7 @@ class BoardContent extends StatelessWidget {
child: AFBoard( child: AFBoard(
// key: UniqueKey(), // key: UniqueKey(),
scrollController: ScrollController(), scrollController: ScrollController(),
dataController: context.read<BoardBloc>().boardDataController, dataController: context.read<BoardBloc>().afBoardDataController,
headerBuilder: _buildHeader, headerBuilder: _buildHeader,
footBuilder: _buildFooter, footBuilder: _buildFooter,
cardBuilder: (_, data) => _buildCard(context, data), cardBuilder: (_, data) => _buildCard(context, data),
@ -123,9 +132,36 @@ class BoardContent extends StatelessWidget {
onEditEditing: (rowId) { onEditEditing: (rowId) {
context.read<BoardBloc>().add(BoardEvent.endEditRow(rowId)); context.read<BoardBloc>().add(BoardEvent.endEditRow(rowId));
}, },
openCard: (context) => _openCard(
gridId,
fieldCache,
rowPB,
rowCache,
context,
),
), ),
); );
} }
void _openCard(String gridId, GridFieldCache fieldCache, RowPB rowPB,
GridRowCache rowCache, BuildContext context) {
final rowInfo = RowInfo(
gridId: gridId,
fields: UnmodifiableListView(fieldCache.fields),
rowPB: rowPB,
);
final dataController = GridRowDataController(
rowInfo: rowInfo,
fieldCache: fieldCache,
rowCache: rowCache,
);
RowDetailPage(
cellBuilder: GridCellBuilder(delegate: dataController),
dataController: dataController,
).show(context);
}
} }
extension HexColor on Color { extension HexColor on Color {

View File

@ -1,9 +1,10 @@
import 'package:app_flowy/plugins/board/application/card/card_bloc.dart'; import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart'; import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra/theme.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'card_cell_builder.dart'; import 'card_cell_builder.dart';
@ -17,6 +18,7 @@ class BoardCard extends StatefulWidget {
final CardDataController dataController; final CardDataController dataController;
final BoardCellBuilder cellBuilder; final BoardCellBuilder cellBuilder;
final OnEndEditing onEditEditing; final OnEndEditing onEditEditing;
final void Function(BuildContext) openCard;
const BoardCard({ const BoardCard({
required this.gridId, required this.gridId,
@ -24,6 +26,7 @@ class BoardCard extends StatefulWidget {
required this.dataController, required this.dataController,
required this.cellBuilder, required this.cellBuilder,
required this.onEditEditing, required this.onEditEditing,
required this.openCard,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -53,6 +56,9 @@ class _BoardCardState extends State<BoardCard> {
accessoryBuilder: (context) { accessoryBuilder: (context) {
return [const _CardMoreOption()]; return [const _CardMoreOption()];
}, },
onTap: (context) {
widget.openCard(context);
},
child: Column( child: Column(
children: _makeCells(context, state.gridCellMap), children: _makeCells(context, state.gridCellMap),
), ),
@ -85,6 +91,8 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
@override @override
void onTap(BuildContext context) { void onTap(BuildContext context) {
Log.debug('show options'); GridRowActionSheet(
rowData: context.read<BoardCardBloc>().rowInfo(),
).show(context, direction: AnchorDirection.bottomWithCenterAligned);
} }
} }

View File

@ -7,8 +7,10 @@ import 'package:styled_widget/styled_widget.dart';
class BoardCardContainer extends StatelessWidget { class BoardCardContainer extends StatelessWidget {
final Widget child; final Widget child;
final CardAccessoryBuilder? accessoryBuilder; final CardAccessoryBuilder? accessoryBuilder;
final void Function(BuildContext) onTap;
const BoardCardContainer({ const BoardCardContainer({
required this.child, required this.child,
required this.onTap,
this.accessoryBuilder, this.accessoryBuilder,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -30,11 +32,14 @@ class BoardCardContainer extends StatelessWidget {
} }
} }
return Padding( return GestureDetector(
padding: const EdgeInsets.all(8), onTap: () => onTap(context),
child: ConstrainedBox( child: Padding(
constraints: const BoxConstraints(minHeight: 30), padding: const EdgeInsets.all(8),
child: container, child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 30),
child: container,
),
), ),
); );
}, },

View File

@ -18,14 +18,13 @@ class FieldService {
FieldService({required this.gridId, required this.fieldId}); FieldService({required this.gridId, required this.fieldId});
Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) { Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) {
final payload = MoveItemPayloadPB.create() final payload = MoveFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..itemId = fieldId ..fieldId = fieldId
..ty = MoveItemTypePB.MoveField
..fromIndex = fromIndex ..fromIndex = fromIndex
..toIndex = toIndex; ..toIndex = toIndex;
return GridEventMoveItem(payload).send(); return GridEventMoveField(payload).send();
} }
Future<Either<Unit, FlowyError>> updateField({ Future<Either<Unit, FlowyError>> updateField({

View File

@ -23,9 +23,9 @@ class GridFFIService {
} }
Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) { Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) {
CreateRowPayloadPB payload = CreateRowPayloadPB.create()..gridId = gridId; var payload = CreateTableRowPayloadPB.create()..gridId = gridId;
startRowId?.fold(() => null, (id) => payload.startRowId = id); startRowId?.fold(() => null, (id) => payload.startRowId = id);
return GridEventCreateRow(payload).send(); return GridEventCreateTableRow(payload).send();
} }
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) { Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) {

View File

@ -17,19 +17,19 @@ class RowActionSheetBloc
RowActionSheetBloc({required RowInfo rowInfo}) RowActionSheetBloc({required RowInfo rowInfo})
: _rowService = RowFFIService( : _rowService = RowFFIService(
gridId: rowInfo.gridId, gridId: rowInfo.gridId,
blockId: rowInfo.blockId, blockId: rowInfo.rowPB.blockId,
rowId: rowInfo.rowPB.id,
), ),
super(RowActionSheetState.initial(rowInfo)) { super(RowActionSheetState.initial(rowInfo)) {
on<RowActionSheetEvent>( on<RowActionSheetEvent>(
(event, emit) async { (event, emit) async {
await event.map( await event.map(
deleteRow: (_DeleteRow value) async { deleteRow: (_DeleteRow value) async {
final result = await _rowService.deleteRow(); final result = await _rowService.deleteRow(state.rowData.rowPB.id);
logResult(result); logResult(result);
}, },
duplicateRow: (_DuplicateRow value) async { duplicateRow: (_DuplicateRow value) async {
final result = await _rowService.duplicateRow(); final result =
await _rowService.duplicateRow(state.rowData.rowPB.id);
logResult(result); logResult(result);
}, },
); );

View File

@ -20,8 +20,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
required GridRowDataController dataController, required GridRowDataController dataController,
}) : _rowService = RowFFIService( }) : _rowService = RowFFIService(
gridId: rowInfo.gridId, gridId: rowInfo.gridId,
blockId: rowInfo.blockId, blockId: rowInfo.rowPB.blockId,
rowId: rowInfo.rowPB.id,
), ),
_dataController = dataController, _dataController = dataController,
super(RowState.initial(rowInfo, dataController.loadData())) { super(RowState.initial(rowInfo, dataController.loadData())) {
@ -32,7 +31,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
await _startListening(); await _startListening();
}, },
createRow: (_CreateRow value) { createRow: (_CreateRow value) {
_rowService.createRow(); _rowService.createRow(rowInfo.rowPB.id);
}, },
didReceiveCells: (_DidReceiveCells value) async { didReceiveCells: (_DidReceiveCells value) async {
final cells = value.gridCellMap.values final cells = value.gridCellMap.values

View File

@ -255,7 +255,6 @@ class GridRowCache {
RowInfo buildGridRow(RowPB rowPB) { RowInfo buildGridRow(RowPB rowPB) {
return RowInfo( return RowInfo(
gridId: gridId, gridId: gridId,
blockId: block.id,
fields: _fieldNotifier.fields, fields: _fieldNotifier.fields,
rowPB: rowPB, rowPB: rowPB,
); );
@ -283,7 +282,6 @@ class _RowChangesetNotifier extends ChangeNotifier {
class RowInfo with _$RowInfo { class RowInfo with _$RowInfo {
const factory RowInfo({ const factory RowInfo({
required String gridId, required String gridId,
required String blockId,
required UnmodifiableListView<FieldPB> fields, required UnmodifiableListView<FieldPB> fields,
required RowPB rowPB, required RowPB rowPB,
}) = _RowInfo; }) = _RowInfo;

View File

@ -4,36 +4,47 @@ 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/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
class RowFFIService { class RowFFIService {
final String gridId; final String gridId;
final String blockId; final String blockId;
final String rowId;
RowFFIService( RowFFIService({
{required this.gridId, required this.blockId, required this.rowId}); required this.gridId,
required this.blockId,
});
Future<Either<RowPB, FlowyError>> createRow() { Future<Either<RowPB, FlowyError>> createRow(String rowId) {
CreateRowPayloadPB payload = CreateRowPayloadPB.create() final payload = CreateTableRowPayloadPB.create()
..gridId = gridId ..gridId = gridId
..startRowId = rowId; ..startRowId = rowId;
return GridEventCreateRow(payload).send(); return GridEventCreateTableRow(payload).send();
} }
Future<Either<Unit, FlowyError>> moveRow( Future<Either<Unit, FlowyError>> moveRow({
String rowId, int fromIndex, int toIndex) { required String rowId,
final payload = MoveItemPayloadPB.create() required int fromIndex,
..gridId = gridId required int toIndex,
..itemId = rowId required GridLayout layout,
..ty = MoveItemTypePB.MoveRow String? upperRowId,
}) {
var payload = MoveRowPayloadPB.create()
..viewId = gridId
..rowId = rowId
..layout = layout
..fromIndex = fromIndex ..fromIndex = fromIndex
..toIndex = toIndex; ..toIndex = toIndex;
return GridEventMoveItem(payload).send(); if (upperRowId != null) {
payload.upperRowId = upperRowId;
}
return GridEventMoveRow(payload).send();
} }
Future<Either<OptionalRowPB, FlowyError>> getRow() { Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
final payload = RowIdPB.create() final payload = RowIdPB.create()
..gridId = gridId ..gridId = gridId
..blockId = blockId ..blockId = blockId
@ -42,7 +53,7 @@ class RowFFIService {
return GridEventGetRow(payload).send(); return GridEventGetRow(payload).send();
} }
Future<Either<Unit, FlowyError>> deleteRow() { Future<Either<Unit, FlowyError>> deleteRow(String rowId) {
final payload = RowIdPB.create() final payload = RowIdPB.create()
..gridId = gridId ..gridId = gridId
..blockId = blockId ..blockId = blockId
@ -51,7 +62,7 @@ class RowFFIService {
return GridEventDeleteRow(payload).send(); return GridEventDeleteRow(payload).send();
} }
Future<Either<Unit, FlowyError>> duplicateRow() { Future<Either<Unit, FlowyError>> duplicateRow(String rowId) {
final payload = RowIdPB.create() final payload = RowIdPB.create()
..gridId = gridId ..gridId = gridId
..blockId = blockId ..blockId = blockId

View File

@ -239,8 +239,10 @@ class _GridRowsState extends State<_GridRows> {
RowInfo rowInfo, RowInfo rowInfo,
Animation<double> animation, Animation<double> animation,
) { ) {
final rowCache = final rowCache = context.read<GridBloc>().getRowCache(
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.rowPB.id); rowInfo.rowPB.blockId,
rowInfo.rowPB.id,
);
/// Return placeholder widget if the rowCache is null. /// Return placeholder widget if the rowCache is null.
if (rowCache == null) return const SizedBox(); if (rowCache == null) return const SizedBox();

View File

@ -53,7 +53,10 @@ class GridRowActionSheet extends StatelessWidget {
); );
} }
void show(BuildContext overlayContext) { void show(
BuildContext overlayContext, {
AnchorDirection direction = AnchorDirection.leftWithCenterAligned,
}) {
FlowyOverlay.of(overlayContext).insertWithAnchor( FlowyOverlay.of(overlayContext).insertWithAnchor(
widget: OverlayContainer( widget: OverlayContainer(
child: this, child: this,
@ -61,7 +64,7 @@ class GridRowActionSheet extends StatelessWidget {
), ),
identifier: GridRowActionSheet.identifier(), identifier: GridRowActionSheet.identifier(),
anchorContext: overlayContext, anchorContext: overlayContext,
anchorDirection: AnchorDirection.leftWithCenterAligned, anchorDirection: direction,
); );
} }

View File

@ -3,7 +3,7 @@ import 'package:provider/provider.dart';
import 'board_column/board_column.dart'; import 'board_column/board_column.dart';
import 'board_column/board_column_data.dart'; import 'board_column/board_column_data.dart';
import 'board_data.dart'; import 'board_data.dart';
import 'reorder_flex/drag_target_inteceptor.dart'; import 'reorder_flex/drag_target_interceptor.dart';
import 'reorder_flex/reorder_flex.dart'; import 'reorder_flex/reorder_flex.dart';
import 'reorder_phantom/phantom_controller.dart'; import 'reorder_phantom/phantom_controller.dart';
import '../rendering/board_overlay.dart'; import '../rendering/board_overlay.dart';
@ -143,7 +143,7 @@ class _BoardContentState extends State<BoardContent> {
void initState() { void initState() {
_overlayEntry = BoardOverlayEntry( _overlayEntry = BoardOverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
final interceptor = OverlappingDragTargetInteceptor( final interceptor = OverlappingDragTargetInterceptor(
reorderFlexId: widget.dataController.identifier, reorderFlexId: widget.dataController.identifier,
acceptedReorderFlexId: widget.dataController.columnIds, acceptedReorderFlexId: widget.dataController.columnIds,
delegate: widget.delegate, delegate: widget.delegate,

View File

@ -5,7 +5,7 @@ import '../../rendering/board_overlay.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import '../reorder_phantom/phantom_controller.dart'; import '../reorder_phantom/phantom_controller.dart';
import '../reorder_flex/reorder_flex.dart'; import '../reorder_flex/reorder_flex.dart';
import '../reorder_flex/drag_target_inteceptor.dart'; import '../reorder_flex/drag_target_interceptor.dart';
import 'board_column_data.dart'; import 'board_column_data.dart';
typedef OnColumnDragStarted = void Function(int index); typedef OnColumnDragStarted = void Function(int index);
@ -37,7 +37,7 @@ typedef AFBoardColumnFooterBuilder = Widget Function(
AFBoardColumnData columnData, AFBoardColumnData columnData,
); );
abstract class AFBoardColumnDataDataSource extends ReoderFlextDataSource { abstract class AFBoardColumnDataDataSource extends ReoderFlexDataSource {
AFBoardColumnData get columnData; AFBoardColumnData get columnData;
List<String> get acceptedColumnIds; List<String> get acceptedColumnIds;

View File

@ -51,8 +51,11 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
return item; return item;
} }
int removeWhere(bool Function(AFColumnItem) condition) { void removeWhere(bool Function(AFColumnItem) condition) {
return items.indexWhere(condition); final index = items.indexWhere(condition);
if (index != -1) {
removeAt(index);
}
} }
/// Move the item from [fromIndex] to [toIndex]. It will do nothing if the /// Move the item from [fromIndex] to [toIndex]. It will do nothing if the

View File

@ -24,7 +24,7 @@ typedef OnMoveColumnItemToColumn = void Function(
); );
class AFBoardDataController extends ChangeNotifier class AFBoardDataController extends ChangeNotifier
with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlextDataSource { with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlexDataSource {
final List<AFBoardColumnData> _columnDatas = []; final List<AFBoardColumnData> _columnDatas = [];
final OnMoveColumn? onMoveColumn; final OnMoveColumn? onMoveColumn;
final OnMoveColumnItem? onMoveColumnItem; final OnMoveColumnItem? onMoveColumnItem;

View File

@ -13,14 +13,14 @@ abstract class ReorderFlexDraggableTargetBuilder {
Widget child, Widget child,
DragTargetOnStarted onDragStarted, DragTargetOnStarted onDragStarted,
DragTargetOnEnded<T> onDragEnded, DragTargetOnEnded<T> onDragEnded,
DragTargetWillAccpet<T> onWillAccept, DragTargetWillAccepted<T> onWillAccept,
AnimationController insertAnimationController, AnimationController insertAnimationController,
AnimationController deleteAnimationController, AnimationController deleteAnimationController,
); );
} }
/// ///
typedef DragTargetWillAccpet<T extends DragTargetData> = bool Function( typedef DragTargetWillAccepted<T extends DragTargetData> = bool Function(
T dragTargetData); T dragTargetData);
/// ///
@ -51,7 +51,7 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
/// ///
/// [toAccept] represents the dragTarget index, which is the value passed in /// [toAccept] represents the dragTarget index, which is the value passed in
/// when creating the [ReorderDragTarget]. /// when creating the [ReorderDragTarget].
final DragTargetWillAccpet<T> onWillAccept; final DragTargetWillAccepted<T> onWillAccept;
/// Called when an acceptable piece of data was dropped over this drag target. /// Called when an acceptable piece of data was dropped over this drag target.
/// ///
@ -228,7 +228,7 @@ class DragTargetAnimation {
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10)); value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10));
} }
void startDargging() { void startDragging() {
entranceController.value = 1.0; entranceController.value = 1.0;
} }
@ -386,7 +386,7 @@ class FakeDragTarget<T extends DragTargetData> extends StatefulWidget {
final FakeDragTargetEventData eventData; final FakeDragTargetEventData eventData;
final DragTargetOnStarted onDragStarted; final DragTargetOnStarted onDragStarted;
final DragTargetOnEnded<T> onDragEnded; final DragTargetOnEnded<T> onDragEnded;
final DragTargetWillAccpet<T> onWillAccept; final DragTargetWillAccepted<T> onWillAccept;
final Widget child; final Widget child;
final AnimationController insertAnimationController; final AnimationController insertAnimationController;
final AnimationController deleteAnimationController; final AnimationController deleteAnimationController;

View File

@ -40,18 +40,18 @@ abstract class OverlapDragTargetDelegate {
bool canMoveTo(String dragTargetId); bool canMoveTo(String dragTargetId);
} }
/// [OverlappingDragTargetInteceptor] is used to receive the overlapping /// [OverlappingDragTargetInterceptor] is used to receive the overlapping
/// [DragTarget] event. If a [DragTarget] child is [DragTarget], it will /// [DragTarget] event. If a [DragTarget] child is [DragTarget], it will
/// receive the [DragTarget] event when being dragged. /// receive the [DragTarget] event when being dragged.
/// ///
/// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains /// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains
/// the passed in dragTarget' reorderFlexId. /// the passed in dragTarget' reorderFlexId.
class OverlappingDragTargetInteceptor extends DragTargetInterceptor { class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
final String reorderFlexId; final String reorderFlexId;
final List<String> acceptedReorderFlexId; final List<String> acceptedReorderFlexId;
final OverlapDragTargetDelegate delegate; final OverlapDragTargetDelegate delegate;
OverlappingDragTargetInteceptor({ OverlappingDragTargetInterceptor({
required this.delegate, required this.delegate,
required this.reorderFlexId, required this.reorderFlexId,
required this.acceptedReorderFlexId, required this.acceptedReorderFlexId,

View File

@ -7,25 +7,25 @@ import '../../utils/log.dart';
import 'reorder_mixin.dart'; import 'reorder_mixin.dart';
import 'drag_target.dart'; import 'drag_target.dart';
import 'drag_state.dart'; import 'drag_state.dart';
import 'drag_target_inteceptor.dart'; import 'drag_target_interceptor.dart';
typedef OnDragStarted = void Function(int index); typedef OnDragStarted = void Function(int index);
typedef OnDragEnded = void Function(); typedef OnDragEnded = void Function();
typedef OnReorder = void Function(int fromIndex, int toIndex); typedef OnReorder = void Function(int fromIndex, int toIndex);
typedef OnDeleted = void Function(int deletedIndex); typedef OnDeleted = void Function(int deletedIndex);
typedef OnInserted = void Function(int insertedIndex); typedef OnInserted = void Function(int insertedIndex);
typedef OnReveivePassedInPhantom = void Function( typedef OnReceivePassedInPhantom = void Function(
FlexDragTargetData dragTargetData, int phantomIndex); FlexDragTargetData dragTargetData, int phantomIndex);
abstract class ReoderFlextDataSource { abstract class ReoderFlexDataSource {
/// [identifier] represents the id the [ReorderFlex]. It must be unique. /// [identifier] represents the id the [ReorderFlex]. It must be unique.
String get identifier; String get identifier;
/// The number of [ReoderFlexItem]s will be displaied in the [ReorderFlex]. /// The number of [ReoderFlexItem]s will be displayed in the [ReorderFlex].
UnmodifiableListView<ReoderFlexItem> get items; UnmodifiableListView<ReoderFlexItem> get items;
} }
/// Each item displaied in the [ReorderFlex] required to implement the [ReoderFlexItem]. /// Each item displayed in the [ReorderFlex] required to implement the [ReoderFlexItem].
abstract class ReoderFlexItem { abstract class ReoderFlexItem {
/// [id] is used to identify the item. It must be unique. /// [id] is used to identify the item. It must be unique.
String get id; String get id;
@ -70,7 +70,7 @@ class ReorderFlex extends StatefulWidget {
/// [onDragEnded] is called when dragTarget did end dragging /// [onDragEnded] is called when dragTarget did end dragging
final OnDragEnded? onDragEnded; final OnDragEnded? onDragEnded;
final ReoderFlextDataSource dataSource; final ReoderFlexDataSource dataSource;
final DragTargetInterceptor? interceptor; final DragTargetInterceptor? interceptor;
@ -187,7 +187,7 @@ class ReorderFlexState extends State<ReorderFlex>
void _requestAnimationToNextIndex({bool isAcceptingNewTarget = false}) { void _requestAnimationToNextIndex({bool isAcceptingNewTarget = false}) {
/// Update the dragState and animate to the next index if the current /// Update the dragState and animate to the next index if the current
/// dragging animation is completed. Otherwise, it will get called again /// dragging animation is completed. Otherwise, it will get called again
/// when the animation finishs. /// when the animation finish.
if (_animation.entranceController.isCompleted) { if (_animation.entranceController.isCompleted) {
dragState.removePhantom(); dragState.removePhantom();
@ -425,7 +425,7 @@ class ReorderFlexState extends State<ReorderFlex>
) { ) {
setState(() { setState(() {
dragState.startDragging(draggingWidget, dragIndex, feedbackSize); dragState.startDragging(draggingWidget, dragIndex, feedbackSize);
_animation.startDargging(); _animation.startDragging();
}); });
} }

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart';
import '../../utils/log.dart'; import '../../utils/log.dart';
import '../board_column/board_column_data.dart'; import '../board_column/board_column_data.dart';
import '../reorder_flex/drag_state.dart'; import '../reorder_flex/drag_state.dart';
import '../reorder_flex/drag_target.dart'; import '../reorder_flex/drag_target.dart';
import '../reorder_flex/drag_target_inteceptor.dart'; import '../reorder_flex/drag_target_interceptor.dart';
import 'phantom_state.dart'; import 'phantom_state.dart';
abstract class BoardPhantomControllerDelegate { abstract class BoardPhantomControllerDelegate {
@ -61,7 +62,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate
columnsState.setColumnIsDragging(columnId, false); columnsState.setColumnIsDragging(columnId, false);
} }
/// Remove the phanton in the column when the column is end dragging. /// Remove the phantom in the column when the column is end dragging.
void columnEndDragging(String columnId) { void columnEndDragging(String columnId) {
columnsState.setColumnIsDragging(columnId, true); columnsState.setColumnIsDragging(columnId, true);
if (phantomRecord == null) return; if (phantomRecord == null) return;
@ -331,7 +332,7 @@ class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
Widget child, Widget child,
DragTargetOnStarted onDragStarted, DragTargetOnStarted onDragStarted,
DragTargetOnEnded<T> onDragEnded, DragTargetOnEnded<T> onDragEnded,
DragTargetWillAccpet<T> onWillAccept, DragTargetWillAccepted<T> onWillAccept,
AnimationController insertAnimationController, AnimationController insertAnimationController,
AnimationController deleteAnimationController, AnimationController deleteAnimationController,
) { ) {

View File

@ -11,7 +11,7 @@ pub enum GridNotification {
DidUpdateRow = 30, DidUpdateRow = 30,
DidUpdateCell = 40, DidUpdateCell = 40,
DidUpdateField = 50, DidUpdateField = 50,
DidUpdateBoard = 60, DidUpdateGroup = 60,
} }
impl std::default::Default for GridNotification { impl std::default::Default for GridNotification {

View File

@ -1,5 +1,5 @@
use crate::entities::{BlockPB, FieldIdPB}; use crate::entities::{BlockPB, FieldIdPB, GridLayout};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode; use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::parser::NotEmptyStr;
@ -52,25 +52,51 @@ impl std::convert::From<&str> for GridBlockIdPB {
} }
} }
#[derive(Debug, Clone, ProtoBuf_Enum)]
pub enum MoveItemTypePB {
MoveField = 0,
MoveRow = 1,
}
impl std::default::Default for MoveItemTypePB {
fn default() -> Self {
MoveItemTypePB::MoveField
}
}
#[derive(Debug, Clone, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct MoveItemPayloadPB { pub struct MoveFieldPayloadPB {
#[pb(index = 1)] #[pb(index = 1)]
pub grid_id: String, pub grid_id: String,
#[pb(index = 2)] #[pb(index = 2)]
pub item_id: String, pub field_id: String,
#[pb(index = 3)]
pub from_index: i32,
#[pb(index = 4)]
pub to_index: i32,
}
#[derive(Clone)]
pub struct MoveFieldParams {
pub grid_id: String,
pub field_id: String,
pub from_index: i32,
pub to_index: i32,
}
impl TryInto<MoveFieldParams> for MoveFieldPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<MoveFieldParams, Self::Error> {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
let item_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::InvalidData)?;
Ok(MoveFieldParams {
grid_id: grid_id.0,
field_id: item_id.0,
from_index: self.from_index,
to_index: self.to_index,
})
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct MoveRowPayloadPB {
#[pb(index = 1)]
pub view_id: String,
#[pb(index = 2)]
pub row_id: String,
#[pb(index = 3)] #[pb(index = 3)]
pub from_index: i32, pub from_index: i32,
@ -79,30 +105,38 @@ pub struct MoveItemPayloadPB {
pub to_index: i32, pub to_index: i32,
#[pb(index = 5)] #[pb(index = 5)]
pub ty: MoveItemTypePB, pub layout: GridLayout,
#[pb(index = 6, one_of)]
pub upper_row_id: Option<String>,
} }
#[derive(Clone)] pub struct MoveRowParams {
pub struct MoveItemParams { pub view_id: String,
pub grid_id: String, pub row_id: String,
pub item_id: String,
pub from_index: i32, pub from_index: i32,
pub to_index: i32, pub to_index: i32,
pub ty: MoveItemTypePB, pub layout: GridLayout,
pub upper_row_id: Option<String>,
} }
impl TryInto<MoveItemParams> for MoveItemPayloadPB { impl TryInto<MoveRowParams> for MoveRowPayloadPB {
type Error = ErrorCode; type Error = ErrorCode;
fn try_into(self) -> Result<MoveItemParams, Self::Error> { fn try_into(self) -> Result<MoveRowParams, Self::Error> {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::GridViewIdIsEmpty)?;
let item_id = NotEmptyStr::parse(self.item_id).map_err(|_| ErrorCode::InvalidData)?; let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
Ok(MoveItemParams { let upper_row_id = match self.upper_row_id {
grid_id: grid_id.0, None => None,
item_id: item_id.0, Some(upper_row_id) => Some(NotEmptyStr::parse(upper_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0),
};
Ok(MoveRowParams {
view_id: view_id.0,
row_id: row_id.0,
from_index: self.from_index, from_index: self.from_index,
to_index: self.to_index, to_index: self.to_index,
ty: self.ty, layout: self.layout,
upper_row_id,
}) })
} }
} }

View File

@ -1,4 +1,4 @@
use crate::entities::{CreateRowParams, RowPB}; use crate::entities::{CreateRowParams, GridLayout};
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode; use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::parser::NotEmptyStr;
@ -22,46 +22,7 @@ impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
grid_id: grid_id.0, grid_id: grid_id.0,
start_row_id: None, start_row_id: None,
group_id: Some(group_id.0), group_id: Some(group_id.0),
layout: GridLayout::Board,
}) })
} }
} }
#[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()
}
}
}

View File

@ -0,0 +1,43 @@
use crate::entities::{InsertedRowPB, RowPB};
use flowy_derive::ProtoBuf;
#[derive(Debug, Default, ProtoBuf)]
pub struct GroupRowsChangesetPB {
#[pb(index = 1)]
pub group_id: String,
#[pb(index = 2)]
pub inserted_rows: Vec<InsertedRowPB>,
#[pb(index = 3)]
pub deleted_rows: Vec<String>,
#[pb(index = 4)]
pub updated_rows: Vec<RowPB>,
}
impl GroupRowsChangesetPB {
pub fn insert(group_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
Self {
group_id,
inserted_rows,
..Default::default()
}
}
pub fn delete(group_id: String, deleted_rows: Vec<String>) -> Self {
Self {
group_id,
deleted_rows,
..Default::default()
}
}
pub fn update(group_id: String, updated_rows: Vec<RowPB>) -> Self {
Self {
group_id,
updated_rows,
..Default::default()
}
}
}

View File

@ -1,7 +1,9 @@
mod board_card; mod board_card;
mod configuration; mod configuration;
mod group; mod group;
mod group_changeset;
pub use board_card::*; pub use board_card::*;
pub use configuration::*; pub use configuration::*;
pub use group::*; pub use group::*;
pub use group_changeset::*;

View File

@ -1,3 +1,4 @@
use crate::entities::GridLayout;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode; use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::parser::NotEmptyStr;
@ -46,7 +47,7 @@ pub struct BlockRowIdPB {
} }
#[derive(ProtoBuf, Default)] #[derive(ProtoBuf, Default)]
pub struct CreateRowPayloadPB { pub struct CreateTableRowPayloadPB {
#[pb(index = 1)] #[pb(index = 1)]
pub grid_id: String, pub grid_id: String,
@ -59,17 +60,20 @@ pub struct CreateRowParams {
pub grid_id: String, pub grid_id: String,
pub start_row_id: Option<String>, pub start_row_id: Option<String>,
pub group_id: Option<String>, pub group_id: Option<String>,
pub layout: GridLayout,
} }
impl TryInto<CreateRowParams> for CreateRowPayloadPB { impl TryInto<CreateRowParams> for CreateTableRowPayloadPB {
type Error = ErrorCode; type Error = ErrorCode;
fn try_into(self) -> Result<CreateRowParams, Self::Error> { fn try_into(self) -> Result<CreateRowParams, Self::Error> {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
Ok(CreateRowParams { Ok(CreateRowParams {
grid_id: grid_id.0, grid_id: grid_id.0,
start_row_id: self.start_row_id, start_row_id: self.start_row_id,
group_id: None, group_id: None,
layout: GridLayout::Table,
}) })
} }
} }

View File

@ -19,7 +19,7 @@ pub struct GridSettingPB {
pub layouts: Vec<GridLayoutPB>, pub layouts: Vec<GridLayoutPB>,
#[pb(index = 2)] #[pb(index = 2)]
pub current_layout_type: Layout, pub current_layout_type: GridLayout,
#[pb(index = 3)] #[pb(index = 3)]
pub filter_configuration_by_field_id: HashMap<String, RepeatedGridConfigurationFilterPB>, pub filter_configuration_by_field_id: HashMap<String, RepeatedGridConfigurationFilterPB>,
@ -34,13 +34,13 @@ pub struct GridSettingPB {
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct GridLayoutPB { pub struct GridLayoutPB {
#[pb(index = 1)] #[pb(index = 1)]
ty: Layout, ty: GridLayout,
} }
impl GridLayoutPB { impl GridLayoutPB {
pub fn all() -> Vec<GridLayoutPB> { pub fn all() -> Vec<GridLayoutPB> {
let mut layouts = vec![]; let mut layouts = vec![];
for layout_ty in Layout::iter() { for layout_ty in GridLayout::iter() {
layouts.push(GridLayoutPB { ty: layout_ty }) layouts.push(GridLayoutPB { ty: layout_ty })
} }
@ -50,31 +50,31 @@ impl GridLayoutPB {
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)] #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)]
#[repr(u8)] #[repr(u8)]
pub enum Layout { pub enum GridLayout {
Table = 0, Table = 0,
Board = 1, Board = 1,
} }
impl std::default::Default for Layout { impl std::default::Default for GridLayout {
fn default() -> Self { fn default() -> Self {
Layout::Table GridLayout::Table
} }
} }
impl std::convert::From<LayoutRevision> for Layout { impl std::convert::From<LayoutRevision> for GridLayout {
fn from(rev: LayoutRevision) -> Self { fn from(rev: LayoutRevision) -> Self {
match rev { match rev {
LayoutRevision::Table => Layout::Table, LayoutRevision::Table => GridLayout::Table,
LayoutRevision::Board => Layout::Board, LayoutRevision::Board => GridLayout::Board,
} }
} }
} }
impl std::convert::From<Layout> for LayoutRevision { impl std::convert::From<GridLayout> for LayoutRevision {
fn from(layout: Layout) -> Self { fn from(layout: GridLayout) -> Self {
match layout { match layout {
Layout::Table => LayoutRevision::Table, GridLayout::Table => LayoutRevision::Table,
Layout::Board => LayoutRevision::Board, GridLayout::Board => LayoutRevision::Board,
} }
} }
} }
@ -85,7 +85,7 @@ pub struct GridSettingChangesetPayloadPB {
pub grid_id: String, pub grid_id: String,
#[pb(index = 2)] #[pb(index = 2)]
pub layout_type: Layout, pub layout_type: GridLayout,
#[pb(index = 3, one_of)] #[pb(index = 3, one_of)]
pub insert_filter: Option<CreateGridFilterPayloadPB>, pub insert_filter: Option<CreateGridFilterPayloadPB>,

View File

@ -203,13 +203,13 @@ pub(crate) async fn create_field_type_option_data_handler(
} }
#[tracing::instrument(level = "trace", skip(data, manager), err)] #[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn move_item_handler( pub(crate) async fn move_field_handler(
data: Data<MoveItemPayloadPB>, data: Data<MoveFieldPayloadPB>,
manager: AppData<Arc<GridManager>>, manager: AppData<Arc<GridManager>>,
) -> Result<(), FlowyError> { ) -> Result<(), FlowyError> {
let params: MoveItemParams = data.into_inner().try_into()?; let params: MoveFieldParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.grid_id)?; let editor = manager.get_grid_editor(&params.grid_id)?;
let _ = editor.move_item(params).await?; let _ = editor.move_field(params).await?;
Ok(()) Ok(())
} }
@ -260,8 +260,19 @@ pub(crate) async fn duplicate_row_handler(
} }
#[tracing::instrument(level = "debug", skip(data, manager), err)] #[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn create_row_handler( pub(crate) async fn move_row_handler(
data: Data<CreateRowPayloadPB>, data: Data<MoveRowPayloadPB>,
manager: AppData<Arc<GridManager>>,
) -> Result<(), FlowyError> {
let params: MoveRowParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.view_id)?;
let _ = editor.move_row(params).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn create_table_row_handler(
data: Data<CreateTableRowPayloadPB>,
manager: AppData<Arc<GridManager>>, manager: AppData<Arc<GridManager>>,
) -> DataResult<RowPB, FlowyError> { ) -> DataResult<RowPB, FlowyError> {
let params: CreateRowParams = data.into_inner().try_into()?; let params: CreateRowParams = data.into_inner().try_into()?;

View File

@ -20,14 +20,15 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
.event(GridEvent::DeleteField, delete_field_handler) .event(GridEvent::DeleteField, delete_field_handler)
.event(GridEvent::SwitchToField, switch_to_field_handler) .event(GridEvent::SwitchToField, switch_to_field_handler)
.event(GridEvent::DuplicateField, duplicate_field_handler) .event(GridEvent::DuplicateField, duplicate_field_handler)
.event(GridEvent::MoveItem, move_item_handler) .event(GridEvent::MoveField, move_field_handler)
.event(GridEvent::GetFieldTypeOption, get_field_type_option_data_handler) .event(GridEvent::GetFieldTypeOption, get_field_type_option_data_handler)
.event(GridEvent::CreateFieldTypeOption, create_field_type_option_data_handler) .event(GridEvent::CreateFieldTypeOption, create_field_type_option_data_handler)
// Row // Row
.event(GridEvent::CreateRow, create_row_handler) .event(GridEvent::CreateTableRow, create_table_row_handler)
.event(GridEvent::GetRow, get_row_handler) .event(GridEvent::GetRow, get_row_handler)
.event(GridEvent::DeleteRow, delete_row_handler) .event(GridEvent::DeleteRow, delete_row_handler)
.event(GridEvent::DuplicateRow, duplicate_row_handler) .event(GridEvent::DuplicateRow, duplicate_row_handler)
.event(GridEvent::MoveRow, move_row_handler)
// Cell // Cell
.event(GridEvent::GetCell, get_cell_handler) .event(GridEvent::GetCell, get_cell_handler)
.event(GridEvent::UpdateCell, update_cell_handler) .event(GridEvent::UpdateCell, update_cell_handler)
@ -130,8 +131,8 @@ pub enum GridEvent {
/// [MoveItem] event is used to move an item. For the moment, Item has two types defined in /// [MoveItem] event is used to move an item. For the moment, Item has two types defined in
/// [MoveItemTypePB]. /// [MoveItemTypePB].
#[event(input = "MoveItemPayloadPB")] #[event(input = "MoveFieldPayloadPB")]
MoveItem = 22, MoveField = 22,
/// [FieldTypeOptionIdPB] event is used to get the FieldTypeOption data for a specific field type. /// [FieldTypeOptionIdPB] event is used to get the FieldTypeOption data for a specific field type.
/// ///
@ -166,8 +167,8 @@ pub enum GridEvent {
#[event(input = "SelectOptionChangesetPayloadPB")] #[event(input = "SelectOptionChangesetPayloadPB")]
UpdateSelectOption = 32, UpdateSelectOption = 32,
#[event(input = "CreateRowPayloadPB", output = "RowPB")] #[event(input = "CreateTableRowPayloadPB", output = "RowPB")]
CreateRow = 50, CreateTableRow = 50,
/// [GetRow] event is used to get the row data,[RowPB]. [OptionalRowPB] is a wrapper that enables /// [GetRow] event is used to get the row data,[RowPB]. [OptionalRowPB] is a wrapper that enables
/// to return a nullable row data. /// to return a nullable row data.
@ -180,6 +181,9 @@ pub enum GridEvent {
#[event(input = "RowIdPB")] #[event(input = "RowIdPB")]
DuplicateRow = 53, DuplicateRow = 53,
#[event(input = "MoveRowPayloadPB")]
MoveRow = 54,
#[event(input = "GridCellIdPB", output = "GridCellPB")] #[event(input = "GridCellIdPB", output = "GridCellPB")]
GetCell = 70, GetCell = 70,

View File

@ -59,7 +59,7 @@ impl GridBlockRevisionEditor {
if let Some(start_row_id) = prev_row_id.as_ref() { if let Some(start_row_id) = prev_row_id.as_ref() {
match block_pad.index_of_row(start_row_id) { match block_pad.index_of_row(start_row_id) {
None => {} None => {}
Some(index) => row_index = Some(index + 1), Some(index) => row_index = Some(index as i32 + 1),
} }
} }
@ -100,6 +100,10 @@ impl GridBlockRevisionEditor {
Ok(()) Ok(())
} }
pub async fn index_of_row(&self, row_id: &str) -> Option<usize> {
self.pad.read().await.index_of_row(row_id)
}
pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<Arc<RowRevision>>> { pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<Arc<RowRevision>>> {
let row_ids = vec![Cow::Borrowed(row_id)]; let row_ids = vec![Cow::Borrowed(row_id)];
let row_rev = self.get_row_revs(Some(row_ids)).await?.pop(); let row_rev = self.get_row_revs(Some(row_ids)).await?.pop();

View File

@ -52,7 +52,7 @@ impl GridBlockManager {
} }
} }
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> { pub(crate) async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
let block_id = self.persistence.get_block_id(row_id)?; let block_id = self.persistence.get_block_id(row_id)?;
Ok(self.get_block_editor(&block_id).await?) Ok(self.get_block_editor(&block_id).await?)
} }
@ -122,6 +122,7 @@ impl GridBlockManager {
Ok(()) Ok(())
} }
#[tracing::instrument(level = "trace", skip_all, err)]
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let row_id = row_id.to_owned(); let row_id = row_id.to_owned();
let block_id = self.persistence.get_block_id(&row_id)?; let block_id = self.persistence.get_block_id(&row_id)?;
@ -155,7 +156,7 @@ impl GridBlockManager {
Ok(changesets) Ok(changesets)
} }
// This function will be moved to GridViewRevisionEditor
pub(crate) async fn move_row(&self, row_rev: Arc<RowRevision>, from: usize, to: usize) -> FlowyResult<()> { 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 = self.get_editor_from_row_id(&row_rev.id).await?;
let _ = editor.move_row(&row_rev.id, from, to).await?; let _ = editor.move_row(&row_rev.id, from, to).await?;
@ -180,6 +181,14 @@ impl GridBlockManager {
Ok(()) Ok(())
} }
// This function will be moved to GridViewRevisionEditor.
pub async fn index_of_row(&self, row_id: &str) -> Option<usize> {
match self.get_editor_from_row_id(row_id).await {
Ok(editor) => editor.index_of_row(row_id).await,
Err(_) => None,
}
}
pub async fn update_cell<F>(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()> pub async fn update_cell<F>(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()>
where where
F: FnOnce(Arc<RowRevision>) -> RowPB, F: FnOnce(Arc<RowRevision>) -> RowPB,

View File

@ -342,7 +342,7 @@ impl GridRevisionEditor {
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let _ = self.block_manager.delete_row(row_id).await?; let _ = self.block_manager.delete_row(row_id).await?;
self.view_manager.delete_row(row_id).await; self.view_manager.did_delete_row(row_id).await;
Ok(()) Ok(())
} }
@ -484,21 +484,22 @@ impl GridRevisionEditor {
Ok(snapshots) Ok(snapshots)
} }
pub async fn move_item(&self, params: MoveItemParams) -> FlowyResult<()> { pub async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
match params.ty { self.view_manager.move_row(params).await
MoveItemTypePB::MoveField => {
self.move_field(&params.item_id, params.from_index, params.to_index)
.await
}
MoveItemTypePB::MoveRow => self.move_row(&params.item_id, params.from_index, params.to_index).await,
}
} }
pub async fn move_field(&self, field_id: &str, from: i32, to: i32) -> FlowyResult<()> { pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> {
let MoveFieldParams {
grid_id: _,
field_id,
from_index,
to_index,
} = params;
let _ = self let _ = self
.modify(|grid_pad| Ok(grid_pad.move_field(field_id, from as usize, to as usize)?)) .modify(|grid_pad| Ok(grid_pad.move_field(&field_id, from_index as usize, to_index as usize)?))
.await?; .await?;
if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(field_id) { if let Some((index, field_rev)) = self.grid_pad.read().await.get_field_rev(&field_id) {
let delete_field_order = FieldIdPB::from(field_id); let delete_field_order = FieldIdPB::from(field_id);
let insert_field = IndexFieldPB::from_field_rev(field_rev, index); let insert_field = IndexFieldPB::from_field_rev(field_rev, index);
let notified_changeset = FieldChangesetPB { let notified_changeset = FieldChangesetPB {
@ -513,10 +514,6 @@ impl GridRevisionEditor {
Ok(()) Ok(())
} }
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
self.view_manager.move_row(row_id, from, to).await
}
pub async fn delta_bytes(&self) -> Bytes { pub async fn delta_bytes(&self) -> Bytes {
self.grid_pad.read().await.delta_bytes() self.grid_pad.read().await.delta_bytes()
} }

View File

@ -1,13 +1,17 @@
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, RowPB}; use crate::entities::{
CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB,
RowPB,
};
use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService}; use crate::services::group::{default_group_configuration, Group, GroupConfigurationDelegate, GroupService};
use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision}; use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision};
use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder}; use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder};
use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad}; use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
use flowy_sync::entities::revision::Revision; use flowy_sync::entities::revision::Revision;
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::services::setting::make_grid_setting; use crate::services::setting::make_grid_setting;
use flowy_sync::entities::grid::GridSettingChangesetParams; use flowy_sync::entities::grid::GridSettingChangesetParams;
use lib_infra::future::{wrap_future, AFFuture, FutureResult}; use lib_infra::future::{wrap_future, AFFuture, FutureResult};
@ -19,7 +23,7 @@ pub trait GridViewRevisionDelegate: Send + Sync + 'static {
fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>>; fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>>;
} }
pub trait GridViewRevisionDataSource: Send + Sync + 'static { pub trait GridViewRevisionRowDataSource: Send + Sync + 'static {
fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>>; fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>>;
} }
@ -30,8 +34,9 @@ pub struct GridViewRevisionEditor {
pad: Arc<RwLock<GridViewRevisionPad>>, pad: Arc<RwLock<GridViewRevisionPad>>,
rev_manager: Arc<RevisionManager>, rev_manager: Arc<RevisionManager>,
delegate: Arc<dyn GridViewRevisionDelegate>, delegate: Arc<dyn GridViewRevisionDelegate>,
data_source: Arc<dyn GridViewRevisionDataSource>, data_source: Arc<dyn GridViewRevisionRowDataSource>,
group_service: Arc<RwLock<GroupService>>, group_service: Arc<RwLock<GroupService>>,
groups: Arc<RwLock<Vec<Group>>>,
scheduler: Arc<dyn GridServiceTaskScheduler>, scheduler: Arc<dyn GridServiceTaskScheduler>,
} }
@ -47,7 +52,7 @@ impl GridViewRevisionEditor {
) -> FlowyResult<Self> ) -> FlowyResult<Self>
where where
Delegate: GridViewRevisionDelegate, Delegate: GridViewRevisionDelegate,
DataSource: GridViewRevisionDataSource, DataSource: GridViewRevisionRowDataSource,
{ {
let cloud = Arc::new(GridViewRevisionCloudService { let cloud = Arc::new(GridViewRevisionCloudService {
token: token.to_owned(), token: token.to_owned(),
@ -57,52 +62,87 @@ impl GridViewRevisionEditor {
let rev_manager = Arc::new(rev_manager); let rev_manager = Arc::new(rev_manager);
let group_service = GroupService::new(Box::new(pad.clone())).await; let group_service = GroupService::new(Box::new(pad.clone())).await;
let user_id = user_id.to_owned(); let user_id = user_id.to_owned();
let groups = Arc::new(RwLock::new(vec![]));
Ok(Self { Ok(Self {
pad, pad,
user_id, user_id,
view_id, view_id,
rev_manager, rev_manager,
scheduler, scheduler,
groups,
delegate: Arc::new(delegate), delegate: Arc::new(delegate),
data_source: Arc::new(data_source), data_source: Arc::new(data_source),
group_service: Arc::new(RwLock::new(group_service)), group_service: Arc::new(RwLock::new(group_service)),
}) })
} }
pub(crate) async fn create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
match params.group_id.as_ref() { match params.layout {
None => {} GridLayout::Table => {
Some(group_id) => { // Table can be grouped too
self.group_service
.read()
.await
.update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id))
.await;
} }
GridLayout::Board => match params.group_id.as_ref() {
None => {}
Some(group_id) => {
self.group_service
.read()
.await
.update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id))
.await;
}
},
} }
todo!()
} }
pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
// Send the group notification if the current view has groups
match params.group_id.as_ref() { match params.group_id.as_ref() {
None => {} None => {}
Some(group_id) => { Some(group_id) => {
self.group_service.read().await.did_create_row(group_id, row_pb).await; let inserted_row = InsertedRowPB {
row: row_pb.clone(),
index: None,
};
let changeset = GroupRowsChangesetPB::insert(group_id.clone(), vec![inserted_row]);
self.notify_did_update_group(changeset).await;
} }
} }
} }
pub(crate) async fn delete_row(&self, row_id: &str) { pub(crate) async fn did_delete_row(&self, row_id: &str) {
self.group_service.read().await.did_delete_card(row_id.to_owned()).await; // Send the group notification if the current view has groups;
match self.group_id_of_row(row_id).await {
None => {}
Some(group_id) => {
let changeset = GroupRowsChangesetPB::delete(group_id, vec![row_id.to_owned()]);
self.notify_did_update_group(changeset).await;
}
}
}
async fn group_id_of_row(&self, row_id: &str) -> Option<String> {
let read_guard = self.groups.read().await;
for group in read_guard.iter() {
if group.rows.iter().any(|row| row.id == row_id) {
return Some(group.id.clone());
}
}
None
} }
pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> { pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
let field_revs = self.delegate.get_field_revs().await; let field_revs = self.delegate.get_field_revs().await;
let row_revs = self.data_source.row_revs().await; let row_revs = self.data_source.row_revs().await;
//
let mut write_guard = self.group_service.write().await; let mut write_guard = self.group_service.write().await;
match write_guard.load_groups(&field_revs, row_revs).await { match write_guard.load_groups(&field_revs, row_revs).await {
None => Ok(vec![]), None => Ok(vec![]),
Some(groups) => Ok(groups), Some(groups) => {
*self.groups.write().await = groups.clone();
Ok(groups.into_iter().map(GroupPB::from).collect())
}
} }
} }
@ -129,6 +169,12 @@ impl GridViewRevisionEditor {
} }
} }
async fn notify_did_update_group(&self, changeset: GroupRowsChangesetPB) {
send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup)
.payload(changeset)
.send();
}
async fn modify<F>(&self, f: F) -> FlowyResult<()> async fn modify<F>(&self, f: F) -> FlowyResult<()>
where where
F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>, F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,

View File

@ -1,8 +1,12 @@
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB}; use crate::entities::{
CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, MoveRowParams, RepeatedGridGroupPB, RowPB,
};
use crate::manager::GridUser; use crate::manager::GridUser;
use crate::services::block_manager::GridBlockManager; use crate::services::block_manager::GridBlockManager;
use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::grid_view_editor::{GridViewRevisionDataSource, GridViewRevisionDelegate, GridViewRevisionEditor}; use crate::services::grid_view_editor::{
GridViewRevisionDelegate, GridViewRevisionEditor, GridViewRevisionRowDataSource,
};
use bytes::Bytes; use bytes::Bytes;
use dashmap::DashMap; use dashmap::DashMap;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
@ -45,7 +49,7 @@ impl GridViewManager {
pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
for view_editor in self.view_editors.iter() { for view_editor in self.view_editors.iter() {
view_editor.create_row(row_rev, params).await; view_editor.update_row(row_rev, params).await;
} }
} }
@ -55,9 +59,9 @@ impl GridViewManager {
} }
} }
pub(crate) async fn delete_row(&self, row_id: &str) { pub(crate) async fn did_delete_row(&self, row_id: &str) {
for view_editor in self.view_editors.iter() { for view_editor in self.view_editors.iter() {
view_editor.delete_row(row_id).await; view_editor.did_delete_row(row_id).await;
} }
} }
@ -83,15 +87,35 @@ impl GridViewManager {
Ok(RepeatedGridGroupPB { items: groups }) Ok(RepeatedGridGroupPB { items: groups })
} }
pub(crate) async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> { pub(crate) async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
match self.block_manager.get_row_rev(row_id).await? { let MoveRowParams {
view_id: _,
row_id,
from_index,
to_index,
layout,
upper_row_id,
} = params;
let from_index = from_index as usize;
match self.block_manager.get_row_rev(&row_id).await? {
None => tracing::warn!("Move row failed, can not find the row:{}", row_id), None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
Some(row_rev) => { Some(row_rev) => match layout {
let _ = self GridLayout::Table => {
.block_manager tracing::trace!("Move row from {} to {}", from_index, to_index);
.move_row(row_rev.clone(), from as usize, to as usize) let to_index = to_index as usize;
.await?; let _ = self.block_manager.move_row(row_rev, from_index, to_index).await?;
} }
GridLayout::Board => {
if let Some(upper_row_id) = upper_row_id {
if let Some(to_index) = self.block_manager.index_of_row(&upper_row_id).await {
tracing::trace!("Move row from {} to {}", from_index, to_index);
let _ = self.block_manager.move_row(row_rev, from_index, to_index).await?;
}
}
}
},
} }
Ok(()) Ok(())
} }
@ -132,7 +156,7 @@ async fn make_view_editor<Delegate, DataSource>(
) -> FlowyResult<GridViewRevisionEditor> ) -> FlowyResult<GridViewRevisionEditor>
where where
Delegate: GridViewRevisionDelegate, Delegate: GridViewRevisionDelegate,
DataSource: GridViewRevisionDataSource, DataSource: GridViewRevisionRowDataSource,
{ {
tracing::trace!("Open view:{} editor", view_id); tracing::trace!("Open view:{} editor", view_id);
@ -170,7 +194,7 @@ impl RevisionCompactor for GridViewRevisionCompactor {
} }
} }
impl GridViewRevisionDataSource for Arc<GridBlockManager> { impl GridViewRevisionRowDataSource for Arc<GridBlockManager> {
fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>> { fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>> {
let block_manager = self.clone(); let block_manager = self.clone();

View File

@ -22,7 +22,7 @@ impl GroupActionHandler for CheckboxGroupController {
&self.field_id &self.field_id
} }
fn get_groups(&self) -> Vec<Group> { fn build_groups(&self) -> Vec<Group> {
self.make_groups() self.make_groups()
} }

View File

@ -28,7 +28,7 @@ pub trait Groupable {
pub trait GroupActionHandler: Send + Sync { pub trait GroupActionHandler: Send + Sync {
fn field_id(&self) -> &str; fn field_id(&self) -> &str;
fn get_groups(&self) -> Vec<Group>; fn build_groups(&self) -> Vec<Group>;
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>; 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); fn update_card(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
} }

View File

@ -30,7 +30,7 @@ impl GroupActionHandler for SingleSelectGroupController {
&self.field_id &self.field_id
} }
fn get_groups(&self) -> Vec<Group> { fn build_groups(&self) -> Vec<Group> {
self.make_groups() self.make_groups()
} }
@ -94,7 +94,7 @@ impl GroupActionHandler for MultiSelectGroupController {
&self.field_id &self.field_id
} }
fn get_groups(&self) -> Vec<Group> { fn build_groups(&self) -> Vec<Group> {
self.make_groups() self.make_groups()
} }

View File

@ -1,11 +1,9 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::entities::{ use crate::entities::{
BoardCardChangesetPB, CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupPB, CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, NumberGroupConfigurationPB,
NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB,
UrlGroupConfigurationPB,
}; };
use crate::services::group::{ use crate::services::group::{
CheckboxGroupController, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController, CheckboxGroupController, Group, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController,
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
@ -36,7 +34,7 @@ impl GroupService {
&mut self, &mut self,
field_revs: &[Arc<FieldRevision>], field_revs: &[Arc<FieldRevision>],
row_revs: Vec<Arc<RowRevision>>, row_revs: Vec<Arc<RowRevision>>,
) -> Option<Vec<GroupPB>> { ) -> Option<Vec<Group>> {
let field_rev = find_group_field(field_revs).unwrap(); let field_rev = find_group_field(field_revs).unwrap();
let field_type: FieldType = field_rev.field_type_rev.into(); let field_type: FieldType = field_rev.field_type_rev.into();
let configuration = self.delegate.get_group_configuration(field_rev.clone()).await; let configuration = self.delegate.get_group_configuration(field_rev.clone()).await;
@ -79,26 +77,6 @@ impl GroupService {
// let row_pb = make_row_from_row_rev(row_rev); // let row_pb = make_row_from_row_rev(row_rev);
todo!() todo!()
} }
#[allow(dead_code)]
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_row(&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.action_handler.is_none() {
return;
}
send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard)
.payload(changeset)
.send();
}
#[tracing::instrument(level = "trace", skip_all, err)] #[tracing::instrument(level = "trace", skip_all, err)]
async fn build_groups( async fn build_groups(
@ -107,7 +85,7 @@ impl GroupService {
field_rev: &Arc<FieldRevision>, field_rev: &Arc<FieldRevision>,
row_revs: Vec<Arc<RowRevision>>, row_revs: Vec<Arc<RowRevision>>,
configuration: GroupConfigurationRevision, configuration: GroupConfigurationRevision,
) -> FlowyResult<Vec<GroupPB>> { ) -> FlowyResult<Vec<Group>> {
match field_type { match field_type {
FieldType::RichText => { FieldType::RichText => {
// let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration); // let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
@ -139,11 +117,11 @@ impl GroupService {
if let Some(group_action_handler) = self.action_handler.as_ref() { if let Some(group_action_handler) = self.action_handler.as_ref() {
let mut write_guard = group_action_handler.write().await; let mut write_guard = group_action_handler.write().await;
let _ = write_guard.group_rows(&row_revs, field_rev)?; let _ = write_guard.group_rows(&row_revs, field_rev)?;
groups = write_guard.get_groups(); groups = write_guard.build_groups();
drop(write_guard); drop(write_guard);
} }
Ok(groups.into_iter().map(GroupPB::from).collect()) Ok(groups)
} }
} }

View File

@ -1,5 +1,5 @@
use crate::entities::{ use crate::entities::{
GridLayoutPB, GridSettingPB, Layout, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, GridLayout, GridLayoutPB, GridSettingPB, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB,
RepeatedGridSortPB, RepeatedGridSortPB,
}; };
use flowy_grid_data_model::revision::{FieldRevision, SettingRevision}; use flowy_grid_data_model::revision::{FieldRevision, SettingRevision};
@ -12,7 +12,7 @@ pub struct GridSettingChangesetBuilder {
} }
impl GridSettingChangesetBuilder { impl GridSettingChangesetBuilder {
pub fn new(grid_id: &str, layout_type: &Layout) -> Self { pub fn new(grid_id: &str, layout_type: &GridLayout) -> Self {
let params = GridSettingChangesetParams { let params = GridSettingChangesetParams {
grid_id: grid_id.to_string(), grid_id: grid_id.to_string(),
layout_type: layout_type.clone().into(), layout_type: layout_type.clone().into(),
@ -42,7 +42,7 @@ impl GridSettingChangesetBuilder {
} }
pub fn make_grid_setting(grid_setting_rev: &SettingRevision, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB { pub fn make_grid_setting(grid_setting_rev: &SettingRevision, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB {
let current_layout_type: Layout = grid_setting_rev.layout.clone().into(); let current_layout_type: GridLayout = grid_setting_rev.layout.clone().into();
let filters_by_field_id = grid_setting_rev let filters_by_field_id = grid_setting_rev
.get_all_filters(field_revs) .get_all_filters(field_revs)
.map(|filters_by_field_id| { .map(|filters_by_field_id| {

View File

@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
use crate::grid::block_test::util::GridRowTestBuilder; use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest; use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, RowPB}; use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, GridLayout, RowPB};
use flowy_grid::services::field::*; use flowy_grid::services::field::*;
use flowy_grid_data_model::revision::{ use flowy_grid_data_model::revision::{
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
@ -81,6 +81,7 @@ impl GridRowTest {
grid_id: self.editor.grid_id.clone(), grid_id: self.editor.grid_id.clone(),
start_row_id: None, start_row_id: None,
group_id: None, group_id: None,
layout: GridLayout::Table,
}; };
let row_order = self.editor.create_row(params).await.unwrap(); let row_order = self.editor.create_row(params).await.unwrap();
self.row_order_by_row_id self.row_order_by_row_id

View File

@ -3,7 +3,7 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_imports)] #![allow(unused_imports)]
use flowy_grid::entities::{CreateGridFilterPayloadPB, Layout, GridSettingPB}; use flowy_grid::entities::{CreateGridFilterPayloadPB, GridLayout, GridSettingPB};
use flowy_grid::services::setting::GridSettingChangesetBuilder; use flowy_grid::services::setting::GridSettingChangesetBuilder;
use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision}; use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams}; use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
@ -55,7 +55,7 @@ impl GridFilterTest {
} }
FilterScript::InsertGridTableFilter { payload } => { FilterScript::InsertGridTableFilter { payload } => {
let params: CreateGridFilterParams = payload.try_into().unwrap(); let params: CreateGridFilterParams = payload.try_into().unwrap();
let layout_type = Layout::Table; let layout_type = GridLayout::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.insert_filter(params) .insert_filter(params)
.build(); .build();
@ -66,7 +66,7 @@ impl GridFilterTest {
assert_eq!(count as usize, filters.len()); assert_eq!(count as usize, filters.len());
} }
FilterScript::DeleteGridTableFilter { filter_id, field_rev} => { FilterScript::DeleteGridTableFilter { filter_id, field_rev} => {
let layout_type = Layout::Table; let layout_type = GridLayout::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.delete_filter(DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.field_type_rev }) .delete_filter(DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.field_type_rev })
.build(); .build();

View File

@ -91,6 +91,9 @@ pub enum ErrorCode {
#[display(fmt = "Grid id is empty")] #[display(fmt = "Grid id is empty")]
GridIdIsEmpty = 410, GridIdIsEmpty = 410,
#[display(fmt = "Grid view id is empty")]
GridViewIdIsEmpty = 411,
#[display(fmt = "Grid block id is empty")] #[display(fmt = "Grid block id is empty")]
BlockIdIsEmpty = 420, BlockIdIsEmpty = 420,
#[display(fmt = "Row id is empty")] #[display(fmt = "Row id is empty")]

View File

@ -139,12 +139,8 @@ impl GridBlockRevisionPad {
self.block.rows.len() as i32 self.block.rows.len() as i32
} }
pub fn index_of_row(&self, row_id: &str) -> Option<i32> { pub fn index_of_row(&self, row_id: &str) -> Option<usize> {
self.block self.block.rows.iter().position(|row| row.id == row_id)
.rows
.iter()
.position(|row| row.id == row_id)
.map(|index| index as i32)
} }
pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockRevisionChangeset>> { pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockRevisionChangeset>> {