mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #867 from AppFlowy-IO/feat/move_card
feat: move card from one column to another
This commit is contained in:
commit
a0753cea2d
2
frontend/.vscode/launch.json
vendored
2
frontend/.vscode/launch.json
vendored
@ -29,7 +29,7 @@
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug"
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
|
@ -16,7 +16,7 @@ class BlankPluginBuilder extends PluginBuilder {
|
||||
String get menuName => "Blank";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.blank.type();
|
||||
PluginType get pluginType => PluginType.blank;
|
||||
}
|
||||
|
||||
class BlankPluginConfig implements PluginConfig {
|
||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
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/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
@ -21,13 +22,15 @@ part 'board_bloc.freezed.dart';
|
||||
class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
final BoardDataController _dataController;
|
||||
late final AFBoardDataController afBoardDataController;
|
||||
List<GroupController> groupControllers = [];
|
||||
final MoveRowFFIService _rowService;
|
||||
Map<String, GroupController> groupControllers = {};
|
||||
|
||||
GridFieldCache get fieldCache => _dataController.fieldCache;
|
||||
String get gridId => _dataController.gridId;
|
||||
|
||||
BoardBloc({required ViewPB view})
|
||||
: _dataController = BoardDataController(view: view),
|
||||
: _rowService = MoveRowFFIService(gridId: view.id),
|
||||
_dataController = BoardDataController(view: view),
|
||||
super(BoardState.initial(view.id)) {
|
||||
afBoardDataController = AFBoardDataController(
|
||||
onMoveColumn: (
|
||||
@ -38,13 +41,21 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
columnId,
|
||||
fromIndex,
|
||||
toIndex,
|
||||
) {},
|
||||
) {
|
||||
final fromRow = groupControllers[columnId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[columnId]?.rowAtIndex(toIndex);
|
||||
_moveRow(fromRow, toRow);
|
||||
},
|
||||
onMoveColumnItemToColumn: (
|
||||
fromColumnId,
|
||||
fromIndex,
|
||||
toColumnId,
|
||||
toIndex,
|
||||
) {},
|
||||
) {
|
||||
final fromRow = groupControllers[fromColumnId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[toColumnId]?.rowAtIndex(toIndex);
|
||||
_moveRow(fromRow, toRow);
|
||||
},
|
||||
);
|
||||
|
||||
on<BoardEvent>(
|
||||
@ -76,15 +87,31 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
didReceiveRows: (List<RowInfo> rowInfos) {
|
||||
emit(state.copyWith(rowInfos: rowInfos));
|
||||
},
|
||||
didReceiveError: (FlowyError error) {
|
||||
emit(state.copyWith(noneOrError: some(error)));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _moveRow(RowPB? fromRow, RowPB? toRow) {
|
||||
if (fromRow != null && toRow != null) {
|
||||
_rowService
|
||||
.moveRow(
|
||||
fromRowId: fromRow.id,
|
||||
toRowId: toRow.id,
|
||||
)
|
||||
.then((result) {
|
||||
result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _dataController.dispose();
|
||||
for (final controller in groupControllers) {
|
||||
for (final controller in groupControllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
return super.close();
|
||||
@ -94,11 +121,12 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
for (final group in groups) {
|
||||
final delegate = GroupControllerDelegateImpl(afBoardDataController);
|
||||
final controller = GroupController(
|
||||
gridId: state.gridId,
|
||||
group: group,
|
||||
delegate: delegate,
|
||||
);
|
||||
controller.startListening();
|
||||
groupControllers.add(controller);
|
||||
groupControllers[controller.group.groupId] = (controller);
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +190,7 @@ class BoardEvent with _$BoardEvent {
|
||||
const factory BoardEvent.initial() = InitialGrid;
|
||||
const factory BoardEvent.createRow(String groupId) = _CreateRow;
|
||||
const factory BoardEvent.endEditRow(String rowId) = _EndEditRow;
|
||||
const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError;
|
||||
const factory BoardEvent.didReceiveRows(List<RowInfo> rowInfos) =
|
||||
_DidReceiveRows;
|
||||
const factory BoardEvent.didReceiveGridUpdate(
|
||||
@ -177,6 +206,7 @@ class BoardState with _$BoardState {
|
||||
required Option<RowPB> editingRow,
|
||||
required List<RowInfo> rowInfos,
|
||||
required GridLoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _BoardState;
|
||||
|
||||
factory BoardState.initial(String gridId) => BoardState(
|
||||
@ -184,6 +214,7 @@ class BoardState with _$BoardState {
|
||||
grid: none(),
|
||||
gridId: gridId,
|
||||
editingRow: none(),
|
||||
noneOrError: none(),
|
||||
loadingState: const _Loading(),
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
|
||||
import 'group_listener.dart';
|
||||
|
||||
typedef OnGroupError = void Function(FlowyError);
|
||||
|
||||
abstract class GroupControllerDelegate {
|
||||
void removeRow(String groupId, String rowId);
|
||||
void insertRow(String groupId, RowPB row, int? index);
|
||||
@ -14,8 +17,19 @@ class GroupController {
|
||||
final GroupListener _listener;
|
||||
final GroupControllerDelegate delegate;
|
||||
|
||||
GroupController({required this.group, required this.delegate})
|
||||
: _listener = GroupListener(group);
|
||||
GroupController({
|
||||
required String gridId,
|
||||
required this.group,
|
||||
required this.delegate,
|
||||
}) : _listener = GroupListener(group);
|
||||
|
||||
RowPB? rowAtIndex(int index) {
|
||||
if (index < group.rows.length) {
|
||||
return group.rows[index];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void startListening() {
|
||||
_listener.start(onGroupChanged: (result) {
|
||||
|
@ -20,13 +20,13 @@ class BoardPluginBuilder implements PluginBuilder {
|
||||
String get menuName => "Board";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.board.type();
|
||||
PluginType get pluginType => PluginType.board;
|
||||
|
||||
@override
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.Database;
|
||||
|
||||
@override
|
||||
SubViewDataTypePB get subDataType => SubViewDataTypePB.Board;
|
||||
ViewLayoutTypePB? get subDataType => ViewLayoutTypePB.Board;
|
||||
}
|
||||
|
||||
class BoardPluginConfig implements PluginConfig {
|
||||
|
@ -42,10 +42,10 @@ class DocumentPluginBuilder extends PluginBuilder {
|
||||
String get menuName => LocaleKeys.document_menuName.tr();
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.editor.type();
|
||||
PluginType get pluginType => PluginType.editor;
|
||||
|
||||
@override
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.TextBlock;
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.Text;
|
||||
}
|
||||
|
||||
class DocumentPlugin implements Plugin {
|
||||
|
@ -111,7 +111,7 @@ class SelectOptionCellEditorBloc
|
||||
void _loadOptions() {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_selectOptionService.getOpitonContext().then((result) {
|
||||
_selectOptionService.getOptionContext().then((result) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class SelectOptionService {
|
||||
return GridEventUpdateSelectOption(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<SelectOptionCellDataPB, FlowyError>> getOpitonContext() {
|
||||
Future<Either<SelectOptionCellDataPB, FlowyError>> getOptionContext() {
|
||||
final payload = GridCellIdPB.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
|
@ -4,7 +4,6 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
|
||||
|
||||
class RowFFIService {
|
||||
final String gridId;
|
||||
@ -23,27 +22,6 @@ class RowFFIService {
|
||||
return GridEventCreateTableRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
required String rowId,
|
||||
required int fromIndex,
|
||||
required int toIndex,
|
||||
required GridLayout layout,
|
||||
String? upperRowId,
|
||||
}) {
|
||||
var payload = MoveRowPayloadPB.create()
|
||||
..viewId = gridId
|
||||
..rowId = rowId
|
||||
..layout = layout
|
||||
..fromIndex = fromIndex
|
||||
..toIndex = toIndex;
|
||||
|
||||
if (upperRowId != null) {
|
||||
payload.upperRowId = upperRowId;
|
||||
}
|
||||
|
||||
return GridEventMoveRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
@ -71,3 +49,23 @@ class RowFFIService {
|
||||
return GridEventDuplicateRow(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
class MoveRowFFIService {
|
||||
final String gridId;
|
||||
|
||||
MoveRowFFIService({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
required String fromRowId,
|
||||
required String toRowId,
|
||||
}) {
|
||||
var payload = MoveRowPayloadPB.create()
|
||||
..viewId = gridId
|
||||
..fromRowId = fromRowId
|
||||
..toRowId = toRowId;
|
||||
|
||||
return GridEventMoveRow(payload).send();
|
||||
}
|
||||
}
|
||||
|
@ -22,13 +22,13 @@ class GridPluginBuilder implements PluginBuilder {
|
||||
String get menuName => LocaleKeys.grid_menuName.tr();
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.grid.type();
|
||||
PluginType get pluginType => PluginType.grid;
|
||||
|
||||
@override
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.Database;
|
||||
|
||||
@override
|
||||
SubViewDataTypePB? get subDataType => SubViewDataTypePB.Grid;
|
||||
ViewLayoutTypePB? get subDataType => ViewLayoutTypePB.Grid;
|
||||
}
|
||||
|
||||
class GridPluginConfig implements PluginConfig {
|
||||
|
@ -65,6 +65,8 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_delayOperation = null;
|
||||
_cellBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -72,15 +74,10 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
|
||||
Future<void> focusChanged() async {
|
||||
if (mounted) {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 300), () {
|
||||
_delayOperation = Timer(const Duration(milliseconds: 30), () {
|
||||
if (_cellBloc.isClosed == false &&
|
||||
_controller.text != contentFromState(_cellBloc.state)) {
|
||||
_cellBloc.add(NumberCellEvent.updateCell(_controller.text));
|
||||
|
||||
if (!mounted) {
|
||||
_delayOperation = null;
|
||||
_cellBloc.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = null;
|
||||
_cellBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
@ -85,7 +85,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
|
||||
Future<void> focusChanged() async {
|
||||
if (mounted) {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 300), () {
|
||||
_delayOperation = Timer(const Duration(milliseconds: 30), () {
|
||||
if (_cellBloc.isClosed == false &&
|
||||
_controller.text != _cellBloc.state.content) {
|
||||
_cellBloc.add(TextCellEvent.updateText(_controller.text));
|
||||
|
@ -23,7 +23,7 @@ class MenuTrash extends StatelessWidget {
|
||||
onTap: () {
|
||||
getIt<MenuSharedState>().latestOpenView = null;
|
||||
getIt<HomeStackManager>()
|
||||
.setPlugin(makePlugin(pluginType: DefaultPlugin.trash.type()));
|
||||
.setPlugin(makePlugin(pluginType: PluginType.trash));
|
||||
},
|
||||
child: _render(context),
|
||||
),
|
||||
|
@ -34,7 +34,7 @@ class TrashPluginBuilder extends PluginBuilder {
|
||||
String get menuName => "TrashPB";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.trash.type();
|
||||
PluginType get pluginType => PluginType.trash;
|
||||
}
|
||||
|
||||
class TrashPluginConfig implements PluginConfig {
|
||||
|
@ -9,7 +9,7 @@ import 'package:flutter/widgets.dart';
|
||||
|
||||
export "./src/sandbox.dart";
|
||||
|
||||
enum DefaultPlugin {
|
||||
enum PluginType {
|
||||
editor,
|
||||
blank,
|
||||
trash,
|
||||
@ -17,24 +17,24 @@ enum DefaultPlugin {
|
||||
board,
|
||||
}
|
||||
|
||||
extension FlowyDefaultPluginExt on DefaultPlugin {
|
||||
int type() {
|
||||
switch (this) {
|
||||
case DefaultPlugin.editor:
|
||||
return 0;
|
||||
case DefaultPlugin.blank:
|
||||
return 1;
|
||||
case DefaultPlugin.trash:
|
||||
return 2;
|
||||
case DefaultPlugin.grid:
|
||||
return 3;
|
||||
case DefaultPlugin.board:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
// extension FlowyDefaultPluginExt on DefaultPlugin {
|
||||
// int type() {
|
||||
// switch (this) {
|
||||
// case DefaultPlugin.editor:
|
||||
// return 0;
|
||||
// case DefaultPlugin.blank:
|
||||
// return 1;
|
||||
// case DefaultPlugin.trash:
|
||||
// return 2;
|
||||
// case DefaultPlugin.grid:
|
||||
// return 3;
|
||||
// case DefaultPlugin.board:
|
||||
// return 4;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
typedef PluginType = int;
|
||||
// typedef PluginType = int;
|
||||
typedef PluginId = String;
|
||||
|
||||
abstract class Plugin {
|
||||
@ -54,9 +54,9 @@ abstract class PluginBuilder {
|
||||
|
||||
PluginType get pluginType;
|
||||
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.TextBlock;
|
||||
ViewDataTypePB get dataType => ViewDataTypePB.Text;
|
||||
|
||||
SubViewDataTypePB? get subDataType => null;
|
||||
ViewLayoutTypePB? get subDataType => null;
|
||||
}
|
||||
|
||||
abstract class PluginConfig {
|
||||
|
@ -6,8 +6,10 @@ import '../plugin.dart';
|
||||
import 'runner.dart';
|
||||
|
||||
class PluginSandbox {
|
||||
final LinkedHashMap<PluginType, PluginBuilder> _pluginBuilders = LinkedHashMap();
|
||||
final Map<PluginType, PluginConfig> _pluginConfigs = <PluginType, PluginConfig>{};
|
||||
final LinkedHashMap<PluginType, PluginBuilder> _pluginBuilders =
|
||||
LinkedHashMap();
|
||||
final Map<PluginType, PluginConfig> _pluginConfigs =
|
||||
<PluginType, PluginConfig>{};
|
||||
late PluginRunner pluginRunner;
|
||||
|
||||
PluginSandbox() {
|
||||
@ -15,9 +17,11 @@ class PluginSandbox {
|
||||
}
|
||||
|
||||
int indexOf(PluginType pluginType) {
|
||||
final index = _pluginBuilders.keys.toList().indexWhere((ty) => ty == pluginType);
|
||||
final index =
|
||||
_pluginBuilders.keys.toList().indexWhere((ty) => ty == pluginType);
|
||||
if (index == -1) {
|
||||
throw PlatformException(code: '-1', message: "Can't find the flowy plugin type: $pluginType");
|
||||
throw PlatformException(
|
||||
code: '-1', message: "Can't find the flowy plugin type: $pluginType");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
@ -27,9 +31,11 @@ class PluginSandbox {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
void registerPlugin(PluginType pluginType, PluginBuilder builder, {PluginConfig? config}) {
|
||||
void registerPlugin(PluginType pluginType, PluginBuilder builder,
|
||||
{PluginConfig? config}) {
|
||||
if (_pluginBuilders.containsKey(pluginType)) {
|
||||
throw PlatformException(code: '-1', message: "$pluginType was registered before");
|
||||
throw PlatformException(
|
||||
code: '-1', message: "$pluginType was registered before");
|
||||
}
|
||||
_pluginBuilders[pluginType] = builder;
|
||||
|
||||
@ -38,7 +44,7 @@ class PluginSandbox {
|
||||
}
|
||||
}
|
||||
|
||||
List<int> get supportPluginTypes => _pluginBuilders.keys.toList();
|
||||
List<PluginType> get supportPluginTypes => _pluginBuilders.keys.toList();
|
||||
|
||||
List<PluginBuilder> get builders => _pluginBuilders.values.toList();
|
||||
|
||||
|
@ -86,7 +86,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
||||
desc: value.desc,
|
||||
dataType: value.dataType,
|
||||
pluginType: value.pluginType,
|
||||
subDataType: value.subDataType,
|
||||
layout: value.layout,
|
||||
);
|
||||
viewOrFailed.fold(
|
||||
(view) => emit(state.copyWith(
|
||||
@ -140,7 +140,7 @@ class AppEvent with _$AppEvent {
|
||||
String name,
|
||||
String desc,
|
||||
ViewDataTypePB dataType,
|
||||
SubViewDataTypePB? subDataType,
|
||||
ViewLayoutTypePB layout,
|
||||
PluginType pluginType,
|
||||
) = CreateView;
|
||||
const factory AppEvent.delete() = Delete;
|
||||
|
@ -26,18 +26,14 @@ class AppService {
|
||||
required String desc,
|
||||
required ViewDataTypePB dataType,
|
||||
required PluginType pluginType,
|
||||
SubViewDataTypePB? subDataType,
|
||||
required ViewLayoutTypePB layout,
|
||||
}) {
|
||||
var payload = CreateViewPayloadPB.create()
|
||||
..belongToId = appId
|
||||
..name = name
|
||||
..desc = desc
|
||||
..dataType = dataType
|
||||
..pluginType = pluginType;
|
||||
|
||||
if (subDataType != null) {
|
||||
payload.subDataType = subDataType;
|
||||
}
|
||||
..layout = layout;
|
||||
|
||||
return FolderEventCreateView(payload).send();
|
||||
}
|
||||
|
@ -113,6 +113,6 @@ class MenuState with _$MenuState {
|
||||
factory MenuState.initial() => MenuState(
|
||||
apps: [],
|
||||
successOrFailure: left(unit),
|
||||
plugin: makePlugin(pluginType: DefaultPlugin.blank.type()),
|
||||
plugin: makePlugin(pluginType: PluginType.blank),
|
||||
);
|
||||
}
|
||||
|
@ -40,6 +40,19 @@ extension ViewExtension on ViewPB {
|
||||
return widget;
|
||||
}
|
||||
|
||||
PluginType get pluginType {
|
||||
switch (layout) {
|
||||
case ViewLayoutTypePB.Board:
|
||||
return PluginType.board;
|
||||
case ViewLayoutTypePB.Document:
|
||||
return PluginType.editor;
|
||||
case ViewLayoutTypePB.Grid:
|
||||
return PluginType.grid;
|
||||
}
|
||||
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
Plugin plugin() {
|
||||
final plugin = makePlugin(pluginType: pluginType, data: this);
|
||||
return plugin;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/edit_panel/panel_animation.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/float_bubble/question_bubble.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
|
@ -107,7 +107,7 @@ class HomeStackNotifier extends ChangeNotifier {
|
||||
Widget get titleWidget => _plugin.display.leftBarItem;
|
||||
|
||||
HomeStackNotifier({Plugin? plugin})
|
||||
: _plugin = plugin ?? makePlugin(pluginType: DefaultPlugin.blank.type());
|
||||
: _plugin = plugin ?? makePlugin(pluginType: PluginType.blank);
|
||||
|
||||
set plugin(Plugin newPlugin) {
|
||||
if (newPlugin.id == _plugin.id) {
|
||||
|
@ -114,7 +114,7 @@ class MenuAppHeader extends StatelessWidget {
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
"",
|
||||
pluginBuilder.dataType,
|
||||
pluginBuilder.subDataType,
|
||||
pluginBuilder.subDataType!,
|
||||
pluginBuilder.pluginType,
|
||||
));
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ const DART_LOG = "Dart_LOG";
|
||||
class Log {
|
||||
// static const enableLog = bool.hasEnvironment(DART_LOG);
|
||||
// static final shared = Log();
|
||||
static const enableLog = true;
|
||||
static const enableLog = false;
|
||||
|
||||
static void info(String? message) {
|
||||
if (enableLog) {
|
||||
|
@ -84,6 +84,9 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
|
||||
Log.debug(
|
||||
'[$AFBoardColumnDataController] $columnData insert $item at $index');
|
||||
|
||||
if (_containsItem(item)) {
|
||||
return false;
|
||||
} else {
|
||||
if (columnData._items.length > index) {
|
||||
columnData._items.insert(index, item);
|
||||
} else {
|
||||
@ -93,12 +96,17 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
|
||||
if (notify) notifyListeners();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool add(AFColumnItem item, {bool notify = true}) {
|
||||
if (_containsItem(item)) {
|
||||
return false;
|
||||
} else {
|
||||
columnData._items.add(item);
|
||||
if (notify) notifyListeners();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace the item at index with the [newItem].
|
||||
void replace(int index, AFColumnItem newItem) {
|
||||
@ -114,6 +122,11 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin {
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool _containsItem(AFColumnItem item) {
|
||||
return columnData._items.indexWhere((element) => element.id == item.id) !=
|
||||
-1;
|
||||
}
|
||||
}
|
||||
|
||||
/// [AFBoardColumnData] represents the data of each Column of the Board.
|
||||
|
@ -35,7 +35,7 @@ packages:
|
||||
path: "packages/appflowy_editor"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
version: "0.0.2"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
impl_def_and_def_mut,
|
||||
};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_folder_data_model::revision::{gen_view_id, ViewDataTypeRevision, ViewRevision};
|
||||
use flowy_folder_data_model::revision::{gen_view_id, ViewDataTypeRevision, ViewLayoutTypeRevision, ViewRevision};
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
@ -16,7 +16,7 @@ pub struct ViewPB {
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub belong_to_id: String,
|
||||
pub app_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub name: String,
|
||||
@ -31,35 +31,29 @@ pub struct ViewPB {
|
||||
pub create_time: i64,
|
||||
|
||||
#[pb(index = 7)]
|
||||
pub plugin_type: i32,
|
||||
pub layout: ViewLayoutTypePB,
|
||||
}
|
||||
|
||||
impl std::convert::From<ViewRevision> for ViewPB {
|
||||
fn from(rev: ViewRevision) -> Self {
|
||||
ViewPB {
|
||||
id: rev.id,
|
||||
belong_to_id: rev.belong_to_id,
|
||||
app_id: rev.app_id,
|
||||
name: rev.name,
|
||||
data_type: rev.data_type.into(),
|
||||
modified_time: rev.modified_time,
|
||||
create_time: rev.create_time,
|
||||
plugin_type: rev.plugin_type,
|
||||
layout: rev.layout.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)]
|
||||
pub enum ViewDataTypePB {
|
||||
TextBlock = 0,
|
||||
Text = 0,
|
||||
Database = 1,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)]
|
||||
pub enum SubViewDataTypePB {
|
||||
Grid = 0,
|
||||
Board = 1,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewDataTypePB {
|
||||
fn default() -> Self {
|
||||
ViewDataTypeRevision::default().into()
|
||||
@ -69,7 +63,7 @@ impl std::default::Default for ViewDataTypePB {
|
||||
impl std::convert::From<ViewDataTypeRevision> for ViewDataTypePB {
|
||||
fn from(rev: ViewDataTypeRevision) -> Self {
|
||||
match rev {
|
||||
ViewDataTypeRevision::TextBlock => ViewDataTypePB::TextBlock,
|
||||
ViewDataTypeRevision::Text => ViewDataTypePB::Text,
|
||||
ViewDataTypeRevision::Database => ViewDataTypePB::Database,
|
||||
}
|
||||
}
|
||||
@ -78,12 +72,45 @@ impl std::convert::From<ViewDataTypeRevision> for ViewDataTypePB {
|
||||
impl std::convert::From<ViewDataTypePB> for ViewDataTypeRevision {
|
||||
fn from(ty: ViewDataTypePB) -> Self {
|
||||
match ty {
|
||||
ViewDataTypePB::TextBlock => ViewDataTypeRevision::TextBlock,
|
||||
ViewDataTypePB::Text => ViewDataTypeRevision::Text,
|
||||
ViewDataTypePB::Database => ViewDataTypeRevision::Database,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)]
|
||||
pub enum ViewLayoutTypePB {
|
||||
Document = 0,
|
||||
Grid = 3,
|
||||
Board = 4,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewLayoutTypePB {
|
||||
fn default() -> Self {
|
||||
ViewLayoutTypePB::Grid
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<ViewLayoutTypeRevision> for ViewLayoutTypePB {
|
||||
fn from(rev: ViewLayoutTypeRevision) -> Self {
|
||||
match rev {
|
||||
ViewLayoutTypeRevision::Grid => ViewLayoutTypePB::Grid,
|
||||
ViewLayoutTypeRevision::Board => ViewLayoutTypePB::Board,
|
||||
ViewLayoutTypeRevision::Document => ViewLayoutTypePB::Document,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<ViewLayoutTypePB> for ViewLayoutTypeRevision {
|
||||
fn from(rev: ViewLayoutTypePB) -> Self {
|
||||
match rev {
|
||||
ViewLayoutTypePB::Grid => ViewLayoutTypeRevision::Grid,
|
||||
ViewLayoutTypePB::Board => ViewLayoutTypeRevision::Board,
|
||||
ViewLayoutTypePB::Document => ViewLayoutTypeRevision::Document,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
|
||||
pub struct RepeatedViewPB {
|
||||
#[pb(index = 1)]
|
||||
@ -121,14 +148,11 @@ pub struct CreateViewPayloadPB {
|
||||
#[pb(index = 5)]
|
||||
pub data_type: ViewDataTypePB,
|
||||
|
||||
#[pb(index = 6, one_of)]
|
||||
pub sub_data_type: Option<SubViewDataTypePB>,
|
||||
#[pb(index = 6)]
|
||||
pub layout: ViewLayoutTypePB,
|
||||
|
||||
#[pb(index = 7)]
|
||||
pub plugin_type: i32,
|
||||
|
||||
#[pb(index = 8)]
|
||||
pub data: Vec<u8>,
|
||||
pub view_content_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -138,10 +162,9 @@ pub struct CreateViewParams {
|
||||
pub desc: String,
|
||||
pub thumbnail: String,
|
||||
pub data_type: ViewDataTypePB,
|
||||
pub sub_data_type: Option<SubViewDataTypePB>,
|
||||
pub layout: ViewLayoutTypePB,
|
||||
pub view_id: String,
|
||||
pub data: Vec<u8>,
|
||||
pub plugin_type: i32,
|
||||
pub view_content_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryInto<CreateViewParams> for CreateViewPayloadPB {
|
||||
@ -161,11 +184,10 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
|
||||
name,
|
||||
desc: self.desc,
|
||||
data_type: self.data_type,
|
||||
sub_data_type: self.sub_data_type,
|
||||
layout: self.layout,
|
||||
thumbnail,
|
||||
view_id,
|
||||
data: self.data,
|
||||
plugin_type: self.plugin_type,
|
||||
view_content_data: self.view_content_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::view::ViewDataTypePB;
|
||||
use crate::entities::SubViewDataTypePB;
|
||||
use crate::entities::ViewLayoutTypePB;
|
||||
use crate::services::folder_editor::FolderRevisionCompactor;
|
||||
use crate::{
|
||||
dart_notification::{send_dart_notification, FolderNotification},
|
||||
@ -222,7 +222,7 @@ impl DefaultFolderBuilder {
|
||||
};
|
||||
let _ = view_controller.set_latest_view(&view.id);
|
||||
let _ = view_controller
|
||||
.create_view(&view.id, ViewDataTypePB::TextBlock, Bytes::from(view_data))
|
||||
.create_view(&view.id, ViewDataTypePB::Text, Bytes::from(view_data))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
@ -261,7 +261,7 @@ pub trait ViewDataProcessor {
|
||||
&self,
|
||||
user_id: &str,
|
||||
view_id: &str,
|
||||
sub_data_type: Option<SubViewDataTypePB>,
|
||||
layout: ViewLayoutTypePB,
|
||||
) -> FutureResult<Bytes, FlowyError>;
|
||||
|
||||
fn create_view_from_delta_data(
|
||||
|
@ -13,7 +13,7 @@ use flowy_database::{
|
||||
SqliteConnection,
|
||||
};
|
||||
|
||||
use flowy_folder_data_model::revision::{ViewDataTypeRevision, ViewRevision};
|
||||
use flowy_folder_data_model::revision::{ViewDataTypeRevision, ViewLayoutTypeRevision, ViewRevision};
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
pub struct ViewTableSql();
|
||||
@ -87,13 +87,13 @@ pub(crate) struct ViewTable {
|
||||
impl ViewTable {
|
||||
pub fn new(view_rev: ViewRevision) -> Self {
|
||||
let data_type = match view_rev.data_type {
|
||||
ViewDataTypeRevision::TextBlock => SqlViewDataType::Block,
|
||||
ViewDataTypeRevision::Text => SqlViewDataType::Block,
|
||||
ViewDataTypeRevision::Database => SqlViewDataType::Grid,
|
||||
};
|
||||
|
||||
ViewTable {
|
||||
id: view_rev.id,
|
||||
belong_to_id: view_rev.belong_to_id,
|
||||
belong_to_id: view_rev.app_id,
|
||||
name: view_rev.name,
|
||||
desc: view_rev.desc,
|
||||
modified_time: view_rev.modified_time,
|
||||
@ -110,13 +110,13 @@ impl ViewTable {
|
||||
impl std::convert::From<ViewTable> for ViewRevision {
|
||||
fn from(table: ViewTable) -> Self {
|
||||
let data_type = match table.view_type {
|
||||
SqlViewDataType::Block => ViewDataTypeRevision::TextBlock,
|
||||
SqlViewDataType::Block => ViewDataTypeRevision::Text,
|
||||
SqlViewDataType::Grid => ViewDataTypeRevision::Database,
|
||||
};
|
||||
|
||||
ViewRevision {
|
||||
id: table.id,
|
||||
belong_to_id: table.belong_to_id,
|
||||
app_id: table.belong_to_id,
|
||||
name: table.name,
|
||||
desc: table.desc,
|
||||
data_type,
|
||||
@ -127,8 +127,8 @@ impl std::convert::From<ViewTable> for ViewRevision {
|
||||
ext_data: "".to_string(),
|
||||
thumbnail: table.thumbnail,
|
||||
// Store the view in ViewTable was deprecated since v0.0.2.
|
||||
// No need to worry about plugin_type.
|
||||
plugin_type: 0,
|
||||
// No need to worry about layout.
|
||||
layout: ViewLayoutTypeRevision::Document,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,14 +60,14 @@ impl ViewController {
|
||||
) -> Result<ViewRevision, FlowyError> {
|
||||
let processor = self.get_data_processor(params.data_type.clone())?;
|
||||
let user_id = self.user.user_id()?;
|
||||
if params.data.is_empty() {
|
||||
if params.view_content_data.is_empty() {
|
||||
let view_data = processor
|
||||
.create_default_view(&user_id, ¶ms.view_id, params.sub_data_type.clone())
|
||||
.create_default_view(&user_id, ¶ms.view_id, params.layout.clone())
|
||||
.await?;
|
||||
params.data = view_data.to_vec();
|
||||
params.view_content_data = view_data.to_vec();
|
||||
} else {
|
||||
let delta_data = processor
|
||||
.create_view_from_delta_data(&user_id, ¶ms.view_id, params.data.clone())
|
||||
.create_view_from_delta_data(&user_id, ¶ms.view_id, params.view_content_data.clone())
|
||||
.await?;
|
||||
let _ = self
|
||||
.create_view(¶ms.view_id, params.data_type.clone(), delta_data)
|
||||
@ -99,7 +99,7 @@ impl ViewController {
|
||||
let trash_controller = self.trash_controller.clone();
|
||||
self.persistence
|
||||
.begin_transaction(|transaction| {
|
||||
let belong_to_id = view_rev.belong_to_id.clone();
|
||||
let belong_to_id = view_rev.app_id.clone();
|
||||
let _ = transaction.create_view(view_rev)?;
|
||||
let _ = notify_views_changed(&belong_to_id, trash_controller, &transaction)?;
|
||||
Ok(())
|
||||
@ -139,7 +139,7 @@ impl ViewController {
|
||||
|
||||
let view_info = ViewInfoPB {
|
||||
id: view_rev.id,
|
||||
belong_to_id: view_rev.belong_to_id,
|
||||
belong_to_id: view_rev.app_id,
|
||||
name: view_rev.name,
|
||||
desc: view_rev.desc,
|
||||
data_type: view_rev.data_type.into(),
|
||||
@ -197,7 +197,7 @@ impl ViewController {
|
||||
.begin_transaction(|transaction| {
|
||||
let _ = transaction.move_view(view_id, from, to)?;
|
||||
let view = transaction.read_view(view_id)?;
|
||||
let _ = notify_views_changed(&view.belong_to_id, self.trash_controller.clone(), &transaction)?;
|
||||
let _ = notify_views_changed(&view.app_id, self.trash_controller.clone(), &transaction)?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
@ -214,15 +214,14 @@ impl ViewController {
|
||||
let processor = self.get_data_processor(view_rev.data_type.clone())?;
|
||||
let delta_bytes = processor.get_delta_data(view_id).await?;
|
||||
let duplicate_params = CreateViewParams {
|
||||
belong_to_id: view_rev.belong_to_id.clone(),
|
||||
belong_to_id: view_rev.app_id.clone(),
|
||||
name: format!("{} (copy)", &view_rev.name),
|
||||
desc: view_rev.desc,
|
||||
thumbnail: view_rev.thumbnail,
|
||||
data_type: view_rev.data_type.into(),
|
||||
sub_data_type: None,
|
||||
data: delta_bytes.to_vec(),
|
||||
layout: view_rev.layout.into(),
|
||||
view_content_data: delta_bytes.to_vec(),
|
||||
view_id: gen_view_id(),
|
||||
plugin_type: view_rev.plugin_type,
|
||||
};
|
||||
|
||||
let _ = self.create_view_from_params(duplicate_params).await?;
|
||||
@ -252,7 +251,7 @@ impl ViewController {
|
||||
send_dart_notification(&view_id, FolderNotification::ViewUpdated)
|
||||
.payload(view)
|
||||
.send();
|
||||
let _ = notify_views_changed(&view_rev.belong_to_id, self.trash_controller.clone(), &transaction)?;
|
||||
let _ = notify_views_changed(&view_rev.app_id, self.trash_controller.clone(), &transaction)?;
|
||||
Ok(view_rev)
|
||||
})
|
||||
.await?;
|
||||
@ -395,7 +394,7 @@ async fn handle_trash_event(
|
||||
.begin_transaction(|transaction| {
|
||||
let view_revs = read_local_views_with_transaction(identifiers, &transaction)?;
|
||||
for view_rev in view_revs {
|
||||
let _ = notify_views_changed(&view_rev.belong_to_id, trash_can.clone(), &transaction)?;
|
||||
let _ = notify_views_changed(&view_rev.app_id, trash_can.clone(), &transaction)?;
|
||||
notify_dart(view_rev.into(), FolderNotification::ViewDeleted);
|
||||
}
|
||||
Ok(())
|
||||
@ -408,7 +407,7 @@ async fn handle_trash_event(
|
||||
.begin_transaction(|transaction| {
|
||||
let view_revs = read_local_views_with_transaction(identifiers, &transaction)?;
|
||||
for view_rev in view_revs {
|
||||
let _ = notify_views_changed(&view_rev.belong_to_id, trash_can.clone(), &transaction)?;
|
||||
let _ = notify_views_changed(&view_rev.app_id, trash_can.clone(), &transaction)?;
|
||||
notify_dart(view_rev.into(), FolderNotification::ViewRestored);
|
||||
}
|
||||
Ok(())
|
||||
@ -425,7 +424,7 @@ async fn handle_trash_event(
|
||||
for identifier in identifiers.items {
|
||||
let view = transaction.read_view(&identifier.id)?;
|
||||
let _ = transaction.delete_view(&view.id)?;
|
||||
notify_ids.insert(view.belong_to_id.clone());
|
||||
notify_ids.insert(view.app_id.clone());
|
||||
views.push(view);
|
||||
}
|
||||
for notify_id in notify_ids {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
|
||||
use flowy_folder::entities::view::ViewDataTypePB;
|
||||
use flowy_folder::entities::workspace::CreateWorkspacePayloadPB;
|
||||
|
||||
use flowy_revision::disk::RevisionState;
|
||||
use flowy_test::{event_builder::*, FlowySDKTest};
|
||||
|
||||
@ -134,7 +133,7 @@ async fn app_create_with_view() {
|
||||
CreateView {
|
||||
name: "View A".to_owned(),
|
||||
desc: "View A description".to_owned(),
|
||||
data_type: ViewDataTypePB::TextBlock,
|
||||
data_type: ViewDataTypePB::Text,
|
||||
},
|
||||
CreateView {
|
||||
name: "Grid".to_owned(),
|
||||
@ -198,7 +197,7 @@ async fn view_delete_all() {
|
||||
CreateView {
|
||||
name: "View A".to_owned(),
|
||||
desc: "View A description".to_owned(),
|
||||
data_type: ViewDataTypePB::TextBlock,
|
||||
data_type: ViewDataTypePB::Text,
|
||||
},
|
||||
CreateView {
|
||||
name: "Grid".to_owned(),
|
||||
@ -231,7 +230,7 @@ async fn view_delete_all_permanent() {
|
||||
CreateView {
|
||||
name: "View A".to_owned(),
|
||||
desc: "View A description".to_owned(),
|
||||
data_type: ViewDataTypePB::TextBlock,
|
||||
data_type: ViewDataTypePB::Text,
|
||||
},
|
||||
ReadApp(app.id.clone()),
|
||||
])
|
||||
@ -330,7 +329,7 @@ async fn folder_sync_revision_with_new_view() {
|
||||
CreateView {
|
||||
name: view_name.clone(),
|
||||
desc: view_desc.clone(),
|
||||
data_type: ViewDataTypePB::TextBlock,
|
||||
data_type: ViewDataTypePB::Text,
|
||||
},
|
||||
AssertCurrentRevId(3),
|
||||
AssertNextSyncRevId(Some(3)),
|
||||
|
@ -5,7 +5,7 @@ use flowy_folder::entities::{
|
||||
trash::{RepeatedTrashPB, TrashIdPB, TrashType},
|
||||
view::{CreateViewPayloadPB, UpdateViewPayloadPB},
|
||||
workspace::{CreateWorkspacePayloadPB, RepeatedWorkspacePB},
|
||||
SubViewDataTypePB,
|
||||
ViewLayoutTypePB,
|
||||
};
|
||||
use flowy_folder::entities::{
|
||||
app::{AppPB, RepeatedAppPB},
|
||||
@ -99,7 +99,8 @@ impl FolderTest {
|
||||
&app.id,
|
||||
"Folder View",
|
||||
"Folder test view",
|
||||
ViewDataTypePB::TextBlock,
|
||||
ViewDataTypePB::Text,
|
||||
ViewLayoutTypePB::Document,
|
||||
)
|
||||
.await;
|
||||
app.belongings = RepeatedViewPB {
|
||||
@ -180,7 +181,11 @@ impl FolderTest {
|
||||
}
|
||||
|
||||
FolderScript::CreateView { name, desc, data_type } => {
|
||||
let view = create_view(sdk, &self.app.id, &name, &desc, data_type).await;
|
||||
let layout = match data_type {
|
||||
ViewDataTypePB::Text => ViewLayoutTypePB::Document,
|
||||
ViewDataTypePB::Database => ViewLayoutTypePB::Grid,
|
||||
};
|
||||
let view = create_view(sdk, &self.app.id, &name, &desc, data_type, layout).await;
|
||||
self.view = view;
|
||||
}
|
||||
FolderScript::AssertView(view) => {
|
||||
@ -353,21 +358,16 @@ pub async fn create_view(
|
||||
name: &str,
|
||||
desc: &str,
|
||||
data_type: ViewDataTypePB,
|
||||
layout: ViewLayoutTypePB,
|
||||
) -> ViewPB {
|
||||
let sub_data_type = match data_type {
|
||||
ViewDataTypePB::TextBlock => None,
|
||||
ViewDataTypePB::Database => Some(SubViewDataTypePB::Grid),
|
||||
};
|
||||
|
||||
let request = CreateViewPayloadPB {
|
||||
belong_to_id: app_id.to_string(),
|
||||
name: name.to_string(),
|
||||
desc: desc.to_string(),
|
||||
thumbnail: None,
|
||||
data_type,
|
||||
sub_data_type,
|
||||
plugin_type: 0,
|
||||
data: vec![],
|
||||
layout,
|
||||
view_content_data: vec![],
|
||||
};
|
||||
let view = FolderEventBuilder::new(sdk.clone())
|
||||
.event(CreateView)
|
||||
|
@ -62,6 +62,16 @@ impl std::convert::From<&RowRevision> for RowPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&mut RowRevision> for RowPB {
|
||||
fn from(rev: &mut RowRevision) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&Arc<RowRevision>> for RowPB {
|
||||
fn from(rev: &Arc<RowRevision>) -> Self {
|
||||
Self {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::{CellRevision, RowMetaChangeset};
|
||||
use flowy_grid_data_model::revision::{CellRevision, RowChangeset};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
@ -135,7 +135,7 @@ pub struct CellChangesetPB {
|
||||
pub content: Option<String>,
|
||||
}
|
||||
|
||||
impl std::convert::From<CellChangesetPB> for RowMetaChangeset {
|
||||
impl std::convert::From<CellChangesetPB> for RowChangeset {
|
||||
fn from(changeset: CellChangesetPB) -> Self {
|
||||
let mut cell_by_field_id = HashMap::with_capacity(1);
|
||||
let field_id = changeset.field_id;
|
||||
@ -144,7 +144,7 @@ impl std::convert::From<CellChangesetPB> for RowMetaChangeset {
|
||||
};
|
||||
cell_by_field_id.insert(field_id, cell_rev);
|
||||
|
||||
RowMetaChangeset {
|
||||
RowChangeset {
|
||||
row_id: changeset.row_id,
|
||||
height: None,
|
||||
visibility: None,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::entities::{BlockPB, FieldIdPB, GridLayout};
|
||||
use crate::entities::{BlockPB, FieldIdPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
@ -96,28 +96,16 @@ pub struct MoveRowPayloadPB {
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub row_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub from_index: i32,
|
||||
pub from_row_id: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub to_index: i32,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub layout: GridLayout,
|
||||
|
||||
#[pb(index = 6, one_of)]
|
||||
pub upper_row_id: Option<String>,
|
||||
pub to_row_id: String,
|
||||
}
|
||||
|
||||
pub struct MoveRowParams {
|
||||
pub view_id: String,
|
||||
pub row_id: String,
|
||||
pub from_index: i32,
|
||||
pub to_index: i32,
|
||||
pub layout: GridLayout,
|
||||
pub upper_row_id: Option<String>,
|
||||
pub from_row_id: String,
|
||||
pub to_row_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<MoveRowParams> for MoveRowPayloadPB {
|
||||
@ -125,18 +113,13 @@ impl TryInto<MoveRowParams> for MoveRowPayloadPB {
|
||||
|
||||
fn try_into(self) -> Result<MoveRowParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::GridViewIdIsEmpty)?;
|
||||
let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
let upper_row_id = match self.upper_row_id {
|
||||
None => None,
|
||||
Some(upper_row_id) => Some(NotEmptyStr::parse(upper_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0),
|
||||
};
|
||||
let from_row_id = NotEmptyStr::parse(self.from_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
let to_row_id = NotEmptyStr::parse(self.to_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
|
||||
Ok(MoveRowParams {
|
||||
view_id: view_id.0,
|
||||
row_id: row_id.0,
|
||||
from_index: self.from_index,
|
||||
to_index: self.to_index,
|
||||
layout: self.layout,
|
||||
upper_row_id,
|
||||
from_row_id: from_row_id.0,
|
||||
to_row_id: to_row_id.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,20 @@ impl std::convert::From<&GroupConfigurationRevision> for GridGroupConfigurationP
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct RepeatedGridGroupPB {
|
||||
#[pb(index = 1)]
|
||||
pub(crate) items: Vec<GroupPB>,
|
||||
pub items: Vec<GroupPB>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for RepeatedGridGroupPB {
|
||||
type Target = Vec<GroupPB>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for RepeatedGridGroupPB {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.items
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::entities::{InsertedRowPB, RowPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct GroupRowsChangesetPB {
|
||||
@ -16,6 +17,23 @@ pub struct GroupRowsChangesetPB {
|
||||
pub updated_rows: Vec<RowPB>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GroupRowsChangesetPB {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for inserted_row in &self.inserted_rows {
|
||||
let _ = f.write_fmt(format_args!(
|
||||
"Insert: {} row at {:?}",
|
||||
inserted_row.row.id, inserted_row.index
|
||||
))?;
|
||||
}
|
||||
|
||||
for deleted_row in &self.deleted_rows {
|
||||
let _ = f.write_fmt(format_args!("Delete: {} row", deleted_row))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupRowsChangesetPB {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inserted_rows.is_empty() && self.deleted_rows.is_empty() && self.updated_rows.is_empty()
|
||||
|
@ -225,7 +225,7 @@ async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType)
|
||||
Ok(type_option_data)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
// #[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn get_row_handler(
|
||||
data: Data<RowIdPB>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::entities::RowPB;
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision};
|
||||
use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowChangeset, RowRevision};
|
||||
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
|
||||
use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad};
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
@ -88,7 +88,7 @@ impl GridBlockRevisionEditor {
|
||||
Ok(row_count)
|
||||
}
|
||||
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
|
||||
pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
|
||||
let _ = self.modify(|block_pad| Ok(block_pad.update_row(changeset)?)).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -127,7 +127,7 @@ impl GridBlockRevisionEditor {
|
||||
Ok(cell_revs)
|
||||
}
|
||||
|
||||
pub async fn get_row_info(&self, row_id: &str) -> FlowyResult<Option<RowPB>> {
|
||||
pub async fn get_row_pb(&self, row_id: &str) -> FlowyResult<Option<RowPB>> {
|
||||
let row_ids = Some(vec![Cow::Borrowed(row_id)]);
|
||||
Ok(self.get_row_infos(row_ids).await?.pop())
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlo
|
||||
use dashmap::DashMap;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
|
||||
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision,
|
||||
};
|
||||
use flowy_revision::disk::SQLiteGridBlockRevisionPersistence;
|
||||
use flowy_revision::{RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
|
||||
@ -103,17 +103,14 @@ impl GridBlockManager {
|
||||
Ok(changesets)
|
||||
}
|
||||
|
||||
pub async fn update_row<F>(&self, changeset: RowMetaChangeset, row_builder: F) -> FlowyResult<()>
|
||||
where
|
||||
F: FnOnce(Arc<RowRevision>) -> RowPB,
|
||||
{
|
||||
pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
|
||||
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) => {
|
||||
let block_order_changeset =
|
||||
GridBlockChangesetPB::update(&editor.block_id, vec![row_builder(row_rev.clone())]);
|
||||
let row_pb = make_row_from_row_rev(row_rev.clone());
|
||||
let block_order_changeset = GridBlockChangesetPB::update(&editor.block_id, vec![row_pb]);
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
||||
.await?;
|
||||
@ -123,21 +120,23 @@ impl GridBlockManager {
|
||||
}
|
||||
|
||||
#[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<Option<Arc<RowRevision>>> {
|
||||
let row_id = row_id.to_owned();
|
||||
let block_id = self.persistence.get_block_id(&row_id)?;
|
||||
let editor = self.get_block_editor(&block_id).await?;
|
||||
match editor.get_row_info(&row_id).await? {
|
||||
None => {}
|
||||
Some(row_info) => {
|
||||
match editor.get_row_rev(&row_id).await? {
|
||||
None => Ok(None),
|
||||
Some(row_rev) => {
|
||||
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
|
||||
let _ = self
|
||||
.notify_did_update_block(&block_id, GridBlockChangesetPB::delete(&block_id, vec![row_info.id]))
|
||||
.notify_did_update_block(
|
||||
&block_id,
|
||||
GridBlockChangesetPB::delete(&block_id, vec![row_rev.id.clone()]),
|
||||
)
|
||||
.await?;
|
||||
Ok(Some(row_rev))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
||||
@ -189,12 +188,9 @@ impl GridBlockManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_cell<F>(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()>
|
||||
where
|
||||
F: FnOnce(Arc<RowRevision>) -> RowPB,
|
||||
{
|
||||
let row_changeset: RowMetaChangeset = changeset.clone().into();
|
||||
let _ = self.update_row(row_changeset, row_builder).await?;
|
||||
pub async fn update_cell(&self, changeset: CellChangesetPB) -> FlowyResult<()> {
|
||||
let row_changeset: RowChangeset = changeset.clone().into();
|
||||
let _ = self.update_row(row_changeset).await?;
|
||||
self.notify_did_update_cell(changeset).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::grid_view_manager::{GridViewRowDelegate, GridViewRowOperation};
|
||||
use flowy_error::FlowyResult;
|
||||
use crate::services::grid_view_manager::GridViewRowDelegate;
|
||||
|
||||
use flowy_grid_data_model::revision::RowRevision;
|
||||
use lib_infra::future::{wrap_future, AFFuture};
|
||||
use std::sync::Arc;
|
||||
@ -36,10 +36,3 @@ impl GridViewRowDelegate for Arc<GridBlockManager> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GridViewRowOperation for Arc<GridBlockManager> {
|
||||
fn gv_move_row(&self, row_rev: Arc<RowRevision>, from: usize, to: usize) -> AFFuture<FlowyResult<()>> {
|
||||
let block_manager = self.clone();
|
||||
wrap_future(async move { block_manager.move_row(row_rev, from, to).await })
|
||||
}
|
||||
}
|
||||
|
@ -65,10 +65,8 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSel
|
||||
let select_option_changeset = changeset.try_into_inner()?;
|
||||
let new_cell_data: String;
|
||||
if let Some(insert_option_id) = select_option_changeset.insert_option_id {
|
||||
tracing::trace!("Insert single select option: {}", &insert_option_id);
|
||||
new_cell_data = insert_option_id;
|
||||
} else {
|
||||
tracing::trace!("Delete single select option");
|
||||
new_cell_data = "".to_string()
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,7 @@ use crate::services::field::{default_type_option_builder_from_type, type_option_
|
||||
use crate::services::filter::GridFilterService;
|
||||
use crate::services::grid_view_manager::GridViewManager;
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::row::{
|
||||
make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder,
|
||||
};
|
||||
use crate::services::row::{make_grid_blocks, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::*;
|
||||
@ -72,7 +70,6 @@ impl GridRevisionEditor {
|
||||
user.clone(),
|
||||
Arc::new(grid_pad.clone()),
|
||||
Arc::new(block_manager.clone()),
|
||||
Arc::new(block_manager.clone()),
|
||||
Arc::new(task_scheduler.clone()),
|
||||
)
|
||||
.await?,
|
||||
@ -289,7 +286,7 @@ impl GridRevisionEditor {
|
||||
pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult<RowPB> {
|
||||
let mut row_rev = self.create_row_rev().await?;
|
||||
|
||||
self.view_manager.fill_row(&mut row_rev, ¶ms).await;
|
||||
self.view_manager.will_create_row(&mut row_rev, ¶ms).await;
|
||||
|
||||
let row_pb = self.create_row_pb(row_rev, params.start_row_id.clone()).await?;
|
||||
|
||||
@ -315,9 +312,9 @@ impl GridRevisionEditor {
|
||||
Ok(row_orders)
|
||||
}
|
||||
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
|
||||
pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
|
||||
let row_id = changeset.row_id.clone();
|
||||
let _ = self.block_manager.update_row(changeset, make_row_from_row_rev).await?;
|
||||
let _ = self.block_manager.update_row(changeset).await?;
|
||||
self.view_manager.did_update_row(&row_id).await;
|
||||
Ok(())
|
||||
}
|
||||
@ -346,8 +343,10 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.block_manager.delete_row(row_id).await?;
|
||||
self.view_manager.did_delete_row(row_id).await;
|
||||
let row_rev = self.block_manager.delete_row(row_id).await?;
|
||||
if let Some(row_rev) = row_rev {
|
||||
self.view_manager.did_delete_row(row_rev).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -394,12 +393,11 @@ impl GridRevisionEditor {
|
||||
|
||||
match self.grid_pad.read().await.get_field_rev(&field_id) {
|
||||
None => {
|
||||
let msg = format!("Field not found with id: {}", &field_id);
|
||||
let msg = format!("Field:{} not found", &field_id);
|
||||
Err(FlowyError::internal().context(msg))
|
||||
}
|
||||
Some((_, field_rev)) => {
|
||||
tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, content);
|
||||
|
||||
let cell_rev = self.get_cell_rev(&row_id, &field_id).await?;
|
||||
// Update the changeset.data property with the return value.
|
||||
content = Some(apply_cell_data_changeset(content.unwrap(), cell_rev, field_rev)?);
|
||||
@ -409,11 +407,7 @@ impl GridRevisionEditor {
|
||||
field_id,
|
||||
content,
|
||||
};
|
||||
let _ = self
|
||||
.block_manager
|
||||
.update_cell(cell_changeset, make_row_from_row_rev)
|
||||
.await?;
|
||||
|
||||
let _ = self.block_manager.update_cell(cell_changeset).await?;
|
||||
self.view_manager.did_update_row(&row_id).await;
|
||||
Ok(())
|
||||
}
|
||||
@ -492,7 +486,42 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
|
||||
self.view_manager.move_row(params).await
|
||||
let MoveRowParams {
|
||||
view_id: _,
|
||||
from_row_id,
|
||||
to_row_id,
|
||||
} = params;
|
||||
|
||||
match self.block_manager.get_row_rev(&from_row_id).await? {
|
||||
None => tracing::warn!("Move row failed, can not find the row:{}", from_row_id),
|
||||
Some(row_rev) => {
|
||||
match (
|
||||
self.block_manager.index_of_row(&from_row_id).await,
|
||||
self.block_manager.index_of_row(&to_row_id).await,
|
||||
) {
|
||||
(Some(from_index), Some(to_index)) => {
|
||||
tracing::trace!("Move row from {} to {}", from_index, to_index);
|
||||
let _ = self
|
||||
.block_manager
|
||||
.move_row(row_rev.clone(), from_index, to_index)
|
||||
.await?;
|
||||
|
||||
if let Some(row_changeset) = self.view_manager.move_row(row_rev, to_row_id.clone()).await {
|
||||
tracing::trace!("Receive row changeset after moving the row");
|
||||
match self.block_manager.update_row(row_changeset).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("Apply row changeset error:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(_, None) => tracing::warn!("Can not find the from row id: {}", from_row_id),
|
||||
(None, _) => tracing::warn!("Can not find the to row id: {}", to_row_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_field(&self, params: MoveFieldParams) -> FlowyResult<()> {
|
||||
|
@ -1,19 +1,19 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::entities::{
|
||||
CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB,
|
||||
RowPB,
|
||||
CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, GroupRowsChangesetPB, InsertedRowPB, RowPB,
|
||||
};
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
|
||||
use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService};
|
||||
use crate::services::setting::make_grid_setting;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision};
|
||||
use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder};
|
||||
use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
use lib_infra::future::{wrap_future, AFFuture, FutureResult};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@ -27,6 +27,7 @@ pub struct GridViewRevisionEditor {
|
||||
row_delegate: Arc<dyn GridViewRowDelegate>,
|
||||
group_service: Arc<RwLock<GroupService>>,
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
did_load_group: AtomicBool,
|
||||
}
|
||||
|
||||
impl GridViewRevisionEditor {
|
||||
@ -47,6 +48,7 @@ impl GridViewRevisionEditor {
|
||||
let rev_manager = Arc::new(rev_manager);
|
||||
let group_service = GroupService::new(Box::new(pad.clone())).await;
|
||||
let user_id = user_id.to_owned();
|
||||
let did_load_group = AtomicBool::new(false);
|
||||
Ok(Self {
|
||||
pad,
|
||||
user_id,
|
||||
@ -56,26 +58,22 @@ impl GridViewRevisionEditor {
|
||||
field_delegate,
|
||||
row_delegate,
|
||||
group_service: Arc::new(RwLock::new(group_service)),
|
||||
did_load_group,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn fill_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
|
||||
match params.layout {
|
||||
GridLayout::Table => {
|
||||
// Table can be grouped too
|
||||
}
|
||||
GridLayout::Board => match params.group_id.as_ref() {
|
||||
pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
|
||||
match params.group_id.as_ref() {
|
||||
None => {}
|
||||
Some(group_id) => {
|
||||
self.group_service
|
||||
.read()
|
||||
.await
|
||||
.fill_row(row_rev, group_id, |field_id| {
|
||||
.will_create_row(row_rev, group_id, |field_id| {
|
||||
self.field_delegate.get_field_rev(&field_id)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,12 +92,16 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn did_delete_row(&self, row_id: &str) {
|
||||
pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) {
|
||||
// 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()]);
|
||||
if let Some(changesets) = self
|
||||
.group_service
|
||||
.write()
|
||||
.await
|
||||
.did_delete_row(row_rev, |field_id| self.field_delegate.get_field_rev(&field_id))
|
||||
.await
|
||||
{
|
||||
for changeset in changesets {
|
||||
self.notify_did_update_group(changeset).await;
|
||||
}
|
||||
}
|
||||
@ -119,32 +121,33 @@ impl GridViewRevisionEditor {
|
||||
}
|
||||
}
|
||||
|
||||
async fn group_id_of_row(&self, row_id: &str) -> Option<String> {
|
||||
let read_guard = &self.group_service.read().await.groups;
|
||||
for group in read_guard.iter() {
|
||||
if group.contains_row(row_id) {
|
||||
return Some(group.id.clone());
|
||||
pub(crate) async fn did_move_row(
|
||||
&self,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
upper_row_id: &str,
|
||||
) {
|
||||
if let Some(changesets) = self
|
||||
.group_service
|
||||
.write()
|
||||
.await
|
||||
.did_move_row(row_rev, row_changeset, upper_row_id, |field_id| {
|
||||
self.field_delegate.get_field_rev(&field_id)
|
||||
})
|
||||
.await
|
||||
{
|
||||
for changeset in changesets {
|
||||
tracing::trace!("Group: {} changeset: {}", changeset.group_id, changeset);
|
||||
self.notify_did_update_group(changeset).await;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// async fn get_mut_group<F>(&self, group_id: &str, f: F) -> FlowyResult<()>
|
||||
// where
|
||||
// F: Fn(&mut Group) -> FlowyResult<()>,
|
||||
// {
|
||||
// for group in self.groups.write().await.iter_mut() {
|
||||
// if group.id == group_id {
|
||||
// let _ = f(group)?;
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
|
||||
let groups = if !self.did_load_group.load(Ordering::SeqCst) {
|
||||
self.did_load_group.store(true, Ordering::SeqCst);
|
||||
let field_revs = self.field_delegate.get_field_revs().await;
|
||||
let row_revs = self.row_delegate.gv_row_revs().await;
|
||||
|
||||
match self
|
||||
.group_service
|
||||
.write()
|
||||
@ -152,9 +155,14 @@ impl GridViewRevisionEditor {
|
||||
.load_groups(&field_revs, row_revs)
|
||||
.await
|
||||
{
|
||||
None => Ok(vec![]),
|
||||
Some(groups) => Ok(groups.into_iter().map(GroupPB::from).collect()),
|
||||
None => vec![],
|
||||
Some(groups) => groups,
|
||||
}
|
||||
} else {
|
||||
self.group_service.read().await.groups().await
|
||||
};
|
||||
|
||||
Ok(groups.into_iter().map(GroupPB::from).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_setting(&self) -> GridSettingPB {
|
||||
|
@ -1,17 +1,13 @@
|
||||
use crate::entities::{
|
||||
CreateRowParams, GridFilterConfiguration, GridLayout, GridSettingPB, MoveRowParams, RepeatedGridGroupPB, RowPB,
|
||||
};
|
||||
use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB};
|
||||
use crate::manager::GridUser;
|
||||
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::grid_view_editor::GridViewRevisionEditor;
|
||||
use bytes::Bytes;
|
||||
use dashmap::DashMap;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
|
||||
use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
|
||||
|
||||
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
||||
use flowy_sync::entities::revision::Revision;
|
||||
use flowy_sync::util::make_text_delta_from_revisions;
|
||||
@ -31,17 +27,11 @@ pub trait GridViewRowDelegate: Send + Sync + 'static {
|
||||
fn gv_row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>>;
|
||||
}
|
||||
|
||||
pub trait GridViewRowOperation: Send + Sync + 'static {
|
||||
// Will be removed in the future.
|
||||
fn gv_move_row(&self, row_rev: Arc<RowRevision>, from: usize, to: usize) -> AFFuture<FlowyResult<()>>;
|
||||
}
|
||||
|
||||
pub(crate) struct GridViewManager {
|
||||
grid_id: String,
|
||||
user: Arc<dyn GridUser>,
|
||||
field_delegate: Arc<dyn GridViewFieldDelegate>,
|
||||
row_delegate: Arc<dyn GridViewRowDelegate>,
|
||||
row_operation: Arc<dyn GridViewRowOperation>,
|
||||
view_editors: DashMap<ViewId, Arc<GridViewRevisionEditor>>,
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
}
|
||||
@ -52,7 +42,6 @@ impl GridViewManager {
|
||||
user: Arc<dyn GridUser>,
|
||||
field_delegate: Arc<dyn GridViewFieldDelegate>,
|
||||
row_delegate: Arc<dyn GridViewRowDelegate>,
|
||||
row_operation: Arc<dyn GridViewRowOperation>,
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
) -> FlowyResult<Self> {
|
||||
Ok(Self {
|
||||
@ -61,17 +50,25 @@ impl GridViewManager {
|
||||
scheduler,
|
||||
field_delegate,
|
||||
row_delegate,
|
||||
row_operation,
|
||||
view_editors: DashMap::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn fill_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
|
||||
/// When the row was created, we may need to modify the [RowRevision] according to the [CreateRowParams].
|
||||
pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
|
||||
for view_editor in self.view_editors.iter() {
|
||||
view_editor.fill_row(row_rev, params).await;
|
||||
view_editor.will_create_row(row_rev, params).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify the view that the row was created. For the moment, the view is just sending notifications.
|
||||
pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
|
||||
for view_editor in self.view_editors.iter() {
|
||||
view_editor.did_create_row(row_pb, params).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert/Delete the group's row if the corresponding data was changed.
|
||||
pub(crate) async fn did_update_row(&self, row_id: &str) {
|
||||
match self.row_delegate.gv_get_row_rev(row_id).await {
|
||||
None => {
|
||||
@ -85,15 +82,9 @@ impl GridViewManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
|
||||
pub(crate) async fn did_delete_row(&self, row_rev: Arc<RowRevision>) {
|
||||
for view_editor in self.view_editors.iter() {
|
||||
view_editor.did_create_row(row_pb, params).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn did_delete_row(&self, row_id: &str) {
|
||||
for view_editor in self.view_editors.iter() {
|
||||
view_editor.did_delete_row(row_id).await;
|
||||
view_editor.did_delete_row(&row_rev).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,37 +110,20 @@ impl GridViewManager {
|
||||
Ok(RepeatedGridGroupPB { items: groups })
|
||||
}
|
||||
|
||||
pub(crate) async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
|
||||
let MoveRowParams {
|
||||
view_id: _,
|
||||
row_id,
|
||||
from_index,
|
||||
to_index,
|
||||
layout,
|
||||
upper_row_id,
|
||||
} = params;
|
||||
/// It may generate a RowChangeset when the Row was moved from one group to another.
|
||||
/// The return value, [RowChangeset], contains the changes made by the groups.
|
||||
///
|
||||
pub(crate) async fn move_row(&self, row_rev: Arc<RowRevision>, to_row_id: String) -> Option<RowChangeset> {
|
||||
let mut row_changeset = RowChangeset::new(row_rev.id.clone());
|
||||
for view_editor in self.view_editors.iter() {
|
||||
view_editor.did_move_row(&row_rev, &mut row_changeset, &to_row_id).await;
|
||||
}
|
||||
|
||||
let from_index = from_index as usize;
|
||||
|
||||
match self.row_delegate.gv_get_row_rev(&row_id).await {
|
||||
None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
|
||||
Some(row_rev) => match layout {
|
||||
GridLayout::Table => {
|
||||
tracing::trace!("Move row from {} to {}", from_index, to_index);
|
||||
let to_index = to_index as usize;
|
||||
let _ = self.row_operation.gv_move_row(row_rev, from_index, to_index).await?;
|
||||
if row_changeset.has_changed() {
|
||||
Some(row_changeset)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
GridLayout::Board => {
|
||||
if let Some(upper_row_id) = upper_row_id {
|
||||
if let Some(to_index) = self.row_delegate.gv_index_of_row(&upper_row_id).await {
|
||||
tracing::trace!("Move row from {} to {}", from_index, to_index);
|
||||
let _ = self.row_operation.gv_move_row(row_rev, from_index, to_index).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_view_editor(&self, view_id: &str) -> FlowyResult<Arc<GridViewRevisionEditor>> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::{CheckboxGroupConfigurationPB, GroupRowsChangesetPB};
|
||||
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
|
||||
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
|
||||
use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable};
|
||||
@ -19,13 +19,36 @@ impl Groupable for CheckboxGroupController {
|
||||
false
|
||||
}
|
||||
|
||||
fn group_row(&mut self, _row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
fn add_row_if_match(
|
||||
&mut self,
|
||||
_row_rev: &RowRevision,
|
||||
_cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
_row_rev: &RowRevision,
|
||||
_cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn move_row_if_match(
|
||||
&mut self,
|
||||
_field_rev: &FieldRevision,
|
||||
_row_rev: &RowRevision,
|
||||
_row_changeset: &mut RowChangeset,
|
||||
_cell_data: &Self::CellDataType,
|
||||
_to_row_id: &str,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for CheckboxGroupController {
|
||||
fn fill_row(&self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {
|
||||
fn will_create_row(&mut self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@ -36,11 +59,22 @@ impl GroupGenerator for CheckboxGroupGenerator {
|
||||
type TypeOptionType = CheckboxTypeOptionPB;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
_type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
let check_group = Group::new("true".to_string(), "".to_string(), CHECK.to_string());
|
||||
let uncheck_group = Group::new("false".to_string(), "".to_string(), UNCHECK.to_string());
|
||||
let check_group = Group::new(
|
||||
"true".to_string(),
|
||||
field_id.to_owned(),
|
||||
"".to_string(),
|
||||
CHECK.to_string(),
|
||||
);
|
||||
let uncheck_group = Group::new(
|
||||
"false".to_string(),
|
||||
field_id.to_owned(),
|
||||
"".to_string(),
|
||||
UNCHECK.to_string(),
|
||||
);
|
||||
vec![check_group, uncheck_group]
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::services::cell::{decode_any_cell_data, CellBytesParser};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{
|
||||
FieldRevision, GroupConfigurationRevision, RowRevision, TypeOptionDataDeserializer,
|
||||
FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -14,6 +14,7 @@ pub trait GroupGenerator {
|
||||
type TypeOptionType;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group>;
|
||||
@ -22,23 +23,51 @@ pub trait GroupGenerator {
|
||||
pub trait Groupable: Send + Sync {
|
||||
type CellDataType;
|
||||
fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
|
||||
fn group_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB>;
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB>;
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB>;
|
||||
|
||||
fn move_row_if_match(
|
||||
&mut self,
|
||||
field_rev: &FieldRevision,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
cell_data: &Self::CellDataType,
|
||||
to_row_id: &str,
|
||||
) -> Vec<GroupRowsChangesetPB>;
|
||||
}
|
||||
|
||||
pub trait GroupController: GroupControllerSharedAction + Send + Sync {
|
||||
fn fill_row(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
|
||||
}
|
||||
|
||||
pub trait GroupControllerSharedAction: Send + Sync {
|
||||
// The field that is used for grouping the rows
|
||||
fn field_id(&self) -> &str;
|
||||
fn build_groups(&self) -> Vec<Group>;
|
||||
fn groups(&self) -> Vec<Group>;
|
||||
fn group_rows(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
|
||||
fn did_update_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
|
||||
fn did_move_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
field_rev: &FieldRevision,
|
||||
to_row_id: &str,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>>;
|
||||
}
|
||||
|
||||
const DEFAULT_GROUP_ID: &str = "default_group";
|
||||
@ -60,6 +89,7 @@ pub struct GenericGroupController<C, T, G, P> {
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
pub id: String,
|
||||
pub field_id: String,
|
||||
pub desc: String,
|
||||
rows: Vec<RowPB>,
|
||||
pub content: String,
|
||||
@ -76,9 +106,10 @@ impl std::convert::From<Group> for GroupPB {
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn new(id: String, desc: String, content: String) -> Self {
|
||||
pub fn new(id: String, field_id: String, desc: String, content: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
field_id,
|
||||
desc,
|
||||
rows: vec![],
|
||||
content,
|
||||
@ -106,6 +137,22 @@ impl Group {
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_row(&mut self, index: usize, row_pb: RowPB) {
|
||||
if index < self.rows.len() {
|
||||
self.rows.insert(index, row_pb);
|
||||
} else {
|
||||
tracing::error!("Insert row index:{} beyond the bounds:{},", index, self.rows.len());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_of_row(&self, row_id: &str) -> Option<usize> {
|
||||
self.rows.iter().position(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn number_of_row(&self) -> usize {
|
||||
self.rows.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, G, P> GenericGroupController<C, T, G, P>
|
||||
@ -121,10 +168,11 @@ 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::generate_groups(&configuration, &type_option);
|
||||
let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
|
||||
|
||||
let default_group = Group::new(
|
||||
DEFAULT_GROUP_ID.to_owned(),
|
||||
field_rev.id.clone(),
|
||||
format!("No {}", field_rev.name),
|
||||
"".to_string(),
|
||||
);
|
||||
@ -150,7 +198,7 @@ where
|
||||
&self.field_id
|
||||
}
|
||||
|
||||
fn build_groups(&self) -> Vec<Group> {
|
||||
fn groups(&self) -> Vec<Group> {
|
||||
let default_group = self.default_group.clone();
|
||||
let mut groups: Vec<Group> = self.groups_map.values().cloned().collect();
|
||||
if !default_group.rows.is_empty() {
|
||||
@ -203,56 +251,44 @@ where
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.group_row(row_rev, &cell_data))
|
||||
Ok(self.add_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn did_delete_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>> {
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.remove_row_if_match(row_rev, &cell_data))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn did_move_row(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
field_rev: &FieldRevision,
|
||||
to_row_id: &str,
|
||||
) -> FlowyResult<Vec<GroupRowsChangesetPB>> {
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
tracing::trace!("Move row:{} to row:{}", row_rev.id, to_row_id);
|
||||
Ok(self.move_row_if_match(field_rev, row_rev, row_changeset, &cell_data, to_row_id))
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl<C, T, G, P> GroupController<C, T, G, P>
|
||||
// where
|
||||
// P: CellBytesParser,
|
||||
// Self: Groupable<CellDataType = P::Object>,
|
||||
// {
|
||||
// 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_id) {
|
||||
// let mut records: Vec<GroupRecord> = vec![];
|
||||
// let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||
// let cell_data = cell_bytes.parser::<P>()?;
|
||||
// for group in self.groups_map.values() {
|
||||
// if self.can_group(&group.content, &cell_data) {
|
||||
// records.push(GroupRecord {
|
||||
// row: row.into(),
|
||||
// group_id: group.id.clone(),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
struct GroupRecord {
|
||||
row: RowPB,
|
||||
group_id: String,
|
@ -1,7 +1,7 @@
|
||||
mod checkbox_group;
|
||||
mod generator;
|
||||
mod group_controller;
|
||||
mod select_option_group;
|
||||
|
||||
pub use checkbox_group::*;
|
||||
pub use generator::*;
|
||||
pub use group_controller::*;
|
||||
pub use select_option_group::*;
|
||||
|
@ -5,7 +5,7 @@ use crate::services::field::{
|
||||
};
|
||||
use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable};
|
||||
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
||||
|
||||
// SingleSelect
|
||||
pub type SingleSelectGroupController = GenericGroupController<
|
||||
@ -21,23 +21,59 @@ impl Groupable for SingleSelectGroupController {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn group_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
group_select_option_row(group, &mut changesets, cell_data, row_rev);
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn move_row_if_match(
|
||||
&mut self,
|
||||
field_rev: &FieldRevision,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
cell_data: &Self::CellDataType,
|
||||
to_row_id: &str,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut group_changeset = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
move_row(
|
||||
group,
|
||||
&mut group_changeset,
|
||||
field_rev,
|
||||
row_rev,
|
||||
row_changeset,
|
||||
cell_data,
|
||||
to_row_id,
|
||||
);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for SingleSelectGroupController {
|
||||
fn fill_row(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&Group> = self.groups_map.get(group_id);
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&mut Group> = self.groups_map.get_mut(group_id);
|
||||
match group {
|
||||
None => {}
|
||||
Some(group) => {
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_rev.cells.insert(field_rev.id.clone(), cell_rev);
|
||||
group.add_row(RowPB::from(row_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,6 +84,7 @@ impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
type ConfigurationType = SelectOptionGroupConfigurationPB;
|
||||
type TypeOptionType = SingleSelectTypeOptionPB;
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
@ -56,7 +93,14 @@ impl GroupGenerator for SingleSelectGroupGenerator {
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| Group::new(option.id.clone(), option.name.clone(), option.id.clone()))
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@ -77,18 +121,52 @@ impl Groupable for MultiSelectGroupController {
|
||||
cell_data.select_options.iter().any(|option| option.id == content)
|
||||
}
|
||||
|
||||
fn group_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
group_select_option_row(group, &mut changesets, cell_data, row_rev);
|
||||
add_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn remove_row_if_match(
|
||||
&mut self,
|
||||
row_rev: &RowRevision,
|
||||
cell_data: &Self::CellDataType,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut changesets = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
remove_row(group, &mut changesets, cell_data, row_rev);
|
||||
});
|
||||
changesets
|
||||
}
|
||||
|
||||
fn move_row_if_match(
|
||||
&mut self,
|
||||
field_rev: &FieldRevision,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
cell_data: &Self::CellDataType,
|
||||
to_row_id: &str,
|
||||
) -> Vec<GroupRowsChangesetPB> {
|
||||
let mut group_changeset = vec![];
|
||||
self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| {
|
||||
move_row(
|
||||
group,
|
||||
&mut group_changeset,
|
||||
field_rev,
|
||||
row_rev,
|
||||
row_changeset,
|
||||
cell_data,
|
||||
to_row_id,
|
||||
);
|
||||
});
|
||||
group_changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupController for MultiSelectGroupController {
|
||||
fn fill_row(&self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
|
||||
let group: Option<&Group> = self.groups_map.get(group_id);
|
||||
match group {
|
||||
None => tracing::warn!("Can not find the group: {}", group_id),
|
||||
@ -106,6 +184,7 @@ impl GroupGenerator for MultiSelectGroupGenerator {
|
||||
type TypeOptionType = MultiSelectTypeOptionPB;
|
||||
|
||||
fn generate_groups(
|
||||
field_id: &str,
|
||||
_configuration: &Option<Self::ConfigurationType>,
|
||||
type_option: &Option<Self::TypeOptionType>,
|
||||
) -> Vec<Group> {
|
||||
@ -114,21 +193,27 @@ impl GroupGenerator for MultiSelectGroupGenerator {
|
||||
Some(type_option) => type_option
|
||||
.options
|
||||
.iter()
|
||||
.map(|option| Group::new(option.id.clone(), option.name.clone(), option.id.clone()))
|
||||
.map(|option| {
|
||||
Group::new(
|
||||
option.id.clone(),
|
||||
field_id.to_owned(),
|
||||
option.name.clone(),
|
||||
option.id.clone(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn group_select_option_row(
|
||||
fn add_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id {
|
||||
if !group.contains_row(&row_rev.id) {
|
||||
if option.id == group.id && !group.contains_row(&row_rev.id) {
|
||||
let row_pb = RowPB::from(row_rev);
|
||||
changesets.push(GroupRowsChangesetPB::insert(
|
||||
group.id.clone(),
|
||||
@ -136,9 +221,62 @@ fn group_select_option_row(
|
||||
));
|
||||
group.add_row(row_pb);
|
||||
}
|
||||
} else if group.contains_row(&row_rev.id) {
|
||||
group.remove_row(&row_rev.id);
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_row(
|
||||
group: &mut Group,
|
||||
changesets: &mut Vec<GroupRowsChangesetPB>,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
row_rev: &RowRevision,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
if option.id == group.id && group.contains_row(&row_rev.id) {
|
||||
changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn move_row(
|
||||
group: &mut Group,
|
||||
group_changeset: &mut Vec<GroupRowsChangesetPB>,
|
||||
field_rev: &FieldRevision,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
cell_data: &SelectOptionCellDataPB,
|
||||
to_row_id: &str,
|
||||
) {
|
||||
cell_data.select_options.iter().for_each(|option| {
|
||||
// Remove the row in which group contains the row
|
||||
let is_group_contains = group.contains_row(&row_rev.id);
|
||||
let to_index = group.index_of_row(to_row_id);
|
||||
|
||||
if option.id == group.id && is_group_contains {
|
||||
group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()]));
|
||||
group.remove_row(&row_rev.id);
|
||||
}
|
||||
|
||||
// Find the inserted group
|
||||
if let Some(to_index) = to_index {
|
||||
let row_pb = RowPB::from(row_rev);
|
||||
let inserted_row = InsertedRowPB {
|
||||
row: row_pb.clone(),
|
||||
index: Some(to_index as i32),
|
||||
};
|
||||
group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row]));
|
||||
if group.number_of_row() == to_index {
|
||||
group.add_row(row_pb);
|
||||
} else {
|
||||
group.insert_row(to_index, row_pb);
|
||||
}
|
||||
}
|
||||
|
||||
// If the inserted row comes from other group, it needs to update the corresponding cell content.
|
||||
if to_index.is_some() && option.id != group.id {
|
||||
// Update the corresponding row's cell content.
|
||||
let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
|
||||
row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ use crate::services::group::{
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision};
|
||||
use flowy_grid_data_model::revision::{
|
||||
gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision,
|
||||
};
|
||||
use lib_infra::future::AFFuture;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
@ -18,7 +20,6 @@ pub trait GroupConfigurationDelegate: Send + Sync + 'static {
|
||||
}
|
||||
|
||||
pub(crate) struct GroupService {
|
||||
pub groups: Vec<Group>,
|
||||
delegate: Box<dyn GroupConfigurationDelegate>,
|
||||
group_controller: Option<Arc<RwLock<dyn GroupController>>>,
|
||||
}
|
||||
@ -26,12 +27,19 @@ pub(crate) struct GroupService {
|
||||
impl GroupService {
|
||||
pub(crate) async fn new(delegate: Box<dyn GroupConfigurationDelegate>) -> Self {
|
||||
Self {
|
||||
groups: vec![],
|
||||
delegate,
|
||||
group_controller: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn groups(&self) -> Vec<Group> {
|
||||
if let Some(group_action_handler) = self.group_controller.as_ref() {
|
||||
group_action_handler.read().await.groups()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn load_groups(
|
||||
&mut self,
|
||||
field_revs: &[Arc<FieldRevision>],
|
||||
@ -44,15 +52,12 @@ impl GroupService {
|
||||
.build_groups(&field_type, &field_rev, row_revs, configuration)
|
||||
.await
|
||||
{
|
||||
Ok(groups) => {
|
||||
self.groups = groups.clone();
|
||||
Some(groups)
|
||||
}
|
||||
Ok(groups) => Some(groups),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn fill_row<F, O>(&self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F)
|
||||
pub(crate) async fn will_create_row<F, O>(&self, row_rev: &mut RowRevision, group_id: &str, get_field_fn: F)
|
||||
where
|
||||
F: FnOnce(String) -> O,
|
||||
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
|
||||
@ -62,12 +67,65 @@ impl GroupService {
|
||||
match get_field_fn(field_id).await {
|
||||
None => {}
|
||||
Some(field_rev) => {
|
||||
group_controller.write().await.fill_row(row_rev, &field_rev, group_id);
|
||||
group_controller
|
||||
.write()
|
||||
.await
|
||||
.will_create_row(row_rev, &field_rev, group_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn did_delete_row<F, O>(
|
||||
&self,
|
||||
row_rev: &RowRevision,
|
||||
get_field_fn: F,
|
||||
) -> Option<Vec<GroupRowsChangesetPB>>
|
||||
where
|
||||
F: FnOnce(String) -> O,
|
||||
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
|
||||
{
|
||||
let group_controller = self.group_controller.as_ref()?;
|
||||
let field_id = group_controller.read().await.field_id().to_owned();
|
||||
let field_rev = get_field_fn(field_id).await?;
|
||||
|
||||
match group_controller.write().await.did_delete_row(row_rev, &field_rev) {
|
||||
Ok(changesets) => Some(changesets),
|
||||
Err(e) => {
|
||||
tracing::error!("Delete group data failed, {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn did_move_row<F, O>(
|
||||
&self,
|
||||
row_rev: &RowRevision,
|
||||
row_changeset: &mut RowChangeset,
|
||||
upper_row_id: &str,
|
||||
get_field_fn: F,
|
||||
) -> Option<Vec<GroupRowsChangesetPB>>
|
||||
where
|
||||
F: FnOnce(String) -> O,
|
||||
O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
|
||||
{
|
||||
let group_controller = self.group_controller.as_ref()?;
|
||||
let field_id = group_controller.read().await.field_id().to_owned();
|
||||
let field_rev = get_field_fn(field_id).await?;
|
||||
|
||||
match group_controller
|
||||
.write()
|
||||
.await
|
||||
.did_move_row(row_rev, row_changeset, &field_rev, upper_row_id)
|
||||
{
|
||||
Ok(changesets) => Some(changesets),
|
||||
Err(e) => {
|
||||
tracing::error!("Move group data failed, {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub(crate) async fn did_update_row<F, O>(
|
||||
&self,
|
||||
@ -130,7 +188,7 @@ impl GroupService {
|
||||
if let Some(group_action_handler) = self.group_controller.as_ref() {
|
||||
let mut write_guard = group_action_handler.write().await;
|
||||
let _ = write_guard.group_rows(&row_revs, field_rev)?;
|
||||
groups = write_guard.build_groups();
|
||||
groups = write_guard.groups();
|
||||
drop(write_guard);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest};
|
||||
use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER};
|
||||
use flowy_grid::entities::FieldType;
|
||||
use flowy_grid::services::field::{SELECTION_IDS_SEPARATOR, UNCHECK};
|
||||
use flowy_grid_data_model::revision::RowMetaChangeset;
|
||||
use flowy_grid_data_model::revision::RowChangeset;
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_create_row_count_test() {
|
||||
@ -24,7 +24,7 @@ async fn grid_create_row_count_test() {
|
||||
async fn grid_update_row() {
|
||||
let mut test = GridRowTest::new().await;
|
||||
let row_rev = test.row_builder().build();
|
||||
let changeset = RowMetaChangeset {
|
||||
let changeset = RowChangeset {
|
||||
row_id: row_rev.id.clone(),
|
||||
height: None,
|
||||
visibility: None,
|
||||
|
@ -5,7 +5,7 @@ use crate::grid::grid_editor::GridEditorTest;
|
||||
use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, GridLayout, RowPB};
|
||||
use flowy_grid::services::field::*;
|
||||
use flowy_grid_data_model::revision::{
|
||||
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
|
||||
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@ -17,7 +17,7 @@ pub enum RowScript {
|
||||
row_rev: RowRevision,
|
||||
},
|
||||
UpdateRow {
|
||||
changeset: RowMetaChangeset,
|
||||
changeset: RowChangeset,
|
||||
},
|
||||
AssertRow {
|
||||
expected_row: RowRevision,
|
||||
@ -56,7 +56,7 @@ pub struct GridRowTest {
|
||||
|
||||
impl GridRowTest {
|
||||
pub async fn new() -> Self {
|
||||
let editor_test = GridEditorTest::new().await;
|
||||
let editor_test = GridEditorTest::new_table().await;
|
||||
Self { inner: editor_test }
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ pub struct GridCellTest {
|
||||
|
||||
impl GridCellTest {
|
||||
pub async fn new() -> Self {
|
||||
let inner = GridEditorTest::new().await;
|
||||
let inner = GridEditorTest::new_table().await;
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub struct GridFieldTest {
|
||||
|
||||
impl GridFieldTest {
|
||||
pub async fn new() -> Self {
|
||||
let editor_test = GridEditorTest::new().await;
|
||||
let editor_test = GridEditorTest::new_table().await;
|
||||
Self { inner: editor_test }
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub struct GridFilterTest {
|
||||
|
||||
impl GridFilterTest {
|
||||
pub async fn new() -> Self {
|
||||
let editor_test = GridEditorTest::new().await;
|
||||
let editor_test = GridEditorTest::new_table().await;
|
||||
Self {
|
||||
inner: editor_test
|
||||
}
|
||||
|
@ -36,12 +36,25 @@ pub struct GridEditorTest {
|
||||
}
|
||||
|
||||
impl GridEditorTest {
|
||||
pub async fn new() -> Self {
|
||||
pub async fn new_table() -> Self {
|
||||
Self::new(GridLayout::Table).await
|
||||
}
|
||||
|
||||
pub async fn new_board() -> Self {
|
||||
Self::new(GridLayout::Board).await
|
||||
}
|
||||
|
||||
pub async fn new(layout: GridLayout) -> Self {
|
||||
let sdk = FlowySDKTest::default();
|
||||
let _ = sdk.init_user().await;
|
||||
let build_context = make_test_grid();
|
||||
let view_data: Bytes = build_context.into();
|
||||
let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
|
||||
|
||||
let test = match layout {
|
||||
GridLayout::Table => ViewTest::new_grid_view(&sdk, view_data.to_vec()).await,
|
||||
GridLayout::Board => ViewTest::new_board_view(&sdk, view_data.to_vec()).await,
|
||||
};
|
||||
|
||||
let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
|
||||
let field_revs = editor.get_field_revs(None).await.unwrap();
|
||||
let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
|
||||
|
@ -0,0 +1,2 @@
|
||||
mod script;
|
||||
mod test;
|
97
frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs
Normal file
97
frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
use flowy_grid::entities::{GroupPB, MoveRowParams, RowPB};
|
||||
|
||||
pub enum GroupScript {
|
||||
AssertGroup {
|
||||
group_index: usize,
|
||||
row_count: usize,
|
||||
},
|
||||
AssertGroupCount(usize),
|
||||
AssertGroupRow {
|
||||
group_index: usize,
|
||||
row_index: usize,
|
||||
row: RowPB,
|
||||
},
|
||||
MoveRow {
|
||||
from_group_index: usize,
|
||||
from_row_index: usize,
|
||||
to_group_index: usize,
|
||||
to_row_index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct GridGroupTest {
|
||||
inner: GridEditorTest,
|
||||
}
|
||||
|
||||
impl GridGroupTest {
|
||||
pub async fn new() -> Self {
|
||||
let editor_test = GridEditorTest::new_board().await;
|
||||
Self { inner: editor_test }
|
||||
}
|
||||
|
||||
pub async fn run_scripts(&mut self, scripts: Vec<GroupScript>) {
|
||||
for script in scripts {
|
||||
self.run_script(script).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_script(&mut self, script: GroupScript) {
|
||||
match script {
|
||||
GroupScript::AssertGroup { group_index, row_count } => {
|
||||
assert_eq!(row_count, self.group_at_index(group_index).await.rows.len());
|
||||
}
|
||||
GroupScript::AssertGroupCount(count) => {
|
||||
let groups = self.editor.load_groups().await.unwrap();
|
||||
assert_eq!(count, groups.len());
|
||||
}
|
||||
GroupScript::MoveRow {
|
||||
from_group_index,
|
||||
from_row_index,
|
||||
to_group_index,
|
||||
to_row_index,
|
||||
} => {
|
||||
let groups: Vec<GroupPB> = self.editor.load_groups().await.unwrap().items;
|
||||
let from_row = groups.get(from_group_index).unwrap().rows.get(from_row_index).unwrap();
|
||||
let to_row = groups.get(to_group_index).unwrap().rows.get(to_row_index).unwrap();
|
||||
let params = MoveRowParams {
|
||||
view_id: self.inner.grid_id.clone(),
|
||||
from_row_id: from_row.id.clone(),
|
||||
to_row_id: to_row.id.clone(),
|
||||
};
|
||||
|
||||
self.editor.move_row(params).await.unwrap();
|
||||
}
|
||||
GroupScript::AssertGroupRow {
|
||||
group_index,
|
||||
row_index,
|
||||
row,
|
||||
} => {
|
||||
//
|
||||
let group = self.group_at_index(group_index).await;
|
||||
let compare_row = group.rows.get(row_index).unwrap().clone();
|
||||
|
||||
assert_eq!(row.id, compare_row.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn group_at_index(&self, index: usize) -> GroupPB {
|
||||
let groups = self.editor.load_groups().await.unwrap().items;
|
||||
groups.get(index).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for GridGroupTest {
|
||||
type Target = GridEditorTest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for GridGroupTest {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
101
frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs
Normal file
101
frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use crate::grid::group_test::script::GridGroupTest;
|
||||
use crate::grid::group_test::script::GroupScript::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn board_init_test() {
|
||||
let mut test = GridGroupTest::new().await;
|
||||
let scripts = vec![
|
||||
AssertGroupCount(3),
|
||||
AssertGroup {
|
||||
group_index: 0,
|
||||
row_count: 2,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 1,
|
||||
row_count: 2,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 2,
|
||||
row_count: 1,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn board_move_row_test() {
|
||||
let mut test = GridGroupTest::new().await;
|
||||
let group = test.group_at_index(0).await;
|
||||
let scripts = vec![
|
||||
MoveRow {
|
||||
from_group_index: 0,
|
||||
from_row_index: 0,
|
||||
to_group_index: 0,
|
||||
to_row_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 0,
|
||||
row_count: 2,
|
||||
},
|
||||
AssertGroupRow {
|
||||
group_index: 0,
|
||||
row_index: 1,
|
||||
row: group.rows.get(0).unwrap().clone(),
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn board_move_row_to_other_group_test() {
|
||||
let mut test = GridGroupTest::new().await;
|
||||
let group = test.group_at_index(0).await;
|
||||
let scripts = vec![
|
||||
MoveRow {
|
||||
from_group_index: 0,
|
||||
from_row_index: 0,
|
||||
to_group_index: 1,
|
||||
to_row_index: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 0,
|
||||
row_count: 1,
|
||||
},
|
||||
AssertGroup {
|
||||
group_index: 1,
|
||||
row_count: 3,
|
||||
},
|
||||
AssertGroupRow {
|
||||
group_index: 1,
|
||||
row_index: 1,
|
||||
row: group.rows.get(0).unwrap().clone(),
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn board_move_row_to_other_group_and_reorder_test() {
|
||||
let mut test = GridGroupTest::new().await;
|
||||
let group = test.group_at_index(0).await;
|
||||
let scripts = vec![
|
||||
MoveRow {
|
||||
from_group_index: 0,
|
||||
from_row_index: 0,
|
||||
to_group_index: 1,
|
||||
to_row_index: 1,
|
||||
},
|
||||
MoveRow {
|
||||
from_group_index: 1,
|
||||
from_row_index: 1,
|
||||
to_group_index: 1,
|
||||
to_row_index: 2,
|
||||
},
|
||||
AssertGroupRow {
|
||||
group_index: 1,
|
||||
row_index: 2,
|
||||
row: group.rows.get(0).unwrap().clone(),
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
@ -3,3 +3,4 @@ mod cell_test;
|
||||
mod field_test;
|
||||
mod filter_test;
|
||||
mod grid_editor;
|
||||
mod group_test;
|
||||
|
@ -305,7 +305,7 @@ impl FolderCouldServiceV1 for LocalServer {
|
||||
let time = timestamp();
|
||||
let view = ViewRevision {
|
||||
id: params.view_id,
|
||||
belong_to_id: params.belong_to_id,
|
||||
app_id: params.belong_to_id,
|
||||
name: params.name,
|
||||
desc: params.desc,
|
||||
data_type: params.data_type.into(),
|
||||
@ -315,7 +315,7 @@ impl FolderCouldServiceV1 for LocalServer {
|
||||
create_time: time,
|
||||
ext_data: "".to_string(),
|
||||
thumbnail: params.thumbnail,
|
||||
plugin_type: params.plugin_type,
|
||||
layout: params.layout.into(),
|
||||
};
|
||||
FutureResult::new(async { Ok(view) })
|
||||
}
|
||||
|
@ -320,9 +320,7 @@ impl RevisionSyncSequence {
|
||||
fn compact(&self) -> Option<(RevisionRange, VecDeque<i64>)> {
|
||||
// Make sure there are two rev_id going to sync. No need to compact if there is only
|
||||
// one rev_id in queue.
|
||||
if self.next_rev_id().is_none() {
|
||||
return None;
|
||||
}
|
||||
self.next_rev_id()?;
|
||||
|
||||
let mut new_seq = self.0.clone();
|
||||
let mut drained = new_seq.drain(1..).collect::<VecDeque<_>>();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use bytes::Bytes;
|
||||
use flowy_database::ConnectionPool;
|
||||
use flowy_folder::entities::{SubViewDataTypePB, ViewDataTypePB};
|
||||
use flowy_folder::entities::{ViewDataTypePB, ViewLayoutTypePB};
|
||||
use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
|
||||
use flowy_folder::{
|
||||
errors::{internal_error, FlowyError},
|
||||
@ -184,8 +184,9 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
|
||||
&self,
|
||||
user_id: &str,
|
||||
view_id: &str,
|
||||
_sub_data_type: Option<SubViewDataTypePB>,
|
||||
layout: ViewLayoutTypePB,
|
||||
) -> FutureResult<Bytes, FlowyError> {
|
||||
debug_assert_eq!(layout, ViewLayoutTypePB::Document);
|
||||
let user_id = user_id.to_string();
|
||||
let view_id = view_id.to_string();
|
||||
let manager = self.0.clone();
|
||||
@ -209,7 +210,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
|
||||
}
|
||||
|
||||
fn data_type(&self) -> ViewDataTypePB {
|
||||
ViewDataTypePB::TextBlock
|
||||
ViewDataTypePB::Text
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,16 +262,20 @@ impl ViewDataProcessor for GridViewDataProcessor {
|
||||
&self,
|
||||
user_id: &str,
|
||||
view_id: &str,
|
||||
sub_data_type: Option<SubViewDataTypePB>,
|
||||
layout: ViewLayoutTypePB,
|
||||
) -> FutureResult<Bytes, FlowyError> {
|
||||
let build_context = match sub_data_type.unwrap() {
|
||||
SubViewDataTypePB::Grid => make_default_grid(),
|
||||
SubViewDataTypePB::Board => make_default_board(),
|
||||
let build_context = match layout {
|
||||
ViewLayoutTypePB::Grid => make_default_grid(),
|
||||
ViewLayoutTypePB::Board => make_default_board(),
|
||||
ViewLayoutTypePB::Document => {
|
||||
return FutureResult::new(async move {
|
||||
Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
|
||||
});
|
||||
}
|
||||
};
|
||||
let user_id = user_id.to_string();
|
||||
let view_id = view_id.to_string();
|
||||
let grid_manager = self.0.clone();
|
||||
|
||||
FutureResult::new(async move { make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await })
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String {
|
||||
filters.push(format!("lib_ot={}", level));
|
||||
filters.push(format!("lib_ws={}", level));
|
||||
filters.push(format!("lib_infra={}", level));
|
||||
filters.push(format!("flowy_sync={}", level));
|
||||
// filters.push(format!("flowy_sync={}", level));
|
||||
// filters.push(format!("flowy_revision={}", level));
|
||||
// filters.push(format!("lib_dispatch={}", level));
|
||||
|
||||
|
@ -25,11 +25,11 @@ pub struct ViewTest {
|
||||
|
||||
impl ViewTest {
|
||||
#[allow(dead_code)]
|
||||
pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataTypePB, data: Vec<u8>) -> Self {
|
||||
pub async fn new(sdk: &FlowySDKTest, data_type: ViewDataTypePB, layout: ViewLayoutTypePB, data: Vec<u8>) -> Self {
|
||||
let workspace = create_workspace(sdk, "Workspace", "").await;
|
||||
open_workspace(sdk, &workspace.id).await;
|
||||
let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
||||
let view = create_view(sdk, &app.id, data_type, data).await;
|
||||
let view = create_view(sdk, &app.id, data_type, layout, data).await;
|
||||
Self {
|
||||
sdk: sdk.clone(),
|
||||
workspace,
|
||||
@ -39,11 +39,15 @@ impl ViewTest {
|
||||
}
|
||||
|
||||
pub async fn new_grid_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewDataTypePB::Database, data).await
|
||||
Self::new(sdk, ViewDataTypePB::Database, ViewLayoutTypePB::Grid, data).await
|
||||
}
|
||||
|
||||
pub async fn new_board_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewDataTypePB::Database, ViewLayoutTypePB::Board, data).await
|
||||
}
|
||||
|
||||
pub async fn new_text_block_view(sdk: &FlowySDKTest) -> Self {
|
||||
Self::new(sdk, ViewDataTypePB::TextBlock, vec![]).await
|
||||
Self::new(sdk, ViewDataTypePB::Text, ViewLayoutTypePB::Document, vec![]).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,16 +94,21 @@ async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &s
|
||||
app
|
||||
}
|
||||
|
||||
async fn create_view(sdk: &FlowySDKTest, app_id: &str, data_type: ViewDataTypePB, data: Vec<u8>) -> ViewPB {
|
||||
async fn create_view(
|
||||
sdk: &FlowySDKTest,
|
||||
app_id: &str,
|
||||
data_type: ViewDataTypePB,
|
||||
layout: ViewLayoutTypePB,
|
||||
data: Vec<u8>,
|
||||
) -> ViewPB {
|
||||
let request = CreateViewPayloadPB {
|
||||
belong_to_id: app_id.to_string(),
|
||||
name: "View A".to_string(),
|
||||
desc: "".to_string(),
|
||||
thumbnail: Some("http://1.png".to_string()),
|
||||
data_type,
|
||||
sub_data_type: None,
|
||||
plugin_type: 0,
|
||||
data,
|
||||
layout,
|
||||
view_content_data: data,
|
||||
};
|
||||
|
||||
let view = FolderEventBuilder::new(sdk.clone())
|
||||
|
@ -9,7 +9,9 @@ pub fn gen_view_id() -> String {
|
||||
pub struct ViewRevision {
|
||||
pub id: String,
|
||||
|
||||
pub belong_to_id: String,
|
||||
// Maybe app_id or vi
|
||||
#[serde(rename = "belong_to_id")]
|
||||
pub app_id: String,
|
||||
|
||||
pub name: String,
|
||||
|
||||
@ -33,9 +35,10 @@ pub struct ViewRevision {
|
||||
pub thumbnail: String,
|
||||
|
||||
#[serde(default = "DEFAULT_PLUGIN_TYPE")]
|
||||
pub plugin_type: i32,
|
||||
#[serde(rename = "plugin_type")]
|
||||
pub layout: ViewLayoutTypeRevision,
|
||||
}
|
||||
const DEFAULT_PLUGIN_TYPE: fn() -> i32 = || 0;
|
||||
const DEFAULT_PLUGIN_TYPE: fn() -> ViewLayoutTypeRevision = || ViewLayoutTypeRevision::Document;
|
||||
|
||||
impl std::convert::From<ViewRevision> for TrashRevision {
|
||||
fn from(view_rev: ViewRevision) -> Self {
|
||||
@ -52,12 +55,27 @@ impl std::convert::From<ViewRevision> for TrashRevision {
|
||||
#[derive(Eq, PartialEq, Debug, Clone, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ViewDataTypeRevision {
|
||||
TextBlock = 0,
|
||||
Text = 0,
|
||||
Database = 1,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewDataTypeRevision {
|
||||
fn default() -> Self {
|
||||
ViewDataTypeRevision::TextBlock
|
||||
ViewDataTypeRevision::Text
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ViewLayoutTypeRevision {
|
||||
Document = 0,
|
||||
// The for historical reasons, the value of Grid is not 1.
|
||||
Grid = 3,
|
||||
Board = 4,
|
||||
}
|
||||
|
||||
impl std::default::Default for ViewLayoutTypeRevision {
|
||||
fn default() -> Self {
|
||||
ViewLayoutTypeRevision::Document
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::revision::{
|
||||
gen_app_id, gen_view_id, gen_workspace_id, AppRevision, ViewDataTypeRevision, ViewRevision, WorkspaceRevision,
|
||||
gen_app_id, gen_view_id, gen_workspace_id, AppRevision, ViewDataTypeRevision, ViewLayoutTypeRevision, ViewRevision,
|
||||
WorkspaceRevision,
|
||||
};
|
||||
use chrono::Utc;
|
||||
|
||||
@ -46,16 +47,16 @@ fn create_default_view(app_id: String, time: chrono::DateTime<Utc>) -> ViewRevis
|
||||
|
||||
ViewRevision {
|
||||
id: view_id,
|
||||
belong_to_id: app_id,
|
||||
app_id,
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
data_type: ViewDataTypeRevision::TextBlock,
|
||||
data_type: ViewDataTypeRevision::Text,
|
||||
version: 0,
|
||||
belongings: vec![],
|
||||
modified_time: time.timestamp(),
|
||||
create_time: time.timestamp(),
|
||||
ext_data: "".to_string(),
|
||||
thumbnail: "".to_string(),
|
||||
plugin_type: 0,
|
||||
layout: ViewLayoutTypeRevision::Document,
|
||||
}
|
||||
}
|
||||
|
@ -42,13 +42,28 @@ impl RowRevision {
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RowMetaChangeset {
|
||||
pub struct RowChangeset {
|
||||
pub row_id: String,
|
||||
pub height: Option<i32>,
|
||||
pub visibility: Option<bool>,
|
||||
pub cell_by_field_id: HashMap<FieldId, CellRevision>,
|
||||
}
|
||||
|
||||
impl RowChangeset {
|
||||
pub fn new(row_id: String) -> Self {
|
||||
Self {
|
||||
row_id,
|
||||
height: None,
|
||||
visibility: None,
|
||||
cell_by_field_id: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_changed(&self) -> bool {
|
||||
self.height.is_some() || self.visibility.is_some() || !self.cell_by_field_id.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct CellRevision {
|
||||
pub data: String,
|
||||
|
@ -202,7 +202,7 @@ impl FolderPad {
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self), fields(view_name=%view_rev.name), err)]
|
||||
pub fn create_view(&mut self, view_rev: ViewRevision) -> CollaborateResult<Option<FolderChangeset>> {
|
||||
let app_id = view_rev.belong_to_id.clone();
|
||||
let app_id = view_rev.app_id.clone();
|
||||
self.with_app(&app_id, move |app| {
|
||||
if app.belongings.contains(&view_rev) {
|
||||
tracing::warn!("[RootFolder]: Duplicate view");
|
||||
@ -243,7 +243,7 @@ impl FolderPad {
|
||||
modified_time: i64,
|
||||
) -> CollaborateResult<Option<FolderChangeset>> {
|
||||
let view = self.read_view(view_id)?;
|
||||
self.with_view(&view.belong_to_id, view_id, |view| {
|
||||
self.with_view(&view.app_id, view_id, |view| {
|
||||
if let Some(name) = name {
|
||||
view.name = name;
|
||||
}
|
||||
@ -260,7 +260,7 @@ impl FolderPad {
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult<Option<FolderChangeset>> {
|
||||
let view = self.read_view(view_id)?;
|
||||
self.with_app(&view.belong_to_id, |app| {
|
||||
self.with_app(&view.app_id, |app| {
|
||||
app.belongings.retain(|view| view.id != view_id);
|
||||
Ok(Some(()))
|
||||
})
|
||||
@ -269,7 +269,7 @@ impl FolderPad {
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub fn move_view(&mut self, view_id: &str, from: usize, to: usize) -> CollaborateResult<Option<FolderChangeset>> {
|
||||
let view = self.read_view(view_id)?;
|
||||
self.with_app(&view.belong_to_id, |app| {
|
||||
self.with_app(&view.app_id, |app| {
|
||||
match move_vec_element(&mut app.belongings, |view| view.id == view_id, from, to).map_err(internal_error)? {
|
||||
true => Ok(Some(())),
|
||||
false => Ok(None),
|
||||
@ -807,7 +807,7 @@ mod tests {
|
||||
fn test_view_folder() -> (FolderPad, FolderDelta, ViewRevision) {
|
||||
let (mut folder, mut initial_delta, app) = test_app_folder();
|
||||
let mut view_rev = ViewRevision::default();
|
||||
view_rev.belong_to_id = app.id.clone();
|
||||
view_rev.app_id = app.id.clone();
|
||||
view_rev.name = "🎃 my first view".to_owned();
|
||||
|
||||
initial_delta = initial_delta
|
||||
|
@ -2,7 +2,7 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
||||
use crate::errors::{CollaborateError, CollaborateResult};
|
||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
||||
use flowy_grid_data_model::revision::{
|
||||
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
|
||||
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowChangeset, RowRevision,
|
||||
};
|
||||
use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
|
||||
use std::borrow::Cow;
|
||||
@ -143,7 +143,7 @@ impl GridBlockRevisionPad {
|
||||
self.block.rows.iter().position(|row| row.id == row_id)
|
||||
}
|
||||
|
||||
pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult<Option<GridBlockRevisionChangeset>> {
|
||||
pub fn update_row(&mut self, changeset: RowChangeset) -> CollaborateResult<Option<GridBlockRevisionChangeset>> {
|
||||
let row_id = changeset.row_id.clone();
|
||||
self.modify_row(&row_id, |row| {
|
||||
let mut is_changed = None;
|
||||
@ -178,8 +178,12 @@ impl GridBlockRevisionPad {
|
||||
if let Some(position) = row_revs.iter().position(|row_rev| row_rev.id == row_id) {
|
||||
debug_assert_eq!(from, position);
|
||||
let row_rev = row_revs.remove(position);
|
||||
if to > row_revs.len() {
|
||||
Err(CollaborateError::out_of_bound())
|
||||
} else {
|
||||
row_revs.insert(to, row_rev);
|
||||
Ok(Some(()))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@ -200,10 +204,6 @@ impl GridBlockRevisionPad {
|
||||
None => Ok(None),
|
||||
Some(delta) => {
|
||||
tracing::trace!("[GridBlockRevision] Composing delta {}", delta.json_str());
|
||||
// tracing::debug!(
|
||||
// "[GridBlockMeta] current delta: {}",
|
||||
// self.delta.to_str().unwrap_or_else(|_| "".to_string())
|
||||
// );
|
||||
self.delta = self.delta.compose(&delta)?;
|
||||
Ok(Some(GridBlockRevisionChangeset {
|
||||
delta,
|
||||
@ -275,7 +275,7 @@ impl std::default::Default for GridBlockRevisionPad {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::client_grid::GridBlockRevisionPad;
|
||||
use flowy_grid_data_model::revision::{RowMetaChangeset, RowRevision};
|
||||
use flowy_grid_data_model::revision::{RowChangeset, RowRevision};
|
||||
use lib_ot::core::TextDelta;
|
||||
use std::borrow::Cow;
|
||||
|
||||
@ -400,7 +400,7 @@ mod tests {
|
||||
visibility: false,
|
||||
};
|
||||
|
||||
let changeset = RowMetaChangeset {
|
||||
let changeset = RowChangeset {
|
||||
row_id: row.id.clone(),
|
||||
height: Some(100),
|
||||
visibility: Some(true),
|
||||
|
Loading…
Reference in New Issue
Block a user