chore: lazy load cell

This commit is contained in:
appflowy 2022-03-26 20:27:32 +08:00
parent 28948e567c
commit 16598650ff
50 changed files with 628 additions and 469 deletions

View File

@ -149,8 +149,8 @@ void _resolveGridDeps(GetIt getIt) {
getIt.registerFactoryParam<RowBloc, GridRowData, void>(
(data, _) => RowBloc(
rowService: RowService(data),
listener: RowListener(rowId: data.rowId),
rowData: data,
rowlistener: RowListener(rowId: data.rowId),
),
);
@ -174,38 +174,43 @@ void _resolveGridDeps(GetIt getIt) {
),
);
getIt.registerFactoryParam<TextCellBloc, GridCellData, void>(
(context, _) => TextCellBloc(
service: CellService(context),
getIt.registerFactoryParam<TextCellBloc, FutureCellData, void>(
(cellData, _) => TextCellBloc(
service: CellService(),
cellData: cellData,
),
);
getIt.registerFactoryParam<SelectionCellBloc, GridCellData, void>(
(context, _) => SelectionCellBloc(
service: CellService(context),
getIt.registerFactoryParam<SelectionCellBloc, FutureCellData, void>(
(cellData, _) => SelectionCellBloc(
service: CellService(),
cellData: cellData,
),
);
getIt.registerFactoryParam<NumberCellBloc, GridCellData, void>(
(context, _) => NumberCellBloc(
service: CellService(context),
getIt.registerFactoryParam<NumberCellBloc, FutureCellData, void>(
(cellData, _) => NumberCellBloc(
service: CellService(),
cellData: cellData,
),
);
getIt.registerFactoryParam<DateCellBloc, GridCellData, void>(
(context, _) => DateCellBloc(
service: CellService(context),
getIt.registerFactoryParam<DateCellBloc, FutureCellData, void>(
(cellData, _) => DateCellBloc(
service: CellService(),
cellData: cellData,
),
);
getIt.registerFactoryParam<CheckboxCellBloc, GridCellData, void>(
(context, _) => CheckboxCellBloc(
service: CellService(context),
getIt.registerFactoryParam<CheckboxCellBloc, FutureCellData, void>(
(cellData, _) => CheckboxCellBloc(
service: CellService(),
cellData: cellData,
),
);
getIt.registerFactoryParam<SwitchFieldTypeBloc, EditFieldContext, void>(
(editContext, _) => SwitchFieldTypeBloc(editContext),
getIt.registerFactoryParam<SwitchFieldTypeBloc, SwitchFieldContext, void>(
(context, _) => SwitchFieldTypeBloc(context),
);
getIt.registerFactory<SelectionTypeOptionBloc>(

View File

@ -112,7 +112,7 @@ class ApplicationBlocObserver extends BlocObserver {
// ignore: unnecessary_overrides
void onTransition(Bloc bloc, Transition transition) {
// Log.debug("[current]: ${transition.currentState} \n\n[next]: ${transition.nextState}");
//Log.debug("${transition.nextState}");
Log.debug("${transition.nextState}");
super.onTransition(bloc, transition);
}

View File

@ -12,7 +12,6 @@ import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/dart_notification.pb.dart' as user;
import 'package:flowy_sdk/rust_stream.dart';
typedef UserProfileUpdatedNotifierValue = Either<UserProfile, FlowyError>;
typedef AuthNotifierValue = Either<Unit, FlowyError>;
typedef WorkspaceUpdatedNotifierValue = Either<List<Workspace>, FlowyError>;
@ -23,8 +22,8 @@ class UserListener {
final authDidChangedNotifier = PublishNotifier<AuthNotifierValue>();
final workspaceUpdatedNotifier = PublishNotifier<WorkspaceUpdatedNotifierValue>();
late FolderNotificationParser _workspaceParser;
late UserNotificationParser _userParser;
FolderNotificationParser? _workspaceParser;
UserNotificationParser? _userParser;
late UserProfile _user;
UserListener({
required UserProfile user,
@ -36,12 +35,14 @@ class UserListener {
_workspaceParser = FolderNotificationParser(id: _user.token, callback: _notificationCallback);
_userParser = UserNotificationParser(id: _user.token, callback: _userNotificationCallback);
_subscription = RustStreamReceiver.listen((observable) {
_workspaceParser.parse(observable);
_userParser.parse(observable);
_workspaceParser?.parse(observable);
_userParser?.parse(observable);
});
}
Future<void> stop() async {
_workspaceParser = null;
_userParser = null;
await _subscription?.cancel();
profileUpdatedNotifier.dispose();
authDidChangedNotifier.dispose();

View File

@ -17,7 +17,7 @@ class AppListener {
StreamSubscription<SubscribeObject>? _subscription;
ViewsDidChangeCallback? _viewsChanged;
AppDidUpdateCallback? _updated;
late FolderNotificationParser _parser;
FolderNotificationParser? _parser;
String appId;
AppListener({
@ -28,7 +28,7 @@ class AppListener {
_viewsChanged = viewsChanged;
_updated = appUpdated;
_parser = FolderNotificationParser(id: appId, callback: _bservableCallback);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _bservableCallback(FolderNotification ty, Either<Uint8List, FlowyError> result) {
@ -61,6 +61,7 @@ class AppListener {
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
_viewsChanged = null;
_updated = null;

View File

@ -1,19 +1,21 @@
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
class CellService {
final GridCellData context;
CellService();
CellService(this.context);
Future<Either<void, FlowyError>> updateCell({required String data}) {
Future<Either<void, FlowyError>> updateCell({
required String gridId,
required String fieldId,
required String rowId,
required String data,
}) {
final payload = CellMetaChangeset.create()
..gridId = context.gridId
..fieldId = context.field.id
..rowId = context.rowId
..gridId = gridId
..fieldId = fieldId
..rowId = rowId
..data = data;
return GridEventUpdateCell(payload).send();
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -8,10 +9,14 @@ part 'checkbox_cell_bloc.freezed.dart';
class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
final CellService service;
// final FutureCellData cellData;
CheckboxCellBloc({
required this.service,
}) : super(CheckboxCellState.initial(service.context.cell)) {
required FutureCellData cellData,
}) : super(CheckboxCellState.initial()) {
cellData.then((a) {});
on<CheckboxCellEvent>(
(event, emit) async {
await event.map(
@ -38,5 +43,5 @@ class CheckboxCellState with _$CheckboxCellState {
required Cell? cell,
}) = _CheckboxCellState;
factory CheckboxCellState.initial(Cell? cell) => CheckboxCellState(cell: cell);
factory CheckboxCellState.initial() => const CheckboxCellState(cell: null);
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -8,10 +9,12 @@ part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
final CellService service;
final FutureCellData cellData;
DateCellBloc({
required this.service,
}) : super(DateCellState.initial(service.context.cell)) {
required this.cellData,
}) : super(DateCellState.initial()) {
on<DateCellEvent>(
(event, emit) async {
await event.map(
@ -35,8 +38,8 @@ class DateCellEvent with _$DateCellEvent {
@freezed
class DateCellState with _$DateCellState {
const factory DateCellState({
required Cell? cell,
Cell? cell,
}) = _DateCellState;
factory DateCellState.initial(Cell? cell) => DateCellState(cell: cell);
factory DateCellState.initial() => const DateCellState();
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -11,7 +12,8 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
NumberCellBloc({
required this.service,
}) : super(NumberCellState.initial(service.context.cell)) {
required FutureCellData cellData,
}) : super(NumberCellState.initial()) {
on<NumberCellEvent>(
(event, emit) async {
await event.map(
@ -35,8 +37,8 @@ class NumberCellEvent with _$NumberCellEvent {
@freezed
class NumberCellState with _$NumberCellState {
const factory NumberCellState({
required Cell? cell,
Cell? cell,
}) = _NumberCellState;
factory NumberCellState.initial(Cell? cell) => NumberCellState(cell: cell);
factory NumberCellState.initial() => const NumberCellState();
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -11,7 +12,8 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
SelectionCellBloc({
required this.service,
}) : super(SelectionCellState.initial(service.context.cell)) {
required FutureCellData cellData,
}) : super(SelectionCellState.initial()) {
on<SelectionCellEvent>(
(event, emit) async {
await event.map(
@ -35,8 +37,8 @@ class SelectionCellEvent with _$SelectionCellEvent {
@freezed
class SelectionCellState with _$SelectionCellState {
const factory SelectionCellState({
required Cell? cell,
Cell? cell,
}) = _SelectionCellState;
factory SelectionCellState.initial(Cell? cell) => SelectionCellState(cell: cell);
factory SelectionCellState.initial() => const SelectionCellState();
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -10,20 +11,47 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
TextCellBloc({
required this.service,
}) : super(TextCellState.initial(service.context.cell?.content ?? "")) {
required FutureCellData cellData,
}) : super(TextCellState.initial()) {
cellData.then((cellData) {
if (cellData != null) {
add(TextCellEvent.didReceiveCellData(cellData));
}
});
on<TextCellEvent>(
(event, emit) async {
await event.map(
initial: (_InitialCell value) async {},
updateText: (_UpdateText value) {
service.updateCell(data: value.text);
updateCellContent(value.text);
emit(state.copyWith(content: value.text));
},
didReceiveCellData: (_DidReceiveCellData value) {
emit(state.copyWith(
cellData: value.cellData,
content: value.cellData.cell?.content ?? "",
));
},
);
},
);
}
void updateCellContent(String content) {
if (state.cellData != null) {
final fieldId = state.cellData!.field.id;
final gridId = state.cellData!.gridId;
final rowId = state.cellData!.rowId;
service.updateCell(
data: content,
fieldId: fieldId,
gridId: gridId,
rowId: rowId,
);
}
}
@override
Future<void> close() async {
return super.close();
@ -33,6 +61,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
@freezed
class TextCellEvent with _$TextCellEvent {
const factory TextCellEvent.initial() = _InitialCell;
const factory TextCellEvent.didReceiveCellData(GridCellData cellData) = _DidReceiveCellData;
const factory TextCellEvent.updateText(String text) = _UpdateText;
}
@ -40,7 +69,8 @@ class TextCellEvent with _$TextCellEvent {
class TextCellState with _$TextCellState {
const factory TextCellState({
required String content,
GridCellData? cellData,
}) = _TextCellState;
factory TextCellState.initial(String content) => TextCellState(content: content);
factory TextCellState.initial() => const TextCellState(content: "");
}

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
@ -12,19 +14,22 @@ part 'create_field_bloc.freezed.dart';
class CreateFieldBloc extends Bloc<CreateFieldEvent, CreateFieldState> {
final FieldService service;
CreateFieldBloc({required this.service}) : super(CreateFieldState.initial()) {
CreateFieldBloc({required this.service}) : super(CreateFieldState.initial(service.gridId)) {
on<CreateFieldEvent>(
(event, emit) async {
await event.map(
initial: (_InitialField value) async {
final result = await service.getEditFieldContext(FieldType.RichText);
result.fold(
(editContext) => emit(state.copyWith(editContext: Some(editContext))),
(err) => Log.error(err),
);
await _getEditFieldContext(emit);
},
updateName: (_UpdateName value) {
emit(state.copyWith(fieldName: value.name));
},
switchField: (_SwitchField value) {
emit(state.copyWith(field: Some(value.field), typeOptionData: value.typeOptionData));
},
done: (_Done value) async {
await _saveField(emit);
},
updateName: (_UpdateName value) {},
done: (_Done value) {},
);
},
);
@ -34,24 +39,54 @@ class CreateFieldBloc extends Bloc<CreateFieldEvent, CreateFieldState> {
Future<void> close() async {
return super.close();
}
Future<void> _saveField(Emitter<CreateFieldState> emit) async {
await state.field.fold(
() async => null,
(field) async {
final result = await service.createField(field: field, typeOptionData: state.typeOptionData);
result.fold((l) => null, (r) => null);
},
);
}
Future<void> _getEditFieldContext(Emitter<CreateFieldState> emit) async {
final result = await service.getEditFieldContext(FieldType.RichText);
result.fold(
(editContext) {
emit(state.copyWith(
field: Some(editContext.gridField),
typeOptionData: editContext.typeOptionData,
));
},
(err) => Log.error(err),
);
}
}
@freezed
class CreateFieldEvent with _$CreateFieldEvent {
const factory CreateFieldEvent.initial() = _InitialField;
const factory CreateFieldEvent.updateName(String newName) = _UpdateName;
const factory CreateFieldEvent.updateName(String name) = _UpdateName;
const factory CreateFieldEvent.switchField(Field field, Uint8List typeOptionData) = _SwitchField;
const factory CreateFieldEvent.done() = _Done;
}
@freezed
class CreateFieldState with _$CreateFieldState {
const factory CreateFieldState({
required String fieldName,
required String gridId,
required String errorText,
required Option<EditFieldContext> editContext,
required Option<Field> field,
required List<int> typeOptionData,
}) = _CreateFieldState;
factory CreateFieldState.initial() => CreateFieldState(
editContext: none(),
factory CreateFieldState.initial(String gridId) => CreateFieldState(
gridId: gridId,
fieldName: '',
field: none(),
errorText: '',
typeOptionData: List<int>.empty(),
);
}

View File

@ -21,66 +21,19 @@ class FieldService {
return GridEventCreateEditFieldContext(payload).send();
}
Future<Either<Unit, FlowyError>> createTextField(
String gridId,
Field field,
RichTextTypeOption typeOption,
Future<Either<Unit, FlowyError>> createField({
required Field field,
List<int>? typeOptionData,
String? startFieldId,
) {
final typeOptionData = typeOption.writeToBuffer();
return createField(field, typeOptionData, startFieldId);
}
Future<Either<Unit, FlowyError>> createSingleSelectField(
String gridId,
Field field,
SingleSelectTypeOption typeOption,
String? startFieldId,
) {
final typeOptionData = typeOption.writeToBuffer();
return createField(field, typeOptionData, startFieldId);
}
Future<Either<Unit, FlowyError>> createMultiSelectField(
String gridId,
Field field,
MultiSelectTypeOption typeOption,
String? startFieldId,
) {
final typeOptionData = typeOption.writeToBuffer();
return createField(field, typeOptionData, startFieldId);
}
Future<Either<Unit, FlowyError>> createNumberField(
String gridId,
Field field,
NumberTypeOption typeOption,
String? startFieldId,
) {
final typeOptionData = typeOption.writeToBuffer();
return createField(field, typeOptionData, startFieldId);
}
Future<Either<Unit, FlowyError>> createDateField(
String gridId,
Field field,
DateTypeOption typeOption,
String? startFieldId,
) {
final typeOptionData = typeOption.writeToBuffer();
return createField(field, typeOptionData, startFieldId);
}
Future<Either<Unit, FlowyError>> createField(
Field field,
Uint8List? typeOptionData,
String? startFieldId,
) {
final payload = CreateFieldPayload.create()
}) {
var payload = CreateFieldPayload.create()
..gridId = gridId
..field_2 = field
..typeOptionData = typeOptionData ?? Uint8List.fromList([])
..startFieldId = startFieldId ?? "";
..typeOptionData = typeOptionData ?? [];
if (startFieldId != null) {
payload.startFieldId = startFieldId;
}
return GridEventCreateField(payload).send();
}

View File

@ -10,7 +10,7 @@ import 'field_service.dart';
part 'switch_field_type_bloc.freezed.dart';
class SwitchFieldTypeBloc extends Bloc<SwitchFieldTypeEvent, SwitchFieldTypeState> {
SwitchFieldTypeBloc(EditFieldContext editContext) : super(SwitchFieldTypeState.initial(editContext)) {
SwitchFieldTypeBloc(SwitchFieldContext editContext) : super(SwitchFieldTypeState.initial(editContext)) {
on<SwitchFieldTypeEvent>(
(event, emit) async {
await event.map(
@ -53,9 +53,17 @@ class SwitchFieldTypeState with _$SwitchFieldTypeState {
required Uint8List typeOptionData,
}) = _SwitchFieldTypeState;
factory SwitchFieldTypeState.initial(EditFieldContext editContext) => SwitchFieldTypeState(
gridId: editContext.gridId,
field: editContext.gridField,
typeOptionData: Uint8List.fromList(editContext.typeOptionData),
factory SwitchFieldTypeState.initial(SwitchFieldContext switchContext) => SwitchFieldTypeState(
gridId: switchContext.gridId,
field: switchContext.field,
typeOptionData: Uint8List.fromList(switchContext.typeOptionData),
);
}
class SwitchFieldContext {
final String gridId;
final Field field;
final List<int> typeOptionData;
SwitchFieldContext(this.gridId, this.field, this.typeOptionData);
}

View File

@ -6,6 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:equatable/equatable.dart';
import 'grid_block_service.dart';
import 'grid_listenr.dart';
import 'grid_service.dart';
@ -63,6 +64,20 @@ class GridBloc extends Bloc<GridEvent, GridState> {
await _loadGrid(emit);
}
Future<void> _initGridBlock(Grid grid) async {
_blockService = GridBlockService(
gridId: grid.id,
blockOrders: grid.blockOrders,
);
_blockService.blocksUpdateNotifier?.addPublishListener((result) {
result.fold(
(blockMap) => add(GridEvent.rowsDidUpdate(_buildRows(blockMap))),
(err) => Log.error('$err'),
);
});
}
Future<void> _loadGrid(Emitter<GridState> emit) async {
final result = await service.openGrid(gridId: view.id);
return Future(
@ -78,7 +93,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
return Future(
() => result.fold(
(fields) {
_initGridBlockService(grid);
_initGridBlock(grid);
emit(state.copyWith(
grid: Some(grid),
fields: fields.items,
@ -90,29 +105,12 @@ class GridBloc extends Bloc<GridEvent, GridState> {
);
}
Future<void> _initGridBlockService(Grid grid) async {
_blockService = GridBlockService(
gridId: grid.id,
blockOrders: grid.blockOrders,
);
_blockService.blocksUpdateNotifier.addPublishListener((result) {
result.fold(
(blockMap) => add(GridEvent.rowsDidUpdate(_buildRows(blockMap))),
(err) => Log.error('$err'),
);
});
_gridListener.start();
}
List<GridRowData> _buildRows(GridBlockMap blockMap) {
List<GridRowData> rows = [];
List<GridBlockRow> _buildRows(GridBlockMap blockMap) {
List<GridBlockRow> rows = [];
blockMap.forEach((_, GridBlock gridBlock) {
rows.addAll(gridBlock.rowOrders.map(
(rowOrder) => GridRowData(
(rowOrder) => GridBlockRow(
gridId: view.id,
fields: state.fields,
blockId: gridBlock.id,
rowId: rowOrder.rowId,
height: rowOrder.height.toDouble(),
@ -130,7 +128,7 @@ 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.rowsDidUpdate(List<GridRowData> rows) = _RowsDidUpdate;
const factory GridEvent.rowsDidUpdate(List<GridBlockRow> rows) = _RowsDidUpdate;
const factory GridEvent.fieldsDidUpdate(List<Field> fields) = _FieldsDidUpdate;
}
@ -139,7 +137,7 @@ class GridState with _$GridState {
const factory GridState({
required GridLoadingState loadingState,
required List<Field> fields,
required List<GridRowData> rows,
required List<GridBlockRow> rows,
required Option<Grid> grid,
}) = _GridState;
@ -156,3 +154,46 @@ class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
}
class GridBlockRow {
final String gridId;
final String rowId;
final String blockId;
final double height;
const GridBlockRow({
required this.gridId,
required this.rowId,
required this.blockId,
required this.height,
});
}
class GridRowData extends Equatable {
final String gridId;
final String rowId;
final String blockId;
final List<Field> fields;
final double height;
const GridRowData({
required this.gridId,
required this.rowId,
required this.blockId,
required this.fields,
required this.height,
});
factory GridRowData.fromBlockRow(GridBlockRow row, List<Field> fields) {
return GridRowData(
gridId: row.gridId,
rowId: row.rowId,
blockId: row.blockId,
fields: fields,
height: row.height,
);
}
@override
List<Object> get props => [rowId, fields];
}

View File

@ -19,7 +19,7 @@ class GridBlockService {
String gridId;
GridBlockMap blockMap = GridBlockMap();
late GridBlockListener _blockListener;
PublishNotifier<BlocksUpdateNotifierValue> blocksUpdateNotifier = PublishNotifier();
PublishNotifier<BlocksUpdateNotifierValue>? blocksUpdateNotifier = PublishNotifier();
GridBlockService({required this.gridId, required List<GridBlockOrder> blockOrders}) {
_loadGridBlocks(blockOrders);
@ -36,6 +36,8 @@ class GridBlockService {
Future<void> stop() async {
await _blockListener.stop();
blocksUpdateNotifier?.dispose();
blocksUpdateNotifier = null;
}
void _loadGridBlocks(List<GridBlockOrder> blockOrders) {
@ -49,9 +51,9 @@ class GridBlockService {
for (final gridBlock in repeatedBlocks.items) {
blockMap[gridBlock.id] = gridBlock;
}
blocksUpdateNotifier.value = left(blockMap);
blocksUpdateNotifier?.value = left(blockMap);
},
(err) => blocksUpdateNotifier.value = right(err),
(err) => blocksUpdateNotifier?.value = right(err),
);
});
}
@ -61,7 +63,7 @@ class GridBlockListener {
final String gridId;
PublishNotifier<Either<List<GridBlockOrder>, FlowyError>> blockUpdateNotifier = PublishNotifier(comparable: null);
StreamSubscription<SubscribeObject>? _subscription;
late GridNotificationParser _parser;
GridNotificationParser? _parser;
GridBlockListener({required this.gridId});
@ -73,7 +75,7 @@ class GridBlockListener {
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
@ -91,6 +93,7 @@ class GridBlockListener {
}
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
blockUpdateNotifier.dispose();
}

View File

@ -13,9 +13,9 @@ import 'package:app_flowy/core/notification_helper.dart';
class GridListener {
final String gridId;
PublishNotifier<Either<List<Field>, FlowyError>> fieldsUpdateNotifier = PublishNotifier(comparable: null);
PublishNotifier<Either<List<Field>, FlowyError>> fieldsUpdateNotifier = PublishNotifier();
StreamSubscription<SubscribeObject>? _subscription;
late GridNotificationParser _parser;
GridNotificationParser? _parser;
GridListener({required this.gridId});
void start() {
@ -26,7 +26,7 @@ class GridListener {
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
@ -43,6 +43,7 @@ class GridListener {
}
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
fieldsUpdateNotifier.dispose();
}

View File

@ -3,7 +3,6 @@ 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/grid.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
class GridService {
Future<Either<Grid, FlowyError>> openGrid({required String gridId}) async {
@ -34,22 +33,3 @@ class GridService {
return GridEventGetFields(payload).send();
}
}
class GridRowData extends Equatable {
final String gridId;
final String rowId;
final String blockId;
final List<Field> fields;
final double height;
const GridRowData({
required this.gridId,
required this.rowId,
required this.blockId,
required this.fields,
required this.height,
});
@override
List<Object> get props => [rowId];
}

View File

@ -1,4 +1,6 @@
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -6,20 +8,34 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'row_listener.dart';
import 'row_service.dart';
import 'package:dartz/dartz.dart';
part 'row_bloc.freezed.dart';
typedef CellDataMap = HashMap<String, GridCellData>;
class RowBloc extends Bloc<RowEvent, RowState> {
final RowService rowService;
final RowListener listener;
final RowListener rowlistener;
final RowFieldListener fieldListener;
RowBloc({required this.rowService, required this.listener}) : super(RowState.initial(rowService.rowData)) {
RowBloc({required GridRowData rowData, required this.rowlistener})
: rowService = RowService(
gridId: rowData.gridId,
blockId: rowData.blockId,
rowId: rowData.rowId,
),
fieldListener = RowFieldListener(
gridId: rowData.gridId,
),
super(RowState.initial(rowData)) {
on<RowEvent>(
(event, emit) async {
await event.map(
initial: (_InitialRow value) async {
_startRowListening();
_startListening();
await _loadRow(emit);
add(const RowEvent.didUpdateCell());
},
createRow: (_CreateRow value) {
rowService.createRow();
@ -30,6 +46,19 @@ class RowBloc extends Bloc<RowEvent, RowState> {
disactiveRow: (_DisactiveRow value) {
emit(state.copyWith(active: false));
},
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
emit(state.copyWith(fields: value.fields));
add(const RowEvent.didUpdateCell());
},
didUpdateCell: (_DidUpdateCell value) {
final Future<CellDataMap> cellDataMap = state.row.then(
(someRow) => someRow.fold(
() => HashMap.identity(),
(row) => _makeCellDatas(row),
),
);
emit(state.copyWith(cellDataMap: cellDataMap));
},
);
},
);
@ -37,53 +66,66 @@ class RowBloc extends Bloc<RowEvent, RowState> {
@override
Future<void> close() async {
await listener.close();
await rowlistener.close();
await fieldListener.close();
return super.close();
}
Future<void> _startRowListening() async {
listener.updateRowNotifier.addPublishListener((result) {
result.fold((row) {
//
}, (err) => null);
Future<void> _startListening() async {
rowlistener.updateRowNotifier.addPublishListener((result) {
result.fold(
(row) {
//
},
(err) => Log.error(err),
);
});
listener.updateCellNotifier.addPublishListener((result) {
result.fold((repeatedCvell) {
//
Log.info("$repeatedCvell");
}, (r) => null);
rowlistener.updateCellNotifier.addPublishListener((result) {
result.fold(
(repeatedCell) {
Log.info("$repeatedCell");
},
(err) => Log.error(err),
);
});
listener.start();
fieldListener.updateFieldNotifier.addPublishListener((result) {
result.fold(
(fields) => add(RowEvent.didReceiveFieldUpdate(fields)),
(err) => Log.error(err),
);
});
rowlistener.start();
fieldListener.start();
}
Future<void> _loadRow(Emitter<RowState> emit) async {
final Future<List<GridCellData>> cellDatas = rowService.getRow().then((result) {
final Future<Option<Row>> row = rowService.getRow().then((result) {
return result.fold(
(row) => _makeCellDatas(row),
(e) {
Log.error(e);
return [];
(row) => Some(row),
(err) {
Log.error(err);
return none();
},
);
});
emit(state.copyWith(cellDatas: cellDatas));
emit(state.copyWith(row: row));
}
List<GridCellData> _makeCellDatas(Row row) {
return rowService.rowData.fields.map((field) {
final cell = row.cellByFieldId[field.id];
final rowData = rowService.rowData;
return GridCellData(
CellDataMap _makeCellDatas(Row row) {
var map = CellDataMap.new();
for (final field in state.fields) {
map[field.id] = GridCellData(
rowId: row.id,
gridId: rowData.gridId,
blockId: rowData.blockId,
cell: cell,
gridId: rowService.gridId,
blockId: rowService.blockId,
cell: row.cellByFieldId[field.id],
field: field,
);
}).toList();
}
return map;
}
}
@ -93,21 +135,27 @@ class RowEvent with _$RowEvent {
const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.activeRow() = _ActiveRow;
const factory RowEvent.disactiveRow() = _DisactiveRow;
const factory RowEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
const factory RowEvent.didUpdateCell() = _DidUpdateCell;
}
@freezed
class RowState with _$RowState {
const factory RowState({
required String rowId,
required double rowHeight,
required Future<List<GridCellData>> cellDatas,
required bool active,
required double rowHeight,
required List<Field> fields,
required Future<Option<Row>> row,
required Future<CellDataMap> cellDataMap,
}) = _RowState;
factory RowState.initial(GridRowData data) => RowState(
rowId: data.rowId,
rowHeight: data.height,
cellDatas: Future(() => []),
active: false,
rowHeight: data.height,
fields: data.fields,
row: Future(() => none()),
cellDataMap: Future(() => CellDataMap.identity()),
);
}

View File

@ -11,13 +11,14 @@ import 'package:dartz/dartz.dart';
typedef UpdateCellNotifiedValue = Either<RepeatedCell, FlowyError>;
typedef UpdateRowNotifiedValue = Either<Row, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<Field>, FlowyError>;
class RowListener {
final String rowId;
PublishNotifier<UpdateCellNotifiedValue> updateCellNotifier = PublishNotifier<UpdateCellNotifiedValue>();
PublishNotifier<UpdateRowNotifiedValue> updateRowNotifier = PublishNotifier<UpdateRowNotifiedValue>();
PublishNotifier<UpdateCellNotifiedValue> updateCellNotifier = PublishNotifier();
PublishNotifier<UpdateRowNotifiedValue> updateRowNotifier = PublishNotifier();
StreamSubscription<SubscribeObject>? _subscription;
late GridNotificationParser _parser;
GridNotificationParser? _parser;
RowListener({required this.rowId});
@ -29,7 +30,7 @@ class RowListener {
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
@ -40,15 +41,54 @@ class RowListener {
(error) => updateCellNotifier.value = right(error),
);
break;
default:
break;
}
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
updateCellNotifier.dispose();
updateRowNotifier.dispose();
}
}
class RowFieldListener {
final String gridId;
PublishNotifier<UpdateFieldNotifiedValue> updateFieldNotifier = PublishNotifier();
StreamSubscription<SubscribeObject>? _subscription;
GridNotificationParser? _parser;
RowFieldListener({required this.gridId});
void start() {
_parser = GridNotificationParser(
id: gridId,
callback: (ty, result) {
_handleObservableType(ty, result);
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(GridNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridNotification.DidUpdateFields:
result.fold(
(payload) => updateFieldNotifier.value = left(RepeatedField.fromBuffer(payload).items),
(error) => updateFieldNotifier.value = right(error),
);
break;
default:
break;
}
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
updateFieldNotifier.dispose();
}
}

View File

@ -1,45 +1,51 @@
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
class RowService {
final GridRowData rowData;
final String gridId;
final String rowId;
final String blockId;
RowService(this.rowData);
RowService({required this.gridId, required this.rowId, required this.blockId});
Future<Either<Row, FlowyError>> createRow() {
CreateRowPayload payload = CreateRowPayload.create()
..gridId = rowData.gridId
..startRowId = rowData.rowId;
..gridId = gridId
..startRowId = rowId;
return GridEventCreateRow(payload).send();
}
Future<Either<Row, FlowyError>> getRow() {
QueryRowPayload payload = QueryRowPayload.create()
..gridId = rowData.gridId
..blockId = rowData.blockId
..rowId = rowData.rowId;
..gridId = gridId
..blockId = blockId
..rowId = rowId;
return GridEventGetRow(payload).send();
}
}
class GridCellData {
typedef FutureCellData = Future<GridCellData?>;
class GridCellData extends Equatable {
final String gridId;
final String rowId;
final String blockId;
final Field field;
final Cell? cell;
GridCellData({
const GridCellData({
required this.rowId,
required this.gridId,
required this.blockId,
required this.field,
required this.cell,
});
@override
List<Object?> get props => [cell, field];
}

View File

@ -13,12 +13,12 @@ typedef TrashUpdatedCallback = void Function(Either<List<Trash>, FlowyError> tra
class TrashListener {
StreamSubscription<SubscribeObject>? _subscription;
TrashUpdatedCallback? _trashUpdated;
late FolderNotificationParser _parser;
FolderNotificationParser? _parser;
void start({TrashUpdatedCallback? trashUpdated}) {
_trashUpdated = trashUpdated;
_parser = FolderNotificationParser(callback: _bservableCallback);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _bservableCallback(FolderNotification ty, Either<Uint8List, FlowyError> result) {
@ -40,6 +40,7 @@ class TrashListener {
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
_trashUpdated = null;
}

View File

@ -18,7 +18,7 @@ class ViewListener {
PublishNotifier<UpdateViewNotifiedValue> updatedNotifier = PublishNotifier<UpdateViewNotifiedValue>();
PublishNotifier<DeleteViewNotifyValue> deletedNotifier = PublishNotifier<DeleteViewNotifyValue>();
PublishNotifier<RestoreViewNotifiedValue> restoredNotifier = PublishNotifier<RestoreViewNotifiedValue>();
late FolderNotificationParser _parser;
FolderNotificationParser? _parser;
View view;
ViewListener({
@ -33,7 +33,7 @@ class ViewListener {
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(FolderNotification ty, Either<Uint8List, FlowyError> result) {
@ -62,6 +62,7 @@ class ViewListener {
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
updatedNotifier.dispose();
deletedNotifier.dispose();

View File

@ -12,7 +12,6 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
typedef WorkspaceAppsChangedCallback = void Function(Either<List<App>, FlowyError> appsOrFail);
typedef WorkspaceUpdatedCallback = void Function(String name, String desc);
@ -31,12 +30,11 @@ class WorkspaceListener {
}
}
class WorkspaceListenerService {
StreamSubscription<SubscribeObject>? _subscription;
WorkspaceAppsChangedCallback? _appsChanged;
WorkspaceUpdatedCallback? _update;
late FolderNotificationParser _parser;
FolderNotificationParser? _parser;
final UserProfile user;
final String workspaceId;
@ -59,7 +57,7 @@ class WorkspaceListenerService {
},
);
_subscription = RustStreamReceiver.listen((observable) => _parser.parse(observable));
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
void _handleObservableType(FolderNotification ty, Either<Uint8List, FlowyError> result) {
@ -91,6 +89,7 @@ class WorkspaceListenerService {
}
Future<void> close() async {
_parser = null;
await _subscription?.cancel();
// _appsChanged = null;
// _update = null;

View File

@ -1,15 +1,13 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';
import 'controller/grid_scroll.dart';
import 'layout/layout.dart';
import 'layout/sizes.dart';
@ -92,16 +90,33 @@ class _FlowyGridState extends State<FlowyGrid> {
return const Center(child: CircularProgressIndicator.adaptive());
}
return _wrapScrollbar(state.fields, [
_buildHeader(gridId, state.fields),
_buildRows(context),
const GridFooter(),
]);
final child = BlocBuilder<GridBloc, GridState>(
builder: (context, state) {
return SizedBox(
width: GridLayout.headerWidth(state.fields),
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: CustomScrollView(
shrinkWrap: true,
physics: StyledScrollPhysics(),
controller: _scrollController.verticalController,
slivers: [
_buildHeader(gridId),
_buildRows(context),
const GridFooter(),
],
),
),
);
},
);
return _wrapScrollbar(child);
},
);
}
Widget _wrapScrollbar(List<Field> fields, List<Widget> children) {
Widget _wrapScrollbar(Widget child) {
return ScrollbarListStack(
axis: Axis.vertical,
controller: _scrollController.verticalController,
@ -109,38 +124,39 @@ class _FlowyGridState extends State<FlowyGrid> {
child: StyledSingleChildScrollView(
controller: _scrollController.horizontalController,
axis: Axis.horizontal,
child: SizedBox(
width: GridLayout.headerWidth(fields),
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: CustomScrollView(
physics: StyledScrollPhysics(),
controller: _scrollController.verticalController,
slivers: <Widget>[...children],
),
),
),
child: child,
),
).padding(right: 0, top: GridSize.headerHeight, bottom: GridSize.scrollBarSize);
);
}
Widget _buildHeader(String gridId, List<Field> fields) {
return SliverPersistentHeader(
delegate: GridHeaderDelegate(gridId: gridId, fields: fields),
floating: true,
pinned: true,
Widget _buildHeader(String gridId) {
return BlocBuilder<GridBloc, GridState>(
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
builder: (context, state) {
return SliverPersistentHeader(
delegate: GridHeaderDelegate(gridId: gridId, fields: state.fields),
floating: true,
pinned: true,
);
},
);
}
Widget _buildRows(BuildContext context) {
return BlocBuilder<GridBloc, GridState>(
buildWhen: (previous, current) => previous.rows.length != current.rows.length,
buildWhen: (previous, current) {
final rowChanged = previous.rows.length != current.rows.length;
// final fieldChanged = previous.fields.length != current.fields.length;
return rowChanged;
},
builder: (context, state) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final rowData = context.read<GridBloc>().state.rows[index];
return GridRowWidget(data: rowData);
final blockRow = context.read<GridBloc>().state.rows[index];
final fields = context.read<GridBloc>().state.fields;
final rowData = GridRowData.fromBlockRow(blockRow, fields);
return GridRowWidget(data: rowData, key: ValueKey(rowData.rowId));
},
childCount: context.read<GridBloc>().state.rows.length,
addRepaintBoundaries: true,

View File

@ -7,20 +7,20 @@ import 'number_cell.dart';
import 'selection_cell.dart';
import 'text_cell.dart';
Widget buildGridCell(GridCellData cellData) {
switch (cellData.field.fieldType) {
Widget buildGridCell(FieldType fieldType, FutureCellData cellData) {
switch (fieldType) {
case FieldType.Checkbox:
return CheckboxCell(cellData: cellData);
return CheckboxCell(cellData: cellData, key: ObjectKey(cellData));
case FieldType.DateTime:
return DateCell(cellData: cellData);
return DateCell(cellData: cellData, key: ObjectKey(cellData));
case FieldType.MultiSelect:
return MultiSelectCell(cellContext: cellData);
return MultiSelectCell(cellData: cellData, key: ObjectKey(cellData));
case FieldType.Number:
return NumberCell(cellData: cellData);
return NumberCell(cellData: cellData, key: ObjectKey(cellData));
case FieldType.RichText:
return GridTextCell(cellData: cellData);
return GridTextCell(cellData: cellData, key: ObjectKey(cellData));
case FieldType.SingleSelect:
return SingleSelectCell(cellContext: cellData);
return SingleSelectCell(cellData: cellData, key: ObjectKey(cellData));
default:
throw UnimplementedError;
}

View File

@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CheckboxCell extends StatefulWidget {
final GridCellData cellData;
final FutureCellData cellData;
const CheckboxCell({
required this.cellData,

View File

@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DateCell extends StatefulWidget {
final GridCellData cellData;
final FutureCellData cellData;
const DateCell({
required this.cellData,

View File

@ -4,61 +4,38 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.d
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
import 'cell_container.dart';
class GridRowWidget extends StatefulWidget {
class GridRowWidget extends StatelessWidget {
final GridRowData data;
GridRowWidget({required this.data, Key? key}) : super(key: ValueKey(data.rowId));
@override
State<GridRowWidget> createState() => _GridRowWidgetState();
}
class _GridRowWidgetState extends State<GridRowWidget> {
late RowBloc _rowBloc;
@override
void initState() {
_rowBloc = getIt<RowBloc>(param1: widget.data)..add(const RowEvent.initial());
super.initState();
}
const GridRowWidget({required this.data, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _rowBloc,
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(),
],
),
);
},
),
return BlocProvider(
create: (context) => getIt<RowBloc>(param1: data)..add(const RowEvent.initial()),
child: BlocBuilder<RowBloc, RowState>(
buildWhen: (p, c) => p.rowHeight != c.rowHeight,
builder: (context, state) {
return SizedBox(
height: state.rowHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
_RowLeading(),
_RowCells(),
_RowTrailing(),
],
),
);
},
),
);
}
@override
Future<void> dispose() async {
_rowBloc.close();
super.dispose();
}
}
class _RowLeading extends StatelessWidget {
@ -115,30 +92,19 @@ class _RowCells extends StatelessWidget {
@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,
);
return Row(children: [
...state.fields.map(
(field) {
final cellData = state.cellDataMap.then((fut) => fut[field.id]);
return CellContainer(
width: field.width.toDouble(),
child: buildGridCell(field.fieldType, cellData),
);
},
),
]);
},
);
}
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),
);
}
}

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NumberCell extends StatefulWidget {
final GridCellData cellData;
final FutureCellData cellData;
const NumberCell({
required this.cellData,

View File

@ -3,10 +3,10 @@ import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:flutter/material.dart';
class SingleSelectCell extends StatefulWidget {
final GridCellData cellContext;
final FutureCellData cellData;
const SingleSelectCell({
required this.cellContext,
required this.cellData,
Key? key,
}) : super(key: key);
@ -19,7 +19,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
@override
void initState() {
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellContext);
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData);
super.initState();
}
@ -37,10 +37,10 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
//----------------------------------------------------------------
class MultiSelectCell extends StatefulWidget {
final GridCellData cellContext;
final FutureCellData cellData;
const MultiSelectCell({
required this.cellContext,
required this.cellData,
Key? key,
}) : super(key: key);
@ -53,7 +53,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
@override
void initState() {
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellContext);
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellData);
super.initState();
}

View File

@ -8,7 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
/// The interface of base cell.
class GridTextCell extends StatefulWidget {
final GridCellData cellData;
final FutureCellData cellData;
const GridTextCell({
required this.cellData,
Key? key,
@ -36,9 +36,11 @@ class _GridTextCellState extends State<GridTextCell> {
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc!,
child: BlocBuilder<TextCellBloc, TextCellState>(
buildWhen: (previous, current) {
return _controller.text != current.content;
child: BlocConsumer<TextCellBloc, TextCellState>(
listener: (context, state) {
if (_controller.text != state.content) {
_controller.text = state.content;
}
},
builder: (context, state) {
return TextField(

View File

@ -1,10 +1,10 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/field/create_field_bloc.dart';
import 'package:app_flowy/workspace/application/grid/field/switch_field_type_bloc.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'field_name_input.dart';
@ -21,7 +21,7 @@ class CreateFieldPannel extends FlowyOverlayDelegate {
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: _CreateFieldPannelWidget(_createFieldBloc),
constraints: BoxConstraints.loose(const Size(220, 200)),
constraints: BoxConstraints.loose(const Size(220, 400)),
),
identifier: identifier(),
anchorContext: context,
@ -36,7 +36,7 @@ class CreateFieldPannel extends FlowyOverlayDelegate {
}
@override
void didRemove() async {
void didRemove() {
_createFieldBloc.add(const CreateFieldEvent.done());
}
}
@ -51,16 +51,16 @@ class _CreateFieldPannelWidget extends StatelessWidget {
value: createFieldBloc,
child: BlocBuilder<CreateFieldBloc, CreateFieldState>(
builder: (context, state) {
return state.editContext.fold(
return state.field.fold(
() => const SizedBox(),
(editContext) => ListView(
(field) => ListView(
shrinkWrap: true,
children: [
const FlowyText.medium("Edit property", fontSize: 12),
const VSpace(10),
_FieldNameTextField(editContext.gridField),
const _FieldNameTextField(),
const VSpace(10),
_FieldTypeSwitcher(editContext),
_FieldTypeSwitcher(SwitchFieldContext(state.gridId, field, state.typeOptionData)),
const VSpace(10),
],
),
@ -72,26 +72,35 @@ class _CreateFieldPannelWidget extends StatelessWidget {
}
class _FieldTypeSwitcher extends StatelessWidget {
final EditFieldContext editContext;
const _FieldTypeSwitcher(this.editContext, {Key? key}) : super(key: key);
final SwitchFieldContext switchContext;
const _FieldTypeSwitcher(this.switchContext, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return FieldTypeSwitcher(editContext: editContext);
}
}
class _FieldNameTextField extends StatelessWidget {
final Field field;
const _FieldNameTextField(this.field, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return FieldNameTextField(
name: field.name,
errorText: context.read<CreateFieldBloc>().state.errorText,
onNameChanged: (newName) {
context.read<CreateFieldBloc>().add(CreateFieldEvent.updateName(newName));
return FieldTypeSwitcher(
switchContext: switchContext,
onSelected: (field, typeOptionData) {
context.read<CreateFieldBloc>().add(CreateFieldEvent.switchField(field, typeOptionData));
},
);
}
}
class _FieldNameTextField extends StatelessWidget {
const _FieldNameTextField({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<CreateFieldBloc, CreateFieldState>(
buildWhen: (previous, current) => previous.fieldName != current.fieldName,
builder: (context, state) {
return FieldNameTextField(
name: state.fieldName,
errorText: context.read<CreateFieldBloc>().state.errorText,
onNameChanged: (newName) {
context.read<CreateFieldBloc>().add(CreateFieldEvent.updateName(newName));
},
);
},
);
}

View File

@ -60,7 +60,12 @@ class _FieldTypeSwitcher extends StatelessWidget {
return BlocBuilder<EditFieldBloc, EditFieldState>(
builder: (context, state) {
final editContext = context.read<EditFieldBloc>().state.editContext;
return FieldTypeSwitcher(editContext: editContext);
final switchContext = SwitchFieldContext(
editContext.gridId,
editContext.gridField,
editContext.typeOptionData,
);
return FieldTypeSwitcher(switchContext: switchContext, onSelected: (field, typeOptionData) {});
},
);
}

View File

@ -17,20 +17,22 @@ import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
typedef SelectFieldCallback = void Function(FieldType);
typedef SelectFieldCallback = void Function(Field, Uint8List);
class FieldTypeSwitcher extends StatelessWidget {
final EditFieldContext editContext;
final SwitchFieldContext switchContext;
final SelectFieldCallback onSelected;
const FieldTypeSwitcher({
required this.editContext,
required this.switchContext,
required this.onSelected,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<SwitchFieldTypeBloc>(param1: editContext),
create: (context) => getIt<SwitchFieldTypeBloc>(param1: switchContext),
child: BlocBuilder<SwitchFieldTypeBloc, SwitchFieldTypeState>(
builder: (context, state) {
List<Widget> children = [
@ -133,7 +135,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<NumberTypeOptionBloc>(),
child: Container(),
child: Container(height: 30, color: Colors.green),
);
}
}
@ -157,7 +159,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DateTypeOptionBloc>(),
child: Container(),
child: Container(height: 80, color: Colors.red),
);
}
}
@ -194,7 +196,7 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<SelectionTypeOptionBloc>(),
child: Container(),
child: Container(height: 100, color: Colors.yellow),
);
}
}
@ -219,7 +221,7 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<SelectionTypeOptionBloc>(),
child: Container(),
child: Container(height: 100, color: Colors.blue),
);
}
}

View File

@ -20,7 +20,7 @@ class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return GridHeader(gridId: gridId, fields: fields);
return GridHeader(gridId: gridId, fields: fields, key: ObjectKey(fields));
}
@override

View File

@ -63,13 +63,11 @@ class _StyledSingleChildScrollViewState extends State<StyledSingleChildScrollVie
barSize: widget.barSize,
trackColor: widget.trackColor,
handleColor: widget.handleColor,
child: SizedBox.expand(
child: SingleChildScrollView(
scrollDirection: widget.axis,
physics: StyledScrollPhysics(),
controller: scrollController,
child: widget.child,
),
child: SingleChildScrollView(
scrollDirection: widget.axis,
physics: StyledScrollPhysics(),
controller: scrollController,
child: widget.child,
),
);
}

View File

@ -1,9 +1,9 @@
#[macro_export]
macro_rules! impl_into_box_type_option_builder {
($target: ident) => {
impl std::convert::Into<BoxTypeOptionBuilder> for $target {
fn into(self) -> Box<dyn TypeOptionBuilder> {
Box::new(self)
impl std::convert::From<$target> for BoxTypeOptionBuilder {
fn from(target: $target) -> BoxTypeOptionBuilder {
Box::new(target)
}
}
};

View File

@ -6,8 +6,7 @@ use crate::dart_notification::{send_dart_notification, GridNotification};
use dashmap::DashMap;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{
FieldMeta, GridBlockId, GridBlockMeta, GridBlockMetaChangeset, GridBlockOrder, RepeatedCell, RowMeta,
RowMetaChangeset, RowOrder,
FieldMeta, GridBlockMeta, GridBlockMetaChangeset, GridBlockOrder, RepeatedCell, RowMeta, RowMetaChangeset, RowOrder,
};
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_revision::{
@ -71,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_block_did_update_row(&block_id).await?;
self.notify_block_did_update_row(block_id).await?;
Ok(row_count)
}

View File

@ -69,20 +69,20 @@ fn string_to_bool(bool_str: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::services::cell::CheckboxTypeOption;
use crate::services::field::CheckboxTypeOption;
use crate::services::row::CellDataSerde;
#[test]
fn checkout_box_description_test() {
let type_option = CheckboxTypeOption::default();
assert_eq!(description.serialize_cell_data("true").unwrap(), "1".to_owned());
assert_eq!(description.serialize_cell_data("1").unwrap(), "1".to_owned());
assert_eq!(description.serialize_cell_data("yes").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("true").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("1").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("yes").unwrap(), "1".to_owned());
assert_eq!(description.serialize_cell_data("false").unwrap(), "0".to_owned());
assert_eq!(description.serialize_cell_data("no").unwrap(), "0".to_owned());
assert_eq!(description.serialize_cell_data("123").unwrap(), "0".to_owned());
assert_eq!(type_option.serialize_cell_data("false").unwrap(), "0".to_owned());
assert_eq!(type_option.serialize_cell_data("no").unwrap(), "0".to_owned());
assert_eq!(type_option.serialize_cell_data("123").unwrap(), "0".to_owned());
assert_eq!(description.deserialize_cell_data("1".to_owned()), "1".to_owned());
assert_eq!(type_option.deserialize_cell_data("1".to_owned()), "1".to_owned());
}
}

View File

@ -177,7 +177,7 @@ impl std::default::Default for TimeFormat {
#[cfg(test)]
mod tests {
use crate::services::cell::{DateFormat, DateTypeOption, TimeFormat};
use crate::services::field::{DateFormat, DateTypeOption, TimeFormat};
use crate::services::row::CellDataSerde;
use strum::IntoEnumIterator;
@ -267,6 +267,6 @@ mod tests {
#[should_panic]
fn date_description_invalid_data_test() {
let type_option = DateTypeOption::default();
description.serialize_cell_data("he").unwrap();
type_option.serialize_cell_data("he").unwrap();
}
}

View File

@ -192,41 +192,41 @@ fn make_strip_symbol() -> Vec<String> {
#[cfg(test)]
mod tests {
use crate::services::cell::{NumberFormat, NumberTypeOption};
use crate::services::field::{NumberFormat, NumberTypeOption};
use crate::services::row::CellDataSerde;
use strum::IntoEnumIterator;
#[test]
fn number_description_test() {
let mut description = NumberTypeOption::default();
assert_eq!(description.serialize_cell_data("¥18,443").unwrap(), "18443".to_owned());
assert_eq!(description.serialize_cell_data("$18,443").unwrap(), "18443".to_owned());
assert_eq!(description.serialize_cell_data("€18.443").unwrap(), "18443".to_owned());
let mut type_option = NumberTypeOption::default();
assert_eq!(type_option.serialize_cell_data("¥18,443").unwrap(), "18443".to_owned());
assert_eq!(type_option.serialize_cell_data("$18,443").unwrap(), "18443".to_owned());
assert_eq!(type_option.serialize_cell_data("€18.443").unwrap(), "18443".to_owned());
for format in NumberFormat::iter() {
description.format = format;
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"$18,443".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"¥18,443".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"€18.443".to_owned()
);
}
@ -236,35 +236,35 @@ mod tests {
#[test]
fn number_description_scale_test() {
let mut description = NumberTypeOption {
let mut type_option = NumberTypeOption {
scale: 1,
..Default::default()
};
for format in NumberFormat::iter() {
description.format = format;
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"$1,844.3".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"¥1,844.3".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"€1.844,3".to_owned()
);
}
@ -274,35 +274,35 @@ mod tests {
#[test]
fn number_description_sign_test() {
let mut description = NumberTypeOption {
let mut type_option = NumberTypeOption {
sign_positive: false,
..Default::default()
};
for format in NumberFormat::iter() {
description.format = format;
type_option.format = format;
match format {
NumberFormat::Number => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"18443".to_owned()
);
}
NumberFormat::USD => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"-$18,443".to_owned()
);
}
NumberFormat::CNY => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"-¥18,443".to_owned()
);
}
NumberFormat::EUR => {
assert_eq!(
description.deserialize_cell_data("18443".to_owned()),
type_option.deserialize_cell_data("18443".to_owned()),
"-€18.443".to_owned()
);
}

View File

@ -166,16 +166,16 @@ impl SelectOption {
#[cfg(test)]
mod tests {
use crate::services::cell::{MultiSelectDescription, SingleSelectTypeOption};
use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
use crate::services::row::CellDataSerde;
#[test]
#[should_panic]
fn selection_description_test() {
let type_option = SingleSelectTypeOption::default();
assert_eq!(description.serialize_cell_data("1,2,3").unwrap(), "1".to_owned());
assert_eq!(type_option.serialize_cell_data("1,2,3").unwrap(), "1".to_owned());
let type_option = MultiSelectDescription::default();
assert_eq!(description.serialize_cell_data("1,2,3").unwrap(), "1,2,3".to_owned());
let type_option = MultiSelectTypeOption::default();
assert_eq!(type_option.serialize_cell_data("1,2,3").unwrap(), "1,2,3".to_owned());
}
}

View File

@ -1,7 +1,7 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser;
use crate::services::block_meta_editor::GridBlockMetaEditorManager;
use crate::services::field::{default_type_option_builder_from_type, type_option_json_str_from_bytes, FieldBuilder};
use crate::services::field::{type_option_json_str_from_bytes, FieldBuilder};
use crate::services::row::*;
use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult};
@ -78,8 +78,8 @@ impl ClientGridEditor {
}
pub async fn default_field_meta(&self, field_type: &FieldType) -> FlowyResult<FieldMeta> {
let name = format!("Property {}", self.pad.read().await.fields().len());
let field_meta = FieldBuilder::from_field_type(&field_type).name(&name).build();
let name = format!("Property {}", self.pad.read().await.fields().len() + 1);
let field_meta = FieldBuilder::from_field_type(field_type).name(&name).build();
Ok(field_meta)
}

View File

@ -1,5 +1,5 @@
use crate::services::field::*;
use flowy_grid_data_model::entities::{BuildGridContext, FieldType};
use flowy_grid_data_model::entities::BuildGridContext;
use flowy_sync::client_grid::GridBuilder;
pub fn make_default_grid() -> BuildGridContext {

View File

@ -254,19 +254,19 @@ async fn grid_row_add_cells_test() {
}
FieldType::SingleSelect => {
let type_option = SingleSelectTypeOption::from(field);
let options = description.options.first().unwrap();
let data = description.serialize_cell_data(&options.id).unwrap();
let options = type_option.options.first().unwrap();
let data = type_option.serialize_cell_data(&options.id).unwrap();
builder.add_cell(&field.id, data).unwrap();
}
FieldType::MultiSelect => {
let type_option = MultiSelectTypeOption::from(field);
let options = description
let options = type_option
.options
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(SELECTION_IDS_SEPARATOR);
let data = description.serialize_cell_data(&options).unwrap();
let data = type_option.serialize_cell_data(&options).unwrap();
builder.add_cell(&field.id, data).unwrap();
}
FieldType::Checkbox => {
@ -383,11 +383,11 @@ async fn grid_cell_update() {
FieldType::DateTime => "123".to_string(),
FieldType::SingleSelect => {
let type_option = SingleSelectTypeOption::from(field_meta);
description.options.first().unwrap().id.clone()
type_option.options.first().unwrap().id.clone()
}
FieldType::MultiSelect => {
let type_option = MultiSelectTypeOption::from(field_meta);
description.options.first().unwrap().id.clone()
type_option.options.first().unwrap().id.clone()
}
FieldType::Checkbox => "1".to_string(),
};

View File

@ -3,8 +3,8 @@ use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
use flowy_grid::services::row::CreateRowMetaPayload;
use flowy_grid_data_model::entities::{
BuildGridContext, CellMetaChangeset, CreateFieldPayload, Field, FieldChangeset, FieldMeta, FieldType,
GridBlockMeta, GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder,
BuildGridContext, CellMetaChangeset, Field, FieldChangeset, FieldMeta, FieldType, GridBlockMeta,
GridBlockMetaChangeset, RowMeta, RowMetaChangeset, RowOrder,
};
use flowy_grid_data_model::parser::CreateFieldParams;
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;

View File

@ -4,6 +4,7 @@ mod op_test;
mod serde_test;
mod undo_redo_test;
use derive_more::Display;
use flowy_sync::client_document::{ClientDocument, InitialDocumentText};
use lib_ot::{
core::*,

View File

@ -39,20 +39,6 @@ pub struct Field {
pub width: i32,
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct FieldOrder {
#[pb(index = 1)]
pub field_id: String,
}
impl std::convert::From<&FieldMeta> for FieldOrder {
fn from(field_meta: &FieldMeta) -> Self {
Self {
field_id: field_meta.id.clone(),
}
}
}
impl std::convert::From<FieldMeta> for Field {
fn from(field_meta: FieldMeta) -> Self {
Self {
@ -67,6 +53,20 @@ impl std::convert::From<FieldMeta> for Field {
}
}
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct FieldOrder {
#[pb(index = 1)]
pub field_id: String,
}
impl std::convert::From<&FieldMeta> for FieldOrder {
fn from(field_meta: &FieldMeta) -> Self {
Self {
field_id: field_meta.id.clone(),
}
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct CreateEditFieldContextParams {
#[pb(index = 1)]

View File

@ -4,12 +4,11 @@ use uuid::Uuid;
pub struct NotEmptyUuid(pub String);
impl NotEmptyUuid {
pub fn parse(s: String) -> Result<NotEmptyUuid, ()> {
debug_assert!(Uuid::parse_str(&s).is_ok());
pub fn parse(s: String) -> Result<Self, String> {
if s.trim().is_empty() {
return Err(());
return Err("Input string is empty".to_owned());
}
debug_assert!(Uuid::parse_str(&s).is_ok());
Ok(Self(s))
}

View File

@ -5,7 +5,7 @@ use bytes::Bytes;
use flowy_grid_data_model::entities::{
FieldChangeset, FieldMeta, FieldOrder, GridBlockMeta, GridBlockMetaChangeset, GridMeta, RepeatedFieldOrder,
};
use flowy_grid_data_model::parser::CreateFieldParams;
use lib_infra::uuid;
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use std::collections::HashMap;
@ -43,12 +43,7 @@ impl GridMetaPad {
) -> CollaborateResult<Option<GridChangeset>> {
self.modify_grid(|grid| {
// Check if the field exists or not
if grid
.fields
.iter()
.find(|field_meta| field_meta.id == new_field_meta.id)
.is_some()
{
if grid.fields.iter().any(|field_meta| field_meta.id == new_field_meta.id) {
tracing::warn!("Duplicate grid field");
return Ok(None);
}