mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #457 from AppFlowy-IO/fix_0.0.4_beta_2_bugs
Fix 0.0.4 beta 2 bugs
This commit is contained in:
commit
3af558fd94
2
.github/workflows/rust_test.yml
vendored
2
.github/workflows/rust_test.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
run: RUST_LOG=info cargo test --no-default-features
|
||||
run: RUST_LOG=info cargo test --no-default-features --features="sync"
|
||||
|
||||
- name: Run shared-lib tests
|
||||
working-directory: shared-lib
|
||||
|
@ -17,7 +17,6 @@ import 'package:app_flowy/user/presentation/router.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.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:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
@ -150,16 +149,10 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
(view, _) => GridBloc(view: view),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<RowBloc, RowData, void>(
|
||||
(data, _) => RowBloc(
|
||||
rowData: data,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<GridHeaderBloc, String, List<Field>>(
|
||||
(gridId, fields) => GridHeaderBloc(
|
||||
data: GridHeaderData(gridId: gridId, fields: fields),
|
||||
service: FieldService(gridId: gridId),
|
||||
getIt.registerFactoryParam<GridHeaderBloc, String, GridFieldCache>(
|
||||
(gridId, fieldCache) => GridHeaderBloc(
|
||||
gridId: gridId,
|
||||
fieldCache: fieldCache,
|
||||
),
|
||||
);
|
||||
|
||||
@ -177,32 +170,32 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<TextCellBloc, CellData, void>(
|
||||
getIt.registerFactoryParam<TextCellBloc, GridCellIdentifier, void>(
|
||||
(cellData, _) => TextCellBloc(
|
||||
service: CellService(),
|
||||
cellData: cellData,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<SelectionCellBloc, CellData, void>(
|
||||
getIt.registerFactoryParam<SelectionCellBloc, GridCellIdentifier, void>(
|
||||
(cellData, _) => SelectionCellBloc(
|
||||
cellData: cellData,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<NumberCellBloc, CellData, void>(
|
||||
getIt.registerFactoryParam<NumberCellBloc, GridCellIdentifier, void>(
|
||||
(cellData, _) => NumberCellBloc(
|
||||
cellData: cellData,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<DateCellBloc, CellData, void>(
|
||||
getIt.registerFactoryParam<DateCellBloc, GridCellIdentifier, void>(
|
||||
(cellData, _) => DateCellBloc(
|
||||
cellData: cellData,
|
||||
cellIdentifier: cellData,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<CheckboxCellBloc, CellData, void>(
|
||||
getIt.registerFactoryParam<CheckboxCellBloc, GridCellIdentifier, void>(
|
||||
(cellData, _) => CheckboxCellBloc(
|
||||
service: CellService(),
|
||||
cellData: cellData,
|
||||
@ -229,7 +222,7 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
(typeOption, _) => NumberTypeOptionBloc(typeOption: typeOption),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<GridPropertyBloc, String, List<Field>>(
|
||||
(gridId, fields) => GridPropertyBloc(gridId: gridId, fields: fields),
|
||||
getIt.registerFactoryParam<GridPropertyBloc, String, GridFieldCache>(
|
||||
(gridId, cache) => GridPropertyBloc(gridId: gridId, fieldCache: cache),
|
||||
);
|
||||
}
|
||||
|
@ -123,9 +123,9 @@ class ApplicationBlocObserver extends BlocObserver {
|
||||
super.onError(bloc, error, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
void onEvent(Bloc bloc, Object? event) {
|
||||
Log.debug("$event");
|
||||
super.onEvent(bloc, event);
|
||||
}
|
||||
// @override
|
||||
// void onEvent(Bloc bloc, Object? event) {
|
||||
// Log.debug("$event");
|
||||
// super.onEvent(bloc, event);
|
||||
// }
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ typedef UpdateFieldNotifiedValue = Either<CellNotificationData, FlowyError>;
|
||||
class CellListener {
|
||||
final String rowId;
|
||||
final String fieldId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue> updateCellNotifier = PublishNotifier();
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? updateCellNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
CellListener({required this.rowId, required this.fieldId});
|
||||
|
||||
@ -24,8 +24,8 @@ class CellListener {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateCell:
|
||||
result.fold(
|
||||
(payload) => updateCellNotifier.value = left(CellNotificationData.fromBuffer(payload)),
|
||||
(error) => updateCellNotifier.value = right(error),
|
||||
(payload) => updateCellNotifier?.value = left(CellNotificationData.fromBuffer(payload)),
|
||||
(error) => updateCellNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@ -35,6 +35,7 @@ class CellListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateCellNotifier.dispose();
|
||||
updateCellNotifier?.dispose();
|
||||
updateCellNotifier = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.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/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
|
||||
class CellService {
|
||||
CellService();
|
||||
|
||||
Future<Either<void, FlowyError>> updateCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
required String data,
|
||||
}) {
|
||||
final payload = CellChangeset.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId
|
||||
..data = data;
|
||||
return GridEventUpdateCell(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Cell, FlowyError>> getCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
}) {
|
||||
final payload = CellIdentifierPayload.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
return GridEventGetCell(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
class CellCache {
|
||||
final CellService _cellService;
|
||||
final HashMap<String, Cell> _cellDataMap = HashMap();
|
||||
|
||||
CellCache() : _cellService = CellService();
|
||||
|
||||
Future<Option<Cell>> getCellData(GridCellIdentifier identifier) async {
|
||||
final cellId = _cellId(identifier);
|
||||
final Cell? data = _cellDataMap[cellId];
|
||||
if (data != null) {
|
||||
return Future(() => Some(data));
|
||||
}
|
||||
|
||||
final result = await _cellService.getCell(
|
||||
gridId: identifier.gridId,
|
||||
fieldId: identifier.field.id,
|
||||
rowId: identifier.rowId,
|
||||
);
|
||||
|
||||
return result.fold(
|
||||
(cell) {
|
||||
_cellDataMap[_cellId(identifier)] = cell;
|
||||
return Some(cell);
|
||||
},
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return none();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _cellId(GridCellIdentifier identifier) {
|
||||
return "${identifier.rowId}/${identifier.field.id}";
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
@ -15,7 +15,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
|
||||
CheckboxCellBloc({
|
||||
required CellService service,
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
}) : _service = service,
|
||||
_listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
super(CheckboxCellState.initial(cellData)) {
|
||||
@ -43,7 +43,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.updateCellNotifier.addPublishListener((result) {
|
||||
_listener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) async => await _loadCellData(),
|
||||
(err) => Log.error(err),
|
||||
@ -58,12 +58,11 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
result.fold(
|
||||
(cell) {
|
||||
if (!isClosed) {
|
||||
add(CheckboxCellEvent.didReceiveCellUpdate(cell));
|
||||
}
|
||||
},
|
||||
(cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
@ -88,11 +87,11 @@ class CheckboxCellEvent with _$CheckboxCellEvent {
|
||||
@freezed
|
||||
class CheckboxCellState with _$CheckboxCellState {
|
||||
const factory CheckboxCellState({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
required bool isSelected,
|
||||
}) = _CheckboxCellState;
|
||||
|
||||
factory CheckboxCellState.initial(CellData cellData) {
|
||||
factory CheckboxCellState.initial(GridCellIdentifier cellData) {
|
||||
return CheckboxCellState(cellData: cellData, isSelected: _isSelected(cellData.cell));
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -13,13 +13,13 @@ part 'date_cell_bloc.freezed.dart';
|
||||
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
final CellService _service;
|
||||
final CellListener _cellListener;
|
||||
final FieldListener _fieldListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
|
||||
DateCellBloc({required CellData cellData})
|
||||
DateCellBloc({required GridCellIdentifier cellIdentifier})
|
||||
: _service = CellService(),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_fieldListener = FieldListener(fieldId: cellData.field.id),
|
||||
super(DateCellState.initial(cellData)) {
|
||||
_cellListener = CellListener(rowId: cellIdentifier.rowId, fieldId: cellIdentifier.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellIdentifier.field.id),
|
||||
super(DateCellState.initial(cellIdentifier)) {
|
||||
on<DateCellEvent>(
|
||||
(event, emit) async {
|
||||
event.map(
|
||||
@ -35,6 +35,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
content: value.cell.content,
|
||||
));
|
||||
},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
emit(state.copyWith(field: value.field));
|
||||
_loadCellData();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -48,20 +52,20 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_cellListener.updateCellNotifier.addPublishListener((result) {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) => _loadCellData(),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}, listenWhen: () => !isClosed);
|
||||
_cellListener.start();
|
||||
|
||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||
_fieldListener.updateFieldNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => _loadCellData(),
|
||||
(field) => add(DateCellEvent.didReceiveFieldUpdate(field)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}, listenWhen: () => !isClosed);
|
||||
_fieldListener.start();
|
||||
}
|
||||
|
||||
@ -71,12 +75,11 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
result.fold(
|
||||
(cell) {
|
||||
if (!isClosed) {
|
||||
add(DateCellEvent.didReceiveCellUpdate(cell));
|
||||
}
|
||||
},
|
||||
(cell) => add(DateCellEvent.didReceiveCellUpdate(cell)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
@ -97,18 +100,21 @@ class DateCellEvent with _$DateCellEvent {
|
||||
const factory DateCellEvent.initial() = _InitialCell;
|
||||
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
|
||||
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DateCellState with _$DateCellState {
|
||||
const factory DateCellState({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
required String content,
|
||||
required Field field,
|
||||
DateTime? selectedDay,
|
||||
}) = _DateCellState;
|
||||
|
||||
factory DateCellState.initial(CellData cellData) => DateCellState(
|
||||
factory DateCellState.initial(GridCellIdentifier cellData) => DateCellState(
|
||||
cellData: cellData,
|
||||
field: cellData.field,
|
||||
content: cellData.cell?.content ?? "",
|
||||
);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
@ -11,12 +12,14 @@ part 'number_cell_bloc.freezed.dart';
|
||||
|
||||
class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
final CellService _service;
|
||||
final CellListener _listener;
|
||||
final CellListener _cellListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
|
||||
NumberCellBloc({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
}) : _service = CellService(),
|
||||
_listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellData.field.id),
|
||||
super(NumberCellState.initial(cellData)) {
|
||||
on<NumberCellEvent>(
|
||||
(event, emit) async {
|
||||
@ -27,36 +30,36 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
|
||||
emit(state.copyWith(content: value.cell.content));
|
||||
},
|
||||
updateCell: (_UpdateCell value) {
|
||||
_updateCellValue(value, emit);
|
||||
updateCell: (_UpdateCell value) async {
|
||||
await _updateCellValue(value, emit);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _updateCellValue(_UpdateCell value, Emitter<NumberCellState> emit) {
|
||||
final number = num.tryParse(value.text);
|
||||
if (number == null) {
|
||||
emit(state.copyWith(content: ""));
|
||||
} else {
|
||||
_service.updateCell(
|
||||
gridId: state.cellData.gridId,
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
data: value.text,
|
||||
);
|
||||
}
|
||||
Future<void> _updateCellValue(_UpdateCell value, Emitter<NumberCellState> emit) async {
|
||||
final result = await _service.updateCell(
|
||||
gridId: state.cellData.gridId,
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
data: value.text,
|
||||
);
|
||||
result.fold(
|
||||
(field) => _getCellData(),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener.stop();
|
||||
await _cellListener.stop();
|
||||
await _fieldListener.stop();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.updateCellNotifier.addPublishListener((result) {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) async {
|
||||
await _getCellData();
|
||||
@ -64,7 +67,15 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_listener.start();
|
||||
_cellListener.start();
|
||||
|
||||
_fieldListener.updateFieldNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => _getCellData(),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_fieldListener.start();
|
||||
}
|
||||
|
||||
Future<void> _getCellData() async {
|
||||
@ -73,12 +84,12 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
);
|
||||
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
result.fold(
|
||||
(cell) {
|
||||
if (!isClosed) {
|
||||
add(NumberCellEvent.didReceiveCellUpdate(cell));
|
||||
}
|
||||
},
|
||||
(cell) => add(NumberCellEvent.didReceiveCellUpdate(cell)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
@ -94,11 +105,11 @@ class NumberCellEvent with _$NumberCellEvent {
|
||||
@freezed
|
||||
class NumberCellState with _$NumberCellState {
|
||||
const factory NumberCellState({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
required String content,
|
||||
}) = _NumberCellState;
|
||||
|
||||
factory NumberCellState.initial(CellData cellData) {
|
||||
factory NumberCellState.initial(GridCellIdentifier cellData) {
|
||||
return NumberCellState(cellData: cellData, content: cellData.cell?.content ?? "");
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/select_option_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
@ -13,13 +13,13 @@ part 'selection_cell_bloc.freezed.dart';
|
||||
class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
|
||||
final SelectOptionService _service;
|
||||
final CellListener _cellListener;
|
||||
final FieldListener _fieldListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
|
||||
SelectionCellBloc({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
}) : _service = SelectOptionService(),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_fieldListener = FieldListener(fieldId: cellData.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellData.field.id),
|
||||
super(SelectionCellState.initial(cellData)) {
|
||||
on<SelectionCellEvent>(
|
||||
(event, emit) async {
|
||||
@ -49,22 +49,21 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.fold(
|
||||
(selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
add(SelectionCellEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
));
|
||||
}
|
||||
},
|
||||
(selectOptionContext) => add(SelectionCellEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_cellListener.updateCellNotifier.addPublishListener((result) {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) => _loadOptions(),
|
||||
(err) => Log.error(err),
|
||||
@ -72,7 +71,7 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
|
||||
});
|
||||
_cellListener.start();
|
||||
|
||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||
_fieldListener.updateFieldNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => _loadOptions(),
|
||||
(err) => Log.error(err),
|
||||
@ -94,12 +93,12 @@ class SelectionCellEvent with _$SelectionCellEvent {
|
||||
@freezed
|
||||
class SelectionCellState with _$SelectionCellState {
|
||||
const factory SelectionCellState({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
required List<SelectOption> options,
|
||||
required List<SelectOption> selectedOptions,
|
||||
}) = _SelectionCellState;
|
||||
|
||||
factory SelectionCellState.initial(CellData cellData) => SelectionCellState(
|
||||
factory SelectionCellState.initial(GridCellIdentifier cellData) => SelectionCellState(
|
||||
cellData: cellData,
|
||||
options: [],
|
||||
selectedOptions: [],
|
@ -1,4 +1,4 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
@ -13,16 +13,16 @@ part 'selection_editor_bloc.freezed.dart';
|
||||
|
||||
class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
|
||||
final SelectOptionService _selectOptionService;
|
||||
final FieldListener _fieldListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
final CellListener _cellListener;
|
||||
Timer? _delayOperation;
|
||||
|
||||
SelectOptionEditorBloc({
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
required List<SelectOption> options,
|
||||
required List<SelectOption> selectedOptions,
|
||||
}) : _selectOptionService = SelectOptionService(),
|
||||
_fieldListener = FieldListener(fieldId: cellData.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellData.field.id),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
super(SelectOptionEditorState.initial(cellData, options, selectedOptions)) {
|
||||
on<SelectOptionEditorEvent>(
|
||||
@ -117,16 +117,15 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
|
||||
fieldId: state.field.id,
|
||||
rowId: state.rowId,
|
||||
);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.fold(
|
||||
(selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
add(SelectOptionEditorEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
));
|
||||
}
|
||||
},
|
||||
(selectOptionContext) => add(SelectOptionEditorEvent.didReceiveOptions(
|
||||
selectOptionContext.options,
|
||||
selectOptionContext.selectOptions,
|
||||
)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
@ -134,7 +133,7 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_cellListener.updateCellNotifier.addPublishListener((result) {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) => _loadOptions(),
|
||||
(err) => Log.error(err),
|
||||
@ -142,12 +141,12 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
|
||||
});
|
||||
_cellListener.start();
|
||||
|
||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||
_fieldListener.updateFieldNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => add(SelectOptionEditorEvent.didReceiveFieldUpdate(field)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}, listenWhen: () => !isClosed);
|
||||
_fieldListener.start();
|
||||
}
|
||||
}
|
||||
@ -175,7 +174,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
}) = _SelectOptionEditorState;
|
||||
|
||||
factory SelectOptionEditorState.initial(
|
||||
CellData cellData,
|
||||
GridCellIdentifier cellData,
|
||||
List<SelectOption> options,
|
||||
List<SelectOption> selectedOptions,
|
||||
) {
|
@ -11,7 +11,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
|
||||
TextCellBloc({
|
||||
required this.service,
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
}) : super(TextCellState.initial(cellData)) {
|
||||
on<TextCellEvent>(
|
||||
(event, emit) async {
|
||||
@ -53,7 +53,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
@freezed
|
||||
class TextCellEvent with _$TextCellEvent {
|
||||
const factory TextCellEvent.initial() = _InitialCell;
|
||||
const factory TextCellEvent.didReceiveCellData(CellData cellData) = _DidReceiveCellData;
|
||||
const factory TextCellEvent.didReceiveCellData(GridCellIdentifier cellData) = _DidReceiveCellData;
|
||||
const factory TextCellEvent.updateText(String text) = _UpdateText;
|
||||
}
|
||||
|
||||
@ -61,10 +61,10 @@ class TextCellEvent with _$TextCellEvent {
|
||||
class TextCellState with _$TextCellState {
|
||||
const factory TextCellState({
|
||||
required String content,
|
||||
required CellData cellData,
|
||||
required GridCellIdentifier cellData,
|
||||
}) = _TextCellState;
|
||||
|
||||
factory TextCellState.initial(CellData cellData) => TextCellState(
|
||||
factory TextCellState.initial(GridCellIdentifier cellData) => TextCellState(
|
||||
content: cellData.cell?.content ?? "",
|
||||
cellData: cellData,
|
||||
);
|
@ -1,35 +0,0 @@
|
||||
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/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
|
||||
class CellService {
|
||||
CellService();
|
||||
|
||||
Future<Either<void, FlowyError>> updateCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
required String data,
|
||||
}) {
|
||||
final payload = CellChangeset.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId
|
||||
..data = data;
|
||||
return GridEventUpdateCell(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Cell, FlowyError>> getCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
}) {
|
||||
final payload = CellIdentifierPayload.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
return GridEventGetCell(payload).send();
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
|
||||
class GridHeaderData {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
GridHeaderData({required this.gridId, required this.fields});
|
||||
}
|
@ -9,11 +9,13 @@ import 'dart:async';
|
||||
part 'field_cell_bloc.freezed.dart';
|
||||
|
||||
class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
final FieldListener _fieldListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
final FieldService _fieldService;
|
||||
|
||||
FieldCellBloc({
|
||||
required GridFieldCellContext cellContext,
|
||||
}) : _fieldListener = FieldListener(fieldId: cellContext.field.id),
|
||||
}) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
|
||||
_fieldService = FieldService(gridId: cellContext.gridId),
|
||||
super(FieldCellState.initial(cellContext)) {
|
||||
on<FieldCellEvent>(
|
||||
(event, emit) async {
|
||||
@ -24,6 +26,13 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
emit(state.copyWith(field: value.field));
|
||||
},
|
||||
updateWidth: (_UpdateWidth value) {
|
||||
final defaultWidth = state.field.width.toDouble();
|
||||
final width = defaultWidth + value.offset;
|
||||
if (width > defaultWidth && width < 300) {
|
||||
_fieldService.updateField(fieldId: state.field.id, width: width);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -36,12 +45,12 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||
_fieldListener.updateFieldNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(field) => add(FieldCellEvent.didReceiveFieldUpdate(field)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}, listenWhen: () => !isClosed);
|
||||
_fieldListener.start();
|
||||
}
|
||||
}
|
||||
@ -50,6 +59,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
|
||||
class FieldCellEvent with _$FieldCellEvent {
|
||||
const factory FieldCellEvent.initial() = _InitialCell;
|
||||
const factory FieldCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||
const factory FieldCellEvent.updateWidth(double offset) = _UpdateWidth;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -9,12 +9,12 @@ import 'package:app_flowy/core/notification_helper.dart';
|
||||
|
||||
typedef UpdateFieldNotifiedValue = Either<Field, FlowyError>;
|
||||
|
||||
class FieldListener {
|
||||
class SingleFieldListener {
|
||||
final String fieldId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue> updateFieldNotifier = PublishNotifier();
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? updateFieldNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
FieldListener({required this.fieldId});
|
||||
SingleFieldListener({required this.fieldId});
|
||||
|
||||
void start() {
|
||||
_listener = GridNotificationListener(
|
||||
@ -30,8 +30,8 @@ class FieldListener {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateField:
|
||||
result.fold(
|
||||
(payload) => updateFieldNotifier.value = left(Field.fromBuffer(payload)),
|
||||
(error) => updateFieldNotifier.value = right(error),
|
||||
(payload) => updateFieldNotifier?.value = left(Field.fromBuffer(payload)),
|
||||
(error) => updateFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@ -41,6 +41,7 @@ class FieldListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateFieldNotifier.dispose();
|
||||
updateFieldNotifier?.dispose();
|
||||
updateFieldNotifier = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
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:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'field_service.freezed.dart';
|
||||
|
||||
class FieldService {
|
||||
final String gridId;
|
||||
@ -19,6 +20,26 @@ class FieldService {
|
||||
return GridEventSwitchToField(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(String fieldId, FieldType fieldType) {
|
||||
final payload = GetEditFieldContextPayload.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType;
|
||||
|
||||
return GridEventGetEditFieldContext(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveField(String fieldId, int fromIndex, int toIndex) {
|
||||
final payload = MoveItemPayload.create()
|
||||
..gridId = gridId
|
||||
..itemId = fieldId
|
||||
..ty = MoveItemType.MoveField
|
||||
..fromIndex = fromIndex
|
||||
..toIndex = toIndex;
|
||||
|
||||
return GridEventMoveItem(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateField({
|
||||
required String fieldId,
|
||||
String? name,
|
||||
@ -98,17 +119,12 @@ class FieldService {
|
||||
}
|
||||
}
|
||||
|
||||
class GridFieldCellContext extends Equatable {
|
||||
final String gridId;
|
||||
final Field field;
|
||||
|
||||
const GridFieldCellContext({
|
||||
required this.gridId,
|
||||
required this.field,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [field.id];
|
||||
@freezed
|
||||
class GridFieldCellContext with _$GridFieldCellContext {
|
||||
const factory GridFieldCellContext({
|
||||
required String gridId,
|
||||
required Field field,
|
||||
}) = _GridFieldCellContext;
|
||||
}
|
||||
|
||||
abstract class EditFieldContextLoader {
|
||||
|
@ -7,11 +7,11 @@ import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:app_flowy/core/notification_helper.dart';
|
||||
|
||||
typedef UpdateFieldNotifiedValue = Either<List<Field>, FlowyError>;
|
||||
typedef UpdateFieldNotifiedValue = Either<GridFieldChangeset, FlowyError>;
|
||||
|
||||
class GridFieldsListener {
|
||||
final String gridId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue> updateFieldsNotifier = PublishNotifier();
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? updateFieldsNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
GridFieldsListener({required this.gridId});
|
||||
|
||||
@ -24,10 +24,10 @@ class GridFieldsListener {
|
||||
|
||||
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateGrid:
|
||||
case GridNotification.DidUpdateGridField:
|
||||
result.fold(
|
||||
(payload) => updateFieldsNotifier.value = left(RepeatedField.fromBuffer(payload).items),
|
||||
(error) => updateFieldsNotifier.value = right(error),
|
||||
(payload) => updateFieldsNotifier?.value = left(GridFieldChangeset.fromBuffer(payload)),
|
||||
(error) => updateFieldsNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@ -37,6 +37,7 @@ class GridFieldsListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateFieldsNotifier.dispose();
|
||||
updateFieldsNotifier?.dispose();
|
||||
updateFieldsNotifier = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'field/grid_listenr.dart';
|
||||
import 'grid_listener.dart';
|
||||
import 'grid_service.dart';
|
||||
import 'row/row_service.dart';
|
||||
|
||||
@ -15,20 +12,20 @@ part 'grid_bloc.freezed.dart';
|
||||
|
||||
class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
final GridService _gridService;
|
||||
final GridListener _gridListener;
|
||||
final GridFieldsListener _fieldListener;
|
||||
final GridFieldCache fieldCache;
|
||||
final GridRowCache rowCache;
|
||||
|
||||
GridBloc({required View view})
|
||||
: _fieldListener = GridFieldsListener(gridId: view.id),
|
||||
_gridService = GridService(gridId: view.id),
|
||||
_gridListener = GridListener(gridId: view.id),
|
||||
: _gridService = GridService(gridId: view.id),
|
||||
fieldCache = GridFieldCache(gridId: view.id),
|
||||
rowCache = GridRowCache(gridId: view.id),
|
||||
super(GridState.initial(view.id)) {
|
||||
on<GridEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (InitialGrid value) async {
|
||||
await _initGrid(emit);
|
||||
_startListening();
|
||||
await _loadGrid(emit);
|
||||
},
|
||||
createRow: (_CreateRow value) {
|
||||
_gridService.createRow();
|
||||
@ -37,12 +34,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
emit(state.copyWith(rows: value.rows, listState: value.listState));
|
||||
},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
final rows = state.rows.map((row) => row.copyWith(fields: value.fields)).toList();
|
||||
emit(state.copyWith(
|
||||
rows: rows,
|
||||
fields: value.fields,
|
||||
listState: const GridListState.reload(),
|
||||
));
|
||||
emit(state.copyWith(rows: rowCache.clonedRows, fields: value.fields));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -52,42 +44,21 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _gridService.closeGrid();
|
||||
await _fieldListener.stop();
|
||||
await _gridListener.stop();
|
||||
await fieldCache.dispose();
|
||||
await rowCache.dispose();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
Future<void> _initGrid(Emitter<GridState> emit) async {
|
||||
_fieldListener.updateFieldsNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_fieldListener.start();
|
||||
|
||||
await _loadGrid(emit);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_gridListener.rowsUpdateNotifier.addPublishListener((result) {
|
||||
result.fold((gridBlockChangeset) {
|
||||
for (final changeset in gridBlockChangeset) {
|
||||
if (changeset.insertedRows.isNotEmpty) {
|
||||
_insertRows(changeset.insertedRows);
|
||||
}
|
||||
fieldCache.addListener(
|
||||
onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
|
||||
if (changeset.deletedRows.isNotEmpty) {
|
||||
_deleteRows(changeset.deletedRows);
|
||||
}
|
||||
|
||||
if (changeset.updatedRows.isNotEmpty) {
|
||||
_updateRows(changeset.updatedRows);
|
||||
}
|
||||
}
|
||||
}, (err) => Log.error(err));
|
||||
});
|
||||
_gridListener.start();
|
||||
rowCache.addListener(
|
||||
onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadGrid(Emitter<GridState> emit) async {
|
||||
@ -105,10 +76,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(fields) {
|
||||
fieldCache.fields = fields.items;
|
||||
rowCache.updateWithBlock(grid.blockOrders, fieldCache.unmodifiableFields);
|
||||
|
||||
emit(state.copyWith(
|
||||
grid: Some(grid),
|
||||
fields: fields.items,
|
||||
rows: _buildRows(grid.blockOrders, fields.items),
|
||||
fields: fieldCache.clonedFields,
|
||||
rows: rowCache.clonedRows,
|
||||
loadingState: GridLoadingState.finish(left(unit)),
|
||||
));
|
||||
},
|
||||
@ -116,67 +90,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _deleteRows(List<RowOrder> deletedRows) {
|
||||
final List<RowData> rows = [];
|
||||
final List<Tuple2<int, RowData>> deletedIndex = [];
|
||||
final Map<String, RowOrder> deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder};
|
||||
state.rows.asMap().forEach((index, value) {
|
||||
if (deletedRowMap[value.rowId] == null) {
|
||||
rows.add(value);
|
||||
} else {
|
||||
deletedIndex.add(Tuple2(index, value));
|
||||
}
|
||||
});
|
||||
|
||||
add(GridEvent.didReceiveRowUpdate(rows, GridListState.delete(deletedIndex)));
|
||||
}
|
||||
|
||||
void _insertRows(List<IndexRowOrder> createdRows) {
|
||||
final List<RowData> rows = List.from(state.rows);
|
||||
List<int> insertIndexs = [];
|
||||
for (final newRow in createdRows) {
|
||||
if (newRow.hasIndex()) {
|
||||
insertIndexs.add(newRow.index);
|
||||
rows.insert(newRow.index, _toRowData(newRow.rowOrder));
|
||||
} else {
|
||||
insertIndexs.add(rows.length);
|
||||
rows.add(_toRowData(newRow.rowOrder));
|
||||
}
|
||||
}
|
||||
add(GridEvent.didReceiveRowUpdate(rows, GridListState.insert(insertIndexs)));
|
||||
}
|
||||
|
||||
void _updateRows(List<RowOrder> updatedRows) {
|
||||
final List<RowData> rows = List.from(state.rows);
|
||||
final List<int> updatedIndexs = [];
|
||||
for (final updatedRow in updatedRows) {
|
||||
final index = rows.indexWhere((row) => row.rowId == updatedRow.rowId);
|
||||
if (index != -1) {
|
||||
rows.removeAt(index);
|
||||
rows.insert(index, _toRowData(updatedRow));
|
||||
updatedIndexs.add(index);
|
||||
}
|
||||
}
|
||||
add(GridEvent.didReceiveRowUpdate(rows, const GridListState.reload()));
|
||||
}
|
||||
|
||||
List<RowData> _buildRows(List<GridBlockOrder> blockOrders, List<Field> fields) {
|
||||
return blockOrders.expand((blockOrder) => blockOrder.rowOrders).map((rowOrder) {
|
||||
return RowData.fromBlockRow(state.gridId, rowOrder, fields);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
RowData _toRowData(RowOrder rowOrder) {
|
||||
return RowData.fromBlockRow(state.gridId, rowOrder, state.fields);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridEvent with _$GridEvent {
|
||||
const factory GridEvent.initial() = InitialGrid;
|
||||
const factory GridEvent.createRow() = _CreateRow;
|
||||
const factory GridEvent.didReceiveRowUpdate(List<RowData> rows, GridListState listState) = _DidReceiveRowUpdate;
|
||||
const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridRowChangeReason listState) = _DidReceiveRowUpdate;
|
||||
const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
|
||||
}
|
||||
|
||||
@ -186,9 +106,9 @@ class GridState with _$GridState {
|
||||
required String gridId,
|
||||
required Option<Grid> grid,
|
||||
required List<Field> fields,
|
||||
required List<RowData> rows,
|
||||
required List<GridRow> rows,
|
||||
required GridLoadingState loadingState,
|
||||
required GridListState listState,
|
||||
required GridRowChangeReason listState,
|
||||
}) = _GridState;
|
||||
|
||||
factory GridState.initial(String gridId) => GridState(
|
||||
@ -197,7 +117,7 @@ class GridState with _$GridState {
|
||||
grid: none(),
|
||||
gridId: gridId,
|
||||
loadingState: const _Loading(),
|
||||
listState: const _Reload(),
|
||||
listState: const InitialListState(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -206,10 +126,3 @@ class GridLoadingState with _$GridLoadingState {
|
||||
const factory GridLoadingState.loading() = _Loading;
|
||||
const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridListState with _$GridListState {
|
||||
const factory GridListState.insert(List<int> indexs) = _Insert;
|
||||
const factory GridListState.delete(List<Tuple2<int, RowData>> indexs) = _Delete;
|
||||
const factory GridListState.reload() = _Reload;
|
||||
}
|
||||
|
@ -1,54 +1,49 @@
|
||||
import 'package:app_flowy/workspace/application/grid/data.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.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';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'field_service.dart';
|
||||
import 'grid_service.dart';
|
||||
|
||||
part 'grid_header_bloc.freezed.dart';
|
||||
|
||||
class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
final FieldService service;
|
||||
final GridFieldsListener _fieldListener;
|
||||
final FieldService _fieldService;
|
||||
final GridFieldCache fieldCache;
|
||||
|
||||
GridHeaderBloc({
|
||||
required GridHeaderData data,
|
||||
required this.service,
|
||||
}) : _fieldListener = GridFieldsListener(gridId: data.gridId),
|
||||
super(GridHeaderState.initial(data.fields)) {
|
||||
required String gridId,
|
||||
required this.fieldCache,
|
||||
}) : _fieldService = FieldService(gridId: gridId),
|
||||
super(GridHeaderState.initial(fieldCache.clonedFields)) {
|
||||
on<GridHeaderEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialHeader value) async {
|
||||
_startListening();
|
||||
},
|
||||
createField: (_CreateField value) {},
|
||||
insertField: (_InsertField value) {},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
value.fields.retainWhere((field) => field.visibility);
|
||||
emit(state.copyWith(fields: value.fields));
|
||||
},
|
||||
moveField: (_MoveField value) async {
|
||||
final result = await _fieldService.moveField(value.field.id, value.fromIndex, value.toIndex);
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_fieldListener.updateFieldsNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
|
||||
_fieldListener.start();
|
||||
fieldCache.addListener(
|
||||
onChanged: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _fieldListener.stop();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@ -56,9 +51,8 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
@freezed
|
||||
class GridHeaderEvent with _$GridHeaderEvent {
|
||||
const factory GridHeaderEvent.initial() = _InitialHeader;
|
||||
const factory GridHeaderEvent.createField() = _CreateField;
|
||||
const factory GridHeaderEvent.insertField({required bool onLeft}) = _InsertField;
|
||||
const factory GridHeaderEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
|
||||
const factory GridHeaderEvent.moveField(Field field, int fromIndex, int toIndex) = _MoveField;
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -66,7 +60,8 @@ class GridHeaderState with _$GridHeaderState {
|
||||
const factory GridHeaderState({required List<Field> fields}) = _GridHeaderState;
|
||||
|
||||
factory GridHeaderState.initial(List<Field> fields) {
|
||||
fields.retainWhere((field) => field.visibility);
|
||||
// final List<Field> newFields = List.from(fields);
|
||||
// newFields.retainWhere((field) => field.visibility);
|
||||
return GridHeaderState(fields: fields);
|
||||
}
|
||||
}
|
@ -7,13 +7,12 @@ import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:app_flowy/core/notification_helper.dart';
|
||||
|
||||
class GridListener {
|
||||
class GridRowListener {
|
||||
final String gridId;
|
||||
PublishNotifier<Either<List<GridBlockOrderChangeset>, FlowyError>> rowsUpdateNotifier =
|
||||
PublishNotifier(comparable: null);
|
||||
PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
GridListener({required this.gridId});
|
||||
GridRowListener({required this.gridId});
|
||||
|
||||
void start() {
|
||||
_listener = GridNotificationListener(
|
||||
@ -24,9 +23,9 @@ class GridListener {
|
||||
|
||||
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateGridBlock:
|
||||
case GridNotification.DidUpdateGridRow:
|
||||
result.fold(
|
||||
(payload) => rowsUpdateNotifier.value = left([GridBlockOrderChangeset.fromBuffer(payload)]),
|
||||
(payload) => rowsUpdateNotifier.value = left([GridRowsChangeset.fromBuffer(payload)]),
|
||||
(error) => rowsUpdateNotifier.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
@ -1,8 +1,12 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.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/grid.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
class GridService {
|
||||
final String gridId;
|
||||
@ -35,3 +39,116 @@ class GridService {
|
||||
return FolderEventCloseView(request).send();
|
||||
}
|
||||
}
|
||||
|
||||
class FieldsNotifier extends ChangeNotifier {
|
||||
List<Field> _fields = [];
|
||||
|
||||
set fields(List<Field> fields) {
|
||||
_fields = fields;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<Field> get fields => _fields;
|
||||
}
|
||||
|
||||
class GridFieldCache {
|
||||
final String gridId;
|
||||
late final GridFieldsListener _fieldListener;
|
||||
final FieldsNotifier _fieldNotifier = FieldsNotifier();
|
||||
GridFieldCache({required this.gridId}) {
|
||||
_fieldListener = GridFieldsListener(gridId: gridId);
|
||||
_fieldListener.updateFieldsNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
_deleteFields(changeset.deletedFields);
|
||||
_insertFields(changeset.insertedFields);
|
||||
_updateFields(changeset.updatedFields);
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_fieldListener.start();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _fieldListener.stop();
|
||||
_fieldNotifier.dispose();
|
||||
}
|
||||
|
||||
void applyChangeset(GridFieldChangeset changeset) {}
|
||||
|
||||
UnmodifiableListView<Field> get unmodifiableFields => UnmodifiableListView(_fieldNotifier.fields);
|
||||
|
||||
List<Field> get clonedFields => [..._fieldNotifier.fields];
|
||||
|
||||
set fields(List<Field> fields) {
|
||||
_fieldNotifier.fields = [...fields];
|
||||
}
|
||||
|
||||
VoidCallback addListener(
|
||||
{VoidCallback? listener, void Function(List<Field>)? onChanged, bool Function()? listenWhen}) {
|
||||
f() {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (onChanged != null) {
|
||||
onChanged(clonedFields);
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
_fieldNotifier.addListener(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
void removeListener(VoidCallback f) {
|
||||
_fieldNotifier.removeListener(f);
|
||||
}
|
||||
|
||||
void _deleteFields(List<FieldOrder> deletedFields) {
|
||||
if (deletedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<Field> fields = _fieldNotifier.fields;
|
||||
final Map<String, FieldOrder> deletedFieldMap = {
|
||||
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
|
||||
};
|
||||
|
||||
fields.retainWhere((field) => (deletedFieldMap[field.id] == null));
|
||||
_fieldNotifier.fields = fields;
|
||||
}
|
||||
|
||||
void _insertFields(List<IndexField> insertedFields) {
|
||||
if (insertedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<Field> fields = _fieldNotifier.fields;
|
||||
for (final indexField in insertedFields) {
|
||||
if (fields.length > indexField.index) {
|
||||
fields.insert(indexField.index, indexField.field_1);
|
||||
} else {
|
||||
fields.add(indexField.field_1);
|
||||
}
|
||||
}
|
||||
_fieldNotifier.fields = fields;
|
||||
}
|
||||
|
||||
void _updateFields(List<Field> updatedFields) {
|
||||
if (updatedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<Field> fields = _fieldNotifier.fields;
|
||||
for (final updatedField in updatedFields) {
|
||||
final index = fields.indexWhere((field) => field.id == updatedField.id);
|
||||
if (index != -1) {
|
||||
fields.removeAt(index);
|
||||
fields.insert(index, updatedField);
|
||||
}
|
||||
}
|
||||
_fieldNotifier.fields = fields;
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ export 'grid_bloc.dart';
|
||||
export 'row/row_bloc.dart';
|
||||
export 'row/row_service.dart';
|
||||
export 'grid_service.dart';
|
||||
export 'data.dart';
|
||||
export 'grid_header_bloc.dart';
|
||||
|
||||
// Field
|
||||
export 'field/field_service.dart';
|
||||
export 'field/grid_header_bloc.dart';
|
||||
export 'field/field_action_sheet_bloc.dart';
|
||||
export 'field/field_editor_bloc.dart';
|
||||
export 'field/field_switch_bloc.dart';
|
||||
@ -17,12 +16,12 @@ export 'field/type_option/number_bloc.dart';
|
||||
export 'field/type_option/single_select_bloc.dart';
|
||||
|
||||
// Cell
|
||||
export 'cell_bloc/text_cell_bloc.dart';
|
||||
export 'cell_bloc/number_cell_bloc.dart';
|
||||
export 'cell_bloc/selection_cell_bloc.dart';
|
||||
export 'cell_bloc/date_cell_bloc.dart';
|
||||
export 'cell_bloc/checkbox_cell_bloc.dart';
|
||||
export 'cell_bloc/cell_service.dart';
|
||||
export 'cell/text_cell_bloc.dart';
|
||||
export 'cell/number_cell_bloc.dart';
|
||||
export 'cell/selection_cell_bloc.dart';
|
||||
export 'cell/date_cell_bloc.dart';
|
||||
export 'cell/checkbox_cell_bloc.dart';
|
||||
export 'cell/cell_service.dart';
|
||||
|
||||
// Setting
|
||||
export 'setting/setting_bloc.dart';
|
||||
|
@ -11,7 +11,7 @@ part 'row_action_sheet_bloc.freezed.dart';
|
||||
class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState> {
|
||||
final RowService _rowService;
|
||||
|
||||
RowActionSheetBloc({required RowData rowData})
|
||||
RowActionSheetBloc({required GridRow rowData})
|
||||
: _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId),
|
||||
super(RowActionSheetState.initial(rowData)) {
|
||||
on<RowActionSheetEvent>(
|
||||
@ -49,10 +49,10 @@ class RowActionSheetEvent with _$RowActionSheetEvent {
|
||||
@freezed
|
||||
class RowActionSheetState with _$RowActionSheetState {
|
||||
const factory RowActionSheetState({
|
||||
required RowData rowData,
|
||||
required GridRow rowData,
|
||||
}) = _RowActionSheetState;
|
||||
|
||||
factory RowActionSheetState.initial(RowData rowData) => RowActionSheetState(
|
||||
factory RowActionSheetState.initial(GridRow rowData) => RowActionSheetState(
|
||||
rowData: rowData,
|
||||
);
|
||||
}
|
||||
|
@ -1,111 +1,115 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_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';
|
||||
import 'dart:async';
|
||||
import 'row_listener.dart';
|
||||
import 'row_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'row_bloc.freezed.dart';
|
||||
|
||||
typedef CellDataMap = LinkedHashMap<String, CellData>;
|
||||
typedef CellDataMap = LinkedHashMap<String, GridCellIdentifier>;
|
||||
|
||||
class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
final RowService _rowService;
|
||||
final RowListener _rowlistener;
|
||||
final GridFieldsListener _fieldListener;
|
||||
final GridFieldCache _fieldCache;
|
||||
final GridRowCache _rowCache;
|
||||
void Function()? _rowListenCallback;
|
||||
void Function()? _fieldListenCallback;
|
||||
|
||||
RowBloc({required RowData rowData})
|
||||
: _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId),
|
||||
_fieldListener = GridFieldsListener(gridId: rowData.gridId),
|
||||
_rowlistener = RowListener(rowId: rowData.rowId),
|
||||
RowBloc({
|
||||
required GridRow rowData,
|
||||
required GridFieldCache fieldCache,
|
||||
required GridRowCache rowCache,
|
||||
}) : _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId),
|
||||
_fieldCache = fieldCache,
|
||||
_rowCache = rowCache,
|
||||
super(RowState.initial(rowData)) {
|
||||
on<RowEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialRow value) async {
|
||||
_startListening();
|
||||
await _startListening();
|
||||
await _loadRow(emit);
|
||||
},
|
||||
createRow: (_CreateRow value) {
|
||||
_rowService.createRow();
|
||||
},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) async {
|
||||
await _handleFieldUpdate(emit, value);
|
||||
},
|
||||
didUpdateRow: (_DidUpdateRow value) async {
|
||||
_handleRowUpdate(value, emit);
|
||||
_handleRowUpdate(value.row, emit);
|
||||
},
|
||||
fieldsDidUpdate: (_FieldsDidUpdate value) async {
|
||||
await _handleFieldUpdate(emit);
|
||||
},
|
||||
didLoadRow: (_DidLoadRow value) {
|
||||
_handleRowUpdate(value.row, emit);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleRowUpdate(_DidUpdateRow value, Emitter<RowState> emit) {
|
||||
final CellDataMap cellDataMap = _makeCellDatas(value.row, state.rowData.fields);
|
||||
void _handleRowUpdate(Row row, Emitter<RowState> emit) {
|
||||
final CellDataMap cellDataMap = _makeCellDatas(row, state.rowData.fields);
|
||||
emit(state.copyWith(
|
||||
row: Future(() => Some(value.row)),
|
||||
row: Future(() => Some(row)),
|
||||
cellDataMap: Some(cellDataMap),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _handleFieldUpdate(Emitter<RowState> emit, _DidReceiveFieldUpdate value) async {
|
||||
Future<void> _handleFieldUpdate(Emitter<RowState> emit) async {
|
||||
final optionRow = await state.row;
|
||||
final CellDataMap cellDataMap = optionRow.fold(
|
||||
() => CellDataMap.identity(),
|
||||
(row) => _makeCellDatas(row, value.fields),
|
||||
(row) => _makeCellDatas(row, _fieldCache.unmodifiableFields),
|
||||
);
|
||||
|
||||
emit(state.copyWith(
|
||||
rowData: state.rowData.copyWith(fields: value.fields),
|
||||
rowData: state.rowData.copyWith(fields: _fieldCache.unmodifiableFields),
|
||||
cellDataMap: Some(cellDataMap),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _rowlistener.stop();
|
||||
await _fieldListener.stop();
|
||||
if (_rowListenCallback != null) {
|
||||
_rowCache.removeRowListener(_rowListenCallback!);
|
||||
}
|
||||
|
||||
if (_fieldListenCallback != null) {
|
||||
_fieldCache.removeListener(_fieldListenCallback!);
|
||||
}
|
||||
return super.close();
|
||||
}
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_rowlistener.updateRowNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(row) => add(RowEvent.didUpdateRow(row)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_fieldListenCallback = _fieldCache.addListener(
|
||||
listener: () => add(const RowEvent.fieldsDidUpdate()),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
|
||||
_fieldListener.updateFieldsNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(fields) => add(RowEvent.didReceiveFieldUpdate(fields)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
|
||||
_rowlistener.start();
|
||||
_fieldListener.start();
|
||||
_rowListenCallback = _rowCache.addRowListener(
|
||||
rowId: state.rowData.rowId,
|
||||
onUpdated: (row) => add(RowEvent.didUpdateRow(row)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadRow(Emitter<RowState> emit) async {
|
||||
_rowService.getRow().then((result) {
|
||||
return result.fold(
|
||||
(row) => add(RowEvent.didUpdateRow(row)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
final data = await _rowCache.getRowData(state.rowData.rowId);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
data.foldRight(null, (data, _) => add(RowEvent.didLoadRow(data)));
|
||||
}
|
||||
|
||||
CellDataMap _makeCellDatas(Row row, List<Field> fields) {
|
||||
var map = CellDataMap.new();
|
||||
for (final field in fields) {
|
||||
if (field.visibility) {
|
||||
map[field.id] = CellData(
|
||||
map[field.id] = GridCellIdentifier(
|
||||
rowId: row.id,
|
||||
gridId: _rowService.gridId,
|
||||
cell: row.cellByFieldId[field.id],
|
||||
@ -121,19 +125,20 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
class RowEvent with _$RowEvent {
|
||||
const factory RowEvent.initial() = _InitialRow;
|
||||
const factory RowEvent.createRow() = _CreateRow;
|
||||
const factory RowEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
|
||||
const factory RowEvent.fieldsDidUpdate() = _FieldsDidUpdate;
|
||||
const factory RowEvent.didLoadRow(Row row) = _DidLoadRow;
|
||||
const factory RowEvent.didUpdateRow(Row row) = _DidUpdateRow;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowState with _$RowState {
|
||||
const factory RowState({
|
||||
required RowData rowData,
|
||||
required GridRow rowData,
|
||||
required Future<Option<Row>> row,
|
||||
required Option<CellDataMap> cellDataMap,
|
||||
}) = _RowState;
|
||||
|
||||
factory RowState.initial(RowData rowData) => RowState(
|
||||
factory RowState.initial(GridRow rowData) => RowState(
|
||||
rowData: rowData,
|
||||
row: Future(() => none()),
|
||||
cellDataMap: none(),
|
||||
|
@ -12,7 +12,7 @@ typedef UpdateFieldNotifiedValue = Either<List<Field>, FlowyError>;
|
||||
|
||||
class RowListener {
|
||||
final String rowId;
|
||||
PublishNotifier<UpdateRowNotifiedValue> updateRowNotifier = PublishNotifier();
|
||||
PublishNotifier<UpdateRowNotifiedValue>? updateRowNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
RowListener({required this.rowId});
|
||||
@ -25,8 +25,8 @@ class RowListener {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateRow:
|
||||
result.fold(
|
||||
(payload) => updateRowNotifier.value = left(Row.fromBuffer(payload)),
|
||||
(error) => updateRowNotifier.value = right(error),
|
||||
(payload) => updateRowNotifier?.value = left(Row.fromBuffer(payload)),
|
||||
(error) => updateRowNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@ -36,6 +36,7 @@ class RowListener {
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateRowNotifier.dispose();
|
||||
updateRowNotifier?.dispose();
|
||||
updateRowNotifier = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,206 @@
|
||||
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-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_listener.dart';
|
||||
|
||||
part 'row_service.freezed.dart';
|
||||
|
||||
class RowsNotifier extends ChangeNotifier {
|
||||
List<GridRow> _rows = [];
|
||||
GridRowChangeReason _changeReason = const InitialListState();
|
||||
|
||||
void updateRows(List<GridRow> rows, GridRowChangeReason changeReason) {
|
||||
_rows = rows;
|
||||
_changeReason = changeReason;
|
||||
|
||||
changeReason.map(
|
||||
insert: (_) => notifyListeners(),
|
||||
delete: (_) => notifyListeners(),
|
||||
update: (_) => notifyListeners(),
|
||||
initial: (_) {},
|
||||
);
|
||||
}
|
||||
|
||||
List<GridRow> get rows => _rows;
|
||||
}
|
||||
|
||||
class GridRowCache {
|
||||
final String gridId;
|
||||
final GridRowListener _rowsListener;
|
||||
final RowsNotifier _rowNotifier = RowsNotifier();
|
||||
final HashMap<String, Row> _rowDataMap = HashMap();
|
||||
UnmodifiableListView<Field> _fields = UnmodifiableListView([]);
|
||||
|
||||
GridRowCache({required this.gridId}) : _rowsListener = GridRowListener(gridId: gridId) {
|
||||
_rowsListener.rowsUpdateNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(changesets) {
|
||||
for (final changeset in changesets) {
|
||||
_deleteRows(changeset.deletedRows);
|
||||
_insertRows(changeset.insertedRows);
|
||||
_updateRows(changeset.updatedRows);
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_rowsListener.start();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _rowsListener.stop();
|
||||
_rowNotifier.dispose();
|
||||
}
|
||||
|
||||
List<GridRow> get clonedRows => [..._rowNotifier.rows];
|
||||
|
||||
void addListener({
|
||||
void Function(List<GridRow>, GridRowChangeReason)? onChanged,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
_rowNotifier.addListener(() {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (onChanged != null) {
|
||||
onChanged(clonedRows, _rowNotifier._changeReason);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
VoidCallback addRowListener({
|
||||
required String rowId,
|
||||
void Function(Row)? onUpdated,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
f() {
|
||||
if (onUpdated == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
_rowNotifier._changeReason.whenOrNull(update: (indexs) {
|
||||
final row = _rowDataMap[rowId];
|
||||
if (indexs[rowId] != null && row != null) {
|
||||
onUpdated(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_rowNotifier.addListener(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
void removeRowListener(VoidCallback callback) {
|
||||
_rowNotifier.removeListener(callback);
|
||||
}
|
||||
|
||||
Future<Option<Row>> getRowData(String rowId) async {
|
||||
final Row? data = _rowDataMap[rowId];
|
||||
if (data != null) {
|
||||
return Future(() => Some(data));
|
||||
}
|
||||
|
||||
final payload = RowIdentifierPayload.create()
|
||||
..gridId = gridId
|
||||
..rowId = rowId;
|
||||
|
||||
final result = await GridEventGetRow(payload).send();
|
||||
return Future(() {
|
||||
return result.fold(
|
||||
(data) {
|
||||
data.freeze();
|
||||
_rowDataMap[data.id] = data;
|
||||
return Some(data);
|
||||
},
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return none();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void updateWithBlock(List<GridBlockOrder> blocks, UnmodifiableListView<Field> fields) {
|
||||
_fields = fields;
|
||||
final newRows = blocks.expand((block) => block.rowOrders).map((rowOrder) {
|
||||
return GridRow.fromBlockRow(gridId, rowOrder, _fields);
|
||||
}).toList();
|
||||
|
||||
_rowNotifier.updateRows(newRows, const GridRowChangeReason.initial());
|
||||
}
|
||||
|
||||
void _deleteRows(List<RowOrder> deletedRows) {
|
||||
if (deletedRows.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<GridRow> newRows = [];
|
||||
final DeletedIndexs deletedIndex = [];
|
||||
final Map<String, RowOrder> deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder};
|
||||
|
||||
_rowNotifier.rows.asMap().forEach((index, row) {
|
||||
if (deletedRowMap[row.rowId] == null) {
|
||||
newRows.add(row);
|
||||
} else {
|
||||
deletedIndex.add(DeletedIndex(index: index, row: row));
|
||||
}
|
||||
});
|
||||
|
||||
_rowNotifier.updateRows(newRows, GridRowChangeReason.delete(deletedIndex));
|
||||
}
|
||||
|
||||
void _insertRows(List<IndexRowOrder> createdRows) {
|
||||
if (createdRows.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
InsertedIndexs insertIndexs = [];
|
||||
final List<GridRow> newRows = _rowNotifier.rows;
|
||||
for (final createdRow in createdRows) {
|
||||
final gridRow = GridRow.fromBlockRow(gridId, createdRow.rowOrder, _fields);
|
||||
insertIndexs.add(
|
||||
InsertedIndex(
|
||||
index: createdRow.index,
|
||||
rowId: gridRow.rowId,
|
||||
),
|
||||
);
|
||||
newRows.insert(createdRow.index, gridRow);
|
||||
}
|
||||
_rowNotifier.updateRows(newRows, GridRowChangeReason.insert(insertIndexs));
|
||||
}
|
||||
|
||||
void _updateRows(List<RowOrder> updatedRows) {
|
||||
if (updatedRows.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||
final List<GridRow> newRows = _rowNotifier.rows;
|
||||
for (final rowOrder in updatedRows) {
|
||||
final index = newRows.indexWhere((row) => row.rowId == rowOrder.rowId);
|
||||
if (index != -1) {
|
||||
newRows.removeAt(index);
|
||||
newRows.insert(index, GridRow.fromBlockRow(gridId, rowOrder, _fields));
|
||||
_rowDataMap.remove(rowOrder.rowId);
|
||||
updatedIndexs[rowOrder.rowId] = UpdatedIndex(index: index, rowId: rowOrder.rowId);
|
||||
}
|
||||
}
|
||||
|
||||
_rowNotifier.updateRows(newRows, GridRowChangeReason.update(updatedIndexs));
|
||||
}
|
||||
}
|
||||
|
||||
class RowService {
|
||||
final String gridId;
|
||||
final String rowId;
|
||||
@ -21,6 +215,17 @@ class RowService {
|
||||
return GridEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow(String rowId, int fromIndex, int toIndex) {
|
||||
final payload = MoveItemPayload.create()
|
||||
..gridId = gridId
|
||||
..itemId = rowId
|
||||
..ty = MoveItemType.MoveRow
|
||||
..fromIndex = fromIndex
|
||||
..toIndex = toIndex;
|
||||
|
||||
return GridEventMoveItem(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Row, FlowyError>> getRow() {
|
||||
final payload = RowIdentifierPayload.create()
|
||||
..gridId = gridId
|
||||
@ -47,8 +252,8 @@ class RowService {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CellData with _$CellData {
|
||||
const factory CellData({
|
||||
class GridCellIdentifier with _$GridCellIdentifier {
|
||||
const factory GridCellIdentifier({
|
||||
required String gridId,
|
||||
required String rowId,
|
||||
required Field field,
|
||||
@ -57,20 +262,61 @@ class CellData with _$CellData {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowData with _$RowData {
|
||||
const factory RowData({
|
||||
class GridRow with _$GridRow {
|
||||
const factory GridRow({
|
||||
required String gridId,
|
||||
required String rowId,
|
||||
required List<Field> fields,
|
||||
required double height,
|
||||
}) = _RowData;
|
||||
required Future<Option<Row>> data,
|
||||
}) = _GridRow;
|
||||
|
||||
factory RowData.fromBlockRow(String gridId, RowOrder row, List<Field> fields) {
|
||||
return RowData(
|
||||
factory GridRow.fromBlockRow(String gridId, RowOrder row, List<Field> fields) {
|
||||
return GridRow(
|
||||
gridId: gridId,
|
||||
rowId: row.rowId,
|
||||
fields: fields,
|
||||
rowId: row.rowId,
|
||||
data: Future(() => none()),
|
||||
height: row.height.toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef InsertedIndexs = List<InsertedIndex>;
|
||||
typedef DeletedIndexs = List<DeletedIndex>;
|
||||
typedef UpdatedIndexs = LinkedHashMap<String, UpdatedIndex>;
|
||||
|
||||
class InsertedIndex {
|
||||
int index;
|
||||
String rowId;
|
||||
InsertedIndex({
|
||||
required this.index,
|
||||
required this.rowId,
|
||||
});
|
||||
}
|
||||
|
||||
class DeletedIndex {
|
||||
int index;
|
||||
GridRow row;
|
||||
DeletedIndex({
|
||||
required this.index,
|
||||
required this.row,
|
||||
});
|
||||
}
|
||||
|
||||
class UpdatedIndex {
|
||||
int index;
|
||||
String rowId;
|
||||
UpdatedIndex({
|
||||
required this.index,
|
||||
required this.rowId,
|
||||
});
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridRowChangeReason with _$GridRowChangeReason {
|
||||
const factory GridRowChangeReason.insert(InsertedIndexs items) = _Insert;
|
||||
const factory GridRowChangeReason.delete(DeletedIndexs items) = _Delete;
|
||||
const factory GridRowChangeReason.update(UpdatedIndexs indexs) = _Update;
|
||||
const factory GridRowChangeReason.initial() = InitialListState;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.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';
|
||||
@ -10,12 +10,13 @@ part 'property_bloc.freezed.dart';
|
||||
|
||||
class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
|
||||
final FieldService _service;
|
||||
final GridFieldsListener _fieldListener;
|
||||
final GridFieldCache _fieldCache;
|
||||
Function()? _listenFieldCallback;
|
||||
|
||||
GridPropertyBloc({required String gridId, required List<Field> fields})
|
||||
GridPropertyBloc({required String gridId, required GridFieldCache fieldCache})
|
||||
: _service = FieldService(gridId: gridId),
|
||||
_fieldListener = GridFieldsListener(gridId: gridId),
|
||||
super(GridPropertyState.initial(gridId, fields)) {
|
||||
_fieldCache = fieldCache,
|
||||
super(GridPropertyState.initial(gridId, fieldCache.clonedFields)) {
|
||||
on<GridPropertyEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
@ -32,6 +33,9 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
emit(state.copyWith(fields: value.fields));
|
||||
},
|
||||
moveField: (_MoveField value) {
|
||||
//
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -39,20 +43,17 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _fieldListener.stop();
|
||||
if (_listenFieldCallback != null) {
|
||||
_fieldCache.removeListener(_listenFieldCallback!);
|
||||
}
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_fieldListener.updateFieldsNotifier.addPublishListener((result) {
|
||||
result.fold(
|
||||
(fields) {
|
||||
add(GridPropertyEvent.didReceiveFieldUpdate(fields));
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_fieldListener.start();
|
||||
_listenFieldCallback = _fieldCache.addListener(
|
||||
onChanged: (fields) => add(GridPropertyEvent.didReceiveFieldUpdate(fields)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +62,7 @@ class GridPropertyEvent with _$GridPropertyEvent {
|
||||
const factory GridPropertyEvent.initial() = _Initial;
|
||||
const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility;
|
||||
const factory GridPropertyEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
|
||||
const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) = _MoveField;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -78,10 +78,10 @@ class CreateItem extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final config = HoverDisplayConfig(hoverColor: theme.hover);
|
||||
final config = HoverStyle(hoverColor: theme.hover);
|
||||
|
||||
return FlowyHover(
|
||||
config: config,
|
||||
style: config,
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
onTap: () => onSelected(pluginBuilder),
|
||||
|
@ -43,7 +43,7 @@ class ViewSectionItem extends StatelessWidget {
|
||||
return InkWell(
|
||||
onTap: () => onSelected(context.read<ViewBloc>().state.view),
|
||||
child: FlowyHover(
|
||||
config: HoverDisplayConfig(hoverColor: theme.bg3),
|
||||
style: HoverStyle(hoverColor: theme.bg3),
|
||||
builder: (_, onHover) => _render(context, onHover, state, theme.iconColor),
|
||||
setSelected: () => state.isEditing || isSelected,
|
||||
),
|
||||
|
@ -1,21 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
|
||||
|
||||
class GridScrollController {
|
||||
final ScrollController _verticalController = ScrollController();
|
||||
final ScrollController _horizontalController = ScrollController();
|
||||
final LinkedScrollControllerGroup _scrollGroupContorller;
|
||||
final ScrollController verticalController;
|
||||
final ScrollController horizontalController;
|
||||
|
||||
ScrollController get verticalController => _verticalController;
|
||||
ScrollController get horizontalController => _horizontalController;
|
||||
final List<ScrollController> _linkHorizontalControllers = [];
|
||||
|
||||
GridScrollController();
|
||||
GridScrollController({required LinkedScrollControllerGroup scrollGroupContorller})
|
||||
: _scrollGroupContorller = scrollGroupContorller,
|
||||
verticalController = ScrollController(),
|
||||
horizontalController = scrollGroupContorller.addAndGet();
|
||||
|
||||
// final SelectionChangeCallback? onSelectionChanged;
|
||||
|
||||
// final ShouldApplySelection? shouldApplySelection;
|
||||
|
||||
// final ScrollCallback? onScroll;
|
||||
ScrollController linkHorizontalController() {
|
||||
final controller = _scrollGroupContorller.addAndGet();
|
||||
_linkHorizontalControllers.add(controller);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
for (final controller in _linkHorizontalControllers) {
|
||||
controller.dispose();
|
||||
}
|
||||
verticalController.dispose();
|
||||
horizontalController.dispose();
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_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' show Field;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
|
||||
import 'controller/grid_scroll.dart';
|
||||
import 'layout/layout.dart';
|
||||
import 'layout/sizes.dart';
|
||||
@ -69,12 +70,18 @@ class FlowyGrid extends StatefulWidget {
|
||||
const FlowyGrid({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_FlowyGridState createState() => _FlowyGridState();
|
||||
State<FlowyGrid> createState() => _FlowyGridState();
|
||||
}
|
||||
|
||||
class _FlowyGridState extends State<FlowyGrid> {
|
||||
final _scrollController = GridScrollController();
|
||||
final _key = GlobalKey<SliverAnimatedListState>();
|
||||
final _scrollController = GridScrollController(scrollGroupContorller: LinkedScrollControllerGroup());
|
||||
late ScrollController headerScrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
headerScrollController = _scrollController.linkHorizontalController();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -85,87 +92,120 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
||||
builder: (context, state) {
|
||||
if (state.fields.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
final child = SizedBox(
|
||||
width: GridLayout.headerWidth(state.fields),
|
||||
child: ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: [
|
||||
_renderToolbar(state.gridId),
|
||||
_renderGridHeader(state.gridId),
|
||||
_renderRows(gridId: state.gridId, context: context),
|
||||
const GridFooter(),
|
||||
],
|
||||
),
|
||||
),
|
||||
final contentWidth = GridLayout.headerWidth(state.fields);
|
||||
final child = _wrapScrollView(
|
||||
contentWidth,
|
||||
[
|
||||
const _GridRows(),
|
||||
const _GridFooter(),
|
||||
],
|
||||
);
|
||||
|
||||
return _wrapScrollbar(child);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const _GridToolbarAdaptor(),
|
||||
_gridHeader(context, state.gridId),
|
||||
Flexible(child: child),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _wrapScrollbar(Widget child) {
|
||||
Widget _wrapScrollView(
|
||||
double contentWidth,
|
||||
List<Widget> slivers,
|
||||
) {
|
||||
final verticalScrollView = ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: CustomScrollView(
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: slivers,
|
||||
),
|
||||
);
|
||||
|
||||
final sizedVerticalScrollView = SizedBox(
|
||||
width: contentWidth,
|
||||
child: verticalScrollView,
|
||||
);
|
||||
|
||||
final horizontalScrollView = StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: sizedVerticalScrollView,
|
||||
);
|
||||
|
||||
return ScrollbarListStack(
|
||||
axis: Axis.vertical,
|
||||
controller: _scrollController.verticalController,
|
||||
barSize: GridSize.scrollBarSize,
|
||||
child: StyledSingleChildScrollView(
|
||||
controller: _scrollController.horizontalController,
|
||||
axis: Axis.horizontal,
|
||||
child: child,
|
||||
),
|
||||
child: horizontalScrollView,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderGridHeader(String gridId) {
|
||||
return BlocSelector<GridBloc, GridState, List<Field>>(
|
||||
selector: (state) => state.fields,
|
||||
builder: (context, fields) {
|
||||
return GridHeader(gridId: gridId, fields: List.from(fields));
|
||||
Widget _gridHeader(BuildContext context, String gridId) {
|
||||
final fieldCache = context.read<GridBloc>().fieldCache;
|
||||
return GridHeaderSliverAdaptor(
|
||||
gridId: gridId,
|
||||
fieldCache: fieldCache,
|
||||
anchorScrollController: headerScrollController,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridToolbarAdaptor extends StatelessWidget {
|
||||
const _GridToolbarAdaptor({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<GridBloc, GridState, GridToolbarContext>(
|
||||
selector: (state) {
|
||||
final fieldCache = context.read<GridBloc>().fieldCache;
|
||||
return GridToolbarContext(
|
||||
gridId: state.gridId,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
},
|
||||
builder: (context, toolbarContext) {
|
||||
return GridToolbar(toolbarContext: toolbarContext);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _renderToolbar(String gridId) {
|
||||
return BlocSelector<GridBloc, GridState, List<Field>>(
|
||||
selector: (state) => state.fields,
|
||||
builder: (context, fields) {
|
||||
final toolbarContext = GridToolbarContext(
|
||||
gridId: gridId,
|
||||
fields: fields,
|
||||
);
|
||||
class _GridRows extends StatefulWidget {
|
||||
const _GridRows({Key? key}) : super(key: key);
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: GridToolbar(toolbarContext: toolbarContext),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@override
|
||||
State<_GridRows> createState() => _GridRowsState();
|
||||
}
|
||||
|
||||
Widget _renderRows({required String gridId, required BuildContext context}) {
|
||||
class _GridRowsState extends State<_GridRows> {
|
||||
final _key = GlobalKey<SliverAnimatedListState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<GridBloc, GridState>(
|
||||
listenWhen: (previous, current) => previous.listState != current.listState,
|
||||
listener: (context, state) {
|
||||
state.listState.map(
|
||||
state.listState.mapOrNull(
|
||||
insert: (value) {
|
||||
for (final index in value.indexs) {
|
||||
_key.currentState?.insertItem(index);
|
||||
for (final item in value.items) {
|
||||
_key.currentState?.insertItem(item.index);
|
||||
}
|
||||
},
|
||||
delete: (value) {
|
||||
for (final index in value.indexs) {
|
||||
_key.currentState?.removeItem(index.value1, (context, animation) => _renderRow(index.value2, animation));
|
||||
for (final item in value.items) {
|
||||
_key.currentState?.removeItem(
|
||||
item.index,
|
||||
(context, animation) => _renderRow(context, item.row, animation),
|
||||
);
|
||||
}
|
||||
},
|
||||
reload: (updatedIndexs) {},
|
||||
);
|
||||
},
|
||||
buildWhen: (previous, current) => false,
|
||||
@ -175,17 +215,53 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
initialItemCount: context.read<GridBloc>().state.rows.length,
|
||||
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
|
||||
final rowData = context.read<GridBloc>().state.rows[index];
|
||||
return _renderRow(rowData, animation);
|
||||
return _renderRow(context, rowData, animation);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderRow(RowData rowData, Animation<double> animation) {
|
||||
Widget _renderRow(BuildContext context, GridRow rowData, Animation<double> animation) {
|
||||
final bloc = context.read<GridBloc>();
|
||||
final fieldCache = bloc.fieldCache;
|
||||
final rowCache = bloc.rowCache;
|
||||
|
||||
return SizeTransition(
|
||||
sizeFactor: animation,
|
||||
child: GridRowWidget(data: rowData, key: ValueKey(rowData.rowId)),
|
||||
child: GridRowWidget(
|
||||
blocBuilder: () => RowBloc(
|
||||
rowData: rowData,
|
||||
fieldCache: fieldCache,
|
||||
rowCache: rowCache,
|
||||
),
|
||||
key: ValueKey(rowData.rowId),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridFooter extends StatelessWidget {
|
||||
const _GridFooter({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(bottom: 200),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: GridSize.footerHeight,
|
||||
child: Padding(
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const SizedBox(width: 120, child: GridAddRowButton()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import 'number_cell.dart';
|
||||
import 'selection_cell/selection_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
|
||||
Widget buildGridCell(CellData cellData) {
|
||||
Widget buildGridCell(GridCellIdentifier cellData) {
|
||||
final key = ValueKey(cellData.field.id + cellData.rowId);
|
||||
switch (cellData.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
|
@ -33,9 +33,7 @@ class CellContainer extends StatelessWidget {
|
||||
child: Consumer<CellStateNotifier>(
|
||||
builder: (context, state, _) {
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: width,
|
||||
),
|
||||
constraints: BoxConstraints(maxWidth: width),
|
||||
decoration: _makeBoxDecoration(context, state),
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Center(child: child),
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class CheckboxCell extends StatefulWidget {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const CheckboxCell({
|
||||
required this.cellData,
|
||||
|
@ -9,7 +9,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
class DateCell extends GridCell {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const DateCell({
|
||||
required this.cellData,
|
||||
@ -74,7 +74,13 @@ final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
|
||||
class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final void Function(DateTime) onSelected;
|
||||
final VoidCallback onDismissed;
|
||||
const _CellCalendar({required this.onSelected, required this.onDismissed, Key? key}) : super(key: key);
|
||||
final bool includeTime;
|
||||
const _CellCalendar({
|
||||
required this.onSelected,
|
||||
required this.onDismissed,
|
||||
required this.includeTime,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_CellCalendar> createState() => _CellCalendarState();
|
||||
@ -86,7 +92,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
}) async {
|
||||
_CellCalendar.remove(context);
|
||||
|
||||
final calendar = _CellCalendar(onSelected: onSelected, onDismissed: onDismissed);
|
||||
final calendar = _CellCalendar(
|
||||
onSelected: onSelected,
|
||||
onDismissed: onDismissed,
|
||||
includeTime: false,
|
||||
);
|
||||
// const size = Size(460, 400);
|
||||
// final window = await getWindowInfo();
|
||||
// FlowyOverlay.of(context).insertWithRect(
|
||||
@ -105,11 +115,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: calendar,
|
||||
constraints: BoxConstraints.loose(const Size(300, 320)),
|
||||
constraints: BoxConstraints.tight(const Size(320, 320)),
|
||||
),
|
||||
identifier: _CellCalendar.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomWithCenterAligned,
|
||||
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
delegate: calendar,
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NumberCell extends GridCell {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const NumberCell({
|
||||
required this.cellData,
|
||||
@ -26,7 +26,7 @@ class _NumberCellState extends State<NumberCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: widget.cellData);
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: widget.cellData)..add(const NumberCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode = CellFocusNode();
|
||||
super.initState();
|
||||
@ -48,7 +48,6 @@ class _NumberCellState extends State<NumberCell> {
|
||||
return TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
onChanged: (value) => focusChanged(),
|
||||
onEditingComplete: () => _focusNode.unfocus(),
|
||||
maxLines: 1,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
@ -76,7 +75,12 @@ class _NumberCellState extends State<NumberCell> {
|
||||
_delayOperation?.cancel();
|
||||
_delayOperation = Timer(const Duration(milliseconds: 300), () {
|
||||
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
|
||||
_cellBloc.add(NumberCellEvent.updateCell(_controller.text));
|
||||
final number = num.tryParse(_controller.text);
|
||||
if (number != null) {
|
||||
_cellBloc.add(NumberCellEvent.updateCell(_controller.text));
|
||||
} else {
|
||||
_controller.text = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import 'extension.dart';
|
||||
import 'selection_editor.dart';
|
||||
|
||||
class SingleSelectCell extends GridCell {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const SingleSelectCell({
|
||||
required this.cellData,
|
||||
@ -64,7 +64,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
|
||||
//----------------------------------------------------------------
|
||||
class MultiSelectCell extends GridCell {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const MultiSelectCell({
|
||||
required this.cellData,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'dart:collection';
|
||||
import 'package:app_flowy/workspace/application/grid/cell_bloc/selection_editor_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/selection_editor_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart';
|
||||
@ -25,7 +25,7 @@ import 'text_field.dart';
|
||||
const double _editorPannelWidth = 300;
|
||||
|
||||
class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
final List<SelectOption> options;
|
||||
final List<SelectOption> selectedOptions;
|
||||
final VoidCallback onDismissed;
|
||||
@ -66,7 +66,7 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
|
||||
static void show(
|
||||
BuildContext context,
|
||||
CellData cellData,
|
||||
GridCellIdentifier cellData,
|
||||
List<SelectOption> options,
|
||||
List<SelectOption> selectedOptions,
|
||||
VoidCallback onDismissed,
|
||||
@ -199,7 +199,7 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
context.read<SelectOptionEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
|
||||
},
|
||||
child: FlowyHover(
|
||||
config: HoverDisplayConfig(hoverColor: theme.hover),
|
||||
style: HoverStyle(hoverColor: theme.hover),
|
||||
builder: (_, onHover) {
|
||||
List<Widget> children = [
|
||||
SelectOptionTag(option: option, isSelected: isSelected),
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_container.dart';
|
||||
|
||||
class GridTextCell extends GridCell {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
const GridTextCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
@ -7,30 +6,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class GridFooter extends StatelessWidget {
|
||||
const GridFooter({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: GridSize.footerHeight,
|
||||
child: Padding(
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const SizedBox(width: 120, child: _AddRowButton()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddRowButton extends StatelessWidget {
|
||||
const _AddRowButton({Key? key}) : super(key: key);
|
||||
class GridAddRowButton extends StatelessWidget {
|
||||
const GridAddRowButton({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -4,7 +4,9 @@ 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/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'field_type_extension.dart';
|
||||
@ -25,21 +27,22 @@ class GridFieldCell extends StatelessWidget {
|
||||
child: BlocBuilder<FieldCellBloc, FieldCellState>(
|
||||
builder: (context, state) {
|
||||
final button = FlowyButton(
|
||||
hoverColor: theme.hover,
|
||||
hoverColor: theme.shader6,
|
||||
onTap: () => _showActionSheet(context),
|
||||
rightIcon: svgWidget("editor/details", color: theme.iconColor),
|
||||
leftIcon: svgWidget(state.field.fieldType.iconName(), color: theme.iconColor),
|
||||
text: FlowyText.medium(state.field.name, fontSize: 12),
|
||||
padding: GridSize.cellContentInsets,
|
||||
);
|
||||
|
||||
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
|
||||
final decoration = BoxDecoration(border: Border(top: borderSide, right: borderSide, bottom: borderSide));
|
||||
const line = Positioned(top: 0, bottom: 0, right: 0, child: _DragToExpandLine());
|
||||
|
||||
return Container(
|
||||
return _CellContainer(
|
||||
width: state.field.width.toDouble(),
|
||||
decoration: decoration,
|
||||
child: button,
|
||||
child: Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
fit: StackFit.expand,
|
||||
children: [button, line],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -66,3 +69,65 @@ class GridFieldCell extends StatelessWidget {
|
||||
).show(context);
|
||||
}
|
||||
}
|
||||
|
||||
class _CellContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
final double width;
|
||||
const _CellContainer({
|
||||
required this.child,
|
||||
required this.width,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
|
||||
final decoration = BoxDecoration(
|
||||
border: Border(
|
||||
top: borderSide,
|
||||
right: borderSide,
|
||||
bottom: borderSide,
|
||||
));
|
||||
|
||||
return Container(
|
||||
width: width,
|
||||
decoration: decoration,
|
||||
child: ConstrainedBox(constraints: const BoxConstraints.expand(), child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DragToExpandLine extends StatelessWidget {
|
||||
const _DragToExpandLine({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onHorizontalDragCancel: () {},
|
||||
onHorizontalDragUpdate: (value) {
|
||||
// context.read<FieldCellBloc>().add(FieldCellEvent.updateWidth(value.delta.dx));
|
||||
Log.info(value);
|
||||
},
|
||||
onHorizontalDragEnd: (end) {
|
||||
Log.info(end);
|
||||
},
|
||||
child: FlowyHover(
|
||||
style: HoverStyle(
|
||||
hoverColor: theme.main1,
|
||||
borderRadius: BorderRadius.zero,
|
||||
contentMargin: const EdgeInsets.only(left: 5),
|
||||
),
|
||||
builder: (_, onHover) => const SizedBox(width: 2),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,92 +8,107 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:reorderables/reorderables.dart';
|
||||
import 'field_editor.dart';
|
||||
import 'field_cell.dart';
|
||||
|
||||
class GridHeader extends StatelessWidget {
|
||||
class GridHeaderSliverAdaptor extends StatefulWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
const GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key);
|
||||
final GridFieldCache fieldCache;
|
||||
final ScrollController anchorScrollController;
|
||||
const GridHeaderSliverAdaptor({
|
||||
required this.gridId,
|
||||
required this.fieldCache,
|
||||
required this.anchorScrollController,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<GridHeaderSliverAdaptor> createState() => _GridHeaderSliverAdaptorState();
|
||||
}
|
||||
|
||||
class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
final bloc = getIt<GridHeaderBloc>(param1: gridId, param2: fields);
|
||||
final bloc = getIt<GridHeaderBloc>(param1: widget.gridId, param2: widget.fieldCache);
|
||||
bloc.add(const GridHeaderEvent.initial());
|
||||
return bloc;
|
||||
},
|
||||
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
|
||||
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
||||
builder: (context, state) {
|
||||
return SliverPersistentHeader(
|
||||
delegate: _GridHeaderDelegate(gridId: gridId, fields: List.from(state.fields)),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: widget.anchorScrollController,
|
||||
child: SizedBox(
|
||||
height: GridSize.headerHeight,
|
||||
child: _GridHeader(gridId: widget.gridId),
|
||||
),
|
||||
);
|
||||
|
||||
// return SliverPersistentHeader(
|
||||
// delegate: SliverHeaderDelegateImplementation(gridId: gridId, fields: state.fields),
|
||||
// floating: true,
|
||||
// pinned: true,
|
||||
// );
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
class _GridHeader extends StatefulWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
_GridHeaderDelegate({required this.gridId, required this.fields});
|
||||
const _GridHeader({Key? key, required this.gridId}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return _GridHeaderWidget(gridId: gridId, fields: fields, key: ObjectKey(fields));
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => GridSize.headerHeight;
|
||||
|
||||
@override
|
||||
double get minExtent => GridSize.headerHeight;
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
||||
if (oldDelegate is _GridHeaderDelegate) {
|
||||
return fields.length != oldDelegate.fields.length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
State<_GridHeader> createState() => _GridHeaderState();
|
||||
}
|
||||
|
||||
class _GridHeaderWidget extends StatelessWidget {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
const _GridHeaderWidget({required this.gridId, required this.fields, Key? key}) : super(key: key);
|
||||
|
||||
class _GridHeaderState extends State<_GridHeader> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final cells = fields.map(
|
||||
(field) => GridFieldCell(
|
||||
GridFieldCellContext(gridId: gridId, field: field),
|
||||
),
|
||||
);
|
||||
return BlocBuilder<GridHeaderBloc, GridHeaderState>(
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
builder: (context, state) {
|
||||
final cells = state.fields
|
||||
.where((field) => field.visibility)
|
||||
.map((field) => GridFieldCellContext(gridId: widget.gridId, field: field))
|
||||
.map((ctx) => GridFieldCell(ctx, key: ValueKey(ctx.field.id)))
|
||||
.toList();
|
||||
|
||||
final row = Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const _HeaderLeading(),
|
||||
...cells,
|
||||
_HeaderTrailing(gridId: gridId),
|
||||
],
|
||||
return Container(
|
||||
color: theme.surface,
|
||||
child: RepaintBoundary(
|
||||
child: ReorderableRow(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
scrollController: ScrollController(),
|
||||
header: const _CellLeading(),
|
||||
footer: _CellTrailing(gridId: widget.gridId),
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
_onReorder(cells, oldIndex, context, newIndex);
|
||||
},
|
||||
children: cells,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Container(height: GridSize.headerHeight, color: theme.surface, child: row);
|
||||
void _onReorder(List<GridFieldCell> cells, int oldIndex, BuildContext context, int newIndex) {
|
||||
if (cells.length > oldIndex) {
|
||||
final field = cells[oldIndex].cellContext.field;
|
||||
context.read<GridHeaderBloc>().add(GridHeaderEvent.moveField(field, oldIndex, newIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderLeading extends StatelessWidget {
|
||||
const _HeaderLeading({Key? key}) : super(key: key);
|
||||
class _CellLeading extends StatelessWidget {
|
||||
const _CellLeading({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -103,9 +118,9 @@ class _HeaderLeading extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _HeaderTrailing extends StatelessWidget {
|
||||
class _CellTrailing extends StatelessWidget {
|
||||
final String gridId;
|
||||
const _HeaderTrailing({required this.gridId, Key? key}) : super(key: key);
|
||||
const _CellTrailing({required this.gridId, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -141,3 +156,29 @@ class CreateFieldButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SliverHeaderDelegateImplementation extends SliverPersistentHeaderDelegate {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
SliverHeaderDelegateImplementation({required this.gridId, required this.fields});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return _GridHeader(gridId: gridId);
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => GridSize.headerHeight;
|
||||
|
||||
@override
|
||||
double get minExtent => GridSize.headerHeight;
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
||||
if (oldDelegate is SliverHeaderDelegateImplementation) {
|
||||
return fields.length != oldDelegate.fields.length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NumberCell extends StatefulWidget {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const NumberCell({
|
||||
required this.cellData,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart';
|
||||
@ -12,8 +11,12 @@ import 'package:provider/provider.dart';
|
||||
import 'row_action_sheet.dart';
|
||||
|
||||
class GridRowWidget extends StatefulWidget {
|
||||
final RowData data;
|
||||
const GridRowWidget({required this.data, Key? key}) : super(key: key);
|
||||
final RowBloc Function() blocBuilder;
|
||||
|
||||
const GridRowWidget({
|
||||
required this.blocBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<GridRowWidget> createState() => _GridRowWidgetState();
|
||||
@ -25,7 +28,8 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_rowBloc = getIt<RowBloc>(param1: widget.data)..add(const RowEvent.initial());
|
||||
_rowBloc = widget.blocBuilder();
|
||||
_rowBloc.add(const RowEvent.initial());
|
||||
_rowStateNotifier = _RegionStateNotifier();
|
||||
super.initState();
|
||||
}
|
||||
@ -44,9 +48,10 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
buildWhen: (p, c) => p.rowData.height != c.rowData.height,
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: _rowBloc.state.rowData.height,
|
||||
height: 42,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: const [
|
||||
_RowLeading(),
|
||||
_RowCells(),
|
||||
@ -146,7 +151,11 @@ class _RowCells extends StatelessWidget {
|
||||
buildWhen: (previous, current) => previous.cellDataMap != current.cellDataMap,
|
||||
builder: (context, state) {
|
||||
final List<Widget> children = state.cellDataMap.fold(() => [], _toCells);
|
||||
return Row(children: children);
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NumberCell extends StatefulWidget {
|
||||
final CellData cellData;
|
||||
final GridCellIdentifier cellData;
|
||||
|
||||
const NumberCell({
|
||||
required this.cellData,
|
||||
|
@ -14,7 +14,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class GridRowActionSheet extends StatelessWidget {
|
||||
final RowData rowData;
|
||||
final GridRow rowData;
|
||||
const GridRowActionSheet({required this.rowData, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/setting/property_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart';
|
||||
@ -18,10 +19,10 @@ import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
final GridFieldCache fieldCache;
|
||||
const GridPropertyList({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
required this.fieldCache,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -43,23 +44,22 @@ class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
getIt<GridPropertyBloc>(param1: gridId, param2: fields)..add(const GridPropertyEvent.initial()),
|
||||
getIt<GridPropertyBloc>(param1: gridId, param2: fieldCache)..add(const GridPropertyEvent.initial()),
|
||||
child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
|
||||
builder: (context, state) {
|
||||
final cells = state.fields.map((field) {
|
||||
return _GridPropertyCell(gridId: gridId, field: field);
|
||||
return _GridPropertyCell(gridId: gridId, field: field, key: ValueKey(field.id));
|
||||
}).toList();
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -92,17 +92,7 @@ class _GridPropertyCell extends StatelessWidget {
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(field.name, fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
|
||||
onTap: () {
|
||||
FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldContextLoader: FieldContextLoaderAdaptor(gridId: gridId, field: field),
|
||||
).show(context, anchorDirection: AnchorDirection.bottomRight);
|
||||
},
|
||||
),
|
||||
child: _editFieldButton(theme, context),
|
||||
),
|
||||
),
|
||||
FlowyIconButton(
|
||||
@ -116,4 +106,18 @@ class _GridPropertyCell extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
FlowyButton _editFieldButton(AppTheme theme, BuildContext context) {
|
||||
return FlowyButton(
|
||||
text: FlowyText.medium(field.name, fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
|
||||
onTap: () {
|
||||
FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldContextLoader: FieldContextLoaderAdaptor(gridId: gridId, field: field),
|
||||
).show(context, anchorDirection: AnchorDirection.bottomRight);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/setting/setting_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
@ -7,7 +8,6 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.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';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -18,11 +18,11 @@ import 'grid_property.dart';
|
||||
|
||||
class GridSettingContext {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
final GridFieldCache fieldCache;
|
||||
|
||||
GridSettingContext({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
required this.fieldCache,
|
||||
});
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ class GridSettingList extends StatelessWidget {
|
||||
case GridSettingAction.sortBy:
|
||||
break;
|
||||
case GridSettingAction.properties:
|
||||
GridPropertyList(gridId: settingContext.gridId, fields: settingContext.fields).show(context);
|
||||
GridPropertyList(gridId: settingContext.gridId, fieldCache: settingContext.fieldCache).show(context);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -12,10 +12,10 @@ import 'grid_setting.dart';
|
||||
|
||||
class GridToolbarContext {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
final GridFieldCache fieldCache;
|
||||
GridToolbarContext({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
required this.fieldCache,
|
||||
});
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ class GridToolbar extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final settingContext = GridSettingContext(
|
||||
gridId: toolbarContext.gridId,
|
||||
fields: toolbarContext.fields,
|
||||
fieldCache: toolbarContext.fieldCache,
|
||||
);
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
|
@ -85,7 +85,7 @@ class ActionCell<T extends ActionItem> extends StatelessWidget {
|
||||
final theme = context.watch<AppTheme>();
|
||||
|
||||
return FlowyHover(
|
||||
config: HoverDisplayConfig(hoverColor: theme.hover),
|
||||
style: HoverStyle(hoverColor: theme.hover),
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -31,12 +31,18 @@ class PublishNotifier<T> extends ChangeNotifier {
|
||||
|
||||
T? get currentValue => _value;
|
||||
|
||||
void addPublishListener(void Function(T) callback) {
|
||||
void addPublishListener(void Function(T) callback, {bool Function()? listenWhen}) {
|
||||
super.addListener(
|
||||
() {
|
||||
if (_value != null) {
|
||||
callback(_value!);
|
||||
if (_value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(_value!);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class FlowyButton extends StatelessWidget {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: FlowyHover(
|
||||
config: HoverDisplayConfig(borderRadius: Corners.s6Border, hoverColor: hoverColor),
|
||||
style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: hoverColor),
|
||||
setSelected: () => isSelected,
|
||||
builder: (context, onHover) => _render(),
|
||||
),
|
||||
|
@ -5,14 +5,14 @@ import 'package:flowy_infra/time/duration.dart';
|
||||
typedef HoverBuilder = Widget Function(BuildContext context, bool onHover);
|
||||
|
||||
class FlowyHover extends StatefulWidget {
|
||||
final HoverDisplayConfig config;
|
||||
final HoverStyle style;
|
||||
final HoverBuilder builder;
|
||||
final bool Function()? setSelected;
|
||||
|
||||
const FlowyHover({
|
||||
Key? key,
|
||||
required this.builder,
|
||||
required this.config,
|
||||
required this.style,
|
||||
this.setSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -41,7 +41,7 @@ class _FlowyHoverState extends State<FlowyHover> {
|
||||
|
||||
if (showHover) {
|
||||
return FlowyHoverContainer(
|
||||
config: widget.config,
|
||||
style: widget.style,
|
||||
child: widget.builder(context, _onHover),
|
||||
);
|
||||
} else {
|
||||
@ -50,41 +50,44 @@ class _FlowyHoverState extends State<FlowyHover> {
|
||||
}
|
||||
}
|
||||
|
||||
class HoverDisplayConfig {
|
||||
class HoverStyle {
|
||||
final Color borderColor;
|
||||
final double borderWidth;
|
||||
final Color hoverColor;
|
||||
final BorderRadius borderRadius;
|
||||
final EdgeInsets contentMargin;
|
||||
|
||||
const HoverDisplayConfig(
|
||||
const HoverStyle(
|
||||
{this.borderColor = Colors.transparent,
|
||||
this.borderWidth = 0,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(6)),
|
||||
this.contentMargin = EdgeInsets.zero,
|
||||
required this.hoverColor});
|
||||
}
|
||||
|
||||
class FlowyHoverContainer extends StatelessWidget {
|
||||
final HoverDisplayConfig config;
|
||||
final HoverStyle style;
|
||||
final Widget child;
|
||||
|
||||
const FlowyHoverContainer({
|
||||
Key? key,
|
||||
required this.child,
|
||||
required this.config,
|
||||
required this.style,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hoverBorder = Border.all(
|
||||
color: config.borderColor,
|
||||
width: config.borderWidth,
|
||||
color: style.borderColor,
|
||||
width: style.borderWidth,
|
||||
);
|
||||
|
||||
return Container(
|
||||
margin: style.contentMargin,
|
||||
decoration: BoxDecoration(
|
||||
border: hoverBorder,
|
||||
color: config.hoverColor,
|
||||
borderRadius: config.borderRadius,
|
||||
color: style.hoverColor,
|
||||
borderRadius: style.borderRadius,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
|
@ -74,21 +74,21 @@ class _StyledSingleChildScrollViewState extends State<StyledSingleChildScrollVie
|
||||
}
|
||||
|
||||
class StyledCustomScrollView extends StatefulWidget {
|
||||
final double? contentSize;
|
||||
final Axis axis;
|
||||
final Color? trackColor;
|
||||
final Color? handleColor;
|
||||
final ScrollController? controller;
|
||||
final ScrollController? verticalController;
|
||||
final List<Widget> slivers;
|
||||
final double barSize;
|
||||
|
||||
const StyledCustomScrollView({
|
||||
Key? key,
|
||||
this.contentSize,
|
||||
this.axis = Axis.vertical,
|
||||
this.trackColor,
|
||||
this.handleColor,
|
||||
this.controller,
|
||||
this.verticalController,
|
||||
this.slivers = const <Widget>[],
|
||||
this.barSize = 12,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -96,17 +96,17 @@ class StyledCustomScrollView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _StyledCustomScrollViewState extends State<StyledCustomScrollView> {
|
||||
late ScrollController scrollController;
|
||||
late ScrollController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
scrollController = widget.controller ?? ScrollController();
|
||||
controller = widget.verticalController ?? ScrollController();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -120,19 +120,23 @@ class _StyledCustomScrollViewState extends State<StyledCustomScrollView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ScrollbarListStack(
|
||||
contentSize: widget.contentSize,
|
||||
axis: widget.axis,
|
||||
controller: scrollController,
|
||||
barSize: 12,
|
||||
trackColor: widget.trackColor,
|
||||
handleColor: widget.handleColor,
|
||||
var child = ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: CustomScrollView(
|
||||
scrollDirection: widget.axis,
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: scrollController,
|
||||
controller: controller,
|
||||
slivers: widget.slivers,
|
||||
),
|
||||
);
|
||||
|
||||
return ScrollbarListStack(
|
||||
axis: widget.axis,
|
||||
controller: controller,
|
||||
barSize: widget.barSize,
|
||||
trackColor: widget.trackColor,
|
||||
handleColor: widget.handleColor,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +154,23 @@ class GridEventGetEditFieldContext {
|
||||
}
|
||||
}
|
||||
|
||||
class GridEventMoveItem {
|
||||
MoveItemPayload request;
|
||||
GridEventMoveItem(this.request);
|
||||
|
||||
Future<Either<Unit, FlowyError>> send() {
|
||||
final request = FFIRequest.create()
|
||||
..event = GridEvent.MoveItem.toString()
|
||||
..payload = requestToBytes(this.request);
|
||||
|
||||
return Dispatch.asyncRequest(request)
|
||||
.then((bytesResult) => bytesResult.fold(
|
||||
(bytes) => left(unit),
|
||||
(errBytes) => right(FlowyError.fromBuffer(errBytes)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class GridEventNewSelectOption {
|
||||
SelectOptionName request;
|
||||
GridEventNewSelectOption(this.request);
|
||||
|
@ -254,6 +254,140 @@ class FieldOrder extends $pb.GeneratedMessage {
|
||||
void clearFieldId() => clearField(1);
|
||||
}
|
||||
|
||||
class GridFieldChangeset extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridFieldChangeset', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
|
||||
..pc<IndexField>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedFields', $pb.PbFieldType.PM, subBuilder: IndexField.create)
|
||||
..pc<FieldOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedFields', $pb.PbFieldType.PM, subBuilder: FieldOrder.create)
|
||||
..pc<Field>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedFields', $pb.PbFieldType.PM, subBuilder: Field.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
GridFieldChangeset._() : super();
|
||||
factory GridFieldChangeset({
|
||||
$core.String? gridId,
|
||||
$core.Iterable<IndexField>? insertedFields,
|
||||
$core.Iterable<FieldOrder>? deletedFields,
|
||||
$core.Iterable<Field>? updatedFields,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (gridId != null) {
|
||||
_result.gridId = gridId;
|
||||
}
|
||||
if (insertedFields != null) {
|
||||
_result.insertedFields.addAll(insertedFields);
|
||||
}
|
||||
if (deletedFields != null) {
|
||||
_result.deletedFields.addAll(deletedFields);
|
||||
}
|
||||
if (updatedFields != null) {
|
||||
_result.updatedFields.addAll(updatedFields);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory GridFieldChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory GridFieldChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridFieldChangeset clone() => GridFieldChangeset()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridFieldChangeset copyWith(void Function(GridFieldChangeset) updates) => super.copyWith((message) => updates(message as GridFieldChangeset)) as GridFieldChangeset; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridFieldChangeset create() => GridFieldChangeset._();
|
||||
GridFieldChangeset createEmptyInstance() => create();
|
||||
static $pb.PbList<GridFieldChangeset> createRepeated() => $pb.PbList<GridFieldChangeset>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridFieldChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridFieldChangeset>(create);
|
||||
static GridFieldChangeset? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get gridId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set gridId($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasGridId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearGridId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<IndexField> get insertedFields => $_getList(1);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<FieldOrder> get deletedFields => $_getList(2);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<Field> get updatedFields => $_getList(3);
|
||||
}
|
||||
|
||||
class IndexField extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IndexField', createEmptyInstance: create)
|
||||
..aOM<Field>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'field', subBuilder: Field.create)
|
||||
..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'index', $pb.PbFieldType.O3)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
IndexField._() : super();
|
||||
factory IndexField({
|
||||
Field? field_1,
|
||||
$core.int? index,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (field_1 != null) {
|
||||
_result.field_1 = field_1;
|
||||
}
|
||||
if (index != null) {
|
||||
_result.index = index;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory IndexField.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory IndexField.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
IndexField clone() => IndexField()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
IndexField copyWith(void Function(IndexField) updates) => super.copyWith((message) => updates(message as IndexField)) as IndexField; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static IndexField create() => IndexField._();
|
||||
IndexField createEmptyInstance() => create();
|
||||
static $pb.PbList<IndexField> createRepeated() => $pb.PbList<IndexField>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static IndexField getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<IndexField>(create);
|
||||
static IndexField? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
Field get field_1 => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set field_1(Field v) { setField(1, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasField_1() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearField_1() => clearField(1);
|
||||
@$pb.TagNumber(1)
|
||||
Field ensureField_1() => $_ensure(0);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get index => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set index($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasIndex() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearIndex() => clearField(2);
|
||||
}
|
||||
|
||||
enum GetEditFieldContextPayload_OneOfFieldId {
|
||||
fieldId,
|
||||
notSet
|
||||
@ -857,77 +991,6 @@ class GridBlockOrder extends $pb.GeneratedMessage {
|
||||
$core.List<RowOrder> get rowOrders => $_getList(1);
|
||||
}
|
||||
|
||||
class GridBlockOrderChangeset extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockOrderChangeset', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
|
||||
..pc<IndexRowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create)
|
||||
..pc<RowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
|
||||
..pc<RowOrder>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
GridBlockOrderChangeset._() : super();
|
||||
factory GridBlockOrderChangeset({
|
||||
$core.String? blockId,
|
||||
$core.Iterable<IndexRowOrder>? insertedRows,
|
||||
$core.Iterable<RowOrder>? deletedRows,
|
||||
$core.Iterable<RowOrder>? updatedRows,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (blockId != null) {
|
||||
_result.blockId = blockId;
|
||||
}
|
||||
if (insertedRows != null) {
|
||||
_result.insertedRows.addAll(insertedRows);
|
||||
}
|
||||
if (deletedRows != null) {
|
||||
_result.deletedRows.addAll(deletedRows);
|
||||
}
|
||||
if (updatedRows != null) {
|
||||
_result.updatedRows.addAll(updatedRows);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory GridBlockOrderChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory GridBlockOrderChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridBlockOrderChangeset clone() => GridBlockOrderChangeset()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridBlockOrderChangeset copyWith(void Function(GridBlockOrderChangeset) updates) => super.copyWith((message) => updates(message as GridBlockOrderChangeset)) as GridBlockOrderChangeset; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridBlockOrderChangeset create() => GridBlockOrderChangeset._();
|
||||
GridBlockOrderChangeset createEmptyInstance() => create();
|
||||
static $pb.PbList<GridBlockOrderChangeset> createRepeated() => $pb.PbList<GridBlockOrderChangeset>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridBlockOrderChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlockOrderChangeset>(create);
|
||||
static GridBlockOrderChangeset? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get blockId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set blockId($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasBlockId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearBlockId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<IndexRowOrder> get insertedRows => $_getList(1);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<RowOrder> get deletedRows => $_getList(2);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<RowOrder> get updatedRows => $_getList(3);
|
||||
}
|
||||
|
||||
enum IndexRowOrder_OneOfIndex {
|
||||
index_,
|
||||
notSet
|
||||
@ -1004,6 +1067,77 @@ class IndexRowOrder extends $pb.GeneratedMessage {
|
||||
void clearIndex() => clearField(2);
|
||||
}
|
||||
|
||||
class GridRowsChangeset extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridRowsChangeset', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
|
||||
..pc<IndexRowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create)
|
||||
..pc<RowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
|
||||
..pc<RowOrder>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
GridRowsChangeset._() : super();
|
||||
factory GridRowsChangeset({
|
||||
$core.String? blockId,
|
||||
$core.Iterable<IndexRowOrder>? insertedRows,
|
||||
$core.Iterable<RowOrder>? deletedRows,
|
||||
$core.Iterable<RowOrder>? updatedRows,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (blockId != null) {
|
||||
_result.blockId = blockId;
|
||||
}
|
||||
if (insertedRows != null) {
|
||||
_result.insertedRows.addAll(insertedRows);
|
||||
}
|
||||
if (deletedRows != null) {
|
||||
_result.deletedRows.addAll(deletedRows);
|
||||
}
|
||||
if (updatedRows != null) {
|
||||
_result.updatedRows.addAll(updatedRows);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory GridRowsChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory GridRowsChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridRowsChangeset clone() => GridRowsChangeset()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
GridRowsChangeset copyWith(void Function(GridRowsChangeset) updates) => super.copyWith((message) => updates(message as GridRowsChangeset)) as GridRowsChangeset; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridRowsChangeset create() => GridRowsChangeset._();
|
||||
GridRowsChangeset createEmptyInstance() => create();
|
||||
static $pb.PbList<GridRowsChangeset> createRepeated() => $pb.PbList<GridRowsChangeset>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static GridRowsChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridRowsChangeset>(create);
|
||||
static GridRowsChangeset? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get blockId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set blockId($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasBlockId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearBlockId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<IndexRowOrder> get insertedRows => $_getList(1);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<RowOrder> get deletedRows => $_getList(2);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<RowOrder> get updatedRows => $_getList(3);
|
||||
}
|
||||
|
||||
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') ? '' : 'id')
|
||||
@ -1950,6 +2084,109 @@ class FieldChangesetPayload extends $pb.GeneratedMessage {
|
||||
void clearTypeOptionData() => clearField(9);
|
||||
}
|
||||
|
||||
class MoveItemPayload extends $pb.GeneratedMessage {
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MoveItemPayload', createEmptyInstance: create)
|
||||
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
|
||||
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'itemId')
|
||||
..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fromIndex', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'toIndex', $pb.PbFieldType.O3)
|
||||
..e<MoveItemType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ty', $pb.PbFieldType.OE, defaultOrMaker: MoveItemType.MoveField, valueOf: MoveItemType.valueOf, enumValues: MoveItemType.values)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
MoveItemPayload._() : super();
|
||||
factory MoveItemPayload({
|
||||
$core.String? gridId,
|
||||
$core.String? itemId,
|
||||
$core.int? fromIndex,
|
||||
$core.int? toIndex,
|
||||
MoveItemType? ty,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (gridId != null) {
|
||||
_result.gridId = gridId;
|
||||
}
|
||||
if (itemId != null) {
|
||||
_result.itemId = itemId;
|
||||
}
|
||||
if (fromIndex != null) {
|
||||
_result.fromIndex = fromIndex;
|
||||
}
|
||||
if (toIndex != null) {
|
||||
_result.toIndex = toIndex;
|
||||
}
|
||||
if (ty != null) {
|
||||
_result.ty = ty;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory MoveItemPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory MoveItemPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
MoveItemPayload clone() => MoveItemPayload()..mergeFromMessage(this);
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
MoveItemPayload copyWith(void Function(MoveItemPayload) updates) => super.copyWith((message) => updates(message as MoveItemPayload)) as MoveItemPayload; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static MoveItemPayload create() => MoveItemPayload._();
|
||||
MoveItemPayload createEmptyInstance() => create();
|
||||
static $pb.PbList<MoveItemPayload> createRepeated() => $pb.PbList<MoveItemPayload>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static MoveItemPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MoveItemPayload>(create);
|
||||
static MoveItemPayload? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get gridId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set gridId($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasGridId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearGridId() => clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get itemId => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set itemId($core.String v) { $_setString(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasItemId() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearItemId() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get fromIndex => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set fromIndex($core.int v) { $_setSignedInt32(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasFromIndex() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearFromIndex() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.int get toIndex => $_getIZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set toIndex($core.int v) { $_setSignedInt32(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasToIndex() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearToIndex() => clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
MoveItemType get ty => $_getN(4);
|
||||
@$pb.TagNumber(5)
|
||||
set ty(MoveItemType v) { setField(5, v); }
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasTy() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearTy() => clearField(5);
|
||||
}
|
||||
|
||||
enum CellChangeset_OneOfData {
|
||||
data,
|
||||
notSet
|
||||
|
@ -9,6 +9,21 @@
|
||||
import 'dart:core' as $core;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
class MoveItemType extends $pb.ProtobufEnum {
|
||||
static const MoveItemType MoveField = MoveItemType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveField');
|
||||
static const MoveItemType MoveRow = MoveItemType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveRow');
|
||||
|
||||
static const $core.List<MoveItemType> values = <MoveItemType> [
|
||||
MoveField,
|
||||
MoveRow,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, MoveItemType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
static MoveItemType? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const MoveItemType._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
||||
class FieldType extends $pb.ProtobufEnum {
|
||||
static const FieldType RichText = FieldType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
|
||||
static const FieldType Number = FieldType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number');
|
||||
|
@ -8,6 +8,17 @@
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
@$core.Deprecated('Use moveItemTypeDescriptor instead')
|
||||
const MoveItemType$json = const {
|
||||
'1': 'MoveItemType',
|
||||
'2': const [
|
||||
const {'1': 'MoveField', '2': 0},
|
||||
const {'1': 'MoveRow', '2': 1},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `MoveItemType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List moveItemTypeDescriptor = $convert.base64Decode('CgxNb3ZlSXRlbVR5cGUSDQoJTW92ZUZpZWxkEAASCwoHTW92ZVJvdxAB');
|
||||
@$core.Deprecated('Use fieldTypeDescriptor instead')
|
||||
const FieldType$json = const {
|
||||
'1': 'FieldType',
|
||||
@ -61,6 +72,30 @@ const FieldOrder$json = const {
|
||||
|
||||
/// Descriptor for `FieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List fieldOrderDescriptor = $convert.base64Decode('CgpGaWVsZE9yZGVyEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElk');
|
||||
@$core.Deprecated('Use gridFieldChangesetDescriptor instead')
|
||||
const GridFieldChangeset$json = const {
|
||||
'1': 'GridFieldChangeset',
|
||||
'2': const [
|
||||
const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
|
||||
const {'1': 'inserted_fields', '3': 2, '4': 3, '5': 11, '6': '.IndexField', '10': 'insertedFields'},
|
||||
const {'1': 'deleted_fields', '3': 3, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'deletedFields'},
|
||||
const {'1': 'updated_fields', '3': 4, '4': 3, '5': 11, '6': '.Field', '10': 'updatedFields'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridFieldChangeset`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gridFieldChangesetDescriptor = $convert.base64Decode('ChJHcmlkRmllbGRDaGFuZ2VzZXQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEjQKD2luc2VydGVkX2ZpZWxkcxgCIAMoCzILLkluZGV4RmllbGRSDmluc2VydGVkRmllbGRzEjIKDmRlbGV0ZWRfZmllbGRzGAMgAygLMgsuRmllbGRPcmRlclINZGVsZXRlZEZpZWxkcxItCg51cGRhdGVkX2ZpZWxkcxgEIAMoCzIGLkZpZWxkUg11cGRhdGVkRmllbGRz');
|
||||
@$core.Deprecated('Use indexFieldDescriptor instead')
|
||||
const IndexField$json = const {
|
||||
'1': 'IndexField',
|
||||
'2': const [
|
||||
const {'1': 'field', '3': 1, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
|
||||
const {'1': 'index', '3': 2, '4': 1, '5': 5, '10': 'index'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `IndexField`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List indexFieldDescriptor = $convert.base64Decode('CgpJbmRleEZpZWxkEhwKBWZpZWxkGAEgASgLMgYuRmllbGRSBWZpZWxkEhQKBWluZGV4GAIgASgFUgVpbmRleA==');
|
||||
@$core.Deprecated('Use getEditFieldContextPayloadDescriptor instead')
|
||||
const GetEditFieldContextPayload$json = const {
|
||||
'1': 'GetEditFieldContextPayload',
|
||||
@ -186,19 +221,6 @@ const GridBlockOrder$json = const {
|
||||
|
||||
/// Descriptor for `GridBlockOrder`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gridBlockOrderDescriptor = $convert.base64Decode('Cg5HcmlkQmxvY2tPcmRlchIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIoCgpyb3dfb3JkZXJzGAIgAygLMgkuUm93T3JkZXJSCXJvd09yZGVycw==');
|
||||
@$core.Deprecated('Use gridBlockOrderChangesetDescriptor instead')
|
||||
const GridBlockOrderChangeset$json = const {
|
||||
'1': 'GridBlockOrderChangeset',
|
||||
'2': const [
|
||||
const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
|
||||
const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.IndexRowOrder', '10': 'insertedRows'},
|
||||
const {'1': 'deleted_rows', '3': 3, '4': 3, '5': 11, '6': '.RowOrder', '10': 'deletedRows'},
|
||||
const {'1': 'updated_rows', '3': 4, '4': 3, '5': 11, '6': '.RowOrder', '10': 'updatedRows'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridBlockOrderChangeset`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gridBlockOrderChangesetDescriptor = $convert.base64Decode('ChdHcmlkQmxvY2tPcmRlckNoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M=');
|
||||
@$core.Deprecated('Use indexRowOrderDescriptor instead')
|
||||
const IndexRowOrder$json = const {
|
||||
'1': 'IndexRowOrder',
|
||||
@ -213,6 +235,19 @@ const IndexRowOrder$json = const {
|
||||
|
||||
/// Descriptor for `IndexRowOrder`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List indexRowOrderDescriptor = $convert.base64Decode('Cg1JbmRleFJvd09yZGVyEiYKCXJvd19vcmRlchgBIAEoCzIJLlJvd09yZGVyUghyb3dPcmRlchIWCgVpbmRleBgCIAEoBUgAUgVpbmRleEIOCgxvbmVfb2ZfaW5kZXg=');
|
||||
@$core.Deprecated('Use gridRowsChangesetDescriptor instead')
|
||||
const GridRowsChangeset$json = const {
|
||||
'1': 'GridRowsChangeset',
|
||||
'2': const [
|
||||
const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
|
||||
const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.IndexRowOrder', '10': 'insertedRows'},
|
||||
const {'1': 'deleted_rows', '3': 3, '4': 3, '5': 11, '6': '.RowOrder', '10': 'deletedRows'},
|
||||
const {'1': 'updated_rows', '3': 4, '4': 3, '5': 11, '6': '.RowOrder', '10': 'updatedRows'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridRowsChangeset`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List gridRowsChangesetDescriptor = $convert.base64Decode('ChFHcmlkUm93c0NoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M=');
|
||||
@$core.Deprecated('Use gridBlockDescriptor instead')
|
||||
const GridBlock$json = const {
|
||||
'1': 'GridBlock',
|
||||
@ -370,6 +405,20 @@ const FieldChangesetPayload$json = const {
|
||||
|
||||
/// Descriptor for `FieldChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List fieldChangesetPayloadDescriptor = $convert.base64Decode('ChVGaWVsZENoYW5nZXNldFBheWxvYWQSGQoIZmllbGRfaWQYASABKAlSB2ZpZWxkSWQSFwoHZ3JpZF9pZBgCIAEoCVIGZ3JpZElkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIUCgRkZXNjGAQgASgJSAFSBGRlc2MSKwoKZmllbGRfdHlwZRgFIAEoDjIKLkZpZWxkVHlwZUgCUglmaWVsZFR5cGUSGAoGZnJvemVuGAYgASgISANSBmZyb3plbhIgCgp2aXNpYmlsaXR5GAcgASgISARSCnZpc2liaWxpdHkSFgoFd2lkdGgYCCABKAVIBVIFd2lkdGgSKgoQdHlwZV9vcHRpb25fZGF0YRgJIAEoDEgGUg50eXBlT3B0aW9uRGF0YUINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0ITChFvbmVfb2ZfZmllbGRfdHlwZUIPCg1vbmVfb2ZfZnJvemVuQhMKEW9uZV9vZl92aXNpYmlsaXR5Qg4KDG9uZV9vZl93aWR0aEIZChdvbmVfb2ZfdHlwZV9vcHRpb25fZGF0YQ==');
|
||||
@$core.Deprecated('Use moveItemPayloadDescriptor instead')
|
||||
const MoveItemPayload$json = const {
|
||||
'1': 'MoveItemPayload',
|
||||
'2': const [
|
||||
const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
|
||||
const {'1': 'item_id', '3': 2, '4': 1, '5': 9, '10': 'itemId'},
|
||||
const {'1': 'from_index', '3': 3, '4': 1, '5': 5, '10': 'fromIndex'},
|
||||
const {'1': 'to_index', '3': 4, '4': 1, '5': 5, '10': 'toIndex'},
|
||||
const {'1': 'ty', '3': 5, '4': 1, '5': 14, '6': '.MoveItemType', '10': 'ty'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `MoveItemPayload`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List moveItemPayloadDescriptor = $convert.base64Decode('Cg9Nb3ZlSXRlbVBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhcKB2l0ZW1faWQYAiABKAlSBml0ZW1JZBIdCgpmcm9tX2luZGV4GAMgASgFUglmcm9tSW5kZXgSGQoIdG9faW5kZXgYBCABKAVSB3RvSW5kZXgSHQoCdHkYBSABKA4yDS5Nb3ZlSXRlbVR5cGVSAnR5');
|
||||
@$core.Deprecated('Use cellChangesetDescriptor instead')
|
||||
const CellChangeset$json = const {
|
||||
'1': 'CellChangeset',
|
||||
|
@ -12,19 +12,19 @@ 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 DidCreateBlock = GridNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidCreateBlock');
|
||||
static const GridNotification DidUpdateGridBlock = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateGridBlock');
|
||||
static const GridNotification DidUpdateGridRow = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateGridRow');
|
||||
static const GridNotification DidUpdateGridField = GridNotification._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateGridField');
|
||||
static const GridNotification DidUpdateRow = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateRow');
|
||||
static const GridNotification DidUpdateCell = GridNotification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateCell');
|
||||
static const GridNotification DidUpdateGrid = GridNotification._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateGrid');
|
||||
static const GridNotification DidUpdateField = GridNotification._(41, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateField');
|
||||
static const GridNotification DidUpdateCell = GridNotification._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateCell');
|
||||
static const GridNotification DidUpdateField = GridNotification._(50, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateField');
|
||||
|
||||
static const $core.List<GridNotification> values = <GridNotification> [
|
||||
Unknown,
|
||||
DidCreateBlock,
|
||||
DidUpdateGridBlock,
|
||||
DidUpdateGridRow,
|
||||
DidUpdateGridField,
|
||||
DidUpdateRow,
|
||||
DidUpdateCell,
|
||||
DidUpdateGrid,
|
||||
DidUpdateField,
|
||||
];
|
||||
|
||||
|
@ -14,13 +14,13 @@ const GridNotification$json = const {
|
||||
'2': const [
|
||||
const {'1': 'Unknown', '2': 0},
|
||||
const {'1': 'DidCreateBlock', '2': 11},
|
||||
const {'1': 'DidUpdateGridBlock', '2': 20},
|
||||
const {'1': 'DidUpdateGridRow', '2': 20},
|
||||
const {'1': 'DidUpdateGridField', '2': 21},
|
||||
const {'1': 'DidUpdateRow', '2': 30},
|
||||
const {'1': 'DidUpdateCell', '2': 31},
|
||||
const {'1': 'DidUpdateGrid', '2': 40},
|
||||
const {'1': 'DidUpdateField', '2': 41},
|
||||
const {'1': 'DidUpdateCell', '2': 40},
|
||||
const {'1': 'DidUpdateField', '2': 50},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `GridNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABISCg5EaWRDcmVhdGVCbG9jaxALEhYKEkRpZFVwZGF0ZUdyaWRCbG9jaxAUEhAKDERpZFVwZGF0ZVJvdxAeEhEKDURpZFVwZGF0ZUNlbGwQHxIRCg1EaWRVcGRhdGVHcmlkECgSEgoORGlkVXBkYXRlRmllbGQQKQ==');
|
||||
final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABISCg5EaWRDcmVhdGVCbG9jaxALEhQKEERpZFVwZGF0ZUdyaWRSb3cQFBIWChJEaWRVcGRhdGVHcmlkRmllbGQQFRIQCgxEaWRVcGRhdGVSb3cQHhIRCg1EaWRVcGRhdGVDZWxsECgSEgoORGlkVXBkYXRlRmllbGQQMg==');
|
||||
|
@ -19,6 +19,7 @@ class GridEvent extends $pb.ProtobufEnum {
|
||||
static const GridEvent SwitchToField = GridEvent._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SwitchToField');
|
||||
static const GridEvent DuplicateField = GridEvent._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField');
|
||||
static const GridEvent GetEditFieldContext = GridEvent._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetEditFieldContext');
|
||||
static const GridEvent MoveItem = GridEvent._(17, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveItem');
|
||||
static const GridEvent NewSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewSelectOption');
|
||||
static const GridEvent GetSelectOptionContext = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptionContext');
|
||||
static const GridEvent UpdateSelectOption = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOption');
|
||||
@ -40,6 +41,7 @@ class GridEvent extends $pb.ProtobufEnum {
|
||||
SwitchToField,
|
||||
DuplicateField,
|
||||
GetEditFieldContext,
|
||||
MoveItem,
|
||||
NewSelectOption,
|
||||
GetSelectOptionContext,
|
||||
UpdateSelectOption,
|
||||
|
@ -21,6 +21,7 @@ const GridEvent$json = const {
|
||||
const {'1': 'SwitchToField', '2': 14},
|
||||
const {'1': 'DuplicateField', '2': 15},
|
||||
const {'1': 'GetEditFieldContext', '2': 16},
|
||||
const {'1': 'MoveItem', '2': 17},
|
||||
const {'1': 'NewSelectOption', '2': 30},
|
||||
const {'1': 'GetSelectOptionContext', '2': 31},
|
||||
const {'1': 'UpdateSelectOption', '2': 32},
|
||||
@ -35,4 +36,4 @@ const GridEvent$json = const {
|
||||
};
|
||||
|
||||
/// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtJbnNlcnRGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhMKD05ld1NlbGVjdE9wdGlvbhAeEhoKFkdldFNlbGVjdE9wdGlvbkNvbnRleHQQHxIWChJVcGRhdGVTZWxlY3RPcHRpb24QIBINCglDcmVhdGVSb3cQMhIKCgZHZXRSb3cQMxINCglEZWxldGVSb3cQNBIQCgxEdXBsaWNhdGVSb3cQNRILCgdHZXRDZWxsEEYSDgoKVXBkYXRlQ2VsbBBHEhoKFlVwZGF0ZUNlbGxTZWxlY3RPcHRpb24QSA==');
|
||||
final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtJbnNlcnRGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEgwKCE1vdmVJdGVtEBESEwoPTmV3U2VsZWN0T3B0aW9uEB4SGgoWR2V0U2VsZWN0T3B0aW9uQ29udGV4dBAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlQ2VsbFNlbGVjdE9wdGlvbhBI');
|
||||
|
@ -653,6 +653,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
linked_scroll_controller:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: linked_scroll_controller
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
lint:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -947,6 +954,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1+1"
|
||||
reorderables:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: reorderables
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.3"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -74,6 +74,8 @@ dependencies:
|
||||
device_info_plus: ^3.2.1
|
||||
fluttertoast: ^8.0.8
|
||||
table_calendar: ^3.0.5
|
||||
reorderables:
|
||||
linked_scroll_controller: ^0.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
@ -31,7 +31,7 @@ flowy-derive = {path = "../../../shared-lib/flowy-derive" }
|
||||
[features]
|
||||
default = ["flowy-sdk/dart", "dart-notify/dart", "flutter"]
|
||||
flutter = []
|
||||
http_server = ["flowy-sdk/http_server", "flowy-sdk/use_bunyan"]
|
||||
http_sync = ["flowy-sdk/http_sync", "flowy-sdk/use_bunyan"]
|
||||
#use_serde = ["bincode"]
|
||||
#use_protobuf= ["protobuf"]
|
||||
|
||||
|
@ -44,6 +44,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file
|
||||
|
||||
[features]
|
||||
default = []
|
||||
http_server = []
|
||||
sync = []
|
||||
cloud_sync = ["sync"]
|
||||
flowy_unit_test = ["lib-ot/flowy_unit_test", "flowy-revision/flowy_unit_test"]
|
||||
dart = ["lib-infra/dart"]
|
@ -1,4 +1,3 @@
|
||||
use crate::services::web_socket::make_folder_ws_manager;
|
||||
use flowy_sync::{
|
||||
client_folder::{FolderChange, FolderPad},
|
||||
entities::{revision::Revision, ws_data::ServerRevisionWSData},
|
||||
@ -11,7 +10,6 @@ use flowy_sync::util::make_delta_from_revisions;
|
||||
|
||||
use flowy_revision::{
|
||||
RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
|
||||
RevisionWebSocketManager,
|
||||
};
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_ot::core::PlainTextAttributes;
|
||||
@ -21,13 +19,16 @@ use std::sync::Arc;
|
||||
|
||||
pub struct ClientFolderEditor {
|
||||
user_id: String,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) folder_id: FolderId,
|
||||
pub(crate) folder: Arc<RwLock<FolderPad>>,
|
||||
rev_manager: Arc<RevisionManager>,
|
||||
ws_manager: Arc<RevisionWebSocketManager>,
|
||||
#[cfg(feature = "sync")]
|
||||
ws_manager: Arc<flowy_revision::RevisionWebSocketManager>,
|
||||
}
|
||||
|
||||
impl ClientFolderEditor {
|
||||
#[allow(unused_variables)]
|
||||
pub async fn new(
|
||||
user_id: &str,
|
||||
folder_id: &FolderId,
|
||||
@ -40,7 +41,9 @@ impl ClientFolderEditor {
|
||||
});
|
||||
let folder = Arc::new(RwLock::new(rev_manager.load::<FolderPadBuilder>(Some(cloud)).await?));
|
||||
let rev_manager = Arc::new(rev_manager);
|
||||
let ws_manager = make_folder_ws_manager(
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
let ws_manager = crate::services::web_socket::make_folder_ws_manager(
|
||||
user_id,
|
||||
folder_id.as_ref(),
|
||||
rev_manager.clone(),
|
||||
@ -56,15 +59,23 @@ impl ClientFolderEditor {
|
||||
folder_id,
|
||||
folder,
|
||||
rev_manager,
|
||||
#[cfg(feature = "sync")]
|
||||
ws_manager,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub async fn receive_ws_data(&self, data: ServerRevisionWSData) -> FlowyResult<()> {
|
||||
let _ = self.ws_manager.ws_passthrough_tx.send(data).await.map_err(|e| {
|
||||
let err_msg = format!("{} passthrough error: {}", self.folder_id, e);
|
||||
FlowyError::internal().context(err_msg)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub async fn receive_ws_data(&self, _data: ServerRevisionWSData) -> FlowyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta};
|
||||
use parking_lot::RwLock;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn make_folder_ws_manager(
|
||||
user_id: &str,
|
||||
folder_id: &str,
|
||||
|
@ -6,11 +6,11 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
|
||||
pub enum GridNotification {
|
||||
Unknown = 0,
|
||||
DidCreateBlock = 11,
|
||||
DidUpdateGridBlock = 20,
|
||||
DidUpdateGridRow = 20,
|
||||
DidUpdateGridField = 21,
|
||||
DidUpdateRow = 30,
|
||||
DidUpdateCell = 31,
|
||||
DidUpdateGrid = 40,
|
||||
DidUpdateField = 41,
|
||||
DidUpdateCell = 40,
|
||||
DidUpdateField = 50,
|
||||
}
|
||||
|
||||
impl std::default::Default for GridNotification {
|
||||
|
@ -130,6 +130,17 @@ pub(crate) async fn get_field_context_handler(
|
||||
data_result(edit_context)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn move_item_handler(
|
||||
data: Data<MoveItemPayload>,
|
||||
manager: AppData<Arc<GridManager>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: MoveItemParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(¶ms.grid_id)?;
|
||||
let _ = editor.move_item(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn make_field_edit_context(
|
||||
grid_id: &str,
|
||||
field_id: Option<String>,
|
||||
|
@ -17,6 +17,8 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
.event(GridEvent::DeleteField, delete_field_handler)
|
||||
.event(GridEvent::SwitchToField, switch_to_field_handler)
|
||||
.event(GridEvent::DuplicateField, duplicate_field_handler)
|
||||
.event(GridEvent::GetEditFieldContext, get_field_context_handler)
|
||||
.event(GridEvent::MoveItem, move_item_handler)
|
||||
// Row
|
||||
.event(GridEvent::CreateRow, create_row_handler)
|
||||
.event(GridEvent::GetRow, get_row_handler)
|
||||
@ -29,9 +31,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
|
||||
.event(GridEvent::NewSelectOption, new_select_option_handler)
|
||||
.event(GridEvent::UpdateSelectOption, update_select_option_handler)
|
||||
.event(GridEvent::GetSelectOptionContext, get_select_option_handler)
|
||||
.event(GridEvent::UpdateCellSelectOption, update_cell_select_option_handler)
|
||||
//
|
||||
.event(GridEvent::GetEditFieldContext, get_field_context_handler);
|
||||
.event(GridEvent::UpdateCellSelectOption, update_cell_select_option_handler);
|
||||
|
||||
module
|
||||
}
|
||||
@ -66,6 +66,9 @@ pub enum GridEvent {
|
||||
#[event(input = "GetEditFieldContextPayload", output = "EditFieldContext")]
|
||||
GetEditFieldContext = 16,
|
||||
|
||||
#[event(input = "MoveItemPayload")]
|
||||
MoveItem = 17,
|
||||
|
||||
#[event(input = "SelectOptionName", output = "SelectOption")]
|
||||
NewSelectOption = 30,
|
||||
|
||||
|
@ -27,11 +27,11 @@
|
||||
pub enum GridNotification {
|
||||
Unknown = 0,
|
||||
DidCreateBlock = 11,
|
||||
DidUpdateGridBlock = 20,
|
||||
DidUpdateGridRow = 20,
|
||||
DidUpdateGridField = 21,
|
||||
DidUpdateRow = 30,
|
||||
DidUpdateCell = 31,
|
||||
DidUpdateGrid = 40,
|
||||
DidUpdateField = 41,
|
||||
DidUpdateCell = 40,
|
||||
DidUpdateField = 50,
|
||||
}
|
||||
|
||||
impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
@ -43,11 +43,11 @@ impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(GridNotification::Unknown),
|
||||
11 => ::std::option::Option::Some(GridNotification::DidCreateBlock),
|
||||
20 => ::std::option::Option::Some(GridNotification::DidUpdateGridBlock),
|
||||
20 => ::std::option::Option::Some(GridNotification::DidUpdateGridRow),
|
||||
21 => ::std::option::Option::Some(GridNotification::DidUpdateGridField),
|
||||
30 => ::std::option::Option::Some(GridNotification::DidUpdateRow),
|
||||
31 => ::std::option::Option::Some(GridNotification::DidUpdateCell),
|
||||
40 => ::std::option::Option::Some(GridNotification::DidUpdateGrid),
|
||||
41 => ::std::option::Option::Some(GridNotification::DidUpdateField),
|
||||
40 => ::std::option::Option::Some(GridNotification::DidUpdateCell),
|
||||
50 => ::std::option::Option::Some(GridNotification::DidUpdateField),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
@ -56,10 +56,10 @@ impl ::protobuf::ProtobufEnum for GridNotification {
|
||||
static values: &'static [GridNotification] = &[
|
||||
GridNotification::Unknown,
|
||||
GridNotification::DidCreateBlock,
|
||||
GridNotification::DidUpdateGridBlock,
|
||||
GridNotification::DidUpdateGridRow,
|
||||
GridNotification::DidUpdateGridField,
|
||||
GridNotification::DidUpdateRow,
|
||||
GridNotification::DidUpdateCell,
|
||||
GridNotification::DidUpdateGrid,
|
||||
GridNotification::DidUpdateField,
|
||||
];
|
||||
values
|
||||
@ -89,11 +89,11 @@ impl ::protobuf::reflect::ProtobufValue for GridNotification {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x17dart_notification.proto*\x97\x01\n\x10GridNotification\x12\x0b\n\
|
||||
\x07Unknown\x10\0\x12\x12\n\x0eDidCreateBlock\x10\x0b\x12\x16\n\x12DidUp\
|
||||
dateGridBlock\x10\x14\x12\x10\n\x0cDidUpdateRow\x10\x1e\x12\x11\n\rDidUp\
|
||||
dateCell\x10\x1f\x12\x11\n\rDidUpdateGrid\x10(\x12\x12\n\x0eDidUpdateFie\
|
||||
ld\x10)b\x06proto3\
|
||||
\n\x17dart_notification.proto*\x9a\x01\n\x10GridNotification\x12\x0b\n\
|
||||
\x07Unknown\x10\0\x12\x12\n\x0eDidCreateBlock\x10\x0b\x12\x14\n\x10DidUp\
|
||||
dateGridRow\x10\x14\x12\x16\n\x12DidUpdateGridField\x10\x15\x12\x10\n\
|
||||
\x0cDidUpdateRow\x10\x1e\x12\x11\n\rDidUpdateCell\x10(\x12\x12\n\x0eDidU\
|
||||
pdateField\x102b\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -34,6 +34,7 @@ pub enum GridEvent {
|
||||
SwitchToField = 14,
|
||||
DuplicateField = 15,
|
||||
GetEditFieldContext = 16,
|
||||
MoveItem = 17,
|
||||
NewSelectOption = 30,
|
||||
GetSelectOptionContext = 31,
|
||||
UpdateSelectOption = 32,
|
||||
@ -62,6 +63,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
14 => ::std::option::Option::Some(GridEvent::SwitchToField),
|
||||
15 => ::std::option::Option::Some(GridEvent::DuplicateField),
|
||||
16 => ::std::option::Option::Some(GridEvent::GetEditFieldContext),
|
||||
17 => ::std::option::Option::Some(GridEvent::MoveItem),
|
||||
30 => ::std::option::Option::Some(GridEvent::NewSelectOption),
|
||||
31 => ::std::option::Option::Some(GridEvent::GetSelectOptionContext),
|
||||
32 => ::std::option::Option::Some(GridEvent::UpdateSelectOption),
|
||||
@ -87,6 +89,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
|
||||
GridEvent::SwitchToField,
|
||||
GridEvent::DuplicateField,
|
||||
GridEvent::GetEditFieldContext,
|
||||
GridEvent::MoveItem,
|
||||
GridEvent::NewSelectOption,
|
||||
GridEvent::GetSelectOptionContext,
|
||||
GridEvent::UpdateSelectOption,
|
||||
@ -125,16 +128,16 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0fevent_map.proto*\xef\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
|
||||
\n\x0fevent_map.proto*\xfd\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
|
||||
\0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
|
||||
\x0bUpdateField\x10\x0b\x12\x0f\n\x0bInsertField\x10\x0c\x12\x0f\n\x0bDe\
|
||||
leteField\x10\r\x12\x11\n\rSwitchToField\x10\x0e\x12\x12\n\x0eDuplicateF\
|
||||
ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x13\n\x0fNewSe\
|
||||
lectOption\x10\x1e\x12\x1a\n\x16GetSelectOptionContext\x10\x1f\x12\x16\n\
|
||||
\x12UpdateSelectOption\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\
|
||||
\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\
|
||||
\x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateCellSelec\
|
||||
tOption\x10Hb\x06proto3\
|
||||
ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x0c\n\x08MoveI\
|
||||
tem\x10\x11\x12\x13\n\x0fNewSelectOption\x10\x1e\x12\x1a\n\x16GetSelectO\
|
||||
ptionContext\x10\x1f\x12\x16\n\x12UpdateSelectOption\x10\x20\x12\r\n\tCr\
|
||||
eateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\
|
||||
\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\
|
||||
\x10G\x12\x1a\n\x16UpdateCellSelectOption\x10Hb\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -3,9 +3,9 @@ syntax = "proto3";
|
||||
enum GridNotification {
|
||||
Unknown = 0;
|
||||
DidCreateBlock = 11;
|
||||
DidUpdateGridBlock = 20;
|
||||
DidUpdateGridRow = 20;
|
||||
DidUpdateGridField = 21;
|
||||
DidUpdateRow = 30;
|
||||
DidUpdateCell = 31;
|
||||
DidUpdateGrid = 40;
|
||||
DidUpdateField = 41;
|
||||
DidUpdateCell = 40;
|
||||
DidUpdateField = 50;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ enum GridEvent {
|
||||
SwitchToField = 14;
|
||||
DuplicateField = 15;
|
||||
GetEditFieldContext = 16;
|
||||
MoveItem = 17;
|
||||
NewSelectOption = 30;
|
||||
GetSelectOptionContext = 31;
|
||||
UpdateSelectOption = 32;
|
||||
|
@ -51,16 +51,16 @@ impl ClientGridBlockMetaEditor {
|
||||
let mut row_count = 0;
|
||||
let mut row_index = None;
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
.modify(|block_pad| {
|
||||
if let Some(start_row_id) = start_row_id.as_ref() {
|
||||
match pad.index_of_row(start_row_id) {
|
||||
match block_pad.index_of_row(start_row_id) {
|
||||
None => {}
|
||||
Some(index) => row_index = Some(index + 1),
|
||||
}
|
||||
}
|
||||
|
||||
let change = pad.add_row_meta(row, start_row_id)?;
|
||||
row_count = pad.number_of_rows();
|
||||
let change = block_pad.add_row_meta(row, start_row_id)?;
|
||||
row_count = block_pad.number_of_rows();
|
||||
Ok(change)
|
||||
})
|
||||
.await?;
|
||||
@ -71,9 +71,9 @@ impl ClientGridBlockMetaEditor {
|
||||
pub async fn delete_rows(&self, ids: Vec<Cow<'_, String>>) -> FlowyResult<i32> {
|
||||
let mut row_count = 0;
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
let changeset = pad.delete_rows(ids)?;
|
||||
row_count = pad.number_of_rows();
|
||||
.modify(|block_pad| {
|
||||
let changeset = block_pad.delete_rows(ids)?;
|
||||
row_count = block_pad.number_of_rows();
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
@ -81,7 +81,14 @@ impl ClientGridBlockMetaEditor {
|
||||
}
|
||||
|
||||
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
|
||||
let _ = self.modify(|pad| Ok(pad.update_row(changeset)?)).await?;
|
||||
let _ = self.modify(|block_pad| Ok(block_pad.update_row(changeset)?)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, row_id: &str, from: usize, to: usize) -> FlowyResult<()> {
|
||||
let _ = self
|
||||
.modify(|block_pad| Ok(block_pad.move_row(row_id, from, to)?))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ use crate::dart_notification::{send_dart_notification, GridNotification};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_meta_editor::ClientGridBlockMetaEditor;
|
||||
use crate::services::persistence::block_index::BlockIndexPersistence;
|
||||
use crate::services::row::{group_row_orders, make_rows_from_row_metas, GridBlockSnapshot};
|
||||
use crate::services::row::{group_row_orders, GridBlockSnapshot};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::entities::{
|
||||
CellChangeset, CellMeta, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
|
||||
GridBlockOrderChangeset, IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
|
||||
CellChangeset, CellMeta, CellNotificationData, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset,
|
||||
IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
|
||||
};
|
||||
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
|
||||
use flowy_revision::{RevisionManager, RevisionPersistence};
|
||||
@ -76,7 +76,7 @@ impl GridBlockMetaEditorManager {
|
||||
index_row_order.index = row_index;
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(block_id, vec![index_row_order]))
|
||||
.notify_did_update_block(GridRowsChangeset::insert(block_id, vec![index_row_order]))
|
||||
.await?;
|
||||
Ok(row_count)
|
||||
}
|
||||
@ -98,7 +98,7 @@ impl GridBlockMetaEditorManager {
|
||||
changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(&block_id, inserted_row_orders))
|
||||
.notify_did_update_block(GridRowsChangeset::insert(&block_id, inserted_row_orders))
|
||||
.await?;
|
||||
}
|
||||
|
||||
@ -108,19 +108,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?;
|
||||
|
||||
match editor
|
||||
.get_row_orders(Some(vec![Cow::Borrowed(&changeset.row_id)]))
|
||||
.await?
|
||||
.pop()
|
||||
{
|
||||
None => {}
|
||||
Some(row_order) => {
|
||||
let block_order_changeset = GridBlockOrderChangeset::from_update(&editor.block_id, vec![row_order]);
|
||||
let _ = self.notify_did_update_grid_rows(block_order_changeset).await?;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self.notify_did_update_block_row(&changeset.row_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -131,7 +119,7 @@ impl GridBlockMetaEditorManager {
|
||||
let row_orders = editor.get_row_orders(Some(vec![Cow::Borrowed(&row_id)])).await?;
|
||||
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
|
||||
let _ = self
|
||||
.notify_did_update_grid_rows(GridBlockOrderChangeset::from_delete(&block_id, row_orders))
|
||||
.notify_did_update_block(GridRowsChangeset::delete(&block_id, row_orders))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@ -154,11 +142,35 @@ impl GridBlockMetaEditorManager {
|
||||
Ok(changesets)
|
||||
}
|
||||
|
||||
pub(crate) async fn move_row(&self, row_id: &str, from: usize, to: usize) -> FlowyResult<()> {
|
||||
let editor = self.get_editor_from_row_id(row_id).await?;
|
||||
let _ = editor.move_row(row_id, from, to).await?;
|
||||
|
||||
match editor.get_row_metas(Some(vec![Cow::Borrowed(row_id)])).await?.pop() {
|
||||
None => {}
|
||||
Some(row_meta) => {
|
||||
let row_order = RowOrder::from(&row_meta);
|
||||
let insert_row = IndexRowOrder {
|
||||
row_order: row_order.clone(),
|
||||
index: Some(to as i32),
|
||||
};
|
||||
let notified_changeset = GridRowsChangeset {
|
||||
block_id: editor.block_id.clone(),
|
||||
inserted_rows: vec![insert_row],
|
||||
deleted_rows: vec![row_order],
|
||||
updated_rows: vec![],
|
||||
};
|
||||
|
||||
let _ = self.notify_did_update_block(notified_changeset).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_cell(&self, changeset: CellChangeset) -> FlowyResult<()> {
|
||||
let row_id = changeset.row_id.clone();
|
||||
let editor = self.get_editor_from_row_id(&row_id).await?;
|
||||
let row_changeset: RowMetaChangeset = changeset.clone().into();
|
||||
let _ = editor.update_row(row_changeset).await?;
|
||||
let _ = self.update_row(row_changeset).await?;
|
||||
|
||||
let cell_notification_data = CellNotificationData {
|
||||
grid_id: changeset.grid_id,
|
||||
@ -167,6 +179,7 @@ impl GridBlockMetaEditorManager {
|
||||
content: changeset.data,
|
||||
};
|
||||
self.notify_did_update_cell(cell_notification_data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -213,8 +226,22 @@ impl GridBlockMetaEditorManager {
|
||||
Ok(block_cell_metas)
|
||||
}
|
||||
|
||||
async fn notify_did_update_grid_rows(&self, changeset: GridBlockOrderChangeset) -> FlowyResult<()> {
|
||||
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridBlock)
|
||||
async fn notify_did_update_block_row(&self, row_id: &str) -> FlowyResult<()> {
|
||||
let editor = self.get_editor_from_row_id(row_id).await?;
|
||||
let row_ids = Some(vec![Cow::Borrowed(&row_id)]);
|
||||
match editor.get_row_orders(row_ids).await?.pop() {
|
||||
None => {}
|
||||
Some(row_order) => {
|
||||
let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]);
|
||||
let _ = self.notify_did_update_block(block_order_changeset).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify_did_update_block(&self, changeset: GridRowsChangeset) -> FlowyResult<()> {
|
||||
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridRow)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
Ok(())
|
||||
@ -227,22 +254,6 @@ impl GridBlockMetaEditorManager {
|
||||
.send();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn notify_did_update_row(&self, row_id: &str, field_metas: &[FieldMeta]) -> FlowyResult<()> {
|
||||
match self.get_row_meta(row_id).await? {
|
||||
None => {}
|
||||
Some(row_meta) => {
|
||||
let row_metas = vec![row_meta];
|
||||
if let Some(row) = make_rows_from_row_metas(field_metas, &row_metas).pop() {
|
||||
send_dart_notification(row_id, GridNotification::DidUpdateRow)
|
||||
.payload(row)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn make_block_meta_editor_map(
|
||||
|
@ -47,7 +47,7 @@ impl DateTypeOption {
|
||||
if self.include_time {
|
||||
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
||||
} else {
|
||||
format!("{}", self.date_format.format_str())
|
||||
self.date_format.format_str().to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ impl ClientGridEditor {
|
||||
grid_id,
|
||||
} = params;
|
||||
let field_id = field.id.clone();
|
||||
let _ = self
|
||||
.modify(|grid| {
|
||||
if grid.contain_field(&field.id) {
|
||||
if self.contain_field(&field_id).await {
|
||||
let _ = self
|
||||
.modify(|grid| {
|
||||
let deserializer = TypeOptionJsonDeserializer(field.field_type.clone());
|
||||
let changeset = FieldChangesetParams {
|
||||
field_id: field.id,
|
||||
@ -74,17 +74,22 @@ impl ClientGridEditor {
|
||||
width: Some(field.width),
|
||||
type_option_data: Some(type_option_data),
|
||||
};
|
||||
Ok(grid.update_field(changeset, deserializer)?)
|
||||
} else {
|
||||
// let type_option_json = type_option_json_str_from_bytes(type_option_data, &field.field_type);
|
||||
Ok(grid.update_field_meta(changeset, deserializer)?)
|
||||
})
|
||||
.await?;
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
} else {
|
||||
let _ = self
|
||||
.modify(|grid| {
|
||||
let builder = type_option_builder_from_bytes(type_option_data, &field.field_type);
|
||||
let field_meta = FieldBuilder::from_field(field, builder).build();
|
||||
Ok(grid.create_field(field_meta, start_field_id)?)
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
let _ = self.notify_did_update_grid().await?;
|
||||
let _ = self.notify_did_update_field(&field_id).await?;
|
||||
|
||||
Ok(grid.create_field_meta(field_meta, start_field_id)?)
|
||||
})
|
||||
.await?;
|
||||
let _ = self.notify_did_insert_grid_field(&field_id).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -100,29 +105,31 @@ impl ClientGridEditor {
|
||||
|
||||
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
|
||||
let field_id = params.field_id.clone();
|
||||
let json_deserializer = match self.pad.read().await.get_field(params.field_id.as_str()) {
|
||||
let json_deserializer = match self.pad.read().await.get_field_meta(params.field_id.as_str()) {
|
||||
None => return Err(ErrorCode::FieldDoesNotExist.into()),
|
||||
Some(field_meta) => TypeOptionJsonDeserializer(field_meta.field_type.clone()),
|
||||
Some((_, field_meta)) => TypeOptionJsonDeserializer(field_meta.field_type.clone()),
|
||||
};
|
||||
|
||||
let _ = self
|
||||
.modify(|grid| Ok(grid.update_field(params, json_deserializer)?))
|
||||
.modify(|grid| Ok(grid.update_field_meta(params, json_deserializer)?))
|
||||
.await?;
|
||||
let _ = self.notify_did_update_grid().await?;
|
||||
let _ = self.notify_did_update_field(&field_id).await?;
|
||||
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn replace_field(&self, field_meta: FieldMeta) -> FlowyResult<()> {
|
||||
let field_id = field_meta.id.clone();
|
||||
let _ = self.modify(|pad| Ok(pad.replace_field(field_meta)?)).await?;
|
||||
let _ = self.notify_did_update_field(&field_id).await?;
|
||||
let _ = self.modify(|pad| Ok(pad.replace_field_meta(field_meta)?)).await?;
|
||||
let _ = self.notify_did_update_grid_field(&field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?;
|
||||
let _ = self.notify_did_update_grid().await?;
|
||||
let _ = self.modify(|grid| Ok(grid.delete_field_meta(field_id)?)).await?;
|
||||
let field_order = FieldOrder::from(field_id);
|
||||
let notified_changeset = GridFieldChangeset::delete(&self.grid_id, vec![field_order]);
|
||||
let _ = self.notify_did_update_grid(notified_changeset).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -145,19 +152,24 @@ impl ClientGridEditor {
|
||||
let _ = self
|
||||
.modify(|grid| Ok(grid.switch_to_field(field_id, field_type.clone(), type_option_json_builder)?))
|
||||
.await?;
|
||||
let _ = self.notify_did_update_grid().await?;
|
||||
let _ = self.notify_did_update_field(field_id).await?;
|
||||
|
||||
let _ = self.notify_did_update_grid_field(field_id).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn duplicate_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.modify(|grid| Ok(grid.duplicate_field(field_id)?)).await?;
|
||||
let _ = self.notify_did_update_grid().await?;
|
||||
let duplicated_field_id = gen_field_id();
|
||||
let _ = self
|
||||
.modify(|grid| Ok(grid.duplicate_field_meta(field_id, &duplicated_field_id)?))
|
||||
.await?;
|
||||
|
||||
let _ = self.notify_did_insert_grid_field(&duplicated_field_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_field_meta(&self, field_id: &str) -> Option<FieldMeta> {
|
||||
let field_meta = self.pad.read().await.get_field(field_id)?.clone();
|
||||
let field_meta = self.pad.read().await.get_field_meta(field_id)?.1.clone();
|
||||
Some(field_meta)
|
||||
}
|
||||
|
||||
@ -302,12 +314,12 @@ impl ClientGridEditor {
|
||||
let cell_data_changeset = changeset.data.unwrap();
|
||||
let cell_meta = self.get_cell_meta(&changeset.row_id, &changeset.field_id).await?;
|
||||
tracing::trace!("{}: {:?}", &changeset.field_id, cell_meta);
|
||||
match self.pad.read().await.get_field(&changeset.field_id) {
|
||||
match self.pad.read().await.get_field_meta(&changeset.field_id) {
|
||||
None => {
|
||||
let msg = format!("Field not found with id: {}", &changeset.field_id);
|
||||
Err(FlowyError::internal().context(msg))
|
||||
}
|
||||
Some(field_meta) => {
|
||||
Some((_, field_meta)) => {
|
||||
// Update the changeset.data property with the return value.
|
||||
changeset.data = Some(apply_cell_data_changeset(cell_data_changeset, cell_meta, field_meta)?);
|
||||
let _ = self.block_meta_manager.update_cell(changeset).await?;
|
||||
@ -326,11 +338,6 @@ impl ClientGridEditor {
|
||||
Ok(grid_blocks)
|
||||
}
|
||||
|
||||
// pub async fn get_field_metas<T>(&self, field_ids: Option<Vec<T>>) -> FlowyResult<Vec<FieldMeta>>
|
||||
// where
|
||||
// T: Into<FieldOrder>,
|
||||
// {
|
||||
|
||||
pub async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<()> {
|
||||
let changesets = self.block_meta_manager.delete_rows(row_orders).await?;
|
||||
for changeset in changesets {
|
||||
@ -375,6 +382,43 @@ impl ClientGridEditor {
|
||||
Ok(snapshots)
|
||||
}
|
||||
|
||||
pub async fn move_item(&self, params: MoveItemParams) -> FlowyResult<()> {
|
||||
match params.ty {
|
||||
MoveItemType::MoveField => {
|
||||
self.move_field(¶ms.item_id, params.from_index, params.to_index)
|
||||
.await
|
||||
}
|
||||
MoveItemType::MoveRow => self.move_row(¶ms.item_id, params.from_index, params.to_index).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn move_field(&self, field_id: &str, from: i32, to: i32) -> FlowyResult<()> {
|
||||
let _ = self
|
||||
.modify(|grid_pad| Ok(grid_pad.move_field(field_id, from as usize, to as usize)?))
|
||||
.await?;
|
||||
if let Some((index, field_meta)) = self.pad.read().await.get_field_meta(field_id) {
|
||||
let delete_field_order = FieldOrder::from(field_id);
|
||||
let insert_field = IndexField::from_field_meta(field_meta, index);
|
||||
let notified_changeset = GridFieldChangeset {
|
||||
grid_id: self.grid_id.clone(),
|
||||
inserted_fields: vec![insert_field],
|
||||
deleted_fields: vec![delete_field_order],
|
||||
updated_fields: vec![],
|
||||
};
|
||||
|
||||
let _ = self.notify_did_update_grid(notified_changeset).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
|
||||
let _ = self
|
||||
.block_meta_manager
|
||||
.move_row(row_id, from as usize, to as usize)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delta_bytes(&self) -> Bytes {
|
||||
self.pad.read().await.delta_bytes()
|
||||
}
|
||||
@ -417,28 +461,40 @@ impl ClientGridEditor {
|
||||
}
|
||||
}
|
||||
|
||||
async fn notify_did_update_grid(&self) -> FlowyResult<()> {
|
||||
let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
|
||||
let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
|
||||
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGrid)
|
||||
.payload(repeated_field)
|
||||
.send();
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
async fn notify_did_insert_grid_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
if let Some((index, field_meta)) = self.pad.read().await.get_field_meta(field_id) {
|
||||
let index_field = IndexField::from_field_meta(field_meta, index);
|
||||
let notified_changeset = GridFieldChangeset::insert(&self.grid_id, vec![index_field]);
|
||||
let _ = self.notify_did_update_grid(notified_changeset).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
async fn notify_did_update_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
async fn notify_did_update_grid_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
let mut field_metas = self.get_field_metas(Some(vec![field_id])).await?;
|
||||
debug_assert!(field_metas.len() == 1);
|
||||
|
||||
if let Some(field_meta) = field_metas.pop() {
|
||||
let updated_field = Field::from(field_meta);
|
||||
let notified_changeset = GridFieldChangeset::update(&self.grid_id, vec![updated_field.clone()]);
|
||||
let _ = self.notify_did_update_grid(notified_changeset).await?;
|
||||
|
||||
send_dart_notification(field_id, GridNotification::DidUpdateField)
|
||||
.payload(Field::from(field_meta))
|
||||
.payload(updated_field)
|
||||
.send();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify_did_update_grid(&self, changeset: GridFieldChangeset) -> FlowyResult<()> {
|
||||
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridField)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "flowy_unit_test")]
|
||||
|
@ -38,6 +38,7 @@ tokio = { version = "1", features = ["full"]}
|
||||
futures-util = "0.3.15"
|
||||
|
||||
[features]
|
||||
http_server = ["flowy-user/http_server", "flowy-folder/http_server", "flowy-text-block/http_server"]
|
||||
http_sync = ["flowy-folder/cloud_sync", "flowy-text-block/cloud_sync"]
|
||||
native_sync = ["flowy-folder/cloud_sync", "flowy-text-block/cloud_sync"]
|
||||
use_bunyan = ["lib-log/use_bunyan"]
|
||||
dart = ["flowy-user/dart", "flowy-net/dart", "flowy-folder/dart", "flowy-sync/dart", "flowy-grid/dart", "flowy-text-block/dart"]
|
||||
|
@ -193,7 +193,7 @@ fn mk_local_server(
|
||||
server_config: &ClientServerConfiguration,
|
||||
) -> (Option<Arc<LocalServer>>, Arc<FlowyWebSocketConnect>) {
|
||||
let ws_addr = server_config.ws_addr();
|
||||
if cfg!(feature = "http_server") {
|
||||
if cfg!(feature = "http_sync") {
|
||||
let ws_conn = Arc::new(FlowyWebSocketConnect::new(ws_addr));
|
||||
(None, ws_conn)
|
||||
} else {
|
||||
|
@ -51,6 +51,7 @@ rand = "0.7.3"
|
||||
lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "proto_gen"] }
|
||||
|
||||
[features]
|
||||
http_server = []
|
||||
sync = []
|
||||
cloud_sync = ["sync"]
|
||||
flowy_unit_test = ["lib-ot/flowy_unit_test", "flowy-revision/flowy_unit_test"]
|
||||
dart = ["lib-infra/dart"]
|
@ -1,4 +1,4 @@
|
||||
use crate::web_socket::{make_block_ws_manager, EditorCommandSender};
|
||||
use crate::web_socket::EditorCommandSender;
|
||||
use crate::{
|
||||
errors::FlowyError,
|
||||
queue::{EditBlockQueue, EditorCommand},
|
||||
@ -6,9 +6,7 @@ use crate::{
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
use flowy_revision::{
|
||||
RevisionCloudService, RevisionManager, RevisionObjectBuilder, RevisionWebSocket, RevisionWebSocketManager,
|
||||
};
|
||||
use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder, RevisionWebSocket};
|
||||
use flowy_sync::entities::ws_data::ServerRevisionWSData;
|
||||
use flowy_sync::{
|
||||
entities::{revision::Revision, text_block_info::TextBlockInfo},
|
||||
@ -27,11 +25,13 @@ pub struct ClientTextBlockEditor {
|
||||
pub doc_id: String,
|
||||
#[allow(dead_code)]
|
||||
rev_manager: Arc<RevisionManager>,
|
||||
ws_manager: Arc<RevisionWebSocketManager>,
|
||||
#[cfg(feature = "sync")]
|
||||
ws_manager: Arc<flowy_revision::RevisionWebSocketManager>,
|
||||
edit_cmd_tx: EditorCommandSender,
|
||||
}
|
||||
|
||||
impl ClientTextBlockEditor {
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) async fn new(
|
||||
doc_id: &str,
|
||||
user: Arc<dyn TextBlockUser>,
|
||||
@ -46,7 +46,8 @@ impl ClientTextBlockEditor {
|
||||
let user_id = user.user_id()?;
|
||||
|
||||
let edit_cmd_tx = spawn_edit_queue(user, rev_manager.clone(), delta);
|
||||
let ws_manager = make_block_ws_manager(
|
||||
#[cfg(feature = "sync")]
|
||||
let ws_manager = crate::web_socket::make_block_ws_manager(
|
||||
doc_id.clone(),
|
||||
user_id.clone(),
|
||||
edit_cmd_tx.clone(),
|
||||
@ -57,6 +58,7 @@ impl ClientTextBlockEditor {
|
||||
let editor = Arc::new(Self {
|
||||
doc_id,
|
||||
rev_manager,
|
||||
#[cfg(feature = "sync")]
|
||||
ws_manager,
|
||||
edit_cmd_tx,
|
||||
});
|
||||
@ -158,17 +160,29 @@ impl ClientTextBlockEditor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub fn stop(&self) {
|
||||
self.ws_manager.stop();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub fn stop(&self) {}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub(crate) async fn receive_ws_data(&self, data: ServerRevisionWSData) -> Result<(), FlowyError> {
|
||||
self.ws_manager.receive_ws_data(data).await
|
||||
}
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub(crate) async fn receive_ws_data(&self, _data: ServerRevisionWSData) -> Result<(), FlowyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub(crate) fn receive_ws_state(&self, state: &WSConnectState) {
|
||||
self.ws_manager.connect_state_changed(state.clone());
|
||||
}
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub(crate) fn receive_ws_state(&self, _state: &WSConnectState) {}
|
||||
}
|
||||
|
||||
impl std::ops::Drop for ClientTextBlockEditor {
|
||||
|
@ -23,6 +23,7 @@ use tokio::sync::{
|
||||
pub(crate) type EditorCommandSender = Sender<EditorCommand>;
|
||||
pub(crate) type EditorCommandReceiver = Receiver<EditorCommand>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn make_block_ws_manager(
|
||||
doc_id: String,
|
||||
user_id: String,
|
||||
@ -49,6 +50,7 @@ pub(crate) async fn make_block_ws_manager(
|
||||
ws_manager
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn listen_document_ws_state(_user_id: &str, _doc_id: &str, mut subscriber: broadcast::Receiver<WSConnectState>) {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(state) = subscriber.recv().await {
|
||||
@ -67,6 +69,7 @@ pub(crate) struct TextBlockRevisionWSDataStream {
|
||||
}
|
||||
|
||||
impl TextBlockRevisionWSDataStream {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(conflict_controller: RichTextConflictController) -> Self {
|
||||
Self {
|
||||
conflict_controller: Arc::new(conflict_controller),
|
||||
|
@ -37,7 +37,6 @@ futures = "0.3.15"
|
||||
nanoid = "0.4.0"
|
||||
|
||||
[features]
|
||||
http_server = []
|
||||
dart = ["lib-infra/dart"]
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -84,6 +84,68 @@ impl std::convert::From<String> for FieldOrder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct GridFieldChangeset {
|
||||
#[pb(index = 1)]
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub inserted_fields: Vec<IndexField>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub deleted_fields: Vec<FieldOrder>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub updated_fields: Vec<Field>,
|
||||
}
|
||||
|
||||
impl GridFieldChangeset {
|
||||
pub fn insert(grid_id: &str, inserted_fields: Vec<IndexField>) -> Self {
|
||||
Self {
|
||||
grid_id: grid_id.to_owned(),
|
||||
inserted_fields,
|
||||
deleted_fields: vec![],
|
||||
updated_fields: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(grid_id: &str, deleted_fields: Vec<FieldOrder>) -> Self {
|
||||
Self {
|
||||
grid_id: grid_id.to_string(),
|
||||
inserted_fields: vec![],
|
||||
deleted_fields,
|
||||
updated_fields: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(grid_id: &str, updated_fields: Vec<Field>) -> Self {
|
||||
Self {
|
||||
grid_id: grid_id.to_string(),
|
||||
inserted_fields: vec![],
|
||||
deleted_fields: vec![],
|
||||
updated_fields,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct IndexField {
|
||||
#[pb(index = 1)]
|
||||
pub field: Field,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub index: i32,
|
||||
}
|
||||
|
||||
impl IndexField {
|
||||
pub fn from_field_meta(field_meta: &FieldMeta, index: usize) -> Self {
|
||||
Self {
|
||||
field: Field::from(field_meta.clone()),
|
||||
index: index as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct GetEditFieldContextPayload {
|
||||
#[pb(index = 1)]
|
||||
@ -278,7 +340,16 @@ impl GridBlockOrder {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct GridBlockOrderChangeset {
|
||||
pub struct IndexRowOrder {
|
||||
#[pb(index = 1)]
|
||||
pub row_order: RowOrder,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub index: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct GridRowsChangeset {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
|
||||
@ -292,15 +363,6 @@ pub struct GridBlockOrderChangeset {
|
||||
pub updated_rows: Vec<RowOrder>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct IndexRowOrder {
|
||||
#[pb(index = 1)]
|
||||
pub row_order: RowOrder,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub index: Option<i32>,
|
||||
}
|
||||
|
||||
impl std::convert::From<RowOrder> for IndexRowOrder {
|
||||
fn from(row_order: RowOrder) -> Self {
|
||||
Self { row_order, index: None }
|
||||
@ -314,8 +376,8 @@ impl std::convert::From<&RowMeta> for IndexRowOrder {
|
||||
}
|
||||
}
|
||||
|
||||
impl GridBlockOrderChangeset {
|
||||
pub fn from_insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
|
||||
impl GridRowsChangeset {
|
||||
pub fn insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
inserted_rows,
|
||||
@ -324,7 +386,7 @@ impl GridBlockOrderChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_delete(block_id: &str, deleted_rows: Vec<RowOrder>) -> Self {
|
||||
pub fn delete(block_id: &str, deleted_rows: Vec<RowOrder>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
inserted_rows: vec![],
|
||||
@ -333,7 +395,7 @@ impl GridBlockOrderChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_update(block_id: &str, updated_rows: Vec<RowOrder>) -> Self {
|
||||
pub fn update(block_id: &str, updated_rows: Vec<RowOrder>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
inserted_rows: vec![],
|
||||
@ -656,6 +718,61 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, ProtoBuf_Enum)]
|
||||
pub enum MoveItemType {
|
||||
MoveField = 0,
|
||||
MoveRow = 1,
|
||||
}
|
||||
|
||||
impl std::default::Default for MoveItemType {
|
||||
fn default() -> Self {
|
||||
MoveItemType::MoveField
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct MoveItemPayload {
|
||||
#[pb(index = 1)]
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub item_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub from_index: i32,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub to_index: i32,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub ty: MoveItemType,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MoveItemParams {
|
||||
pub grid_id: String,
|
||||
pub item_id: String,
|
||||
pub from_index: i32,
|
||||
pub to_index: i32,
|
||||
pub ty: MoveItemType,
|
||||
}
|
||||
|
||||
impl TryInto<MoveItemParams> for MoveItemPayload {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<MoveItemParams, Self::Error> {
|
||||
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
||||
let item_id = NotEmptyStr::parse(self.item_id).map_err(|_| ErrorCode::InvalidData)?;
|
||||
Ok(MoveItemParams {
|
||||
grid_id: grid_id.0,
|
||||
item_id: item_id.0,
|
||||
from_index: self.from_index,
|
||||
to_index: self.to_index,
|
||||
ty: self.ty,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,16 @@ message Field {
|
||||
message FieldOrder {
|
||||
string field_id = 1;
|
||||
}
|
||||
message GridFieldChangeset {
|
||||
string grid_id = 1;
|
||||
repeated IndexField inserted_fields = 2;
|
||||
repeated FieldOrder deleted_fields = 3;
|
||||
repeated Field updated_fields = 4;
|
||||
}
|
||||
message IndexField {
|
||||
Field field = 1;
|
||||
int32 index = 2;
|
||||
}
|
||||
message GetEditFieldContextPayload {
|
||||
string grid_id = 1;
|
||||
oneof one_of_field_id { string field_id = 2; };
|
||||
@ -58,16 +68,16 @@ message GridBlockOrder {
|
||||
string block_id = 1;
|
||||
repeated RowOrder row_orders = 2;
|
||||
}
|
||||
message GridBlockOrderChangeset {
|
||||
message IndexRowOrder {
|
||||
RowOrder row_order = 1;
|
||||
oneof one_of_index { int32 index = 2; };
|
||||
}
|
||||
message GridRowsChangeset {
|
||||
string block_id = 1;
|
||||
repeated IndexRowOrder inserted_rows = 2;
|
||||
repeated RowOrder deleted_rows = 3;
|
||||
repeated RowOrder updated_rows = 4;
|
||||
}
|
||||
message IndexRowOrder {
|
||||
RowOrder row_order = 1;
|
||||
oneof one_of_index { int32 index = 2; };
|
||||
}
|
||||
message GridBlock {
|
||||
string id = 1;
|
||||
repeated RowOrder row_orders = 2;
|
||||
@ -123,12 +133,23 @@ message FieldChangesetPayload {
|
||||
oneof one_of_width { int32 width = 8; };
|
||||
oneof one_of_type_option_data { bytes type_option_data = 9; };
|
||||
}
|
||||
message MoveItemPayload {
|
||||
string grid_id = 1;
|
||||
string item_id = 2;
|
||||
int32 from_index = 3;
|
||||
int32 to_index = 4;
|
||||
MoveItemType ty = 5;
|
||||
}
|
||||
message CellChangeset {
|
||||
string grid_id = 1;
|
||||
string row_id = 2;
|
||||
string field_id = 3;
|
||||
oneof one_of_data { string data = 4; };
|
||||
}
|
||||
enum MoveItemType {
|
||||
MoveField = 0;
|
||||
MoveRow = 1;
|
||||
}
|
||||
enum FieldType {
|
||||
RichText = 0;
|
||||
Number = 1;
|
||||
|
@ -149,6 +149,19 @@ impl GridBlockMetaPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn move_row(&mut self, row_id: &str, from: usize, to: usize) -> CollaborateResult<Option<GridBlockMetaChange>> {
|
||||
self.modify(|row_metas| {
|
||||
if let Some(position) = row_metas.iter().position(|row_meta| row_meta.id == row_id) {
|
||||
debug_assert_eq!(from, position);
|
||||
let row_meta = row_metas.remove(position);
|
||||
row_metas.insert(to, row_meta);
|
||||
Ok(Some(()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn modify<F>(&mut self, f: F) -> CollaborateResult<Option<GridBlockMetaChange>>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut Vec<Arc<RowMeta>>) -> CollaborateResult<Option<()>>,
|
||||
|
@ -3,12 +3,11 @@ use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||
use crate::util::{cal_diff, make_delta_from_revisions};
|
||||
use bytes::Bytes;
|
||||
use flowy_grid_data_model::entities::{
|
||||
gen_field_id, gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta,
|
||||
GridBlockMetaChangeset, GridMeta,
|
||||
gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, GridBlockMetaChangeset,
|
||||
GridMeta,
|
||||
};
|
||||
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type GridMetaDelta = PlainTextDelta;
|
||||
@ -41,7 +40,7 @@ impl GridMetaPad {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub fn create_field(
|
||||
pub fn create_field_meta(
|
||||
&mut self,
|
||||
new_field_meta: FieldMeta,
|
||||
start_field_id: Option<String>,
|
||||
@ -70,7 +69,7 @@ impl GridMetaPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
|
||||
pub fn delete_field_meta(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
|
||||
self.modify_grid(
|
||||
|grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
|
||||
None => Ok(None),
|
||||
@ -82,13 +81,17 @@ impl GridMetaPad {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn duplicate_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
|
||||
pub fn duplicate_field_meta(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
duplicated_field_id: &str,
|
||||
) -> CollaborateResult<Option<GridChangeset>> {
|
||||
self.modify_grid(
|
||||
|grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
|
||||
None => Ok(None),
|
||||
Some(index) => {
|
||||
let mut duplicate_field_meta = grid_meta.fields[index].clone();
|
||||
duplicate_field_meta.id = gen_field_id();
|
||||
duplicate_field_meta.id = duplicated_field_id.to_string();
|
||||
duplicate_field_meta.name = format!("{} (copy)", duplicate_field_meta.name);
|
||||
grid_meta.fields.insert(index + 1, duplicate_field_meta);
|
||||
Ok(Some(()))
|
||||
@ -126,7 +129,7 @@ impl GridMetaPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_field<T: JsonDeserializer>(
|
||||
pub fn update_field_meta<T: JsonDeserializer>(
|
||||
&mut self,
|
||||
changeset: FieldChangesetParams,
|
||||
deserializer: T,
|
||||
@ -181,11 +184,15 @@ impl GridMetaPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_field(&self, field_id: &str) -> Option<&FieldMeta> {
|
||||
self.grid_meta.fields.iter().find(|field| field.id == field_id)
|
||||
pub fn get_field_meta(&self, field_id: &str) -> Option<(usize, &FieldMeta)> {
|
||||
self.grid_meta
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, field)| field.id == field_id)
|
||||
}
|
||||
|
||||
pub fn replace_field(&mut self, field_meta: FieldMeta) -> CollaborateResult<Option<GridChangeset>> {
|
||||
pub fn replace_field_meta(&mut self, field_meta: FieldMeta) -> CollaborateResult<Option<GridChangeset>> {
|
||||
self.modify_grid(
|
||||
|grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_meta.id) {
|
||||
None => Ok(None),
|
||||
@ -198,6 +205,25 @@ impl GridMetaPad {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn move_field(
|
||||
&mut self,
|
||||
field_id: &str,
|
||||
_from_index: usize,
|
||||
to_index: usize,
|
||||
) -> CollaborateResult<Option<GridChangeset>> {
|
||||
self.modify_grid(
|
||||
|grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
|
||||
None => Ok(None),
|
||||
Some(index) => {
|
||||
// debug_assert_eq!(index, from_index);
|
||||
let field_meta = grid_meta.fields.remove(index);
|
||||
grid_meta.fields.insert(to_index, field_meta);
|
||||
Ok(Some(()))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn contain_field(&self, field_id: &str) -> bool {
|
||||
self.grid_meta.fields.iter().any(|field| field.id == field_id)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user