mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: save text cell data
This commit is contained in:
parent
c77a31806d
commit
1f30a77f1d
@ -61,12 +61,6 @@ abstract class PluginConfig {
|
||||
}
|
||||
|
||||
abstract class PluginDisplay<T> with NavigationItem {
|
||||
@override
|
||||
Widget get leftBarItem;
|
||||
|
||||
@override
|
||||
Widget? get rightBarItem;
|
||||
|
||||
List<NavigationItem> get navigationItems;
|
||||
|
||||
PublishNotifier<T>? get notifier => null;
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_flowy/user/application/user_settings_service.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
@ -11,7 +13,7 @@ class AppearanceSettingModel extends ChangeNotifier with EquatableMixin {
|
||||
AppearanceSettings setting;
|
||||
AppTheme _theme;
|
||||
Locale _locale;
|
||||
CancelableOperation? _saveOperation;
|
||||
Timer? _saveOperation;
|
||||
|
||||
AppearanceSettingModel(this.setting)
|
||||
: _theme = AppTheme.fromName(name: setting.theme),
|
||||
@ -21,12 +23,10 @@ class AppearanceSettingModel extends ChangeNotifier with EquatableMixin {
|
||||
Locale get locale => _locale;
|
||||
|
||||
Future<void> save() async {
|
||||
_saveOperation?.cancel;
|
||||
_saveOperation = CancelableOperation.fromFuture(
|
||||
Future.delayed(const Duration(seconds: 1), () async {
|
||||
await UserSettingsService().setAppearanceSettings(setting);
|
||||
}),
|
||||
);
|
||||
_saveOperation?.cancel();
|
||||
_saveOperation = Timer(const Duration(seconds: 2), () async {
|
||||
await UserSettingsService().setAppearanceSettings(setting);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -17,6 +17,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
initial: (_InitialCell value) async {},
|
||||
updateText: (_UpdateText value) {
|
||||
service.updateCell(data: value.text);
|
||||
emit(state.copyWith(content: value.text));
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
|
||||
@ -32,7 +33,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
delete: (_Delete value) {},
|
||||
rename: (_Rename value) {},
|
||||
updateDesc: (_Desc value) {},
|
||||
didLoadRows: (_DidLoadRows value) {
|
||||
rowsDidUpdate: (_RowsDidUpdate value) {
|
||||
emit(state.copyWith(rows: value.rows));
|
||||
},
|
||||
);
|
||||
@ -47,10 +48,19 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
Future<void> _startGridListening() async {
|
||||
_blockService.didLoadRowscallback = (rows) {
|
||||
add(GridEvent.didLoadRows(rows));
|
||||
};
|
||||
Future<void> _initGridBlockService(Grid grid, List<Field> fields) async {
|
||||
_blockService = GridBlockService(
|
||||
gridId: grid.id,
|
||||
fields: fields,
|
||||
blockOrders: grid.blockOrders,
|
||||
);
|
||||
|
||||
_blockService.rowsUpdateNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(rows) => add(GridEvent.rowsDidUpdate(rows)),
|
||||
(err) => Log.error('$err'),
|
||||
);
|
||||
});
|
||||
|
||||
_gridListener.start();
|
||||
}
|
||||
@ -70,36 +80,18 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
final result = await service.getFields(gridId: grid.id, fieldOrders: grid.fieldOrders);
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(fields) => _loadGridBlocks(grid, fields.items, emit),
|
||||
(fields) {
|
||||
_initGridBlockService(grid, fields.items);
|
||||
emit(state.copyWith(
|
||||
grid: Some(grid),
|
||||
fields: Some(fields.items),
|
||||
loadingState: GridLoadingState.finish(left(unit)),
|
||||
));
|
||||
},
|
||||
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadGridBlocks(Grid grid, List<Field> fields, Emitter<GridState> emit) async {
|
||||
final result = await service.getGridBlocks(gridId: grid.id, blockOrders: grid.blockOrders);
|
||||
result.fold(
|
||||
(repeatedGridBlock) {
|
||||
final gridBlocks = repeatedGridBlock.items;
|
||||
final gridId = view.id;
|
||||
_blockService = GridBlockService(
|
||||
gridId: gridId,
|
||||
fields: fields,
|
||||
gridBlocks: gridBlocks,
|
||||
);
|
||||
final rows = _blockService.rows();
|
||||
|
||||
_startGridListening();
|
||||
emit(state.copyWith(
|
||||
grid: Some(grid),
|
||||
fields: Some(fields),
|
||||
rows: rows,
|
||||
loadingState: GridLoadingState.finish(left(unit)),
|
||||
));
|
||||
},
|
||||
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)), rows: [])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -109,7 +101,7 @@ abstract class GridEvent with _$GridEvent {
|
||||
const factory GridEvent.updateDesc(String gridId, String desc) = _Desc;
|
||||
const factory GridEvent.delete(String gridId) = _Delete;
|
||||
const factory GridEvent.createRow() = _CreateRow;
|
||||
const factory GridEvent.didLoadRows(List<GridRowData> rows) = _DidLoadRows;
|
||||
const factory GridEvent.rowsDidUpdate(List<GridRowData> rows) = _RowsDidUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:collection';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/subject.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
@ -9,40 +11,38 @@ import 'package:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:app_flowy/core/notification_helper.dart';
|
||||
|
||||
import 'grid_service.dart';
|
||||
|
||||
typedef DidLoadRowsCallback = void Function(List<GridRowData>);
|
||||
typedef GridBlockUpdateNotifiedValue = Either<GridBlockId, FlowyError>;
|
||||
typedef RowsUpdateNotifierValue = Either<List<GridRowData>, FlowyError>;
|
||||
|
||||
class GridBlockService {
|
||||
String gridId;
|
||||
List<Field> fields;
|
||||
LinkedHashMap<String, GridBlock> blockMap = LinkedHashMap();
|
||||
late GridBlockListener _blockListener;
|
||||
DidLoadRowsCallback? didLoadRowscallback;
|
||||
PublishNotifier<RowsUpdateNotifierValue> rowsUpdateNotifier = PublishNotifier<RowsUpdateNotifierValue>();
|
||||
|
||||
GridBlockService({required this.gridId, required this.fields, required List<GridBlock> gridBlocks}) {
|
||||
for (final gridBlock in gridBlocks) {
|
||||
blockMap[gridBlock.blockId] = gridBlock;
|
||||
}
|
||||
GridBlockService({required this.gridId, required this.fields, required List<GridBlockOrder> blockOrders}) {
|
||||
_loadGridBlocks(blockOrders: blockOrders);
|
||||
|
||||
_blockListener = GridBlockListener(gridId: gridId);
|
||||
_blockListener.blockUpdateNotifier.addPublishListener((result) {
|
||||
result.fold((blockId) {
|
||||
//
|
||||
}, (err) => null);
|
||||
_blockListener.rowsUpdateNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(blockId) => _loadGridBlocks(blockOrders: [GridBlockOrder.create()..blockId = blockId.value]),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_blockListener.start();
|
||||
}
|
||||
|
||||
List<GridRowData> rows() {
|
||||
List<GridRowData> buildRows() {
|
||||
List<GridRowData> rows = [];
|
||||
blockMap.forEach((_, GridBlock gridBlock) {
|
||||
rows.addAll(gridBlock.rowOrders.map(
|
||||
(rowOrder) => GridRowData(
|
||||
gridId: gridId,
|
||||
fields: fields,
|
||||
blockId: gridBlock.blockId,
|
||||
blockId: gridBlock.id,
|
||||
rowId: rowOrder.rowId,
|
||||
height: rowOrder.height.toDouble(),
|
||||
),
|
||||
@ -54,11 +54,29 @@ class GridBlockService {
|
||||
Future<void> stop() async {
|
||||
await _blockListener.stop();
|
||||
}
|
||||
|
||||
void _loadGridBlocks({required List<GridBlockOrder> blockOrders}) {
|
||||
final payload = QueryGridBlocksPayload.create()
|
||||
..gridId = gridId
|
||||
..blockOrders.addAll(blockOrders);
|
||||
|
||||
GridEventGetGridBlocks(payload).send().then((result) {
|
||||
result.fold(
|
||||
(repeatedBlocks) {
|
||||
for (final gridBlock in repeatedBlocks.items) {
|
||||
blockMap[gridBlock.id] = gridBlock;
|
||||
}
|
||||
rowsUpdateNotifier.value = left(buildRows());
|
||||
},
|
||||
(err) => rowsUpdateNotifier.value = right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class GridBlockListener {
|
||||
final String gridId;
|
||||
PublishNotifier<GridBlockUpdateNotifiedValue> blockUpdateNotifier = PublishNotifier<GridBlockUpdateNotifiedValue>();
|
||||
PublishNotifier<Either<GridBlockId, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
late GridNotificationParser _parser;
|
||||
|
||||
@ -77,10 +95,10 @@ class GridBlockListener {
|
||||
|
||||
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridNotification.GridDidUpdateBlock:
|
||||
case GridNotification.BlockDidUpdateRow:
|
||||
result.fold(
|
||||
(payload) => blockUpdateNotifier.value = left(GridBlockId.fromBuffer(payload)),
|
||||
(error) => blockUpdateNotifier.value = right(error),
|
||||
(payload) => rowsUpdateNotifier.value = left(GridBlockId.fromBuffer(payload)),
|
||||
(error) => rowsUpdateNotifier.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
||||
@ -91,6 +109,6 @@ class GridBlockListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _subscription?.cancel();
|
||||
blockUpdateNotifier.dispose();
|
||||
rowsUpdateNotifier.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ class GridService {
|
||||
return GridEventGetGridData(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Row, FlowyError>> createRow({required String gridId, Option<String>? upperRowId}) {
|
||||
Future<Either<Row, FlowyError>> createRow({required String gridId, Option<String>? startRowId}) {
|
||||
CreateRowPayload payload = CreateRowPayload.create()..gridId = gridId;
|
||||
upperRowId?.fold(() => null, (id) => payload.startRowId = id);
|
||||
startRowId?.fold(() => null, (id) => payload.startRowId = id);
|
||||
return GridEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
await event.map(
|
||||
initial: (_InitialRow value) async {
|
||||
_startRowListening();
|
||||
await _loadCellDatas(emit);
|
||||
await _loadRow(emit);
|
||||
},
|
||||
createRow: (_CreateRow value) {
|
||||
rowService.createRow();
|
||||
@ -58,20 +58,20 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
listener.start();
|
||||
}
|
||||
|
||||
Future<void> _loadCellDatas(Emitter<RowState> emit) async {
|
||||
final result = await rowService.getRow();
|
||||
result.fold(
|
||||
(row) {
|
||||
emit(state.copyWith(
|
||||
cellDatas: makeGridCellDatas(row),
|
||||
rowHeight: row.height.toDouble(),
|
||||
));
|
||||
},
|
||||
(e) => Log.error(e),
|
||||
);
|
||||
Future<void> _loadRow(Emitter<RowState> emit) async {
|
||||
final Future<List<GridCellData>> cellDatas = rowService.getRow().then((result) {
|
||||
return result.fold(
|
||||
(row) => _makeCellDatas(row),
|
||||
(e) {
|
||||
Log.error(e);
|
||||
return [];
|
||||
},
|
||||
);
|
||||
});
|
||||
emit(state.copyWith(cellDatas: cellDatas));
|
||||
}
|
||||
|
||||
List<GridCellData> makeGridCellDatas(Row row) {
|
||||
List<GridCellData> _makeCellDatas(Row row) {
|
||||
return rowService.rowData.fields.map((field) {
|
||||
final cell = row.cellByFieldId[field.id];
|
||||
final rowData = rowService.rowData;
|
||||
@ -96,18 +96,18 @@ abstract class RowEvent with _$RowEvent {
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class RowState with _$RowState {
|
||||
class RowState with _$RowState {
|
||||
const factory RowState({
|
||||
required String rowId,
|
||||
required double rowHeight,
|
||||
required List<GridCellData> cellDatas,
|
||||
required Future<List<GridCellData>> cellDatas,
|
||||
required bool active,
|
||||
}) = _RowState;
|
||||
|
||||
factory RowState.initial(GridRowData data) => RowState(
|
||||
rowId: data.rowId,
|
||||
active: false,
|
||||
rowHeight: data.height,
|
||||
cellDatas: [],
|
||||
cellDatas: Future(() => []),
|
||||
active: false,
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
@ -64,7 +66,7 @@ class ViewSectionNotifier with ChangeNotifier {
|
||||
bool isDisposed = false;
|
||||
List<View> _views;
|
||||
View? _selectedView;
|
||||
CancelableOperation? _notifyListenerOperation;
|
||||
Timer? _notifyListenerOperation;
|
||||
|
||||
ViewSectionNotifier({
|
||||
required BuildContext context,
|
||||
@ -120,9 +122,7 @@ class ViewSectionNotifier with ChangeNotifier {
|
||||
|
||||
void _notifyListeners() {
|
||||
_notifyListenerOperation?.cancel();
|
||||
_notifyListenerOperation = CancelableOperation.fromFuture(
|
||||
Future.delayed(const Duration(milliseconds: 30), () {}),
|
||||
).then((_) {
|
||||
_notifyListenerOperation = Timer(const Duration(milliseconds: 30), () {
|
||||
if (!isDisposed) {
|
||||
notifyListeners();
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class BlankPagePlugin extends Plugin {
|
||||
PluginType get ty => _pluginType;
|
||||
}
|
||||
|
||||
class BlankPagePluginDisplay extends PluginDisplay {
|
||||
class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
|
||||
@override
|
||||
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr(), fontSize: 12);
|
||||
|
||||
|
@ -10,14 +10,13 @@ import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/appearance.dart';
|
||||
import 'package:app_flowy/workspace/application/doc/share_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/view/view_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
@ -89,7 +88,7 @@ class DocumentPlugin implements Plugin {
|
||||
PluginId get id => _view.id;
|
||||
}
|
||||
|
||||
class DocumentPluginDisplay extends PluginDisplay<int> {
|
||||
class DocumentPluginDisplay extends PluginDisplay<int> with NavigationItem {
|
||||
final PublishNotifier<int> _displayNotifier = PublishNotifier<int>();
|
||||
final View _view;
|
||||
|
||||
@ -99,91 +98,16 @@ class DocumentPluginDisplay extends PluginDisplay<int> {
|
||||
Widget buildWidget() => DocumentPage(view: _view, key: ValueKey(_view.id));
|
||||
|
||||
@override
|
||||
Widget get leftBarItem => DocumentLeftBarItem(view: _view);
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: _view);
|
||||
|
||||
@override
|
||||
Widget? get rightBarItem => DocumentShareButton(view: _view);
|
||||
|
||||
@override
|
||||
List<NavigationItem> get navigationItems => _makeNavigationItems();
|
||||
List<NavigationItem> get navigationItems => [this];
|
||||
|
||||
@override
|
||||
PublishNotifier<int>? get notifier => _displayNotifier;
|
||||
|
||||
List<NavigationItem> _makeNavigationItems() {
|
||||
return [
|
||||
this,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentLeftBarItem extends StatefulWidget {
|
||||
final View view;
|
||||
|
||||
DocumentLeftBarItem({required this.view, Key? key}) : super(key: ValueKey(view.hashCode));
|
||||
|
||||
@override
|
||||
State<DocumentLeftBarItem> createState() => _DocumentLeftBarItemState();
|
||||
}
|
||||
|
||||
class _DocumentLeftBarItemState extends State<DocumentLeftBarItem> {
|
||||
final _controller = TextEditingController();
|
||||
final _focusNode = FocusNode();
|
||||
late ViewService service;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
service = ViewService(/*view: widget.view*/);
|
||||
_focusNode.addListener(_handleFocusChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.removeListener(_handleFocusChanged);
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_controller.text = widget.view.name;
|
||||
|
||||
final theme = context.watch<AppTheme>();
|
||||
return IntrinsicWidth(
|
||||
key: ValueKey(_controller.text),
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
scrollPadding: EdgeInsets.zero,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.textColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
// cursorColor: widget.cursorColor,
|
||||
// obscureText: widget.enableObscure,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleFocusChanged() {
|
||||
if (_controller.text.isEmpty) {
|
||||
_controller.text = widget.view.name;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller.text != widget.view.name) {
|
||||
service.updateView(viewId: widget.view.id, name: _controller.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentShareButton extends StatelessWidget {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:app_flowy/plugin/plugin.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -28,7 +28,7 @@ class GridPluginBuilder implements PluginBuilder {
|
||||
|
||||
class GridPluginConfig implements PluginConfig {
|
||||
@override
|
||||
bool get creatable => false;
|
||||
bool get creatable => true;
|
||||
}
|
||||
|
||||
class GridPlugin extends Plugin {
|
||||
@ -56,7 +56,7 @@ class GridPluginDisplay extends PluginDisplay {
|
||||
GridPluginDisplay({required View view, Key? key}) : _view = view;
|
||||
|
||||
@override
|
||||
Widget get leftBarItem => const FlowyText.medium("Grid demo", fontSize: 12);
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: _view);
|
||||
|
||||
@override
|
||||
Widget buildWidget() => GridPage(view: _view);
|
||||
|
@ -84,42 +84,38 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
builder: (context, state) {
|
||||
return state.fields.fold(
|
||||
() => const Center(child: CircularProgressIndicator.adaptive()),
|
||||
(fields) => _renderGrid(context, fields),
|
||||
(fields) => _wrapScrollbar(fields, [
|
||||
_buildHeader(fields),
|
||||
_buildRows(context),
|
||||
const GridFooter(),
|
||||
]),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderGrid(BuildContext context, List<Field> fields) {
|
||||
return Stack(
|
||||
children: [
|
||||
StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: GridLayout.headerWidth(fields),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: <Widget>[
|
||||
_buildHeader(fields),
|
||||
_buildRows(context),
|
||||
const GridFooter(),
|
||||
],
|
||||
),
|
||||
Widget _wrapScrollbar(List<Field> fields, List<Widget> children) {
|
||||
return ScrollbarListStack(
|
||||
axis: Axis.vertical,
|
||||
controller: _scrollController.verticalController,
|
||||
barSize: GridSize.scrollBarSize,
|
||||
child: StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: GridLayout.headerWidth(fields),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: <Widget>[...children],
|
||||
),
|
||||
),
|
||||
ScrollbarListStack(
|
||||
axis: Axis.vertical,
|
||||
controller: _scrollController.verticalController,
|
||||
barSize: GridSize.scrollBarSize,
|
||||
child: Container(),
|
||||
).padding(right: 0, top: GridSize.headerHeight, bottom: GridSize.scrollBarSize),
|
||||
],
|
||||
);
|
||||
),
|
||||
).padding(right: 0, top: GridSize.headerHeight, bottom: GridSize.scrollBarSize);
|
||||
}
|
||||
|
||||
Widget _buildHeader(List<Field> fields) {
|
||||
@ -131,15 +127,21 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
}
|
||||
|
||||
Widget _buildRows(BuildContext context) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final rowData = context.read<GridBloc>().state.rows[index];
|
||||
return GridRowWidget(data: rowData);
|
||||
},
|
||||
childCount: context.read<GridBloc>().state.rows.length,
|
||||
addRepaintBoundaries: true,
|
||||
),
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) => previous.rows.length != current.rows.length,
|
||||
builder: (context, state) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final rowData = context.read<GridBloc>().state.rows[index];
|
||||
return GridRowWidget(data: rowData);
|
||||
},
|
||||
childCount: context.read<GridBloc>().state.rows.length,
|
||||
addRepaintBoundaries: true,
|
||||
addAutomaticKeepAlives: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import 'cell_container.dart';
|
||||
|
||||
class GridRowWidget extends StatefulWidget {
|
||||
final GridRowData data;
|
||||
GridRowWidget({required this.data, Key? key}) : super(key: ObjectKey(data.rowId));
|
||||
GridRowWidget({required this.data, Key? key}) : super(key: ValueKey(data.rowId));
|
||||
|
||||
@override
|
||||
State<GridRowWidget> createState() => _GridRowWidgetState();
|
||||
@ -30,28 +30,25 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _rowBloc,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
|
||||
onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
|
||||
child: BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (p, c) => p.rowHeight != c.rowHeight,
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: _rowBloc.state.rowHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const LeadingRow(),
|
||||
_buildCells(),
|
||||
const TrailingRow(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
|
||||
onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
|
||||
child: BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (p, c) => p.rowHeight != c.rowHeight,
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: _rowBloc.state.rowHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: const [
|
||||
_RowLeading(),
|
||||
_RowCells(),
|
||||
_RowTrailing(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -62,69 +59,37 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
_rowBloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildCells() {
|
||||
return BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (p, c) => p.cellDatas != c.cellDatas,
|
||||
builder: (context, state) {
|
||||
return Row(
|
||||
children: state.cellDatas
|
||||
.map(
|
||||
(cellData) => CellContainer(
|
||||
width: cellData.field.width.toDouble(),
|
||||
child: buildGridCell(cellData),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LeadingRow extends StatelessWidget {
|
||||
const LeadingRow({Key? key}) : super(key: key);
|
||||
class _RowLeading extends StatelessWidget {
|
||||
const _RowLeading({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<RowBloc, RowState, bool>(
|
||||
selector: (state) => state.active,
|
||||
builder: (context, isActive) {
|
||||
return SizedBox(
|
||||
width: GridSize.leadingHeaderPadding,
|
||||
child: isActive
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
AppendRowButton(),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
);
|
||||
return SizedBox(width: GridSize.leadingHeaderPadding, child: isActive ? _activeWidget() : null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _activeWidget() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
AppendRowButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrailingRow extends StatelessWidget {
|
||||
const TrailingRow({Key? key}) : super(key: key);
|
||||
class _RowTrailing extends StatelessWidget {
|
||||
const _RowTrailing({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
|
||||
|
||||
return BlocBuilder<RowBloc, RowState>(
|
||||
builder: (context, state) {
|
||||
return Container(
|
||||
width: GridSize.trailHeaderPadding,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: borderSide),
|
||||
),
|
||||
padding: GridSize.cellContentInsets,
|
||||
);
|
||||
},
|
||||
);
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,3 +108,37 @@ class AppendRowButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RowCells extends StatelessWidget {
|
||||
const _RowCells({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RowBloc, RowState>(
|
||||
buildWhen: (previous, current) => previous.cellDatas != current.cellDatas,
|
||||
builder: (context, state) {
|
||||
return FutureBuilder(
|
||||
future: state.cellDatas,
|
||||
builder: builder,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget builder(context, AsyncSnapshot<dynamic> snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
List<GridCellData> cellDatas = snapshot.data;
|
||||
return Row(children: cellDatas.map(_toCell).toList());
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _toCell(GridCellData data) {
|
||||
return CellContainer(
|
||||
width: data.field.width.toDouble(),
|
||||
child: buildGridCell(data),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/text_cell_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -19,27 +22,40 @@ class GridTextCell extends StatefulWidget {
|
||||
|
||||
class _GridTextCellState extends State<GridTextCell> {
|
||||
late TextEditingController _controller;
|
||||
Timer? _delayOperation;
|
||||
final _focusNode = FocusNode();
|
||||
late TextCellBloc _cellBloc;
|
||||
TextCellBloc? _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = getIt<TextCellBloc>(param1: widget.cellData);
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode.addListener(_focusChanged);
|
||||
_controller = TextEditingController(text: _cellBloc!.state.content);
|
||||
_focusNode.addListener(save);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
value: _cellBloc!,
|
||||
child: BlocBuilder<TextCellBloc, TextCellState>(
|
||||
buildWhen: (previous, current) {
|
||||
return _controller.text != current.content;
|
||||
},
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
onChanged: (value) {},
|
||||
onChanged: (value) {
|
||||
Log.info("On change");
|
||||
save();
|
||||
},
|
||||
onEditingComplete: () {
|
||||
Log.info("On complete");
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
Log.info("On submit");
|
||||
},
|
||||
maxLines: 1,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
decoration: const InputDecoration(
|
||||
@ -55,13 +71,18 @@ class _GridTextCellState extends State<GridTextCell> {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_cellBloc.close();
|
||||
_focusNode.removeListener(_focusChanged);
|
||||
_cellBloc?.close();
|
||||
_cellBloc = null;
|
||||
_focusNode.removeListener(save);
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _focusChanged() {
|
||||
_cellBloc.add(TextCellEvent.updateText(_controller.text));
|
||||
Future<void> save() async {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(seconds: 2), () {
|
||||
_cellBloc?.add(TextCellEvent.updateText(_controller.text));
|
||||
});
|
||||
// and later, before the timer goes off...
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ class GridHeader extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<ColumnBloc>(param1: fields)..add(const ColumnEvent.initial()),
|
||||
child: BlocBuilder<ColumnBloc, ColumnState>(
|
||||
@ -55,13 +56,16 @@ class GridHeader extends StatelessWidget {
|
||||
)
|
||||
.toList();
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const LeadingHeaderCell(),
|
||||
...headers,
|
||||
const TrailingHeaderCell(),
|
||||
],
|
||||
return Container(
|
||||
color: theme.surface,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const LeadingHeaderCell(),
|
||||
...headers,
|
||||
const TrailingHeaderCell(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -0,0 +1,74 @@
|
||||
import 'package:app_flowy/workspace/application/view/view_service.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ViewLeftBarItem extends StatefulWidget {
|
||||
final View view;
|
||||
|
||||
ViewLeftBarItem({required this.view, Key? key}) : super(key: ValueKey(view.hashCode));
|
||||
|
||||
@override
|
||||
State<ViewLeftBarItem> createState() => _ViewLeftBarItemState();
|
||||
}
|
||||
|
||||
class _ViewLeftBarItemState extends State<ViewLeftBarItem> {
|
||||
final _controller = TextEditingController();
|
||||
final _focusNode = FocusNode();
|
||||
late ViewService serviceService;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
serviceService = ViewService(/*view: widget.view*/);
|
||||
_focusNode.addListener(_handleFocusChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.removeListener(_handleFocusChanged);
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_controller.text = widget.view.name;
|
||||
|
||||
final theme = context.watch<AppTheme>();
|
||||
return IntrinsicWidth(
|
||||
key: ValueKey(_controller.text),
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
scrollPadding: EdgeInsets.zero,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: theme.textColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
// cursorColor: widget.cursorColor,
|
||||
// obscureText: widget.enableObscure,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleFocusChanged() {
|
||||
if (_controller.text.isEmpty) {
|
||||
_controller.text = widget.view.name;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller.text != widget.view.name) {
|
||||
serviceService.updateView(viewId: widget.view.id, name: _controller.text);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class Comparable<T> {
|
||||
bool compare(T? previous, T? current);
|
||||
}
|
||||
|
||||
class ObjectComparable<T> extends Comparable<T> {
|
||||
@override
|
||||
bool compare(T? previous, T? current) {
|
||||
return previous == current;
|
||||
}
|
||||
}
|
||||
|
||||
class PublishNotifier<T> extends ChangeNotifier {
|
||||
T? _value;
|
||||
Comparable<T>? comparable = ObjectComparable();
|
||||
|
||||
PublishNotifier({this.comparable});
|
||||
|
||||
set value(T newValue) {
|
||||
if (_value != newValue) {
|
||||
if (comparable != null) {
|
||||
if (comparable!.compare(_value, newValue)) {
|
||||
_value = newValue;
|
||||
notifyListeners();
|
||||
}
|
||||
} else {
|
||||
_value = newValue;
|
||||
notifyListeners();
|
||||
}
|
||||
|
@ -609,19 +609,19 @@ class GridBlockOrder extends $pb.GeneratedMessage {
|
||||
|
||||
class GridBlock extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlock', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
|
||||
..pc<RowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrders', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
GridBlock._() : super();
|
||||
factory GridBlock({
|
||||
$core.String? blockId,
|
||||
$core.String? id,
|
||||
$core.Iterable<RowOrder>? rowOrders,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (blockId != null) {
|
||||
_result.blockId = blockId;
|
||||
if (id != null) {
|
||||
_result.id = id;
|
||||
}
|
||||
if (rowOrders != null) {
|
||||
_result.rowOrders.addAll(rowOrders);
|
||||
@ -650,13 +650,13 @@ class GridBlock extends $pb.GeneratedMessage {
|
||||
static GridBlock? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get blockId => $_getSZ(0);
|
||||
$core.String get id => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set blockId($core.String v) { $_setString(0, v); }
|
||||
set id($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasBlockId() => $_has(0);
|
||||
$core.bool hasId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearBlockId() => clearField(1);
|
||||
void clearId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<RowOrder> get rowOrders => $_getList(1);
|
||||
|
@ -135,13 +135,13 @@ final $typed_data.Uint8List gridBlockOrderDescriptor = $convert.base64Decode('Cg
|
||||
const GridBlock$json = const {
|
||||
'1': 'GridBlock',
|
||||
'2': const [
|
||||
const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
|
||||
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
|
||||
const {'1': 'row_orders', '3': 2, '4': 3, '5': 11, '6': '.RowOrder', '10': 'rowOrders'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridBlock`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSGQoIYmxvY2tfaWQYASABKAlSB2Jsb2NrSWQSKAoKcm93X29yZGVycxgCIAMoCzIJLlJvd09yZGVyUglyb3dPcmRlcnM=');
|
||||
final $typed_data.Uint8List gridBlockDescriptor = $convert.base64Decode('CglHcmlkQmxvY2sSDgoCaWQYASABKAlSAmlkEigKCnJvd19vcmRlcnMYAiADKAsyCS5Sb3dPcmRlclIJcm93T3JkZXJz');
|
||||
@$core.Deprecated('Use cellDescriptor instead')
|
||||
const Cell$json = const {
|
||||
'1': 'Cell',
|
||||
|
@ -11,15 +11,15 @@ import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class GridNotification extends $pb.ProtobufEnum {
|
||||
static const GridNotification Unknown = GridNotification._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
|
||||
static const GridNotification GridDidUpdateBlock = GridNotification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateBlock');
|
||||
static const GridNotification GridDidCreateBlock = GridNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidCreateBlock');
|
||||
static const GridNotification GridDidUpdateCells = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateCells');
|
||||
static const GridNotification GridDidUpdateFields = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateFields');
|
||||
static const GridNotification BlockDidUpdateRow = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'BlockDidUpdateRow');
|
||||
static const GridNotification GridDidUpdateCells = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateCells');
|
||||
static const GridNotification GridDidUpdateFields = GridNotification._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GridDidUpdateFields');
|
||||
|
||||
static const $core.List<GridNotification> values = <GridNotification> [
|
||||
Unknown,
|
||||
GridDidUpdateBlock,
|
||||
GridDidCreateBlock,
|
||||
BlockDidUpdateRow,
|
||||
GridDidUpdateCells,
|
||||
GridDidUpdateFields,
|
||||
];
|
||||
|
@ -13,12 +13,12 @@ const GridNotification$json = const {
|
||||
'1': 'GridNotification',
|
||||
'2': const [
|
||||
const {'1': 'Unknown', '2': 0},
|
||||
const {'1': 'GridDidUpdateBlock', '2': 10},
|
||||
const {'1': 'GridDidCreateBlock', '2': 11},
|
||||
const {'1': 'GridDidUpdateCells', '2': 20},
|
||||
const {'1': 'GridDidUpdateFields', '2': 30},
|
||||
const {'1': 'BlockDidUpdateRow', '2': 20},
|
||||
const {'1': 'GridDidUpdateCells', '2': 30},
|
||||
const {'1': 'GridDidUpdateFields', '2': 40},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABIWChJHcmlkRGlkVXBkYXRlQmxvY2sQChIWChJHcmlkRGlkQ3JlYXRlQmxvY2sQCxIWChJHcmlkRGlkVXBkYXRlQ2VsbHMQFBIXChNHcmlkRGlkVXBkYXRlRmllbGRzEB4=');
|
||||
final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABIWChJHcmlkRGlkQ3JlYXRlQmxvY2sQCxIVChFCbG9ja0RpZFVwZGF0ZVJvdxAUEhYKEkdyaWREaWRVcGRhdGVDZWxscxAeEhcKE0dyaWREaWRVcGRhdGVGaWVsZHMQKA==');
|
||||
|
@ -5,11 +5,13 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
|
||||
#[derive(ProtoBuf_Enum, Debug)]
|
||||
pub enum GridNotification {
|
||||
Unknown = 0,
|
||||
GridDidUpdateBlock = 10,
|
||||
|
||||
GridDidCreateBlock = 11,
|
||||
|
||||
GridDidUpdateCells = 20,
|
||||
GridDidUpdateFields = 30,
|
||||
BlockDidUpdateRow = 20,
|
||||
|
||||
GridDidUpdateCells = 30,
|
||||
GridDidUpdateFields = 40,
|
||||
}
|
||||
|
||||
impl std::default::Default for GridNotification {
|
||||
|
@ -83,7 +83,7 @@ impl GridManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
// #[tracing::instrument(level = "debug", skip(self), err)]
|
||||
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
|
||||
match self.editor_map.get(grid_id) {
|
||||
None => Err(FlowyError::internal().context("Should call open_grid function first")),
|
||||
|
@ -26,10 +26,10 @@
|
||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
||||
pub enum GridNotification {
|
||||
Unknown = 0,
|
||||
GridDidUpdateBlock = 10,
|
||||
GridDidCreateBlock = 11,
|
||||
GridDidUpdateCells = 20,
|
||||
GridDidUpdateFields = 30,
|
||||
BlockDidUpdateRow = 20,
|
||||
GridDidUpdateCells = 30,
|
||||
GridDidUpdateFields = 40,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
@ -40,10 +40,10 @@ impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
fn from_i32(value: i32) -> ::std::option::Option<GridNotification> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(GridNotification::Unknown),
|
||||
10 => ::std::option::Option::Some(GridNotification::GridDidUpdateBlock),
|
||||
11 => ::std::option::Option::Some(GridNotification::GridDidCreateBlock),
|
||||
20 => ::std::option::Option::Some(GridNotification::GridDidUpdateCells),
|
||||
30 => ::std::option::Option::Some(GridNotification::GridDidUpdateFields),
|
||||
20 => ::std::option::Option::Some(GridNotification::BlockDidUpdateRow),
|
||||
30 => ::std::option::Option::Some(GridNotification::GridDidUpdateCells),
|
||||
40 => ::std::option::Option::Some(GridNotification::GridDidUpdateFields),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
@ -51,8 +51,8 @@ impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
fn values() -> &'static [Self] {
|
||||
static values: &'static [GridNotification] = &[
|
||||
GridNotification::Unknown,
|
||||
GridNotification::GridDidUpdateBlock,
|
||||
GridNotification::GridDidCreateBlock,
|
||||
GridNotification::BlockDidUpdateRow,
|
||||
GridNotification::GridDidUpdateCells,
|
||||
GridNotification::GridDidUpdateFields,
|
||||
];
|
||||
@ -83,10 +83,10 @@ impl ::protobuf::reflect::ProtobufValue for GridNotification {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x17dart_notification.proto*\x80\x01\n\x10GridNotification\x12\x0b\n\
|
||||
\x07Unknown\x10\0\x12\x16\n\x12GridDidUpdateBlock\x10\n\x12\x16\n\x12Gri\
|
||||
dDidCreateBlock\x10\x0b\x12\x16\n\x12GridDidUpdateCells\x10\x14\x12\x17\
|
||||
\n\x13GridDidUpdateFields\x10\x1eb\x06proto3\
|
||||
\n\x17dart_notification.proto*\x7f\n\x10GridNotification\x12\x0b\n\x07Un\
|
||||
known\x10\0\x12\x16\n\x12GridDidCreateBlock\x10\x0b\x12\x15\n\x11BlockDi\
|
||||
dUpdateRow\x10\x14\x12\x16\n\x12GridDidUpdateCells\x10\x1e\x12\x17\n\x13\
|
||||
GridDidUpdateFields\x10(b\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -2,8 +2,8 @@ syntax = "proto3";
|
||||
|
||||
enum GridNotification {
|
||||
Unknown = 0;
|
||||
GridDidUpdateBlock = 10;
|
||||
GridDidCreateBlock = 11;
|
||||
GridDidUpdateCells = 20;
|
||||
GridDidUpdateFields = 30;
|
||||
BlockDidUpdateRow = 20;
|
||||
GridDidUpdateCells = 30;
|
||||
GridDidUpdateFields = 40;
|
||||
}
|
||||
|
@ -46,7 +46,9 @@ impl GridBlockMetaEditorManager {
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
// #[tracing::instrument(level = "trace", skip(self))]
|
||||
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
|
||||
debug_assert!(!block_id.is_empty());
|
||||
match self.editor_map.get(block_id) {
|
||||
None => {
|
||||
tracing::error!("The is a fatal error, block is not exist");
|
||||
@ -68,7 +70,7 @@ impl GridBlockMetaEditorManager {
|
||||
.insert(row_meta.id.clone(), row_meta.block_id.clone());
|
||||
let editor = self.get_editor(&row_meta.block_id).await?;
|
||||
let row_count = editor.create_row(row_meta, start_row_id).await?;
|
||||
self.notify_did_update_block(block_id).await?;
|
||||
self.notify_block_did_update_row(&block_id).await?;
|
||||
Ok(row_count)
|
||||
}
|
||||
|
||||
@ -85,7 +87,7 @@ impl GridBlockMetaEditorManager {
|
||||
row_count = editor.create_row(row.clone(), None).await?;
|
||||
}
|
||||
changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
|
||||
let _ = self.notify_did_update_block(&block_id).await?;
|
||||
let _ = self.notify_block_did_update_row(&block_id).await?;
|
||||
}
|
||||
|
||||
Ok(changesets)
|
||||
@ -108,7 +110,7 @@ impl GridBlockMetaEditorManager {
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
|
||||
let editor = self.get_editor_from_row_id(&changeset.row_id).await?;
|
||||
let _ = editor.update_row(changeset.clone()).await?;
|
||||
let _ = self.notify_did_update_block(&editor.block_id).await?;
|
||||
let _ = self.notify_block_did_update_row(&editor.block_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -183,11 +185,9 @@ impl GridBlockMetaEditorManager {
|
||||
}
|
||||
}
|
||||
|
||||
async fn notify_did_update_block(&self, block_id: &str) -> FlowyResult<()> {
|
||||
let block_id = GridBlockId {
|
||||
value: block_id.to_owned(),
|
||||
};
|
||||
send_dart_notification(&self.grid_id, GridNotification::GridDidUpdateBlock)
|
||||
async fn notify_block_did_update_row(&self, block_id: &str) -> FlowyResult<()> {
|
||||
let block_id: GridBlockId = block_id.into();
|
||||
send_dart_notification(&self.grid_id, GridNotification::BlockDidUpdateRow)
|
||||
.payload(block_id)
|
||||
.send();
|
||||
Ok(())
|
||||
@ -277,7 +277,7 @@ impl ClientGridBlockMetaEditor {
|
||||
let mut row_count = 0;
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
let change = pad.add_row(row, start_row_id)?;
|
||||
let change = pad.add_row_meta(row, start_row_id)?;
|
||||
row_count = pad.number_of_rows();
|
||||
Ok(change)
|
||||
})
|
||||
@ -298,13 +298,22 @@ impl ClientGridBlockMetaEditor {
|
||||
Ok(row_count)
|
||||
}
|
||||
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<RowMeta> {
|
||||
let row_id = changeset.row_id.clone();
|
||||
let _ = self.modify(|pad| Ok(pad.update_row(changeset)?)).await?;
|
||||
Ok(())
|
||||
let mut row_metas = self.get_row_metas(Some(vec![row_id.clone()])).await?;
|
||||
debug_assert_eq!(row_metas.len(), 1);
|
||||
|
||||
if row_metas.is_empty() {
|
||||
return Err(FlowyError::record_not_found().context(format!("Can't find the row with id: {}", &row_id)));
|
||||
} else {
|
||||
let a = (**row_metas.pop().as_ref().unwrap()).clone();
|
||||
Ok(a)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_row_metas(&self, row_ids: Option<Vec<String>>) -> FlowyResult<Vec<Arc<RowMeta>>> {
|
||||
let row_metas = self.pad.read().await.get_rows(row_ids)?;
|
||||
let row_metas = self.pad.read().await.get_row_metas(row_ids)?;
|
||||
Ok(row_metas)
|
||||
}
|
||||
|
||||
@ -313,7 +322,7 @@ impl ClientGridBlockMetaEditor {
|
||||
.pad
|
||||
.read()
|
||||
.await
|
||||
.get_rows(row_ids)?
|
||||
.get_row_metas(row_ids)?
|
||||
.iter()
|
||||
.map(RowOrder::from)
|
||||
.collect::<Vec<RowOrder>>();
|
||||
|
@ -43,10 +43,10 @@ pub(crate) fn make_row_ids_per_block(row_orders: &[RowOrder]) -> Vec<RowIdsPerBl
|
||||
pub(crate) fn make_grid_blocks(block_meta_snapshots: Vec<GridBlockMetaData>) -> FlowyResult<RepeatedGridBlock> {
|
||||
Ok(block_meta_snapshots
|
||||
.into_iter()
|
||||
.map(|row_metas_per_block| {
|
||||
let row_orders = make_row_orders_from_row_metas(&row_metas_per_block.row_metas);
|
||||
.map(|block_meta_data| {
|
||||
let row_orders = make_row_orders_from_row_metas(&block_meta_data.row_metas);
|
||||
GridBlock {
|
||||
block_id: row_metas_per_block.block_id,
|
||||
id: block_meta_data.block_id,
|
||||
row_orders,
|
||||
}
|
||||
})
|
||||
|
@ -67,6 +67,7 @@ impl RevisionManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, fields(object_id) err)]
|
||||
pub async fn load<B>(&mut self, cloud: Option<Arc<dyn RevisionCloudService>>) -> FlowyResult<B::Output>
|
||||
where
|
||||
B: RevisionObjectBuilder,
|
||||
@ -80,6 +81,7 @@ impl RevisionManager {
|
||||
.load()
|
||||
.await?;
|
||||
self.rev_id_counter.set(rev_id);
|
||||
tracing::Span::current().record("object_id", &self.object_id.as_str());
|
||||
B::build_object(&self.object_id, revisions)
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ pub struct GridBlockOrder {
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct GridBlock {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub row_orders: Vec<RowOrder>,
|
||||
@ -190,7 +190,7 @@ pub struct GridBlock {
|
||||
impl GridBlock {
|
||||
pub fn new(block_id: &str, row_orders: Vec<RowOrder>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
id: block_id.to_owned(),
|
||||
row_orders,
|
||||
}
|
||||
}
|
||||
@ -269,6 +269,12 @@ impl AsRef<str> for GridBlockId {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&str> for GridBlockId {
|
||||
fn from(s: &str) -> Self {
|
||||
GridBlockId { value: s.to_owned() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
pub struct CreateRowPayload {
|
||||
#[pb(index = 1)]
|
||||
|
@ -2111,7 +2111,7 @@ impl ::protobuf::reflect::ProtobufValue for GridBlockOrder {
|
||||
#[derive(PartialEq,Clone,Default)]
|
||||
pub struct GridBlock {
|
||||
// message fields
|
||||
pub block_id: ::std::string::String,
|
||||
pub id: ::std::string::String,
|
||||
pub row_orders: ::protobuf::RepeatedField<RowOrder>,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
@ -2129,30 +2129,30 @@ impl GridBlock {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
// string block_id = 1;
|
||||
// string id = 1;
|
||||
|
||||
|
||||
pub fn get_block_id(&self) -> &str {
|
||||
&self.block_id
|
||||
pub fn get_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
pub fn clear_block_id(&mut self) {
|
||||
self.block_id.clear();
|
||||
pub fn clear_id(&mut self) {
|
||||
self.id.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_block_id(&mut self, v: ::std::string::String) {
|
||||
self.block_id = v;
|
||||
pub fn set_id(&mut self, v: ::std::string::String) {
|
||||
self.id = v;
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_block_id(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.block_id
|
||||
pub fn mut_id(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.id
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_block_id(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.block_id, ::std::string::String::new())
|
||||
pub fn take_id(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.id, ::std::string::String::new())
|
||||
}
|
||||
|
||||
// repeated .RowOrder row_orders = 2;
|
||||
@ -2196,7 +2196,7 @@ impl ::protobuf::Message for GridBlock {
|
||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
||||
match field_number {
|
||||
1 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.block_id)?;
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
|
||||
},
|
||||
2 => {
|
||||
::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.row_orders)?;
|
||||
@ -2213,8 +2213,8 @@ impl ::protobuf::Message for GridBlock {
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u32 {
|
||||
let mut my_size = 0;
|
||||
if !self.block_id.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.block_id);
|
||||
if !self.id.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.id);
|
||||
}
|
||||
for value in &self.row_orders {
|
||||
let len = value.compute_size();
|
||||
@ -2226,8 +2226,8 @@ impl ::protobuf::Message for GridBlock {
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
||||
if !self.block_id.is_empty() {
|
||||
os.write_string(1, &self.block_id)?;
|
||||
if !self.id.is_empty() {
|
||||
os.write_string(1, &self.id)?;
|
||||
}
|
||||
for v in &self.row_orders {
|
||||
os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
|
||||
@ -2273,9 +2273,9 @@ impl ::protobuf::Message for GridBlock {
|
||||
descriptor.get(|| {
|
||||
let mut fields = ::std::vec::Vec::new();
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"block_id",
|
||||
|m: &GridBlock| { &m.block_id },
|
||||
|m: &mut GridBlock| { &mut m.block_id },
|
||||
"id",
|
||||
|m: &GridBlock| { &m.id },
|
||||
|m: &mut GridBlock| { &mut m.id },
|
||||
));
|
||||
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowOrder>>(
|
||||
"row_orders",
|
||||
@ -2298,7 +2298,7 @@ impl ::protobuf::Message for GridBlock {
|
||||
|
||||
impl ::protobuf::Clear for GridBlock {
|
||||
fn clear(&mut self) {
|
||||
self.block_id.clear();
|
||||
self.id.clear();
|
||||
self.row_orders.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
@ -4091,24 +4091,24 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\
|
||||
\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\
|
||||
\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"+\n\x0eGridBlockO\
|
||||
rder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\"P\n\tGridBloc\
|
||||
k\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12(\n\nrow_order\
|
||||
s\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\
|
||||
\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\
|
||||
\x20\x01(\tR\x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\
|
||||
\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\
|
||||
\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05valu\
|
||||
e\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\
|
||||
\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid\
|
||||
_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\
|
||||
\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"d\n\x11QueryField\
|
||||
Payload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfie\
|
||||
ld_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"\
|
||||
e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\
|
||||
\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrd\
|
||||
erR\x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\
|
||||
\x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07bloc\
|
||||
kId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowIdb\x06proto3\
|
||||
rder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\"E\n\tGridBloc\
|
||||
k\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\
|
||||
\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\
|
||||
\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\
|
||||
\x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b\
|
||||
2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\
|
||||
\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\
|
||||
\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01\
|
||||
(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\
|
||||
\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstart\
|
||||
RowIdB\x15\n\x13one_of_start_row_id\"d\n\x11QueryFieldPayload\x12\x17\n\
|
||||
\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_orders\x18\x02\
|
||||
\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\x16QueryGridB\
|
||||
locksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x122\n\
|
||||
\x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrder\
|
||||
s\"\\\n\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06g\
|
||||
ridId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\x12\x15\n\x06\
|
||||
row_id\x18\x03\x20\x01(\tR\x05rowIdb\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -44,7 +44,7 @@ message GridBlockOrder {
|
||||
string block_id = 1;
|
||||
}
|
||||
message GridBlock {
|
||||
string block_id = 1;
|
||||
string id = 1;
|
||||
repeated RowOrder row_orders = 2;
|
||||
}
|
||||
message Cell {
|
||||
|
@ -47,20 +47,21 @@ impl GridBlockMetaPad {
|
||||
Self::from_delta(block_delta)
|
||||
}
|
||||
|
||||
pub fn add_row(
|
||||
#[tracing::instrument(level = "trace", skip(self, row), err)]
|
||||
pub fn add_row_meta(
|
||||
&mut self,
|
||||
row: RowMeta,
|
||||
start_row_id: Option<String>,
|
||||
) -> CollaborateResult<Option<GridBlockMetaChange>> {
|
||||
self.modify(|rows| {
|
||||
if let Some(upper_row_id) = start_row_id {
|
||||
if upper_row_id.is_empty() {
|
||||
if let Some(start_row_id) = start_row_id {
|
||||
if start_row_id.is_empty() {
|
||||
rows.insert(0, Arc::new(row));
|
||||
return Ok(Some(()));
|
||||
}
|
||||
|
||||
if let Some(index) = rows.iter().position(|row| row.id == upper_row_id) {
|
||||
rows.insert(index, Arc::new(row));
|
||||
if let Some(index) = rows.iter().position(|row| row.id == start_row_id) {
|
||||
rows.insert(index + 1, Arc::new(row));
|
||||
return Ok(Some(()));
|
||||
}
|
||||
}
|
||||
@ -77,7 +78,7 @@ impl GridBlockMetaPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_rows(&self, row_ids: Option<Vec<String>>) -> CollaborateResult<Vec<Arc<RowMeta>>> {
|
||||
pub fn get_row_metas(&self, row_ids: Option<Vec<String>>) -> CollaborateResult<Vec<Arc<RowMeta>>> {
|
||||
match row_ids {
|
||||
None => Ok(self.row_metas.to_vec()),
|
||||
Some(row_ids) => {
|
||||
@ -229,7 +230,7 @@ mod tests {
|
||||
visibility: false,
|
||||
};
|
||||
|
||||
let change = pad.add_row(row, None).unwrap().unwrap();
|
||||
let change = pad.add_row_meta(row, None).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
change.delta.to_delta_str(),
|
||||
r#"[{"retain":29},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||
@ -243,19 +244,19 @@ mod tests {
|
||||
let row_2 = test_row_meta("2", &pad);
|
||||
let row_3 = test_row_meta("3", &pad);
|
||||
|
||||
let change = pad.add_row(row_1.clone(), None).unwrap().unwrap();
|
||||
let change = pad.add_row_meta(row_1.clone(), None).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
change.delta.to_delta_str(),
|
||||
r#"[{"retain":29},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||
);
|
||||
|
||||
let change = pad.add_row(row_2.clone(), None).unwrap().unwrap();
|
||||
let change = pad.add_row_meta(row_2.clone(), None).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
change.delta.to_delta_str(),
|
||||
r#"[{"retain":106},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||
);
|
||||
|
||||
let change = pad.add_row(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
|
||||
let change = pad.add_row_meta(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
change.delta.to_delta_str(),
|
||||
r#"[{"retain":114},{"insert":"3\",\"block_id\":\"1\",\"cell_by_field_id\":{},\"height\":0,\"visibility\":false},{\"id\":\""},{"retain":72}]"#
|
||||
@ -283,9 +284,9 @@ mod tests {
|
||||
let row_2 = test_row_meta("2", &pad);
|
||||
let row_3 = test_row_meta("3", &pad);
|
||||
|
||||
let _ = pad.add_row(row_1.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row(row_2.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row(row_3.clone(), Some("1".to_string())).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_1.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_2.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_3.clone(), Some("1".to_string())).unwrap().unwrap();
|
||||
|
||||
assert_eq!(*pad.row_metas[0], row_3);
|
||||
assert_eq!(*pad.row_metas[1], row_1);
|
||||
@ -299,9 +300,9 @@ mod tests {
|
||||
let row_2 = test_row_meta("2", &pad);
|
||||
let row_3 = test_row_meta("3", &pad);
|
||||
|
||||
let _ = pad.add_row(row_1.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row(row_2.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row(row_3.clone(), Some("".to_string())).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_1.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_2.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row_3.clone(), Some("".to_string())).unwrap().unwrap();
|
||||
|
||||
assert_eq!(*pad.row_metas[0], row_3);
|
||||
assert_eq!(*pad.row_metas[1], row_1);
|
||||
@ -320,7 +321,7 @@ mod tests {
|
||||
visibility: false,
|
||||
};
|
||||
|
||||
let _ = pad.add_row(row.clone(), None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row.clone(), None).unwrap().unwrap();
|
||||
let change = pad.delete_rows(&[row.id]).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
change.delta.to_delta_str(),
|
||||
@ -348,7 +349,7 @@ mod tests {
|
||||
cell_by_field_id: Default::default(),
|
||||
};
|
||||
|
||||
let _ = pad.add_row(row, None).unwrap().unwrap();
|
||||
let _ = pad.add_row_meta(row, None).unwrap().unwrap();
|
||||
let change = pad.update_row(changeset).unwrap().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
Loading…
Reference in New Issue
Block a user