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