mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-11-09 23:46:19 +03:00
Merge pull request #1570 from AppFlowy-IO/feat/grid_sort
Feat/grid sort
This commit is contained in:
commit
9666269e27
@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
|
||||
@ -229,8 +228,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
}
|
||||
|
||||
GridRowCache? getRowCache(String blockId) {
|
||||
final GridBlockCache? blockCache = _gridDataController.blocks[blockId];
|
||||
return blockCache?.rowCache;
|
||||
return _gridDataController.rowCache;
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/view/grid_view_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
@ -31,11 +31,7 @@ class BoardDataController {
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldController fieldController;
|
||||
final BoardListener _listener;
|
||||
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks =>
|
||||
UnmodifiableMapView(_blocks);
|
||||
late GridViewCache _viewCache;
|
||||
|
||||
OnFieldsChanged? _onFieldsChanged;
|
||||
OnGridChanged? _onGridChanged;
|
||||
@ -43,21 +39,23 @@ class BoardDataController {
|
||||
OnRowsChanged? _onRowsChanged;
|
||||
OnError? _onError;
|
||||
|
||||
List<RowInfo> get rowInfos {
|
||||
final List<RowInfo> rows = [];
|
||||
for (var block in _blocks.values) {
|
||||
rows.addAll(block.rows);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
GridRowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
BoardDataController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
_listener = BoardListener(view.id),
|
||||
// ignore: prefer_collection_literals
|
||||
_blocks = LinkedHashMap(),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldController = GridFieldController(gridId: view.id);
|
||||
fieldController = GridFieldController(gridId: view.id) {
|
||||
//
|
||||
_viewCache = GridViewCache(
|
||||
gridId: view.id,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required OnGridChanged onGridChanged,
|
||||
@ -110,23 +108,21 @@ class BoardDataController {
|
||||
|
||||
Future<Either<Unit, FlowyError>> openGrid() async {
|
||||
final result = await _gridFFIService.openGrid();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
final result = await fieldController.loadFields(
|
||||
fieldIds: grid.fields,
|
||||
);
|
||||
return result.fold(
|
||||
(l) {
|
||||
_loadGroups(grid.blocks);
|
||||
return left(l);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
),
|
||||
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
final result = await fieldController.loadFields(fieldIds: grid.fields);
|
||||
return result.fold(
|
||||
(l) {
|
||||
_loadGroups();
|
||||
_viewCache.rowCache.initializeRows(grid.rows);
|
||||
return left(l);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
}
|
||||
|
||||
@ -138,26 +134,9 @@ class BoardDataController {
|
||||
Future<void> dispose() async {
|
||||
await _gridFFIService.closeGrid();
|
||||
await fieldController.dispose();
|
||||
|
||||
for (final blockCache in _blocks.values) {
|
||||
blockCache.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadGroups(List<BlockPB> blocks) async {
|
||||
for (final block in blocks) {
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
|
||||
cache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
|
||||
Future<void> _loadGroups() async {
|
||||
final result = await _gridFFIService.loadGroups();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
|
@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@ -23,7 +23,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
required bool isEditing,
|
||||
}) : _rowService = RowFFIService(
|
||||
gridId: gridId,
|
||||
blockId: dataController.rowPB.blockId,
|
||||
),
|
||||
_dataController = dataController,
|
||||
super(
|
||||
|
@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
@ -16,8 +16,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../grid/application/row/row_cache.dart';
|
||||
|
@ -1,53 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
|
||||
typedef GridBlockUpdateNotifierValue = Either<GridBlockChangesetPB, FlowyError>;
|
||||
|
||||
class GridBlockListener {
|
||||
final String blockId;
|
||||
PublishNotifier<GridBlockUpdateNotifierValue>? _rowsUpdateNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
GridBlockListener({required this.blockId});
|
||||
|
||||
void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
|
||||
if (_listener != null) {
|
||||
_listener?.stop();
|
||||
}
|
||||
|
||||
_listener = GridNotificationListener(
|
||||
objectId: blockId,
|
||||
handler: _handler,
|
||||
);
|
||||
|
||||
_rowsUpdateNotifier?.addPublishListener(onBlockChanged);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGridBlock:
|
||||
result.fold(
|
||||
(payload) => _rowsUpdateNotifier?.value =
|
||||
left(GridBlockChangesetPB.fromBuffer(payload)),
|
||||
(error) => _rowsUpdateNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_rowsUpdateNotifier?.dispose();
|
||||
_rowsUpdateNotifier = null;
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class GridFieldsListener {
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGridField:
|
||||
case GridDartNotification.DidUpdateGridFields:
|
||||
result.fold(
|
||||
(payload) => updateFieldsNotifier?.value =
|
||||
left(GridFieldChangesetPB.fromBuffer(payload)),
|
||||
|
@ -28,7 +28,7 @@ class CheckboxFilterEditorBloc
|
||||
initial: () async {
|
||||
_startListening();
|
||||
},
|
||||
updateCondition: (CheckboxFilterCondition condition) {
|
||||
updateCondition: (CheckboxFilterConditionPB condition) {
|
||||
_ffiService.insertCheckboxFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
@ -79,7 +79,7 @@ class CheckboxFilterEditorEvent with _$CheckboxFilterEditorEvent {
|
||||
const factory CheckboxFilterEditorEvent.didReceiveFilter(FilterPB filter) =
|
||||
_DidReceiveFilter;
|
||||
const factory CheckboxFilterEditorEvent.updateCondition(
|
||||
CheckboxFilterCondition condition) = _UpdateCondition;
|
||||
CheckboxFilterConditionPB condition) = _UpdateCondition;
|
||||
const factory CheckboxFilterEditorEvent.delete() = _Delete;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ class ChecklistFilterEditorBloc
|
||||
initial: () async {
|
||||
_startListening();
|
||||
},
|
||||
updateCondition: (ChecklistFilterCondition condition) {
|
||||
updateCondition: (ChecklistFilterConditionPB condition) {
|
||||
_ffiService.insertChecklistFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
@ -85,7 +85,7 @@ class ChecklistFilterEditorEvent with _$ChecklistFilterEditorEvent {
|
||||
const factory ChecklistFilterEditorEvent.didReceiveFilter(FilterPB filter) =
|
||||
_DidReceiveFilter;
|
||||
const factory ChecklistFilterEditorEvent.updateCondition(
|
||||
ChecklistFilterCondition condition) = _UpdateCondition;
|
||||
ChecklistFilterConditionPB condition) = _UpdateCondition;
|
||||
const factory ChecklistFilterEditorEvent.delete() = _Delete;
|
||||
}
|
||||
|
||||
|
@ -90,48 +90,48 @@ class GridCreateFilterBloc
|
||||
case FieldType.Checkbox:
|
||||
return _ffiService.insertCheckboxFilter(
|
||||
fieldId: fieldId,
|
||||
condition: CheckboxFilterCondition.IsChecked,
|
||||
condition: CheckboxFilterConditionPB.IsChecked,
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
return _ffiService.insertDateFilter(
|
||||
fieldId: fieldId,
|
||||
condition: DateFilterCondition.DateIs,
|
||||
condition: DateFilterConditionPB.DateIs,
|
||||
timestamp: timestamp,
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
return _ffiService.insertSelectOptionFilter(
|
||||
fieldId: fieldId,
|
||||
condition: SelectOptionCondition.OptionIs,
|
||||
condition: SelectOptionConditionPB.OptionIs,
|
||||
fieldType: FieldType.MultiSelect,
|
||||
);
|
||||
case FieldType.Checklist:
|
||||
return _ffiService.insertChecklistFilter(
|
||||
fieldId: fieldId,
|
||||
condition: ChecklistFilterCondition.IsIncomplete,
|
||||
condition: ChecklistFilterConditionPB.IsIncomplete,
|
||||
);
|
||||
case FieldType.Number:
|
||||
return _ffiService.insertNumberFilter(
|
||||
fieldId: fieldId,
|
||||
condition: NumberFilterCondition.Equal,
|
||||
condition: NumberFilterConditionPB.Equal,
|
||||
content: "",
|
||||
);
|
||||
case FieldType.RichText:
|
||||
return _ffiService.insertTextFilter(
|
||||
fieldId: fieldId,
|
||||
condition: TextFilterCondition.Contains,
|
||||
condition: TextFilterConditionPB.Contains,
|
||||
content: '',
|
||||
);
|
||||
case FieldType.SingleSelect:
|
||||
return _ffiService.insertSelectOptionFilter(
|
||||
fieldId: fieldId,
|
||||
condition: SelectOptionCondition.OptionIs,
|
||||
condition: SelectOptionConditionPB.OptionIs,
|
||||
fieldType: FieldType.SingleSelect,
|
||||
);
|
||||
case FieldType.URL:
|
||||
return _ffiService.insertURLFilter(
|
||||
fieldId: fieldId,
|
||||
condition: TextFilterCondition.Contains,
|
||||
condition: TextFilterConditionPB.Contains,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertTextFilter({
|
||||
required String fieldId,
|
||||
String? filterId,
|
||||
required TextFilterCondition condition,
|
||||
required TextFilterConditionPB condition,
|
||||
required String content,
|
||||
}) {
|
||||
final filter = TextFilterPB()
|
||||
@ -50,7 +50,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertCheckboxFilter({
|
||||
required String fieldId,
|
||||
String? filterId,
|
||||
required CheckboxFilterCondition condition,
|
||||
required CheckboxFilterConditionPB condition,
|
||||
}) {
|
||||
final filter = CheckboxFilterPB()..condition = condition;
|
||||
|
||||
@ -65,7 +65,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertNumberFilter({
|
||||
required String fieldId,
|
||||
String? filterId,
|
||||
required NumberFilterCondition condition,
|
||||
required NumberFilterConditionPB condition,
|
||||
String content = "",
|
||||
}) {
|
||||
final filter = NumberFilterPB()
|
||||
@ -83,7 +83,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertDateFilter({
|
||||
required String fieldId,
|
||||
String? filterId,
|
||||
required DateFilterCondition condition,
|
||||
required DateFilterConditionPB condition,
|
||||
int? start,
|
||||
int? end,
|
||||
int? timestamp,
|
||||
@ -112,7 +112,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertURLFilter({
|
||||
required String fieldId,
|
||||
String? filterId,
|
||||
required TextFilterCondition condition,
|
||||
required TextFilterConditionPB condition,
|
||||
String content = "",
|
||||
}) {
|
||||
final filter = TextFilterPB()
|
||||
@ -130,7 +130,7 @@ class FilterFFIService {
|
||||
Future<Either<Unit, FlowyError>> insertSelectOptionFilter({
|
||||
required String fieldId,
|
||||
required FieldType fieldType,
|
||||
required SelectOptionCondition condition,
|
||||
required SelectOptionConditionPB condition,
|
||||
String? filterId,
|
||||
List<String> optionIds = const [],
|
||||
}) {
|
||||
@ -148,7 +148,7 @@ class FilterFFIService {
|
||||
|
||||
Future<Either<Unit, FlowyError>> insertChecklistFilter({
|
||||
required String fieldId,
|
||||
required ChecklistFilterCondition condition,
|
||||
required ChecklistFilterConditionPB condition,
|
||||
String? filterId,
|
||||
List<String> optionIds = const [],
|
||||
}) {
|
||||
@ -171,6 +171,7 @@ class FilterFFIService {
|
||||
var insertFilterPayload = AlterFilterPayloadPB.create()
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType
|
||||
..viewId = viewId
|
||||
..data = data;
|
||||
|
||||
if (filterId != null) {
|
||||
@ -179,7 +180,7 @@ class FilterFFIService {
|
||||
|
||||
final payload = GridSettingChangesetPB.create()
|
||||
..gridId = viewId
|
||||
..insertFilter = insertFilterPayload;
|
||||
..alterFilter = insertFilterPayload;
|
||||
return GridEventUpdateGridSetting(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(l) => left(l),
|
||||
@ -196,11 +197,12 @@ class FilterFFIService {
|
||||
required String filterId,
|
||||
required FieldType fieldType,
|
||||
}) {
|
||||
TextFilterCondition.DoesNotContain.value;
|
||||
TextFilterConditionPB.DoesNotContain.value;
|
||||
|
||||
final deleteFilterPayload = DeleteFilterPayloadPB.create()
|
||||
..fieldId = fieldId
|
||||
..filterId = filterId
|
||||
..viewId = viewId
|
||||
..fieldType = fieldType;
|
||||
|
||||
final payload = GridSettingChangesetPB.create()
|
||||
|
@ -33,7 +33,7 @@ class SelectOptionFilterEditorBloc
|
||||
_startListening();
|
||||
_loadOptions();
|
||||
},
|
||||
updateCondition: (SelectOptionCondition condition) {
|
||||
updateCondition: (SelectOptionConditionPB condition) {
|
||||
_ffiService.insertSelectOptionFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
@ -114,7 +114,7 @@ class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent {
|
||||
const factory SelectOptionFilterEditorEvent.didReceiveFilter(
|
||||
FilterPB filter) = _DidReceiveFilter;
|
||||
const factory SelectOptionFilterEditorEvent.updateCondition(
|
||||
SelectOptionCondition condition) = _UpdateCondition;
|
||||
SelectOptionConditionPB condition) = _UpdateCondition;
|
||||
const factory SelectOptionFilterEditorEvent.updateContent(
|
||||
List<String> optionIds) = _UpdateContent;
|
||||
const factory SelectOptionFilterEditorEvent.updateFilterDescription(
|
||||
|
@ -28,7 +28,7 @@ class TextFilterEditorBloc
|
||||
initial: () async {
|
||||
_startListening();
|
||||
},
|
||||
updateCondition: (TextFilterCondition condition) {
|
||||
updateCondition: (TextFilterConditionPB condition) {
|
||||
_ffiService.insertTextFilter(
|
||||
filterId: filterInfo.filter.id,
|
||||
fieldId: filterInfo.fieldInfo.id,
|
||||
@ -88,7 +88,7 @@ class TextFilterEditorEvent with _$TextFilterEditorEvent {
|
||||
const factory TextFilterEditorEvent.didReceiveFilter(FilterPB filter) =
|
||||
_DidReceiveFilter;
|
||||
const factory TextFilterEditorEvent.updateCondition(
|
||||
TextFilterCondition condition) = _UpdateCondition;
|
||||
TextFilterConditionPB condition) = _UpdateCondition;
|
||||
const factory TextFilterEditorEvent.updateContent(String content) =
|
||||
_UpdateContent;
|
||||
const factory TextFilterEditorEvent.delete() = _Delete;
|
||||
|
@ -6,7 +6,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'block/block_cache.dart';
|
||||
import 'field/field_controller.dart';
|
||||
import 'grid_data_controller.dart';
|
||||
import 'row/row_cache.dart';
|
||||
@ -38,7 +37,6 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
},
|
||||
deleteRow: (rowInfo) async {
|
||||
final rowService = RowFFIService(
|
||||
blockId: rowInfo.rowPB.blockId,
|
||||
gridId: rowInfo.gridId,
|
||||
);
|
||||
await rowService.deleteRow(rowInfo.rowPB.id);
|
||||
@ -70,8 +68,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
}
|
||||
|
||||
GridRowCache? getRowCache(String blockId, String rowId) {
|
||||
final GridBlockCache? blockCache = gridController.blocks[blockId];
|
||||
return blockCache?.rowCache;
|
||||
return gridController.rowCache;
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
|
@ -1,14 +1,10 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'block/block_cache.dart';
|
||||
import 'view/grid_view_cache.dart';
|
||||
import 'field/field_controller.dart';
|
||||
import 'prelude.dart';
|
||||
import 'row/row_cache.dart';
|
||||
@ -27,29 +23,25 @@ class GridController {
|
||||
final String gridId;
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldController fieldController;
|
||||
late GridViewCache _viewCache;
|
||||
|
||||
OnRowsChanged? _onRowChanged;
|
||||
OnGridChanged? _onGridChanged;
|
||||
|
||||
// Getters
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks =>
|
||||
UnmodifiableMapView(_blocks);
|
||||
|
||||
List<RowInfo> get rowInfos {
|
||||
final List<RowInfo> rows = [];
|
||||
for (var block in _blocks.values) {
|
||||
rows.addAll(block.rows);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
GridRowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
GridController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
// ignore: prefer_collection_literals
|
||||
_blocks = LinkedHashMap(),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldController = GridFieldController(gridId: view.id);
|
||||
fieldController = GridFieldController(gridId: view.id) {
|
||||
_viewCache = GridViewCache(
|
||||
gridId: gridId,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
OnGridChanged? onGridChanged,
|
||||
@ -71,9 +63,8 @@ class GridController {
|
||||
return _gridFFIService.openGrid().then((result) {
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_initialBlocks(grid.blocks);
|
||||
_onGridChanged?.call(grid);
|
||||
|
||||
_viewCache.rowCache.initializeRows(grid.rows);
|
||||
final result = await fieldController.loadFields(
|
||||
fieldIds: grid.fields,
|
||||
);
|
||||
@ -91,30 +82,5 @@ class GridController {
|
||||
Future<void> dispose() async {
|
||||
await _gridFFIService.closeGrid();
|
||||
await fieldController.dispose();
|
||||
|
||||
for (final blockCache in _blocks.values) {
|
||||
blockCache.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
void _initialBlocks(List<BlockPB> blocks) {
|
||||
for (final block in blocks) {
|
||||
if (_blocks[block.id] != null) {
|
||||
Log.warn("Initial duplicate block's cache: ${block.id}");
|
||||
return;
|
||||
}
|
||||
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
|
||||
cache.addListener(onRowsChanged: (reason) {
|
||||
_onRowChanged?.call(rowInfos, reason);
|
||||
});
|
||||
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
|
@ -15,10 +15,7 @@ class RowActionSheetBloc
|
||||
final RowFFIService _rowService;
|
||||
|
||||
RowActionSheetBloc({required RowInfo rowInfo})
|
||||
: _rowService = RowFFIService(
|
||||
gridId: rowInfo.gridId,
|
||||
blockId: rowInfo.rowPB.blockId,
|
||||
),
|
||||
: _rowService = RowFFIService(gridId: rowInfo.gridId),
|
||||
super(RowActionSheetState.initial(rowInfo)) {
|
||||
on<RowActionSheetEvent>(
|
||||
(event, emit) async {
|
||||
|
@ -18,10 +18,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
RowBloc({
|
||||
required RowInfo rowInfo,
|
||||
required GridRowDataController dataController,
|
||||
}) : _rowService = RowFFIService(
|
||||
gridId: rowInfo.gridId,
|
||||
blockId: rowInfo.rowPB.blockId,
|
||||
),
|
||||
}) : _rowService = RowFFIService(gridId: rowInfo.gridId),
|
||||
_dataController = dataController,
|
||||
super(RowState.initial(rowInfo, dataController.loadData())) {
|
||||
on<RowEvent>(
|
||||
|
@ -3,8 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@ -27,7 +26,7 @@ abstract class IGridRowFieldNotifier {
|
||||
|
||||
class GridRowCache {
|
||||
final String gridId;
|
||||
final BlockPB block;
|
||||
final List<RowPB> rows;
|
||||
|
||||
/// _rows containers the current block's rows
|
||||
/// Use List to reverse the order of the GridRow.
|
||||
@ -46,7 +45,7 @@ class GridRowCache {
|
||||
|
||||
GridRowCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required this.rows,
|
||||
required IGridRowFieldNotifier notifier,
|
||||
}) : _cellCache = GridCellCache(gridId: gridId),
|
||||
_rowChangeReasonNotifier = _RowChangesetNotifier(),
|
||||
@ -56,8 +55,10 @@ class GridRowCache {
|
||||
.receive(const RowsChangedReason.fieldDidChange()));
|
||||
notifier.onRowFieldChanged(
|
||||
(field) => _cellCache.removeCellWithFieldId(field.id));
|
||||
}
|
||||
|
||||
for (final row in block.rows) {
|
||||
void initializeRows(List<RowPB> rows) {
|
||||
for (final row in rows) {
|
||||
final rowInfo = buildGridRow(row);
|
||||
_rowList.add(rowInfo);
|
||||
}
|
||||
@ -69,10 +70,13 @@ class GridRowCache {
|
||||
await _cellCache.dispose();
|
||||
}
|
||||
|
||||
void applyChangesets(GridBlockChangesetPB changeset) {
|
||||
void applyRowsChanged(GridViewRowsChangesetPB changeset) {
|
||||
_deleteRows(changeset.deletedRows);
|
||||
_insertRows(changeset.insertedRows);
|
||||
_updateRows(changeset.updatedRows);
|
||||
}
|
||||
|
||||
void applyRowsVisibility(GridRowsVisibilityChangesetPB changeset) {
|
||||
_hideRows(changeset.invisibleRows);
|
||||
_showRows(changeset.visibleRows);
|
||||
}
|
||||
@ -192,7 +196,6 @@ class GridRowCache {
|
||||
Future<void> _loadRow(String rowId) async {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = block.id
|
||||
..rowId = rowId;
|
||||
|
||||
final result = await GridEventGetRow(payload).send();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
import 'row_cache.dart';
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
typedef UpdateRowNotifiedValue = Either<RowPB, FlowyError>;
|
||||
typedef UpdateFieldNotifiedValue = Either<List<FieldPB>, FlowyError>;
|
||||
|
@ -1,18 +1,15 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
class RowFFIService {
|
||||
final String gridId;
|
||||
final String blockId;
|
||||
|
||||
RowFFIService({
|
||||
required this.gridId,
|
||||
required this.blockId,
|
||||
});
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow(String rowId) {
|
||||
@ -26,7 +23,6 @@ class RowFFIService {
|
||||
Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventGetRow(payload).send();
|
||||
@ -35,7 +31,6 @@ class RowFFIService {
|
||||
Future<Either<Unit, FlowyError>> deleteRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventDeleteRow(payload).send();
|
||||
@ -44,7 +39,6 @@ class RowFFIService {
|
||||
Future<Either<Unit, FlowyError>> duplicateRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventDuplicateRow(payload).send();
|
||||
|
@ -1,43 +1,47 @@
|
||||
import 'dart:async';
|
||||
import 'package:app_flowy/plugins/grid/application/view/grid_view_listener.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
|
||||
import '../field/field_controller.dart';
|
||||
import '../row/row_cache.dart';
|
||||
import 'block_listener.dart';
|
||||
|
||||
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
|
||||
class GridBlockCache {
|
||||
class GridViewCache {
|
||||
final String gridId;
|
||||
final BlockPB block;
|
||||
late GridRowCache _rowCache;
|
||||
late GridBlockListener _listener;
|
||||
final GridViewListener _gridViewListener;
|
||||
|
||||
List<RowInfo> get rows => _rowCache.visibleRows;
|
||||
List<RowInfo> get rowInfos => _rowCache.visibleRows;
|
||||
GridRowCache get rowCache => _rowCache;
|
||||
|
||||
GridBlockCache({
|
||||
GridViewCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridFieldController fieldController,
|
||||
}) {
|
||||
}) : _gridViewListener = GridViewListener(viewId: gridId) {
|
||||
_rowCache = GridRowCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
rows: [],
|
||||
notifier: GridRowFieldNotifierImpl(fieldController),
|
||||
);
|
||||
|
||||
_listener = GridBlockListener(blockId: block.id);
|
||||
_listener.start((result) {
|
||||
result.fold(
|
||||
(changeset) => _rowCache.applyChangesets(changeset),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_gridViewListener.start(
|
||||
onRowsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) => _rowCache.applyRowsChanged(changeset),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
onRowsVisibilityChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) => _rowCache.applyRowsVisibility(changeset),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _listener.stop();
|
||||
await _gridViewListener.stop();
|
||||
await _rowCache.dispose();
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/view_entities.pb.dart';
|
||||
|
||||
typedef GridRowsVisibilityNotifierValue
|
||||
= Either<GridRowsVisibilityChangesetPB, FlowyError>;
|
||||
|
||||
typedef GridViewRowsNotifierValue = Either<GridViewRowsChangesetPB, FlowyError>;
|
||||
|
||||
class GridViewListener {
|
||||
final String viewId;
|
||||
PublishNotifier<GridViewRowsNotifierValue>? _rowsNotifier = PublishNotifier();
|
||||
PublishNotifier<GridRowsVisibilityNotifierValue>? _rowsVisibilityNotifier =
|
||||
PublishNotifier();
|
||||
|
||||
GridNotificationListener? _listener;
|
||||
GridViewListener({required this.viewId});
|
||||
|
||||
void start({
|
||||
required void Function(GridViewRowsNotifierValue) onRowsChanged,
|
||||
required void Function(GridRowsVisibilityNotifierValue)
|
||||
onRowsVisibilityChanged,
|
||||
}) {
|
||||
if (_listener != null) {
|
||||
_listener?.stop();
|
||||
}
|
||||
|
||||
_listener = GridNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
|
||||
_rowsNotifier?.addPublishListener(onRowsChanged);
|
||||
_rowsVisibilityNotifier?.addPublishListener(onRowsVisibilityChanged);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGridViewRowsVisibility:
|
||||
result.fold(
|
||||
(payload) => _rowsVisibilityNotifier?.value =
|
||||
left(GridRowsVisibilityChangesetPB.fromBuffer(payload)),
|
||||
(error) => _rowsVisibilityNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
case GridDartNotification.DidUpdateGridViewRows:
|
||||
result.fold(
|
||||
(payload) => _rowsNotifier?.value =
|
||||
left(GridViewRowsChangesetPB.fromBuffer(payload)),
|
||||
(error) => _rowsNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_rowsVisibilityNotifier?.dispose();
|
||||
_rowsVisibilityNotifier = null;
|
||||
|
||||
_rowsNotifier?.dispose();
|
||||
_rowsNotifier = null;
|
||||
}
|
||||
}
|
@ -108,7 +108,7 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
|
||||
children: [
|
||||
FlowyText(state.filterInfo.fieldInfo.name),
|
||||
const HSpace(4),
|
||||
CheckboxFilterConditionList(
|
||||
CheckboxFilterConditionPBList(
|
||||
filterInfo: state.filterInfo,
|
||||
popoverMutex: popoverMutex,
|
||||
onCondition: (condition) {
|
||||
@ -136,11 +136,11 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
|
||||
}
|
||||
}
|
||||
|
||||
class CheckboxFilterConditionList extends StatelessWidget {
|
||||
class CheckboxFilterConditionPBList extends StatelessWidget {
|
||||
final FilterInfo filterInfo;
|
||||
final PopoverMutex popoverMutex;
|
||||
final Function(CheckboxFilterCondition) onCondition;
|
||||
const CheckboxFilterConditionList({
|
||||
final Function(CheckboxFilterConditionPB) onCondition;
|
||||
const CheckboxFilterConditionPBList({
|
||||
required this.filterInfo,
|
||||
required this.popoverMutex,
|
||||
required this.onCondition,
|
||||
@ -154,7 +154,7 @@ class CheckboxFilterConditionList extends StatelessWidget {
|
||||
asBarrier: true,
|
||||
mutex: popoverMutex,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: CheckboxFilterCondition.values
|
||||
actions: CheckboxFilterConditionPB.values
|
||||
.map(
|
||||
(action) => ConditionWrapper(
|
||||
action,
|
||||
@ -177,7 +177,7 @@ class CheckboxFilterConditionList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class ConditionWrapper extends ActionCell {
|
||||
final CheckboxFilterCondition inner;
|
||||
final CheckboxFilterConditionPB inner;
|
||||
final bool isSelected;
|
||||
|
||||
ConditionWrapper(this.inner, this.isSelected);
|
||||
@ -195,12 +195,12 @@ class ConditionWrapper extends ActionCell {
|
||||
String get name => inner.filterName;
|
||||
}
|
||||
|
||||
extension TextFilterConditionExtension on CheckboxFilterCondition {
|
||||
extension TextFilterConditionPBExtension on CheckboxFilterConditionPB {
|
||||
String get filterName {
|
||||
switch (this) {
|
||||
case CheckboxFilterCondition.IsChecked:
|
||||
case CheckboxFilterConditionPB.IsChecked:
|
||||
return LocaleKeys.grid_checkboxFilter_isChecked.tr();
|
||||
case CheckboxFilterCondition.IsUnChecked:
|
||||
case CheckboxFilterConditionPB.IsUnChecked:
|
||||
return LocaleKeys.grid_checkboxFilter_isUnchecked.tr();
|
||||
default:
|
||||
return "";
|
||||
|
@ -92,7 +92,7 @@ class ChecklistState extends State<ChecklistFilterEditor> {
|
||||
children: [
|
||||
FlowyText(state.filterInfo.fieldInfo.name),
|
||||
const HSpace(4),
|
||||
ChecklistFilterConditionList(
|
||||
ChecklistFilterConditionPBList(
|
||||
filterInfo: state.filterInfo,
|
||||
),
|
||||
const Spacer(),
|
||||
@ -117,9 +117,9 @@ class ChecklistState extends State<ChecklistFilterEditor> {
|
||||
}
|
||||
}
|
||||
|
||||
class ChecklistFilterConditionList extends StatelessWidget {
|
||||
class ChecklistFilterConditionPBList extends StatelessWidget {
|
||||
final FilterInfo filterInfo;
|
||||
const ChecklistFilterConditionList({
|
||||
const ChecklistFilterConditionPBList({
|
||||
required this.filterInfo,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -130,7 +130,7 @@ class ChecklistFilterConditionList extends StatelessWidget {
|
||||
return PopoverActionList<ConditionWrapper>(
|
||||
asBarrier: true,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: ChecklistFilterCondition.values
|
||||
actions: ChecklistFilterConditionPB.values
|
||||
.map((action) => ConditionWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
@ -150,7 +150,7 @@ class ChecklistFilterConditionList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class ConditionWrapper extends ActionCell {
|
||||
final ChecklistFilterCondition inner;
|
||||
final ChecklistFilterConditionPB inner;
|
||||
|
||||
ConditionWrapper(this.inner);
|
||||
|
||||
@ -158,12 +158,12 @@ class ConditionWrapper extends ActionCell {
|
||||
String get name => inner.filterName;
|
||||
}
|
||||
|
||||
extension ChecklistFilterConditionExtension on ChecklistFilterCondition {
|
||||
extension ChecklistFilterConditionPBExtension on ChecklistFilterConditionPB {
|
||||
String get filterName {
|
||||
switch (this) {
|
||||
case ChecklistFilterCondition.IsComplete:
|
||||
case ChecklistFilterConditionPB.IsComplete:
|
||||
return LocaleKeys.grid_checklistFilter_isComplete.tr();
|
||||
case ChecklistFilterCondition.IsIncomplete:
|
||||
case ChecklistFilterConditionPB.IsIncomplete:
|
||||
return LocaleKeys.grid_checklistFilter_isIncomplted.tr();
|
||||
default:
|
||||
return "";
|
||||
|
@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
|
||||
class SelectOptionFilterConditionList extends StatelessWidget {
|
||||
final FilterInfo filterInfo;
|
||||
final PopoverMutex popoverMutex;
|
||||
final Function(SelectOptionCondition) onCondition;
|
||||
final Function(SelectOptionConditionPB) onCondition;
|
||||
const SelectOptionFilterConditionList({
|
||||
required this.filterInfo,
|
||||
required this.popoverMutex,
|
||||
@ -27,7 +27,7 @@ class SelectOptionFilterConditionList extends StatelessWidget {
|
||||
asBarrier: true,
|
||||
mutex: popoverMutex,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: SelectOptionCondition.values
|
||||
actions: SelectOptionConditionPB.values
|
||||
.map(
|
||||
(action) => ConditionWrapper(
|
||||
action,
|
||||
@ -59,7 +59,7 @@ class SelectOptionFilterConditionList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class ConditionWrapper extends ActionCell {
|
||||
final SelectOptionCondition inner;
|
||||
final SelectOptionConditionPB inner;
|
||||
final bool isSelected;
|
||||
final FieldType fieldType;
|
||||
|
||||
@ -84,16 +84,16 @@ class ConditionWrapper extends ActionCell {
|
||||
}
|
||||
}
|
||||
|
||||
extension SelectOptionConditionExtension on SelectOptionCondition {
|
||||
extension SelectOptionConditionPBExtension on SelectOptionConditionPB {
|
||||
String get singleSelectFilterName {
|
||||
switch (this) {
|
||||
case SelectOptionCondition.OptionIs:
|
||||
case SelectOptionConditionPB.OptionIs:
|
||||
return LocaleKeys.grid_singleSelectOptionFilter_is.tr();
|
||||
case SelectOptionCondition.OptionIsEmpty:
|
||||
case SelectOptionConditionPB.OptionIsEmpty:
|
||||
return LocaleKeys.grid_singleSelectOptionFilter_isEmpty.tr();
|
||||
case SelectOptionCondition.OptionIsNot:
|
||||
case SelectOptionConditionPB.OptionIsNot:
|
||||
return LocaleKeys.grid_singleSelectOptionFilter_isNot.tr();
|
||||
case SelectOptionCondition.OptionIsNotEmpty:
|
||||
case SelectOptionConditionPB.OptionIsNotEmpty:
|
||||
return LocaleKeys.grid_singleSelectOptionFilter_isNotEmpty.tr();
|
||||
default:
|
||||
return "";
|
||||
@ -102,13 +102,13 @@ extension SelectOptionConditionExtension on SelectOptionCondition {
|
||||
|
||||
String get multiSelectFilterName {
|
||||
switch (this) {
|
||||
case SelectOptionCondition.OptionIs:
|
||||
case SelectOptionConditionPB.OptionIs:
|
||||
return LocaleKeys.grid_multiSelectOptionFilter_contains.tr();
|
||||
case SelectOptionCondition.OptionIsEmpty:
|
||||
case SelectOptionConditionPB.OptionIsEmpty:
|
||||
return LocaleKeys.grid_multiSelectOptionFilter_isEmpty.tr();
|
||||
case SelectOptionCondition.OptionIsNot:
|
||||
case SelectOptionConditionPB.OptionIsNot:
|
||||
return LocaleKeys.grid_multiSelectOptionFilter_doesNotContain.tr();
|
||||
case SelectOptionCondition.OptionIsNotEmpty:
|
||||
case SelectOptionConditionPB.OptionIsNotEmpty:
|
||||
return LocaleKeys.grid_multiSelectOptionFilter_isNotEmpty.tr();
|
||||
default:
|
||||
return "";
|
||||
|
@ -101,9 +101,9 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
|
||||
SliverToBoxAdapter(child: _buildFilterPannel(context, state)),
|
||||
];
|
||||
|
||||
if (state.filter.condition != SelectOptionCondition.OptionIsEmpty &&
|
||||
if (state.filter.condition != SelectOptionConditionPB.OptionIsEmpty &&
|
||||
state.filter.condition !=
|
||||
SelectOptionCondition.OptionIsNotEmpty) {
|
||||
SelectOptionConditionPB.OptionIsNotEmpty) {
|
||||
slivers.add(const SliverToBoxAdapter(child: VSpace(4)));
|
||||
slivers.add(
|
||||
SliverToBoxAdapter(
|
||||
|
@ -64,8 +64,8 @@ class _TextFilterChoicechipState extends State<TextFilterChoicechip> {
|
||||
|
||||
String _makeFilterDesc(TextFilterEditorState state) {
|
||||
String filterDesc = state.filter.condition.choicechipPrefix;
|
||||
if (state.filter.condition == TextFilterCondition.TextIsEmpty ||
|
||||
state.filter.condition == TextFilterCondition.TextIsNotEmpty) {
|
||||
if (state.filter.condition == TextFilterConditionPB.TextIsEmpty ||
|
||||
state.filter.condition == TextFilterConditionPB.TextIsNotEmpty) {
|
||||
return filterDesc;
|
||||
}
|
||||
|
||||
@ -98,8 +98,8 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
|
||||
_buildFilterPannel(context, state),
|
||||
];
|
||||
|
||||
if (state.filter.condition != TextFilterCondition.TextIsEmpty &&
|
||||
state.filter.condition != TextFilterCondition.TextIsNotEmpty) {
|
||||
if (state.filter.condition != TextFilterConditionPB.TextIsEmpty &&
|
||||
state.filter.condition != TextFilterConditionPB.TextIsNotEmpty) {
|
||||
children.add(const VSpace(4));
|
||||
children.add(_buildFilterTextField(context, state));
|
||||
}
|
||||
@ -120,7 +120,7 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
|
||||
children: [
|
||||
FlowyText(state.filterInfo.fieldInfo.name),
|
||||
const HSpace(4),
|
||||
TextFilterConditionList(
|
||||
TextFilterConditionPBList(
|
||||
filterInfo: state.filterInfo,
|
||||
popoverMutex: popoverMutex,
|
||||
onCondition: (condition) {
|
||||
@ -163,11 +163,11 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
|
||||
}
|
||||
}
|
||||
|
||||
class TextFilterConditionList extends StatelessWidget {
|
||||
class TextFilterConditionPBList extends StatelessWidget {
|
||||
final FilterInfo filterInfo;
|
||||
final PopoverMutex popoverMutex;
|
||||
final Function(TextFilterCondition) onCondition;
|
||||
const TextFilterConditionList({
|
||||
final Function(TextFilterConditionPB) onCondition;
|
||||
const TextFilterConditionPBList({
|
||||
required this.filterInfo,
|
||||
required this.popoverMutex,
|
||||
required this.onCondition,
|
||||
@ -181,7 +181,7 @@ class TextFilterConditionList extends StatelessWidget {
|
||||
asBarrier: true,
|
||||
mutex: popoverMutex,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: TextFilterCondition.values
|
||||
actions: TextFilterConditionPB.values
|
||||
.map(
|
||||
(action) => ConditionWrapper(
|
||||
action,
|
||||
@ -204,7 +204,7 @@ class TextFilterConditionList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class ConditionWrapper extends ActionCell {
|
||||
final TextFilterCondition inner;
|
||||
final TextFilterConditionPB inner;
|
||||
final bool isSelected;
|
||||
|
||||
ConditionWrapper(this.inner, this.isSelected);
|
||||
@ -222,24 +222,24 @@ class ConditionWrapper extends ActionCell {
|
||||
String get name => inner.filterName;
|
||||
}
|
||||
|
||||
extension TextFilterConditionExtension on TextFilterCondition {
|
||||
extension TextFilterConditionPBExtension on TextFilterConditionPB {
|
||||
String get filterName {
|
||||
switch (this) {
|
||||
case TextFilterCondition.Contains:
|
||||
case TextFilterConditionPB.Contains:
|
||||
return LocaleKeys.grid_textFilter_contains.tr();
|
||||
case TextFilterCondition.DoesNotContain:
|
||||
case TextFilterConditionPB.DoesNotContain:
|
||||
return LocaleKeys.grid_textFilter_doesNotContain.tr();
|
||||
case TextFilterCondition.EndsWith:
|
||||
case TextFilterConditionPB.EndsWith:
|
||||
return LocaleKeys.grid_textFilter_endsWith.tr();
|
||||
case TextFilterCondition.Is:
|
||||
case TextFilterConditionPB.Is:
|
||||
return LocaleKeys.grid_textFilter_is.tr();
|
||||
case TextFilterCondition.IsNot:
|
||||
case TextFilterConditionPB.IsNot:
|
||||
return LocaleKeys.grid_textFilter_isNot.tr();
|
||||
case TextFilterCondition.StartsWith:
|
||||
case TextFilterConditionPB.StartsWith:
|
||||
return LocaleKeys.grid_textFilter_startWith.tr();
|
||||
case TextFilterCondition.TextIsEmpty:
|
||||
case TextFilterConditionPB.TextIsEmpty:
|
||||
return LocaleKeys.grid_textFilter_isEmpty.tr();
|
||||
case TextFilterCondition.TextIsNotEmpty:
|
||||
case TextFilterConditionPB.TextIsNotEmpty:
|
||||
return LocaleKeys.grid_textFilter_isNotEmpty.tr();
|
||||
default:
|
||||
return "";
|
||||
@ -248,17 +248,17 @@ extension TextFilterConditionExtension on TextFilterCondition {
|
||||
|
||||
String get choicechipPrefix {
|
||||
switch (this) {
|
||||
case TextFilterCondition.DoesNotContain:
|
||||
case TextFilterConditionPB.DoesNotContain:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
|
||||
case TextFilterCondition.EndsWith:
|
||||
case TextFilterConditionPB.EndsWith:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_endWith.tr();
|
||||
case TextFilterCondition.IsNot:
|
||||
case TextFilterConditionPB.IsNot:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
|
||||
case TextFilterCondition.StartsWith:
|
||||
case TextFilterConditionPB.StartsWith:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_startWith.tr();
|
||||
case TextFilterCondition.TextIsEmpty:
|
||||
case TextFilterConditionPB.TextIsEmpty:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_isEmpty.tr();
|
||||
case TextFilterCondition.TextIsNotEmpty:
|
||||
case TextFilterConditionPB.TextIsNotEmpty:
|
||||
return LocaleKeys.grid_textFilter_choicechipPrefix_isNotEmpty.tr();
|
||||
default:
|
||||
return "";
|
||||
|
@ -1,8 +1,5 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/board/application/board_data_controller.dart';
|
||||
import 'package:app_flowy/plugins/board/board.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
|
||||
@ -74,10 +71,6 @@ class BoardTestContext {
|
||||
return _boardDataController.rowInfos;
|
||||
}
|
||||
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks {
|
||||
return _boardDataController.blocks;
|
||||
}
|
||||
|
||||
List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
|
||||
|
||||
GridFieldController get fieldController {
|
||||
@ -113,15 +106,13 @@ class BoardTestContext {
|
||||
String fieldId,
|
||||
) async {
|
||||
final RowInfo rowInfo = rowInfos.last;
|
||||
final blockCache = blocks[rowInfo.rowPB.blockId];
|
||||
final rowCache = blockCache?.rowCache;
|
||||
|
||||
final rowCache = _boardDataController.rowCache;
|
||||
final fieldController = _boardDataController.fieldController;
|
||||
|
||||
final rowDataController = GridRowDataController(
|
||||
rowInfo: rowInfo,
|
||||
fieldController: fieldController,
|
||||
rowCache: rowCache!,
|
||||
rowCache: rowCache,
|
||||
);
|
||||
|
||||
final rowBloc = RowBloc(
|
||||
|
@ -19,7 +19,7 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
|
||||
@ -32,7 +32,7 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
|
||||
@ -60,7 +60,7 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
|
||||
@ -81,7 +81,7 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
|
||||
@ -102,7 +102,7 @@ void main() {
|
||||
await gridResponseFuture();
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsNotEmpty,
|
||||
condition: TextFilterConditionPB.TextIsNotEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.isEmpty);
|
||||
@ -121,7 +121,7 @@ void main() {
|
||||
await gridResponseFuture();
|
||||
await service.insertCheckboxFilter(
|
||||
fieldId: checkboxField.id,
|
||||
condition: CheckboxFilterCondition.IsUnChecked,
|
||||
condition: CheckboxFilterConditionPB.IsUnChecked,
|
||||
);
|
||||
await gridResponseFuture();
|
||||
assert(gridBloc.state.rowInfos.length == 3);
|
||||
@ -140,7 +140,7 @@ void main() {
|
||||
await gridResponseFuture();
|
||||
await service.insertCheckboxFilter(
|
||||
fieldId: checkboxField.id,
|
||||
condition: CheckboxFilterCondition.IsChecked,
|
||||
condition: CheckboxFilterConditionPB.IsChecked,
|
||||
);
|
||||
await gridResponseFuture();
|
||||
assert(gridBloc.state.rowInfos.isEmpty);
|
||||
|
@ -28,7 +28,7 @@ void main() {
|
||||
// Insert filter for the text field
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(menuBloc.state.filters.length == 1);
|
||||
|
@ -23,7 +23,7 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(menuBloc.state.creatableFields.length == 2);
|
||||
@ -42,7 +42,7 @@ void main() {
|
||||
// Create filter
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
|
||||
@ -51,11 +51,11 @@ void main() {
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
filterId: textFilter.filter.id,
|
||||
condition: TextFilterCondition.Is,
|
||||
condition: TextFilterConditionPB.Is,
|
||||
content: "ABC");
|
||||
await gridResponseFuture();
|
||||
assert(menuBloc.state.filters.first.textFilter()!.condition ==
|
||||
TextFilterCondition.Is);
|
||||
TextFilterConditionPB.Is);
|
||||
assert(menuBloc.state.filters.first.textFilter()!.content == "ABC");
|
||||
});
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ void main() {
|
||||
final checkboxField = context.checkboxFieldContext();
|
||||
await service.insertCheckboxFilter(
|
||||
fieldId: checkboxField.id,
|
||||
condition: CheckboxFilterCondition.IsChecked,
|
||||
condition: CheckboxFilterConditionPB.IsChecked,
|
||||
);
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 1,
|
||||
@ -42,7 +42,7 @@ void main() {
|
||||
final checkboxField = context.checkboxFieldContext();
|
||||
await service.insertCheckboxFilter(
|
||||
fieldId: checkboxField.id,
|
||||
condition: CheckboxFilterCondition.IsUnChecked,
|
||||
condition: CheckboxFilterConditionPB.IsUnChecked,
|
||||
);
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 2,
|
||||
|
@ -19,7 +19,7 @@ void main() {
|
||||
// create a new filter
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(context.fieldController.filterInfos.length == 1,
|
||||
@ -46,7 +46,7 @@ void main() {
|
||||
// create a new filter
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsNotEmpty,
|
||||
condition: TextFilterConditionPB.TextIsNotEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 2,
|
||||
@ -71,7 +71,7 @@ void main() {
|
||||
// create a new filter
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterCondition.TextIsEmpty,
|
||||
condition: TextFilterConditionPB.TextIsEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(context.fieldController.filterInfos.length == 1,
|
||||
@ -84,7 +84,7 @@ void main() {
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
filterId: textFilter.filter.id,
|
||||
condition: TextFilterCondition.TextIsNotEmpty,
|
||||
condition: TextFilterConditionPB.TextIsNotEmpty,
|
||||
content: "");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 2);
|
||||
@ -106,7 +106,9 @@ void main() {
|
||||
final textField = context.textFieldContext();
|
||||
// create a new filter
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id, condition: TextFilterCondition.Is, content: "A");
|
||||
fieldId: textField.id,
|
||||
condition: TextFilterConditionPB.Is,
|
||||
content: "A");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 1,
|
||||
"expect 1 but receive ${context.rowInfos.length}");
|
||||
@ -116,7 +118,7 @@ void main() {
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
filterId: textFilter.filter.id,
|
||||
condition: TextFilterCondition.Is,
|
||||
condition: TextFilterConditionPB.Is,
|
||||
content: "B");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 1);
|
||||
@ -125,7 +127,7 @@ void main() {
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
filterId: textFilter.filter.id,
|
||||
condition: TextFilterCondition.Is,
|
||||
condition: TextFilterConditionPB.Is,
|
||||
content: "b");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.length == 1);
|
||||
@ -134,7 +136,7 @@ void main() {
|
||||
await service.insertTextFilter(
|
||||
fieldId: textField.id,
|
||||
filterId: textFilter.filter.id,
|
||||
condition: TextFilterCondition.Is,
|
||||
condition: TextFilterConditionPB.Is,
|
||||
content: "C");
|
||||
await gridResponseFuture();
|
||||
assert(context.rowInfos.isEmpty);
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'dart:collection';
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
|
||||
@ -26,10 +24,6 @@ class GridTestContext {
|
||||
return gridController.rowInfos;
|
||||
}
|
||||
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks {
|
||||
return gridController.blocks;
|
||||
}
|
||||
|
||||
List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
|
||||
|
||||
GridFieldController get fieldController {
|
||||
@ -71,14 +65,13 @@ class GridTestContext {
|
||||
int rowIndex,
|
||||
) async {
|
||||
final RowInfo rowInfo = rowInfos[rowIndex];
|
||||
final blockCache = blocks[rowInfo.rowPB.blockId];
|
||||
final rowCache = blockCache?.rowCache;
|
||||
final rowCache = gridController.rowCache;
|
||||
final fieldController = gridController.fieldController;
|
||||
|
||||
final rowDataController = GridRowDataController(
|
||||
rowInfo: rowInfo,
|
||||
fieldController: fieldController,
|
||||
rowCache: rowCache!,
|
||||
rowCache: rowCache,
|
||||
);
|
||||
|
||||
final rowBloc = RowBloc(
|
||||
|
@ -6,8 +6,9 @@ const OBSERVABLE_CATEGORY: &str = "Grid";
|
||||
pub enum GridDartNotification {
|
||||
Unknown = 0,
|
||||
DidCreateBlock = 11,
|
||||
DidUpdateGridBlock = 20,
|
||||
DidUpdateGridField = 21,
|
||||
DidUpdateGridViewRows = 20,
|
||||
DidUpdateGridViewRowsVisibility = 21,
|
||||
DidUpdateGridFields = 22,
|
||||
DidUpdateRow = 30,
|
||||
DidUpdateCell = 40,
|
||||
DidUpdateField = 50,
|
||||
@ -15,6 +16,7 @@ pub enum GridDartNotification {
|
||||
DidUpdateGroup = 61,
|
||||
DidGroupByNewField = 62,
|
||||
DidUpdateFilter = 63,
|
||||
DidUpdateSort = 64,
|
||||
DidUpdateGridSetting = 70,
|
||||
}
|
||||
|
||||
|
@ -1,245 +0,0 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use grid_rev_model::RowRevision;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// [BlockPB] contains list of row ids. The rows here does not contain any data, just the id
|
||||
/// of the row. Check out [RowPB] for more details.
|
||||
///
|
||||
///
|
||||
/// A grid can have many rows. Rows are therefore grouped into Blocks in order to make
|
||||
/// things more efficient.
|
||||
/// |
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct BlockPB {
|
||||
#[pb(index = 1)]
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub rows: Vec<RowPB>,
|
||||
}
|
||||
|
||||
impl BlockPB {
|
||||
pub fn new(block_id: &str, rows: Vec<RowPB>) -> Self {
|
||||
Self {
|
||||
id: block_id.to_owned(),
|
||||
rows,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [RowPB] Describes a row. Has the id of the parent Block. Has the metadata of the row.
|
||||
#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
|
||||
pub struct RowPB {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl RowPB {
|
||||
pub fn row_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn block_id(&self) -> &str {
|
||||
&self.block_id
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&RowRevision> for RowPB {
|
||||
fn from(rev: &RowRevision) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&mut RowRevision> for RowPB {
|
||||
fn from(rev: &mut RowRevision) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&Arc<RowRevision>> for RowPB {
|
||||
fn from(rev: &Arc<RowRevision>) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct OptionalRowPB {
|
||||
#[pb(index = 1, one_of)]
|
||||
pub row: Option<RowPB>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct RepeatedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub items: Vec<RowPB>,
|
||||
}
|
||||
|
||||
impl std::convert::From<Vec<RowPB>> for RepeatedRowPB {
|
||||
fn from(items: Vec<RowPB>) -> Self {
|
||||
Self { items }
|
||||
}
|
||||
}
|
||||
|
||||
/// [RepeatedBlockPB] contains list of [BlockPB]
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct RepeatedBlockPB {
|
||||
#[pb(index = 1)]
|
||||
pub items: Vec<BlockPB>,
|
||||
}
|
||||
|
||||
impl std::convert::From<Vec<BlockPB>> for RepeatedBlockPB {
|
||||
fn from(items: Vec<BlockPB>) -> Self {
|
||||
Self { items }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct InsertedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub row: RowPB,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub index: Option<i32>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub is_new: bool,
|
||||
}
|
||||
|
||||
impl InsertedRowPB {
|
||||
pub fn new(row: RowPB) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: None,
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_index(row: RowPB, index: i32) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: Some(index),
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<RowPB> for InsertedRowPB {
|
||||
fn from(row: RowPB) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: None,
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&RowRevision> for InsertedRowPB {
|
||||
fn from(row: &RowRevision) -> Self {
|
||||
let row_order = RowPB::from(row);
|
||||
Self::from(row_order)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct UpdatedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub row: RowPB,
|
||||
|
||||
// represents as the cells that were updated in this row.
|
||||
#[pb(index = 2)]
|
||||
pub field_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||
pub struct GridBlockChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub inserted_rows: Vec<InsertedRowPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub deleted_rows: Vec<String>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub updated_rows: Vec<UpdatedRowPB>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub visible_rows: Vec<InsertedRowPB>,
|
||||
|
||||
#[pb(index = 6)]
|
||||
pub invisible_rows: Vec<String>,
|
||||
}
|
||||
impl GridBlockChangesetPB {
|
||||
pub fn insert(block_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
||||
Self {
|
||||
block_id,
|
||||
inserted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(block_id: &str, deleted_rows: Vec<String>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
deleted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowPB>) -> Self {
|
||||
Self {
|
||||
block_id: block_id.to_owned(),
|
||||
updated_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [QueryBlocksPayloadPB] is used to query the data of the block that belongs to the grid whose
|
||||
/// id is grid_id.
|
||||
#[derive(ProtoBuf, Default)]
|
||||
pub struct QueryBlocksPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub block_ids: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct QueryGridBlocksParams {
|
||||
pub grid_id: String,
|
||||
pub block_ids: Vec<String>,
|
||||
}
|
||||
|
||||
impl TryInto<QueryGridBlocksParams> for QueryBlocksPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<QueryGridBlocksParams, Self::Error> {
|
||||
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
||||
Ok(QueryGridBlocksParams {
|
||||
grid_id: grid_id.0,
|
||||
block_ids: self.block_ids,
|
||||
})
|
||||
}
|
||||
}
|
@ -5,35 +5,35 @@ use grid_rev_model::FilterRevision;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CheckboxFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: CheckboxFilterCondition,
|
||||
pub condition: CheckboxFilterConditionPB,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum CheckboxFilterCondition {
|
||||
pub enum CheckboxFilterConditionPB {
|
||||
IsChecked = 0,
|
||||
IsUnChecked = 1,
|
||||
}
|
||||
|
||||
impl std::convert::From<CheckboxFilterCondition> for u32 {
|
||||
fn from(value: CheckboxFilterCondition) -> Self {
|
||||
impl std::convert::From<CheckboxFilterConditionPB> for u32 {
|
||||
fn from(value: CheckboxFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for CheckboxFilterCondition {
|
||||
impl std::default::Default for CheckboxFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
CheckboxFilterCondition::IsChecked
|
||||
CheckboxFilterConditionPB::IsChecked
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for CheckboxFilterCondition {
|
||||
impl std::convert::TryFrom<u8> for CheckboxFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(CheckboxFilterCondition::IsChecked),
|
||||
1 => Ok(CheckboxFilterCondition::IsUnChecked),
|
||||
0 => Ok(CheckboxFilterConditionPB::IsChecked),
|
||||
1 => Ok(CheckboxFilterConditionPB::IsUnChecked),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
@ -42,7 +42,8 @@ impl std::convert::TryFrom<u8> for CheckboxFilterCondition {
|
||||
impl std::convert::From<&FilterRevision> for CheckboxFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
CheckboxFilterPB {
|
||||
condition: CheckboxFilterCondition::try_from(rev.condition).unwrap_or(CheckboxFilterCondition::IsChecked),
|
||||
condition: CheckboxFilterConditionPB::try_from(rev.condition)
|
||||
.unwrap_or(CheckboxFilterConditionPB::IsChecked),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,35 +5,35 @@ use grid_rev_model::FilterRevision;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct ChecklistFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: ChecklistFilterCondition,
|
||||
pub condition: ChecklistFilterConditionPB,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum ChecklistFilterCondition {
|
||||
pub enum ChecklistFilterConditionPB {
|
||||
IsComplete = 0,
|
||||
IsIncomplete = 1,
|
||||
}
|
||||
|
||||
impl std::convert::From<ChecklistFilterCondition> for u32 {
|
||||
fn from(value: ChecklistFilterCondition) -> Self {
|
||||
impl std::convert::From<ChecklistFilterConditionPB> for u32 {
|
||||
fn from(value: ChecklistFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for ChecklistFilterCondition {
|
||||
impl std::default::Default for ChecklistFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
ChecklistFilterCondition::IsIncomplete
|
||||
ChecklistFilterConditionPB::IsIncomplete
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for ChecklistFilterCondition {
|
||||
impl std::convert::TryFrom<u8> for ChecklistFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(ChecklistFilterCondition::IsComplete),
|
||||
1 => Ok(ChecklistFilterCondition::IsIncomplete),
|
||||
0 => Ok(ChecklistFilterConditionPB::IsComplete),
|
||||
1 => Ok(ChecklistFilterConditionPB::IsIncomplete),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
@ -42,8 +42,8 @@ impl std::convert::TryFrom<u8> for ChecklistFilterCondition {
|
||||
impl std::convert::From<&FilterRevision> for ChecklistFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
ChecklistFilterPB {
|
||||
condition: ChecklistFilterCondition::try_from(rev.condition)
|
||||
.unwrap_or(ChecklistFilterCondition::IsIncomplete),
|
||||
condition: ChecklistFilterConditionPB::try_from(rev.condition)
|
||||
.unwrap_or(ChecklistFilterConditionPB::IsIncomplete),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use std::str::FromStr;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct DateFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: DateFilterCondition,
|
||||
pub condition: DateFilterConditionPB,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub start: Option<i64>,
|
||||
@ -20,19 +20,19 @@ pub struct DateFilterPB {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Clone, Debug)]
|
||||
pub struct DateFilterContent {
|
||||
pub struct DateFilterContentPB {
|
||||
pub start: Option<i64>,
|
||||
pub end: Option<i64>,
|
||||
pub timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
impl ToString for DateFilterContent {
|
||||
impl ToString for DateFilterContentPB {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DateFilterContent {
|
||||
impl FromStr for DateFilterContentPB {
|
||||
type Err = serde_json::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@ -42,7 +42,7 @@ impl FromStr for DateFilterContent {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum DateFilterCondition {
|
||||
pub enum DateFilterConditionPB {
|
||||
DateIs = 0,
|
||||
DateBefore = 1,
|
||||
DateAfter = 2,
|
||||
@ -53,42 +53,42 @@ pub enum DateFilterCondition {
|
||||
DateIsNotEmpty = 7,
|
||||
}
|
||||
|
||||
impl std::convert::From<DateFilterCondition> for u32 {
|
||||
fn from(value: DateFilterCondition) -> Self {
|
||||
impl std::convert::From<DateFilterConditionPB> for u32 {
|
||||
fn from(value: DateFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
impl std::default::Default for DateFilterCondition {
|
||||
impl std::default::Default for DateFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
DateFilterCondition::DateIs
|
||||
DateFilterConditionPB::DateIs
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for DateFilterCondition {
|
||||
impl std::convert::TryFrom<u8> for DateFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(DateFilterCondition::DateIs),
|
||||
1 => Ok(DateFilterCondition::DateBefore),
|
||||
2 => Ok(DateFilterCondition::DateAfter),
|
||||
3 => Ok(DateFilterCondition::DateOnOrBefore),
|
||||
4 => Ok(DateFilterCondition::DateOnOrAfter),
|
||||
5 => Ok(DateFilterCondition::DateWithIn),
|
||||
6 => Ok(DateFilterCondition::DateIsEmpty),
|
||||
0 => Ok(DateFilterConditionPB::DateIs),
|
||||
1 => Ok(DateFilterConditionPB::DateBefore),
|
||||
2 => Ok(DateFilterConditionPB::DateAfter),
|
||||
3 => Ok(DateFilterConditionPB::DateOnOrBefore),
|
||||
4 => Ok(DateFilterConditionPB::DateOnOrAfter),
|
||||
5 => Ok(DateFilterConditionPB::DateWithIn),
|
||||
6 => Ok(DateFilterConditionPB::DateIsEmpty),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::convert::From<&FilterRevision> for DateFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
let condition = DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs);
|
||||
let condition = DateFilterConditionPB::try_from(rev.condition).unwrap_or(DateFilterConditionPB::DateIs);
|
||||
let mut filter = DateFilterPB {
|
||||
condition,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Ok(content) = DateFilterContent::from_str(&rev.content) {
|
||||
if let Ok(content) = DateFilterContentPB::from_str(&rev.content) {
|
||||
filter.start = content.start;
|
||||
filter.end = content.end;
|
||||
filter.timestamp = content.timestamp;
|
||||
|
@ -5,7 +5,7 @@ use grid_rev_model::FilterRevision;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct NumberFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: NumberFilterCondition,
|
||||
pub condition: NumberFilterConditionPB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub content: String,
|
||||
@ -13,7 +13,7 @@ pub struct NumberFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum NumberFilterCondition {
|
||||
pub enum NumberFilterConditionPB {
|
||||
Equal = 0,
|
||||
NotEqual = 1,
|
||||
GreaterThan = 2,
|
||||
@ -24,30 +24,30 @@ pub enum NumberFilterCondition {
|
||||
NumberIsNotEmpty = 7,
|
||||
}
|
||||
|
||||
impl std::default::Default for NumberFilterCondition {
|
||||
impl std::default::Default for NumberFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
NumberFilterCondition::Equal
|
||||
NumberFilterConditionPB::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<NumberFilterCondition> for u32 {
|
||||
fn from(value: NumberFilterCondition) -> Self {
|
||||
impl std::convert::From<NumberFilterConditionPB> for u32 {
|
||||
fn from(value: NumberFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
impl std::convert::TryFrom<u8> for NumberFilterCondition {
|
||||
impl std::convert::TryFrom<u8> for NumberFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(n: u8) -> Result<Self, Self::Error> {
|
||||
match n {
|
||||
0 => Ok(NumberFilterCondition::Equal),
|
||||
1 => Ok(NumberFilterCondition::NotEqual),
|
||||
2 => Ok(NumberFilterCondition::GreaterThan),
|
||||
3 => Ok(NumberFilterCondition::LessThan),
|
||||
4 => Ok(NumberFilterCondition::GreaterThanOrEqualTo),
|
||||
5 => Ok(NumberFilterCondition::LessThanOrEqualTo),
|
||||
6 => Ok(NumberFilterCondition::NumberIsEmpty),
|
||||
7 => Ok(NumberFilterCondition::NumberIsNotEmpty),
|
||||
0 => Ok(NumberFilterConditionPB::Equal),
|
||||
1 => Ok(NumberFilterConditionPB::NotEqual),
|
||||
2 => Ok(NumberFilterConditionPB::GreaterThan),
|
||||
3 => Ok(NumberFilterConditionPB::LessThan),
|
||||
4 => Ok(NumberFilterConditionPB::GreaterThanOrEqualTo),
|
||||
5 => Ok(NumberFilterConditionPB::LessThanOrEqualTo),
|
||||
6 => Ok(NumberFilterConditionPB::NumberIsEmpty),
|
||||
7 => Ok(NumberFilterConditionPB::NumberIsNotEmpty),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,7 @@ impl std::convert::TryFrom<u8> for NumberFilterCondition {
|
||||
impl std::convert::From<&FilterRevision> for NumberFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
NumberFilterPB {
|
||||
condition: NumberFilterCondition::try_from(rev.condition).unwrap_or(NumberFilterCondition::Equal),
|
||||
condition: NumberFilterConditionPB::try_from(rev.condition).unwrap_or(NumberFilterConditionPB::Equal),
|
||||
content: rev.content.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use grid_rev_model::FilterRevision;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct SelectOptionFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: SelectOptionCondition,
|
||||
pub condition: SelectOptionConditionPB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub option_ids: Vec<String>,
|
||||
@ -14,34 +14,34 @@ pub struct SelectOptionFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum SelectOptionCondition {
|
||||
pub enum SelectOptionConditionPB {
|
||||
OptionIs = 0,
|
||||
OptionIsNot = 1,
|
||||
OptionIsEmpty = 2,
|
||||
OptionIsNotEmpty = 3,
|
||||
}
|
||||
|
||||
impl std::convert::From<SelectOptionCondition> for u32 {
|
||||
fn from(value: SelectOptionCondition) -> Self {
|
||||
impl std::convert::From<SelectOptionConditionPB> for u32 {
|
||||
fn from(value: SelectOptionConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for SelectOptionCondition {
|
||||
impl std::default::Default for SelectOptionConditionPB {
|
||||
fn default() -> Self {
|
||||
SelectOptionCondition::OptionIs
|
||||
SelectOptionConditionPB::OptionIs
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for SelectOptionCondition {
|
||||
impl std::convert::TryFrom<u8> for SelectOptionConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(SelectOptionCondition::OptionIs),
|
||||
1 => Ok(SelectOptionCondition::OptionIsNot),
|
||||
2 => Ok(SelectOptionCondition::OptionIsEmpty),
|
||||
3 => Ok(SelectOptionCondition::OptionIsNotEmpty),
|
||||
0 => Ok(SelectOptionConditionPB::OptionIs),
|
||||
1 => Ok(SelectOptionConditionPB::OptionIsNot),
|
||||
2 => Ok(SelectOptionConditionPB::OptionIsEmpty),
|
||||
3 => Ok(SelectOptionConditionPB::OptionIsNotEmpty),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
@ -51,7 +51,7 @@ impl std::convert::From<&FilterRevision> for SelectOptionFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
let ids = SelectOptionIds::from(rev.content.clone());
|
||||
SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs),
|
||||
condition: SelectOptionConditionPB::try_from(rev.condition).unwrap_or(SelectOptionConditionPB::OptionIs),
|
||||
option_ids: ids.into_inner(),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use grid_rev_model::FilterRevision;
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct TextFilterPB {
|
||||
#[pb(index = 1)]
|
||||
pub condition: TextFilterCondition,
|
||||
pub condition: TextFilterConditionPB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub content: String,
|
||||
@ -13,7 +13,7 @@ pub struct TextFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum TextFilterCondition {
|
||||
pub enum TextFilterConditionPB {
|
||||
Is = 0,
|
||||
IsNot = 1,
|
||||
Contains = 2,
|
||||
@ -24,31 +24,31 @@ pub enum TextFilterCondition {
|
||||
TextIsNotEmpty = 7,
|
||||
}
|
||||
|
||||
impl std::convert::From<TextFilterCondition> for u32 {
|
||||
fn from(value: TextFilterCondition) -> Self {
|
||||
impl std::convert::From<TextFilterConditionPB> for u32 {
|
||||
fn from(value: TextFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for TextFilterCondition {
|
||||
impl std::default::Default for TextFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
TextFilterCondition::Is
|
||||
TextFilterConditionPB::Is
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for TextFilterCondition {
|
||||
impl std::convert::TryFrom<u8> for TextFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(TextFilterCondition::Is),
|
||||
1 => Ok(TextFilterCondition::IsNot),
|
||||
2 => Ok(TextFilterCondition::Contains),
|
||||
3 => Ok(TextFilterCondition::DoesNotContain),
|
||||
4 => Ok(TextFilterCondition::StartsWith),
|
||||
5 => Ok(TextFilterCondition::EndsWith),
|
||||
6 => Ok(TextFilterCondition::TextIsEmpty),
|
||||
7 => Ok(TextFilterCondition::TextIsNotEmpty),
|
||||
0 => Ok(TextFilterConditionPB::Is),
|
||||
1 => Ok(TextFilterConditionPB::IsNot),
|
||||
2 => Ok(TextFilterConditionPB::Contains),
|
||||
3 => Ok(TextFilterConditionPB::DoesNotContain),
|
||||
4 => Ok(TextFilterConditionPB::StartsWith),
|
||||
5 => Ok(TextFilterConditionPB::EndsWith),
|
||||
6 => Ok(TextFilterConditionPB::TextIsEmpty),
|
||||
7 => Ok(TextFilterConditionPB::TextIsNotEmpty),
|
||||
_ => Err(ErrorCode::InvalidData),
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ impl std::convert::TryFrom<u8> for TextFilterCondition {
|
||||
impl std::convert::From<&FilterRevision> for TextFilterPB {
|
||||
fn from(rev: &FilterRevision) -> Self {
|
||||
TextFilterPB {
|
||||
condition: TextFilterCondition::try_from(rev.condition).unwrap_or(TextFilterCondition::Is),
|
||||
condition: TextFilterConditionPB::try_from(rev.condition).unwrap_or(TextFilterConditionPB::Is),
|
||||
content: rev.content.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{
|
||||
CheckboxFilterPB, ChecklistFilterPB, DateFilterContent, DateFilterPB, FieldType, NumberFilterPB,
|
||||
CheckboxFilterPB, ChecklistFilterPB, DateFilterContentPB, DateFilterPB, FieldType, NumberFilterPB,
|
||||
SelectOptionFilterPB, TextFilterPB,
|
||||
};
|
||||
use crate::services::field::SelectOptionIds;
|
||||
@ -79,12 +79,18 @@ pub struct DeleteFilterPayloadPB {
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub filter_id: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub view_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<DeleteFilterParams> for DeleteFilterPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<DeleteFilterParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id)
|
||||
.map_err(|_| ErrorCode::GridViewIdIsEmpty)?
|
||||
.0;
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
@ -98,12 +104,17 @@ impl TryInto<DeleteFilterParams> for DeleteFilterPayloadPB {
|
||||
field_type: self.field_type,
|
||||
};
|
||||
|
||||
Ok(DeleteFilterParams { filter_id, filter_type })
|
||||
Ok(DeleteFilterParams {
|
||||
view_id,
|
||||
filter_id,
|
||||
filter_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeleteFilterParams {
|
||||
pub view_id: String,
|
||||
pub filter_type: FilterType,
|
||||
pub filter_id: String,
|
||||
}
|
||||
@ -116,18 +127,27 @@ pub struct AlterFilterPayloadPB {
|
||||
#[pb(index = 2)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
/// Create a new filter if the filter_id is None
|
||||
#[pb(index = 3, one_of)]
|
||||
pub filter_id: Option<String>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub data: Vec<u8>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub view_id: String,
|
||||
}
|
||||
|
||||
impl AlterFilterPayloadPB {
|
||||
#[allow(dead_code)]
|
||||
pub fn new<T: TryInto<Bytes, Error = ::protobuf::ProtobufError>>(field_rev: &FieldRevision, data: T) -> Self {
|
||||
pub fn new<T: TryInto<Bytes, Error = ::protobuf::ProtobufError>>(
|
||||
view_id: &str,
|
||||
field_rev: &FieldRevision,
|
||||
data: T,
|
||||
) -> Self {
|
||||
let data = data.try_into().unwrap_or_else(|_| Bytes::new());
|
||||
Self {
|
||||
view_id: view_id.to_owned(),
|
||||
field_id: field_rev.id.clone(),
|
||||
field_type: field_rev.ty.into(),
|
||||
filter_id: None,
|
||||
@ -140,6 +160,10 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<AlterFilterParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id)
|
||||
.map_err(|_| ErrorCode::GridViewIdIsEmpty)?
|
||||
.0;
|
||||
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
@ -169,7 +193,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
|
||||
FieldType::DateTime => {
|
||||
let filter = DateFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?;
|
||||
condition = filter.condition as u8;
|
||||
content = DateFilterContent {
|
||||
content = DateFilterContentPB {
|
||||
start: filter.start,
|
||||
end: filter.end,
|
||||
timestamp: filter.timestamp,
|
||||
@ -184,6 +208,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
|
||||
}
|
||||
|
||||
Ok(AlterFilterParams {
|
||||
view_id,
|
||||
field_id,
|
||||
filter_id,
|
||||
field_type: self.field_type.into(),
|
||||
@ -195,7 +220,9 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AlterFilterParams {
|
||||
pub view_id: String,
|
||||
pub field_id: String,
|
||||
/// Create a new filter if the filter_id is None
|
||||
pub filter_id: Option<String>,
|
||||
pub field_type: FieldTypeRevision,
|
||||
pub condition: u8,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{BlockPB, FieldIdPB};
|
||||
use crate::entities::{FieldIdPB, RowPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
@ -13,7 +13,7 @@ pub struct GridPB {
|
||||
pub fields: Vec<FieldIdPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub blocks: Vec<BlockPB>,
|
||||
pub rows: Vec<RowPB>,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
|
@ -1,4 +1,3 @@
|
||||
pub mod block_entities;
|
||||
mod cell_entities;
|
||||
mod field_entities;
|
||||
pub mod filter_entities;
|
||||
@ -7,8 +6,9 @@ mod group_entities;
|
||||
pub mod parser;
|
||||
mod row_entities;
|
||||
pub mod setting_entities;
|
||||
mod sort_entities;
|
||||
mod view_entities;
|
||||
|
||||
pub use block_entities::*;
|
||||
pub use cell_entities::*;
|
||||
pub use field_entities::*;
|
||||
pub use filter_entities::*;
|
||||
@ -16,3 +16,5 @@ pub use grid_entities::*;
|
||||
pub use group_entities::*;
|
||||
pub use row_entities::*;
|
||||
pub use setting_entities::*;
|
||||
pub use sort_entities::*;
|
||||
pub use view_entities::*;
|
||||
|
@ -2,6 +2,136 @@ use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::GridLayout;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::ErrorCode;
|
||||
use grid_rev_model::RowRevision;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// [RowPB] Describes a row. Has the id of the parent Block. Has the metadata of the row.
|
||||
#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
|
||||
pub struct RowPB {
|
||||
#[pb(index = 1)]
|
||||
pub block_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl RowPB {
|
||||
pub fn row_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn block_id(&self) -> &str {
|
||||
&self.block_id
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&RowRevision> for RowPB {
|
||||
fn from(rev: &RowRevision) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&mut RowRevision> for RowPB {
|
||||
fn from(rev: &mut RowRevision) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&Arc<RowRevision>> for RowPB {
|
||||
fn from(rev: &Arc<RowRevision>) -> Self {
|
||||
Self {
|
||||
block_id: rev.block_id.clone(),
|
||||
id: rev.id.clone(),
|
||||
height: rev.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct OptionalRowPB {
|
||||
#[pb(index = 1, one_of)]
|
||||
pub row: Option<RowPB>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct RepeatedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub items: Vec<RowPB>,
|
||||
}
|
||||
|
||||
impl std::convert::From<Vec<RowPB>> for RepeatedRowPB {
|
||||
fn from(items: Vec<RowPB>) -> Self {
|
||||
Self { items }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct InsertedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub row: RowPB,
|
||||
|
||||
#[pb(index = 2, one_of)]
|
||||
pub index: Option<i32>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub is_new: bool,
|
||||
}
|
||||
|
||||
impl InsertedRowPB {
|
||||
pub fn new(row: RowPB) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: None,
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_index(row: RowPB, index: i32) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: Some(index),
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<RowPB> for InsertedRowPB {
|
||||
fn from(row: RowPB) -> Self {
|
||||
Self {
|
||||
row,
|
||||
index: None,
|
||||
is_new: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&RowRevision> for InsertedRowPB {
|
||||
fn from(row: &RowRevision) -> Self {
|
||||
let row_order = RowPB::from(row);
|
||||
Self::from(row_order)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct UpdatedRowPB {
|
||||
#[pb(index = 1)]
|
||||
pub row: RowPB,
|
||||
|
||||
// represents as the cells that were updated in this row.
|
||||
#[pb(index = 2)]
|
||||
pub field_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||
pub struct RowIdPB {
|
||||
@ -9,15 +139,11 @@ pub struct RowIdPB {
|
||||
pub grid_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub block_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub row_id: String,
|
||||
}
|
||||
|
||||
pub struct RowIdParams {
|
||||
pub grid_id: String,
|
||||
pub block_id: String,
|
||||
pub row_id: String,
|
||||
}
|
||||
|
||||
@ -26,12 +152,10 @@ impl TryInto<RowIdParams> for RowIdPB {
|
||||
|
||||
fn try_into(self) -> Result<RowIdParams, Self::Error> {
|
||||
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
|
||||
let block_id = NotEmptyStr::parse(self.block_id).map_err(|_| ErrorCode::BlockIdIsEmpty)?;
|
||||
let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
|
||||
Ok(RowIdParams {
|
||||
grid_id: grid_id.0,
|
||||
block_id: block_id.0,
|
||||
row_id: row_id.0,
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{
|
||||
AlterFilterParams, AlterFilterPayloadPB, DeleteFilterParams, DeleteFilterPayloadPB, DeleteGroupParams,
|
||||
DeleteGroupPayloadPB, InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB, RepeatedGroupConfigurationPB,
|
||||
AlterFilterParams, AlterFilterPayloadPB, AlterSortParams, AlterSortPayloadPB, DeleteFilterParams,
|
||||
DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, DeleteSortParams, DeleteSortPayloadPB,
|
||||
InsertGroupParams, InsertGroupPayloadPB, RepeatedFilterPB, RepeatedGroupConfigurationPB,
|
||||
};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
@ -83,7 +84,7 @@ pub struct GridSettingChangesetPB {
|
||||
pub layout_type: GridLayout,
|
||||
|
||||
#[pb(index = 3, one_of)]
|
||||
pub insert_filter: Option<AlterFilterPayloadPB>,
|
||||
pub alter_filter: Option<AlterFilterPayloadPB>,
|
||||
|
||||
#[pb(index = 4, one_of)]
|
||||
pub delete_filter: Option<DeleteFilterPayloadPB>,
|
||||
@ -93,6 +94,12 @@ pub struct GridSettingChangesetPB {
|
||||
|
||||
#[pb(index = 6, one_of)]
|
||||
pub delete_group: Option<DeleteGroupPayloadPB>,
|
||||
|
||||
#[pb(index = 7, one_of)]
|
||||
pub alter_sort: Option<AlterSortPayloadPB>,
|
||||
|
||||
#[pb(index = 8, one_of)]
|
||||
pub delete_sort: Option<DeleteSortPayloadPB>,
|
||||
}
|
||||
|
||||
impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
|
||||
@ -103,7 +110,7 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
|
||||
.map_err(|_| ErrorCode::ViewIdInvalid)?
|
||||
.0;
|
||||
|
||||
let insert_filter = match self.insert_filter {
|
||||
let insert_filter = match self.alter_filter {
|
||||
None => None,
|
||||
Some(payload) => Some(payload.try_into()?),
|
||||
};
|
||||
@ -123,6 +130,16 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let alert_sort = match self.alter_sort {
|
||||
None => None,
|
||||
Some(payload) => Some(payload.try_into()?),
|
||||
};
|
||||
|
||||
let delete_sort = match self.delete_sort {
|
||||
None => None,
|
||||
Some(payload) => Some(payload.try_into()?),
|
||||
};
|
||||
|
||||
Ok(GridSettingChangesetParams {
|
||||
grid_id: view_id,
|
||||
layout_type: self.layout_type.into(),
|
||||
@ -130,6 +147,8 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPB {
|
||||
delete_filter,
|
||||
insert_group,
|
||||
delete_group,
|
||||
alert_sort,
|
||||
delete_sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -141,6 +160,8 @@ pub struct GridSettingChangesetParams {
|
||||
pub delete_filter: Option<DeleteFilterParams>,
|
||||
pub insert_group: Option<InsertGroupParams>,
|
||||
pub delete_group: Option<DeleteGroupParams>,
|
||||
pub alert_sort: Option<AlterSortParams>,
|
||||
pub delete_sort: Option<DeleteSortParams>,
|
||||
}
|
||||
|
||||
impl GridSettingChangesetParams {
|
||||
|
153
frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
Normal file
153
frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::sort::SortType;
|
||||
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
use grid_rev_model::FieldTypeRevision;
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct GridSortPB {
|
||||
#[pb(index = 1)]
|
||||
pub id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub condition: GridSortConditionPB,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum GridSortConditionPB {
|
||||
Ascending = 0,
|
||||
Descending = 1,
|
||||
}
|
||||
impl std::default::Default for GridSortConditionPB {
|
||||
fn default() -> Self {
|
||||
Self::Ascending
|
||||
}
|
||||
}
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct AlterSortPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
/// Create a new filter if the filter_id is None
|
||||
#[pb(index = 4, one_of)]
|
||||
pub sort_id: Option<String>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub condition: GridSortConditionPB,
|
||||
}
|
||||
|
||||
impl TryInto<AlterSortParams> for AlterSortPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<AlterSortParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id)
|
||||
.map_err(|_| ErrorCode::GridViewIdIsEmpty)?
|
||||
.0;
|
||||
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
let sort_id = match self.sort_id {
|
||||
None => None,
|
||||
Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FilterIdIsEmpty)?.0),
|
||||
};
|
||||
|
||||
Ok(AlterSortParams {
|
||||
view_id,
|
||||
field_id,
|
||||
sort_id,
|
||||
field_type: self.field_type.into(),
|
||||
condition: self.condition as u8,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AlterSortParams {
|
||||
pub view_id: String,
|
||||
pub field_id: String,
|
||||
/// Create a new sort if the sort is None
|
||||
pub sort_id: Option<String>,
|
||||
pub field_type: FieldTypeRevision,
|
||||
pub condition: u8,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct DeleteSortPayloadPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub sort_id: String,
|
||||
}
|
||||
|
||||
impl TryInto<DeleteSortParams> for DeleteSortPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<DeleteSortParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id)
|
||||
.map_err(|_| ErrorCode::GridViewIdIsEmpty)?
|
||||
.0;
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
|
||||
let sort_id = NotEmptyStr::parse(self.sort_id)
|
||||
.map_err(|_| ErrorCode::UnexpectedEmptyString)?
|
||||
.0;
|
||||
|
||||
let sort_type = SortType {
|
||||
field_id,
|
||||
field_type: self.field_type,
|
||||
};
|
||||
|
||||
Ok(DeleteSortParams {
|
||||
view_id,
|
||||
sort_type,
|
||||
sort_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeleteSortParams {
|
||||
pub view_id: String,
|
||||
pub sort_type: SortType,
|
||||
pub sort_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf)]
|
||||
pub struct SortChangesetNotificationPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub insert_sorts: Vec<GridSortPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub delete_sorts: Vec<GridSortPB>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub update_sorts: Vec<GridSortPB>,
|
||||
}
|
64
frontend/rust-lib/flowy-grid/src/entities/view_entities.rs
Normal file
64
frontend/rust-lib/flowy-grid/src/entities/view_entities.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::entities::{InsertedRowPB, UpdatedRowPB};
|
||||
use flowy_derive::ProtoBuf;
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||
pub struct GridRowsVisibilityChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub visible_rows: Vec<InsertedRowPB>,
|
||||
|
||||
#[pb(index = 6)]
|
||||
pub invisible_rows: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf)]
|
||||
pub struct GridViewRowsChangesetPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub inserted_rows: Vec<InsertedRowPB>,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub deleted_rows: Vec<String>,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub updated_rows: Vec<UpdatedRowPB>,
|
||||
}
|
||||
|
||||
impl GridViewRowsChangesetPB {
|
||||
pub fn from_insert(view_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
inserted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_delete(view_id: String, deleted_rows: Vec<String>) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
deleted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_update(view_id: String, updated_rows: Vec<UpdatedRowPB>) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
updated_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_move(view_id: String, deleted_rows: Vec<String>, inserted_rows: Vec<InsertedRowPB>) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
inserted_rows,
|
||||
deleted_rows,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use crate::services::field::{
|
||||
SelectOptionCellChangesetParams, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPB,
|
||||
SelectOptionPB,
|
||||
};
|
||||
use crate::services::row::{make_block_pbs, make_row_from_row_rev};
|
||||
use crate::services::row::make_row_from_row_rev;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use grid_rev_model::FieldRevision;
|
||||
use lib_dispatch::prelude::{data_result, AFPluginData, AFPluginState, DataResult};
|
||||
@ -19,8 +19,8 @@ pub(crate) async fn get_grid_handler(
|
||||
manager: AFPluginState<Arc<GridManager>>,
|
||||
) -> DataResult<GridPB, FlowyError> {
|
||||
let grid_id: GridIdPB = data.into_inner();
|
||||
let editor = manager.open_grid(grid_id).await?;
|
||||
let grid = editor.get_grid().await?;
|
||||
let editor = manager.open_grid(grid_id.as_ref()).await?;
|
||||
let grid = editor.get_grid(grid_id.as_ref()).await?;
|
||||
data_result(grid)
|
||||
}
|
||||
|
||||
@ -58,6 +58,13 @@ pub(crate) async fn update_grid_setting_handler(
|
||||
if let Some(delete_filter) = params.delete_filter {
|
||||
let _ = editor.delete_filter(delete_filter).await?;
|
||||
}
|
||||
|
||||
if let Some(alter_sort) = params.alert_sort {
|
||||
let _ = editor.create_or_update_sort(alter_sort).await?;
|
||||
}
|
||||
if let Some(delete_sort) = params.delete_sort {
|
||||
let _ = editor.delete_sort(delete_sort).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -74,17 +81,6 @@ pub(crate) async fn get_all_filters_handler(
|
||||
data_result(filters)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub(crate) async fn get_grid_blocks_handler(
|
||||
data: AFPluginData<QueryBlocksPayloadPB>,
|
||||
manager: AFPluginState<Arc<GridManager>>,
|
||||
) -> DataResult<RepeatedBlockPB, FlowyError> {
|
||||
let params: QueryGridBlocksParams = data.into_inner().try_into()?;
|
||||
let editor = manager.get_grid_editor(¶ms.grid_id).await?;
|
||||
let blocks = editor.get_blocks(Some(params.block_ids)).await?;
|
||||
data_result(make_block_pbs(blocks))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn get_fields_handler(
|
||||
data: AFPluginData<GetFieldPayloadPB>,
|
||||
@ -415,14 +411,14 @@ pub(crate) async fn get_select_option_handler(
|
||||
//
|
||||
let cell_rev = editor.get_cell_rev(¶ms.row_id, ¶ms.field_id).await?;
|
||||
let type_option = select_type_option_from_field_rev(&field_rev)?;
|
||||
let any_cell_data: TypeCellData = match cell_rev {
|
||||
let type_cell_data: TypeCellData = match cell_rev {
|
||||
None => TypeCellData {
|
||||
data: "".to_string(),
|
||||
field_type: field_rev.ty.into(),
|
||||
},
|
||||
Some(cell_rev) => cell_rev.try_into()?,
|
||||
};
|
||||
let selected_options = type_option.get_selected_options(any_cell_data.into());
|
||||
let selected_options = type_option.get_selected_options(type_cell_data.into());
|
||||
data_result(selected_options)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ pub fn init(grid_manager: Arc<GridManager>) -> AFPlugin {
|
||||
let mut plugin = AFPlugin::new().name(env!("CARGO_PKG_NAME")).state(grid_manager);
|
||||
plugin = plugin
|
||||
.event(GridEvent::GetGrid, get_grid_handler)
|
||||
.event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
|
||||
// .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
|
||||
.event(GridEvent::GetGridSetting, get_grid_setting_handler)
|
||||
.event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
|
||||
.event(GridEvent::GetAllFilters, get_all_filters_handler)
|
||||
@ -59,13 +59,6 @@ pub enum GridEvent {
|
||||
#[event(input = "GridIdPB", output = "GridPB")]
|
||||
GetGrid = 0,
|
||||
|
||||
/// [GetGridBlocks] event is used to get the grid's block.
|
||||
///
|
||||
/// The event handler accepts a [QueryBlocksPayloadPB] and returns a [RepeatedBlockPB]
|
||||
/// if there are no errors.
|
||||
#[event(input = "QueryBlocksPayloadPB", output = "RepeatedBlockPB")]
|
||||
GetGridBlocks = 1,
|
||||
|
||||
/// [GetGridSetting] event is used to get the grid's settings.
|
||||
///
|
||||
/// The event handler accepts [GridIdPB] and return [GridSettingPB]
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::entities::RowPB;
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_http_model::revision::Revision;
|
||||
@ -114,6 +113,10 @@ impl GridBlockRevisionEditor {
|
||||
self.pad.read().await.index_of_row(row_id)
|
||||
}
|
||||
|
||||
pub async fn number_of_rows(&self) -> i32 {
|
||||
self.pad.read().await.rows.len() as i32
|
||||
}
|
||||
|
||||
pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<(usize, Arc<RowRevision>)>> {
|
||||
let row_rev = self.pad.read().await.get_row_rev(row_id);
|
||||
Ok(row_rev)
|
||||
@ -136,26 +139,6 @@ impl GridBlockRevisionEditor {
|
||||
Ok(cell_revs)
|
||||
}
|
||||
|
||||
pub async fn get_row_pb(&self, row_id: &str) -> FlowyResult<Option<RowPB>> {
|
||||
let row_ids = Some(vec![Cow::Borrowed(row_id)]);
|
||||
Ok(self.get_row_pbs(row_ids).await?.pop())
|
||||
}
|
||||
|
||||
pub async fn get_row_pbs<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<RowPB>>
|
||||
where
|
||||
T: AsRef<str> + ToOwned + ?Sized,
|
||||
{
|
||||
let row_infos = self
|
||||
.pad
|
||||
.read()
|
||||
.await
|
||||
.get_row_revs(row_ids)?
|
||||
.iter()
|
||||
.map(RowPB::from)
|
||||
.collect::<Vec<RowPB>>();
|
||||
Ok(row_infos)
|
||||
}
|
||||
|
||||
async fn modify<F>(&self, f: F) -> FlowyResult<()>
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut GridBlockRevisionPad) -> FlowyResult<Option<GridBlockRevisionChangeset>>,
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridDartNotification};
|
||||
use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB, UpdatedRowPB};
|
||||
use crate::entities::{CellChangesetPB, InsertedRowPB, UpdatedRowPB};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_editor::{GridBlockRevisionEditor, GridBlockRevisionMergeable};
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::persistence::rev_sqlite::{
|
||||
SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionSnapshotPersistence,
|
||||
};
|
||||
use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlock};
|
||||
use crate::services::row::{make_row_from_row_rev, GridBlockRow, GridBlockRowRevision};
|
||||
use dashmap::DashMap;
|
||||
use flowy_database::ConnectionPool;
|
||||
use flowy_error::FlowyResult;
|
||||
@ -15,12 +15,35 @@ use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowC
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GridBlockEvent {
|
||||
InsertRow {
|
||||
block_id: String,
|
||||
row: InsertedRowPB,
|
||||
},
|
||||
UpdateRow {
|
||||
block_id: String,
|
||||
row: UpdatedRowPB,
|
||||
},
|
||||
DeleteRow {
|
||||
block_id: String,
|
||||
row_id: String,
|
||||
},
|
||||
Move {
|
||||
block_id: String,
|
||||
deleted_row_id: String,
|
||||
inserted_row: InsertedRowPB,
|
||||
},
|
||||
}
|
||||
|
||||
type BlockId = String;
|
||||
pub(crate) struct GridBlockManager {
|
||||
user: Arc<dyn GridUser>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
|
||||
event_notifier: broadcast::Sender<GridBlockEvent>,
|
||||
}
|
||||
|
||||
impl GridBlockManager {
|
||||
@ -28,6 +51,7 @@ impl GridBlockManager {
|
||||
user: &Arc<dyn GridUser>,
|
||||
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
event_notifier: broadcast::Sender<GridBlockEvent>,
|
||||
) -> FlowyResult<Self> {
|
||||
let block_editors = make_block_editors(user, block_meta_revs).await?;
|
||||
let user = user.clone();
|
||||
@ -35,6 +59,7 @@ impl GridBlockManager {
|
||||
user,
|
||||
block_editors,
|
||||
persistence,
|
||||
event_notifier,
|
||||
};
|
||||
Ok(manager)
|
||||
}
|
||||
@ -70,12 +95,12 @@ impl GridBlockManager {
|
||||
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
||||
let editor = self.get_block_editor(&row_rev.block_id).await?;
|
||||
|
||||
let mut index_row_order = InsertedRowPB::from(&row_rev);
|
||||
let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
|
||||
index_row_order.index = row_index;
|
||||
let changeset = GridBlockChangesetPB::insert(block_id.clone(), vec![index_row_order]);
|
||||
let _ = self.notify_did_update_block(&block_id, changeset).await?;
|
||||
Ok(row_count)
|
||||
let mut row = InsertedRowPB::from(&row_rev);
|
||||
let (number_of_rows, index) = editor.create_row(row_rev, start_row_id).await?;
|
||||
row.index = index;
|
||||
|
||||
let _ = self.event_notifier.send(GridBlockEvent::InsertRow { block_id, row });
|
||||
Ok(number_of_rows)
|
||||
}
|
||||
|
||||
pub(crate) async fn insert_row(
|
||||
@ -84,28 +109,20 @@ impl GridBlockManager {
|
||||
) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
||||
let mut changesets = vec![];
|
||||
for (block_id, row_revs) in rows_by_block_id {
|
||||
let mut inserted_row_orders = vec![];
|
||||
let editor = self.get_block_editor(&block_id).await?;
|
||||
let mut row_count = 0;
|
||||
for row in row_revs {
|
||||
let _ = self.persistence.insert(&row.block_id, &row.id)?;
|
||||
let mut row_order = InsertedRowPB::from(&row);
|
||||
let (count, index) = editor.create_row(row, None).await?;
|
||||
row_count = count;
|
||||
row_order.index = index;
|
||||
inserted_row_orders.push(row_order);
|
||||
for row_rev in row_revs {
|
||||
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
||||
let mut row = InsertedRowPB::from(&row_rev);
|
||||
row.index = editor.create_row(row_rev, None).await?.1;
|
||||
let _ = self.event_notifier.send(GridBlockEvent::InsertRow {
|
||||
block_id: block_id.clone(),
|
||||
row,
|
||||
});
|
||||
}
|
||||
changesets.push(GridBlockMetaRevisionChangeset::from_row_count(
|
||||
block_id.clone(),
|
||||
row_count,
|
||||
editor.number_of_rows().await,
|
||||
));
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_block(
|
||||
&block_id,
|
||||
GridBlockChangesetPB::insert(block_id.clone(), inserted_row_orders),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(changesets)
|
||||
@ -118,14 +135,15 @@ impl GridBlockManager {
|
||||
None => tracing::error!("Update row failed, can't find the row with id: {}", changeset.row_id),
|
||||
Some((_, row_rev)) => {
|
||||
let changed_field_ids = changeset.cell_by_field_id.keys().cloned().collect::<Vec<String>>();
|
||||
let updated_row = UpdatedRowPB {
|
||||
let row = UpdatedRowPB {
|
||||
row: make_row_from_row_rev(row_rev),
|
||||
field_ids: changed_field_ids,
|
||||
};
|
||||
let block_order_changeset = GridBlockChangesetPB::update(&editor.block_id, vec![updated_row]);
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, block_order_changeset)
|
||||
.await?;
|
||||
|
||||
let _ = self.event_notifier.send(GridBlockEvent::UpdateRow {
|
||||
block_id: editor.block_id.clone(),
|
||||
row,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -140,28 +158,30 @@ impl GridBlockManager {
|
||||
None => Ok(None),
|
||||
Some((_, row_rev)) => {
|
||||
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
|
||||
let _ = self
|
||||
.notify_did_update_block(
|
||||
&block_id,
|
||||
GridBlockChangesetPB::delete(&block_id, vec![row_rev.id.clone()]),
|
||||
)
|
||||
.await?;
|
||||
let _ = self.event_notifier.send(GridBlockEvent::DeleteRow {
|
||||
block_id: editor.block_id.clone(),
|
||||
row_id: row_rev.id.clone(),
|
||||
});
|
||||
|
||||
Ok(Some(row_rev))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
||||
pub(crate) async fn delete_rows(
|
||||
&self,
|
||||
block_rows: Vec<GridBlockRow>,
|
||||
) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
|
||||
let mut changesets = vec![];
|
||||
for grid_block in block_from_row_orders(row_orders) {
|
||||
let editor = self.get_block_editor(&grid_block.id).await?;
|
||||
let row_ids = grid_block
|
||||
.rows
|
||||
for block_row in block_rows {
|
||||
let editor = self.get_block_editor(&block_row.block_id).await?;
|
||||
let row_ids = block_row
|
||||
.row_ids
|
||||
.into_iter()
|
||||
.map(|row_info| Cow::Owned(row_info.row_id().to_owned()))
|
||||
.map(Cow::Owned)
|
||||
.collect::<Vec<Cow<String>>>();
|
||||
let row_count = editor.delete_rows(row_ids).await?;
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(grid_block.id.clone(), row_count);
|
||||
let changeset = GridBlockMetaRevisionChangeset::from_row_count(block_row.block_id, row_count);
|
||||
changesets.push(changeset);
|
||||
}
|
||||
|
||||
@ -179,16 +199,11 @@ impl GridBlockManager {
|
||||
is_new: false,
|
||||
};
|
||||
|
||||
let notified_changeset = GridBlockChangesetPB {
|
||||
let _ = self.event_notifier.send(GridBlockEvent::Move {
|
||||
block_id: editor.block_id.clone(),
|
||||
inserted_rows: vec![insert_row],
|
||||
deleted_rows: vec![delete_row_id],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _ = self
|
||||
.notify_did_update_block(&editor.block_id, notified_changeset)
|
||||
.await?;
|
||||
deleted_row_id: delete_row_id,
|
||||
inserted_row: insert_row,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -213,12 +228,13 @@ impl GridBlockManager {
|
||||
editor.get_row_rev(row_id).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_row_revs(&self, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let editor = self.get_block_editor(block_id).await?;
|
||||
editor.get_row_revs::<&str>(None).await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlock>> {
|
||||
pub(crate) async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockRowRevision>> {
|
||||
let mut blocks = vec![];
|
||||
match block_ids {
|
||||
None => {
|
||||
@ -226,27 +242,20 @@ impl GridBlockManager {
|
||||
let editor = iter.value();
|
||||
let block_id = editor.block_id.clone();
|
||||
let row_revs = editor.get_row_revs::<&str>(None).await?;
|
||||
blocks.push(GridBlock { block_id, row_revs });
|
||||
blocks.push(GridBlockRowRevision { block_id, row_revs });
|
||||
}
|
||||
}
|
||||
Some(block_ids) => {
|
||||
for block_id in block_ids {
|
||||
let editor = self.get_block_editor(&block_id).await?;
|
||||
let row_revs = editor.get_row_revs::<&str>(None).await?;
|
||||
blocks.push(GridBlock { block_id, row_revs });
|
||||
blocks.push(GridBlockRowRevision { block_id, row_revs });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
async fn notify_did_update_block(&self, block_id: &str, changeset: GridBlockChangesetPB) -> FlowyResult<()> {
|
||||
send_dart_notification(block_id, GridDartNotification::DidUpdateGridBlock)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify_did_update_cell(&self, changeset: CellChangesetPB) -> FlowyResult<()> {
|
||||
let id = format!("{}:{}", changeset.row_id, changeset.field_id);
|
||||
send_dart_notification(&id, GridDartNotification::DidUpdateCell).send();
|
||||
|
@ -1,25 +1,32 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellBytes, TypeCellData};
|
||||
use crate::services::field::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use grid_rev_model::{CellRevision, FieldRevision, FieldTypeRevision};
|
||||
|
||||
/// This trait is used when doing filter/search on the grid.
|
||||
pub trait CellFilterOperation<T> {
|
||||
/// Return true if any_cell_data match the filter condition.
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &T) -> FlowyResult<bool>;
|
||||
pub trait CellFilterable<T> {
|
||||
/// Return true if type_cell_data match the filter condition.
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &T) -> FlowyResult<bool>;
|
||||
}
|
||||
|
||||
pub trait CellGroupOperation {
|
||||
fn apply_group(&self, any_cell_data: TypeCellData, group_content: &str) -> FlowyResult<bool>;
|
||||
pub trait CellComparable {
|
||||
fn apply_cmp(&self, type_cell_data: &TypeCellData, other_type_cell_data: &TypeCellData) -> FlowyResult<Ordering>;
|
||||
}
|
||||
|
||||
/// Return object that describes the cell.
|
||||
pub trait CellDisplayable<CD> {
|
||||
/// Serialize the cell data in Protobuf/String format.
|
||||
///
|
||||
/// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
|
||||
/// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
|
||||
/// list of option ids. So it need to be decoded including convert the option's id to
|
||||
/// option's name
|
||||
///
|
||||
pub trait CellDataSerialize<CD> {
|
||||
/// Serialize the cell data into `CellBytes` that will be posted to the `Dart` side. Using the
|
||||
/// corresponding protobuf struct implement in `Dart` to deserialize the data.
|
||||
/// corresponding protobuf struct implemented in `Dart` to deserialize the data.
|
||||
///
|
||||
/// Using `utf8` to encode the cell data if the cell data use `String` as its data container.
|
||||
/// Using `protobuf` to encode the cell data if the cell data use `Protobuf struct` as its data container.
|
||||
@ -43,9 +50,9 @@ pub trait CellDisplayable<CD> {
|
||||
///
|
||||
/// returns: Result<CellBytes, FlowyError>
|
||||
///
|
||||
fn displayed_cell_bytes(
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<CD>,
|
||||
cell_data: IntoCellData<CD>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes>;
|
||||
@ -55,14 +62,14 @@ pub trait CellDisplayable<CD> {
|
||||
/// The cell data is not readable which means it can't display the cell data directly to user.
|
||||
/// For example,
|
||||
/// 1. the cell data is timestamp if its field type is FieldType::Date that is not readable.
|
||||
/// It needs to be parsed as the date string.
|
||||
/// So it needs to be parsed as the date string with custom format setting.
|
||||
///
|
||||
/// 2. the cell data is a commas separated id if its field type if FieldType::MultiSelect that is not readable.
|
||||
/// It needs to be parsed as a commas separated option name.
|
||||
/// So it needs to be parsed as a commas separated option name.
|
||||
///
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<CD>,
|
||||
cell_data: IntoCellData<CD>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String>;
|
||||
@ -76,7 +83,10 @@ pub trait CellDataOperation<CD, CS> {
|
||||
/// FieldType::URL => URLCellData
|
||||
/// FieldType::Date=> DateCellData
|
||||
///
|
||||
/// Each cell data is a opaque data, it needs to deserialized to a concrete data struct
|
||||
/// Each cell data is a opaque data, it needs to deserialized to a concrete data struct.
|
||||
/// Essentially when the field type is SingleSelect/Multi-Select, the cell data contains a
|
||||
/// list of option ids. So it need to be decoded including convert the option's id to
|
||||
/// option's name
|
||||
///
|
||||
/// `cell_data`: the opaque data of the cell.
|
||||
/// `decoded_field_type`: the field type of the cell data when doing serialization
|
||||
@ -86,7 +96,7 @@ pub trait CellDataOperation<CD, CS> {
|
||||
///
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<CD>,
|
||||
cell_data: IntoCellData<CD>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes>;
|
||||
@ -130,14 +140,14 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
|
||||
Ok(TypeCellData::new(s, field_type).to_json())
|
||||
}
|
||||
|
||||
pub fn decode_any_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
|
||||
pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
|
||||
data: T,
|
||||
field_rev: &FieldRevision,
|
||||
) -> (FieldType, CellBytes) {
|
||||
let to_field_type = field_rev.ty.into();
|
||||
match data.try_into() {
|
||||
Ok(any_cell_data) => {
|
||||
let TypeCellData { data, field_type } = any_cell_data;
|
||||
Ok(type_cell_data) => {
|
||||
let TypeCellData { data, field_type } = type_cell_data;
|
||||
match try_decode_cell_data(data.into(), &field_type, &to_field_type, field_rev) {
|
||||
Ok(cell_bytes) => (field_type, cell_bytes),
|
||||
Err(e) => {
|
||||
@ -156,40 +166,40 @@ pub fn decode_any_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_cell_data_to_string(
|
||||
cell_data: CellData<String>,
|
||||
pub fn decode_cell_data_to_string<C: Into<IntoCellData<String>>>(
|
||||
cell_data: C,
|
||||
from_field_type: &FieldType,
|
||||
to_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
let cell_data = cell_data.try_into_inner()?;
|
||||
let cell_data = cell_data.into().try_into_inner()?;
|
||||
let get_cell_display_str = || {
|
||||
let field_type: FieldTypeRevision = to_field_type.into();
|
||||
let result = match to_field_type {
|
||||
FieldType::RichText => field_rev
|
||||
.get_type_option::<RichTextTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::Number => field_rev
|
||||
.get_type_option::<NumberTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::DateTime => field_rev
|
||||
.get_type_option::<DateTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::SingleSelect => field_rev
|
||||
.get_type_option::<SingleSelectTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::MultiSelect => field_rev
|
||||
.get_type_option::<MultiSelectTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::Checklist => field_rev
|
||||
.get_type_option::<ChecklistTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::Checkbox => field_rev
|
||||
.get_type_option::<CheckboxTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
FieldType::URL => field_rev
|
||||
.get_type_option::<URLTypeOptionPB>(field_type)?
|
||||
.displayed_cell_string(cell_data.into(), from_field_type, field_rev),
|
||||
.serialize_cell_data_to_str(cell_data.into(), from_field_type, field_rev),
|
||||
};
|
||||
Some(result)
|
||||
};
|
||||
@ -210,7 +220,7 @@ pub fn decode_cell_data_to_string(
|
||||
/// and `CellDataOperation` traits.
|
||||
///
|
||||
pub fn try_decode_cell_data(
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<String>,
|
||||
from_field_type: &FieldType,
|
||||
to_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
@ -312,9 +322,10 @@ pub trait FromCellString {
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// CellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
|
||||
pub struct CellData<T>(pub Option<T>);
|
||||
impl<T> CellData<T> {
|
||||
/// IntoCellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
|
||||
///
|
||||
pub struct IntoCellData<T>(pub Option<T>);
|
||||
impl<T> IntoCellData<T> {
|
||||
pub fn try_into_inner(self) -> FlowyResult<T> {
|
||||
match self.0 {
|
||||
None => Err(ErrorCode::InvalidData.into()),
|
||||
@ -323,35 +334,35 @@ impl<T> CellData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<String> for CellData<T>
|
||||
impl<T> std::convert::From<String> for IntoCellData<T>
|
||||
where
|
||||
T: FromCellString,
|
||||
{
|
||||
fn from(s: String) -> Self {
|
||||
match T::from_cell_str(&s) {
|
||||
Ok(inner) => CellData(Some(inner)),
|
||||
Ok(inner) => IntoCellData(Some(inner)),
|
||||
Err(e) => {
|
||||
tracing::error!("Deserialize Cell Data failed: {}", e);
|
||||
CellData(None)
|
||||
IntoCellData(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<usize> for CellData<String> {
|
||||
impl std::convert::From<usize> for IntoCellData<String> {
|
||||
fn from(n: usize) -> Self {
|
||||
CellData(Some(n.to_string()))
|
||||
IntoCellData(Some(n.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<T> for CellData<T> {
|
||||
impl<T> std::convert::From<T> for IntoCellData<T> {
|
||||
fn from(val: T) -> Self {
|
||||
CellData(Some(val))
|
||||
IntoCellData(Some(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<CellData<String>> for String {
|
||||
fn from(p: CellData<String>) -> Self {
|
||||
impl std::convert::From<IntoCellData<String>> for String {
|
||||
fn from(p: IntoCellData<String>) -> Self {
|
||||
p.try_into_inner().unwrap_or_else(|_| String::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
mod any_cell_data;
|
||||
mod cell_operation;
|
||||
mod type_cell_data;
|
||||
|
||||
pub use any_cell_data::*;
|
||||
pub use cell_operation::*;
|
||||
pub use type_cell_data::*;
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellData, FromCellString};
|
||||
use crate::services::cell::{FromCellString, IntoCellData};
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use grid_rev_model::CellRevision;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// TypeCellData is a generic CellData, you can parse the cell_data according to the field_type.
|
||||
/// When the type of field is changed, it's different from the field_type of AnyCellData.
|
||||
/// TypeCellData is a generic CellData, you can parse the type_cell_data according to the field_type.
|
||||
/// When the type of field is changed, it's different from the field_type of TypeCellData.
|
||||
/// So it will return an empty data. You could check the CellDataOperation trait for more information.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TypeCellData {
|
||||
@ -60,12 +60,12 @@ impl std::convert::TryFrom<CellRevision> for TypeCellData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::From<TypeCellData> for CellData<T>
|
||||
impl<T> std::convert::From<TypeCellData> for IntoCellData<T>
|
||||
where
|
||||
T: FromCellString,
|
||||
{
|
||||
fn from(any_call_data: TypeCellData) -> Self {
|
||||
CellData::from(any_call_data.data)
|
||||
IntoCellData::from(any_call_data.data)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{CheckboxFilterCondition, CheckboxFilterPB};
|
||||
use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
|
||||
use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB};
|
||||
use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
|
||||
use crate::services::field::{CheckboxCellData, CheckboxTypeOptionPB};
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
@ -7,18 +7,18 @@ impl CheckboxFilterPB {
|
||||
pub fn is_visible(&self, cell_data: &CheckboxCellData) -> bool {
|
||||
let is_check = cell_data.is_check();
|
||||
match self.condition {
|
||||
CheckboxFilterCondition::IsChecked => is_check,
|
||||
CheckboxFilterCondition::IsUnChecked => !is_check,
|
||||
CheckboxFilterConditionPB::IsChecked => is_check,
|
||||
CheckboxFilterConditionPB::IsUnChecked => !is_check,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<CheckboxFilterPB> for CheckboxTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &CheckboxFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_checkbox() {
|
||||
impl CellFilterable<CheckboxFilterPB> for CheckboxTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &CheckboxFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_checkbox() {
|
||||
return Ok(true);
|
||||
}
|
||||
let cell_data: CellData<CheckboxCellData> = any_cell_data.into();
|
||||
let cell_data: IntoCellData<CheckboxCellData> = type_cell_data.into();
|
||||
let checkbox_cell_data = cell_data.try_into_inner()?;
|
||||
Ok(filter.is_visible(&checkbox_cell_data))
|
||||
}
|
||||
@ -26,14 +26,14 @@ impl CellFilterOperation<CheckboxFilterPB> for CheckboxTypeOptionPB {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entities::{CheckboxFilterCondition, CheckboxFilterPB};
|
||||
use crate::entities::{CheckboxFilterConditionPB, CheckboxFilterPB};
|
||||
use crate::services::field::CheckboxCellData;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn checkbox_filter_is_check_test() {
|
||||
let checkbox_filter = CheckboxFilterPB {
|
||||
condition: CheckboxFilterCondition::IsChecked,
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
};
|
||||
for (value, visible) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
|
||||
let data = CheckboxCellData::from_str(value).unwrap();
|
||||
@ -44,7 +44,7 @@ mod tests {
|
||||
#[test]
|
||||
fn checkbox_filter_is_uncheck_test() {
|
||||
let checkbox_filter = CheckboxFilterPB {
|
||||
condition: CheckboxFilterCondition::IsUnChecked,
|
||||
condition: CheckboxFilterConditionPB::IsUnChecked,
|
||||
};
|
||||
for (value, visible) in [("false", true), ("no", true), ("true", false), ("yes", false)] {
|
||||
let data = CheckboxCellData::from_str(value).unwrap();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::{BoxTypeOptionBuilder, CheckboxCellData, TypeOptionBuilder};
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
@ -42,10 +42,10 @@ pub struct CheckboxTypeOptionPB {
|
||||
}
|
||||
impl_type_option!(CheckboxTypeOptionPB, FieldType::Checkbox);
|
||||
|
||||
impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
|
||||
fn displayed_cell_bytes(
|
||||
impl CellDataSerialize<CheckboxCellData> for CheckboxTypeOptionPB {
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<CheckboxCellData>,
|
||||
cell_data: IntoCellData<CheckboxCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -53,9 +53,9 @@ impl CellDisplayable<CheckboxCellData> for CheckboxTypeOptionPB {
|
||||
Ok(CellBytes::new(cell_data))
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<CheckboxCellData>,
|
||||
cell_data: IntoCellData<CheckboxCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
@ -69,7 +69,7 @@ pub type CheckboxCellChangeset = String;
|
||||
impl CellDataOperation<CheckboxCellData, CheckboxCellChangeset> for CheckboxTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<CheckboxCellData>,
|
||||
cell_data: IntoCellData<CheckboxCellData>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -77,7 +77,7 @@ impl CellDataOperation<CheckboxCellData, CheckboxCellChangeset> for CheckboxType
|
||||
return Ok(CellBytes::default());
|
||||
}
|
||||
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -73,7 +73,7 @@ impl CellBytesParser for CheckboxCellDataParser {
|
||||
type Object = CheckboxCellData;
|
||||
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
|
||||
match String::from_utf8(bytes.to_vec()) {
|
||||
Ok(s) => CheckboxCellData::from_str(&s),
|
||||
Ok(s) => CheckboxCellData::from_cell_str(&s),
|
||||
Err(_) => Ok(CheckboxCellData("".to_string())),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{DateFilterCondition, DateFilterPB};
|
||||
use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
|
||||
use crate::entities::{DateFilterConditionPB, DateFilterPB};
|
||||
use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
|
||||
use crate::services::field::{DateTimestamp, DateTypeOptionPB};
|
||||
use chrono::NaiveDateTime;
|
||||
use flowy_error::FlowyResult;
|
||||
@ -7,13 +7,13 @@ use flowy_error::FlowyResult;
|
||||
impl DateFilterPB {
|
||||
pub fn is_visible<T: Into<Option<i64>>>(&self, cell_timestamp: T) -> bool {
|
||||
match cell_timestamp.into() {
|
||||
None => DateFilterCondition::DateIsEmpty == self.condition,
|
||||
None => DateFilterConditionPB::DateIsEmpty == self.condition,
|
||||
Some(timestamp) => {
|
||||
match self.condition {
|
||||
DateFilterCondition::DateIsNotEmpty => {
|
||||
DateFilterConditionPB::DateIsNotEmpty => {
|
||||
return true;
|
||||
}
|
||||
DateFilterCondition::DateIsEmpty => {
|
||||
DateFilterConditionPB::DateIsEmpty => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
@ -45,11 +45,11 @@ impl DateFilterPB {
|
||||
|
||||
// We assume that the cell_timestamp doesn't contain hours, just day.
|
||||
match self.condition {
|
||||
DateFilterCondition::DateIs => cell_date == expected_date,
|
||||
DateFilterCondition::DateBefore => cell_date < expected_date,
|
||||
DateFilterCondition::DateAfter => cell_date > expected_date,
|
||||
DateFilterCondition::DateOnOrBefore => cell_date <= expected_date,
|
||||
DateFilterCondition::DateOnOrAfter => cell_date >= expected_date,
|
||||
DateFilterConditionPB::DateIs => cell_date == expected_date,
|
||||
DateFilterConditionPB::DateBefore => cell_date < expected_date,
|
||||
DateFilterConditionPB::DateAfter => cell_date > expected_date,
|
||||
DateFilterConditionPB::DateOnOrBefore => cell_date <= expected_date,
|
||||
DateFilterConditionPB::DateOnOrAfter => cell_date >= expected_date,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -59,12 +59,12 @@ impl DateFilterPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<DateFilterPB> for DateTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &DateFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_date() {
|
||||
impl CellFilterable<DateFilterPB> for DateTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &DateFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_date() {
|
||||
return Ok(true);
|
||||
}
|
||||
let cell_data: CellData<DateTimestamp> = any_cell_data.into();
|
||||
let cell_data: IntoCellData<DateTimestamp> = type_cell_data.into();
|
||||
let timestamp = cell_data.try_into_inner()?;
|
||||
Ok(filter.is_visible(timestamp))
|
||||
}
|
||||
@ -73,12 +73,12 @@ impl CellFilterOperation<DateFilterPB> for DateTypeOptionPB {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::all)]
|
||||
use crate::entities::{DateFilterCondition, DateFilterPB};
|
||||
use crate::entities::{DateFilterConditionPB, DateFilterPB};
|
||||
|
||||
#[test]
|
||||
fn date_filter_is_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateIs,
|
||||
condition: DateFilterConditionPB::DateIs,
|
||||
timestamp: Some(1668387885),
|
||||
end: None,
|
||||
start: None,
|
||||
@ -91,7 +91,7 @@ mod tests {
|
||||
#[test]
|
||||
fn date_filter_before_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateBefore,
|
||||
condition: DateFilterConditionPB::DateBefore,
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
@ -105,7 +105,7 @@ mod tests {
|
||||
#[test]
|
||||
fn date_filter_before_or_on_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateOnOrBefore,
|
||||
condition: DateFilterConditionPB::DateOnOrBefore,
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
@ -118,7 +118,7 @@ mod tests {
|
||||
#[test]
|
||||
fn date_filter_after_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateAfter,
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
@ -132,7 +132,7 @@ mod tests {
|
||||
#[test]
|
||||
fn date_filter_within_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateWithIn,
|
||||
condition: DateFilterConditionPB::DateWithIn,
|
||||
start: Some(1668272685), // 11/13
|
||||
end: Some(1668618285), // 11/17
|
||||
timestamp: None,
|
||||
@ -150,7 +150,7 @@ mod tests {
|
||||
#[test]
|
||||
fn date_filter_is_empty_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateIsEmpty,
|
||||
condition: DateFilterConditionPB::DateIsEmpty,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: None,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::{
|
||||
BoxTypeOptionBuilder, DateCellChangeset, DateCellDataPB, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
|
||||
};
|
||||
@ -107,21 +107,21 @@ impl DateTypeOptionPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
|
||||
fn displayed_cell_bytes(
|
||||
impl CellDataSerialize<DateTimestamp> for DateTypeOptionPB {
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<DateTimestamp>,
|
||||
cell_data: IntoCellData<DateTimestamp>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
let timestamp = cell_data.try_into_inner()?;
|
||||
let date_cell_data = self.today_desc_from_timestamp(timestamp);
|
||||
CellBytes::from(date_cell_data)
|
||||
let cell_data_pb = self.today_desc_from_timestamp(timestamp);
|
||||
CellBytes::from(cell_data_pb)
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<DateTimestamp>,
|
||||
cell_data: IntoCellData<DateTimestamp>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
@ -134,7 +134,7 @@ impl CellDisplayable<DateTimestamp> for DateTypeOptionPB {
|
||||
impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<DateTimestamp>,
|
||||
cell_data: IntoCellData<DateTimestamp>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -145,7 +145,7 @@ impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOptionPB {
|
||||
if !decoded_field_type.is_date() {
|
||||
return Ok(CellBytes::default());
|
||||
}
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{NumberFilterCondition, NumberFilterPB};
|
||||
use crate::services::cell::{CellFilterOperation, TypeCellData};
|
||||
use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
|
||||
use crate::services::cell::{CellFilterable, TypeCellData};
|
||||
use crate::services::field::{NumberCellData, NumberTypeOptionPB};
|
||||
use flowy_error::FlowyResult;
|
||||
use rust_decimal::prelude::Zero;
|
||||
@ -10,10 +10,10 @@ impl NumberFilterPB {
|
||||
pub fn is_visible(&self, num_cell_data: &NumberCellData) -> bool {
|
||||
if self.content.is_empty() {
|
||||
match self.condition {
|
||||
NumberFilterCondition::NumberIsEmpty => {
|
||||
NumberFilterConditionPB::NumberIsEmpty => {
|
||||
return num_cell_data.is_empty();
|
||||
}
|
||||
NumberFilterCondition::NumberIsNotEmpty => {
|
||||
NumberFilterConditionPB::NumberIsNotEmpty => {
|
||||
return !num_cell_data.is_empty();
|
||||
}
|
||||
_ => {}
|
||||
@ -24,12 +24,12 @@ impl NumberFilterPB {
|
||||
Some(cell_decimal) => {
|
||||
let decimal = Decimal::from_str(&self.content).unwrap_or_else(|_| Decimal::zero());
|
||||
match self.condition {
|
||||
NumberFilterCondition::Equal => cell_decimal == &decimal,
|
||||
NumberFilterCondition::NotEqual => cell_decimal != &decimal,
|
||||
NumberFilterCondition::GreaterThan => cell_decimal > &decimal,
|
||||
NumberFilterCondition::LessThan => cell_decimal < &decimal,
|
||||
NumberFilterCondition::GreaterThanOrEqualTo => cell_decimal >= &decimal,
|
||||
NumberFilterCondition::LessThanOrEqualTo => cell_decimal <= &decimal,
|
||||
NumberFilterConditionPB::Equal => cell_decimal == &decimal,
|
||||
NumberFilterConditionPB::NotEqual => cell_decimal != &decimal,
|
||||
NumberFilterConditionPB::GreaterThan => cell_decimal > &decimal,
|
||||
NumberFilterConditionPB::LessThan => cell_decimal < &decimal,
|
||||
NumberFilterConditionPB::GreaterThanOrEqualTo => cell_decimal >= &decimal,
|
||||
NumberFilterConditionPB::LessThanOrEqualTo => cell_decimal <= &decimal,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
@ -37,13 +37,13 @@ impl NumberFilterPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<NumberFilterPB> for NumberTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &NumberFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_number() {
|
||||
impl CellFilterable<NumberFilterPB> for NumberTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &NumberFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_number() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let cell_data = any_cell_data.data;
|
||||
let cell_data = type_cell_data.data;
|
||||
let num_cell_data = self.format_cell_data(&cell_data)?;
|
||||
|
||||
Ok(filter.is_visible(&num_cell_data))
|
||||
@ -52,12 +52,12 @@ impl CellFilterOperation<NumberFilterPB> for NumberTypeOptionPB {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entities::{NumberFilterCondition, NumberFilterPB};
|
||||
use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
|
||||
use crate::services::field::{NumberCellData, NumberFormat};
|
||||
#[test]
|
||||
fn number_filter_equal_test() {
|
||||
let number_filter = NumberFilterPB {
|
||||
condition: NumberFilterCondition::Equal,
|
||||
condition: NumberFilterConditionPB::Equal,
|
||||
content: "123".to_owned(),
|
||||
};
|
||||
|
||||
@ -75,7 +75,7 @@ mod tests {
|
||||
#[test]
|
||||
fn number_filter_greater_than_test() {
|
||||
let number_filter = NumberFilterPB {
|
||||
condition: NumberFilterCondition::GreaterThan,
|
||||
condition: NumberFilterConditionPB::GreaterThan,
|
||||
content: "12".to_owned(),
|
||||
};
|
||||
for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
|
||||
@ -87,7 +87,7 @@ mod tests {
|
||||
#[test]
|
||||
fn number_filter_less_than_test() {
|
||||
let number_filter = NumberFilterPB {
|
||||
condition: NumberFilterCondition::LessThan,
|
||||
condition: NumberFilterConditionPB::LessThan,
|
||||
content: "100".to_owned(),
|
||||
};
|
||||
for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", false)] {
|
||||
|
@ -1,15 +1,13 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::type_options::number_type_option::format::*;
|
||||
use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
|
||||
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -105,10 +103,10 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
impl CellDisplayable<String> for NumberTypeOptionPB {
|
||||
fn displayed_cell_bytes(
|
||||
impl CellDataSerialize<String> for NumberTypeOptionPB {
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<String>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -119,9 +117,9 @@ impl CellDisplayable<String> for NumberTypeOptionPB {
|
||||
}
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<String>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
@ -135,7 +133,7 @@ pub type NumberCellChangeset = String;
|
||||
impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<String>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -143,7 +141,7 @@ impl CellDataOperation<String, NumberCellChangeset> for NumberTypeOptionPB {
|
||||
return Ok(CellBytes::default());
|
||||
}
|
||||
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -99,6 +99,7 @@ impl CellDataIsEmpty for NumberCellData {
|
||||
self.decimal.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NumberCellDataParser();
|
||||
impl CellBytesParser for NumberCellDataParser {
|
||||
type Object = NumberCellData;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::entities::{ChecklistFilterCondition, ChecklistFilterPB};
|
||||
use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB};
|
||||
use crate::services::field::{SelectOptionPB, SelectedSelectOptions};
|
||||
|
||||
impl ChecklistFilterPB {
|
||||
@ -15,7 +15,7 @@ impl ChecklistFilterPB {
|
||||
.collect::<Vec<&str>>();
|
||||
|
||||
match self.condition {
|
||||
ChecklistFilterCondition::IsComplete => {
|
||||
ChecklistFilterConditionPB::IsComplete => {
|
||||
if selected_option_ids.is_empty() {
|
||||
return false;
|
||||
}
|
||||
@ -23,7 +23,7 @@ impl ChecklistFilterPB {
|
||||
all_option_ids.retain(|option_id| !selected_option_ids.contains(option_id));
|
||||
all_option_ids.is_empty()
|
||||
}
|
||||
ChecklistFilterCondition::IsIncomplete => {
|
||||
ChecklistFilterConditionPB::IsIncomplete => {
|
||||
if selected_option_ids.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
|
||||
use crate::services::field::type_options::util::get_cell_data;
|
||||
use crate::services::field::{
|
||||
@ -41,11 +41,11 @@ impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB {
|
||||
impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for ChecklistTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
|
||||
use crate::services::field::type_options::util::get_cell_data;
|
||||
use crate::services::field::{
|
||||
@ -41,11 +41,11 @@ impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
|
||||
impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSelectTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![allow(clippy::needless_collect)]
|
||||
|
||||
use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionCondition, SelectOptionFilterPB};
|
||||
use crate::services::cell::{CellFilterOperation, TypeCellData};
|
||||
use crate::entities::{ChecklistFilterPB, FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
|
||||
use crate::services::cell::{CellFilterable, TypeCellData};
|
||||
use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
|
||||
use crate::services::field::{SelectTypeOptionSharedAction, SelectedSelectOptions};
|
||||
use flowy_error::FlowyResult;
|
||||
@ -10,7 +10,7 @@ impl SelectOptionFilterPB {
|
||||
pub fn is_visible(&self, selected_options: &SelectedSelectOptions, field_type: FieldType) -> bool {
|
||||
let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
|
||||
match self.condition {
|
||||
SelectOptionCondition::OptionIs => match field_type {
|
||||
SelectOptionConditionPB::OptionIs => match field_type {
|
||||
FieldType::SingleSelect => {
|
||||
if self.option_ids.is_empty() {
|
||||
return true;
|
||||
@ -43,7 +43,7 @@ impl SelectOptionFilterPB {
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
SelectOptionCondition::OptionIsNot => match field_type {
|
||||
SelectOptionConditionPB::OptionIsNot => match field_type {
|
||||
FieldType::SingleSelect => {
|
||||
if self.option_ids.is_empty() {
|
||||
return true;
|
||||
@ -72,39 +72,39 @@ impl SelectOptionFilterPB {
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
|
||||
SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
|
||||
SelectOptionConditionPB::OptionIsEmpty => selected_option_ids.is_empty(),
|
||||
SelectOptionConditionPB::OptionIsNotEmpty => !selected_option_ids.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<SelectOptionFilterPB> for MultiSelectTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_multi_select() {
|
||||
impl CellFilterable<SelectOptionFilterPB> for MultiSelectTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_multi_select() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
|
||||
Ok(filter.is_visible(&selected_options, FieldType::MultiSelect))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<SelectOptionFilterPB> for SingleSelectTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_single_select() {
|
||||
impl CellFilterable<SelectOptionFilterPB> for SingleSelectTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &SelectOptionFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_single_select() {
|
||||
return Ok(true);
|
||||
}
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
|
||||
Ok(filter.is_visible(&selected_options, FieldType::SingleSelect))
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<ChecklistFilterPB> for ChecklistTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_checklist() {
|
||||
impl CellFilterable<ChecklistFilterPB> for ChecklistTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_checklist() {
|
||||
return Ok(true);
|
||||
}
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
|
||||
let selected_options = SelectedSelectOptions::from(self.get_selected_options(type_cell_data.into()));
|
||||
Ok(filter.is_visible(&self.options, &selected_options))
|
||||
}
|
||||
}
|
||||
@ -112,14 +112,14 @@ impl CellFilterOperation<ChecklistFilterPB> for ChecklistTypeOptionPB {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::all)]
|
||||
use crate::entities::{FieldType, SelectOptionCondition, SelectOptionFilterPB};
|
||||
use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
|
||||
use crate::services::field::selection_type_option::{SelectOptionPB, SelectedSelectOptions};
|
||||
|
||||
#[test]
|
||||
fn select_option_filter_is_empty_test() {
|
||||
let option = SelectOptionPB::new("A");
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIsEmpty,
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
};
|
||||
|
||||
@ -152,7 +152,7 @@ mod tests {
|
||||
let option_1 = SelectOptionPB::new("A");
|
||||
let option_2 = SelectOptionPB::new("B");
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIsNotEmpty,
|
||||
condition: SelectOptionConditionPB::OptionIsNotEmpty,
|
||||
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
|
||||
};
|
||||
|
||||
@ -191,7 +191,7 @@ mod tests {
|
||||
let option_2 = SelectOptionPB::new("B");
|
||||
let option_3 = SelectOptionPB::new("C");
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIsNot,
|
||||
condition: SelectOptionConditionPB::OptionIsNot,
|
||||
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
|
||||
};
|
||||
|
||||
@ -215,7 +215,7 @@ mod tests {
|
||||
let option_3 = SelectOptionPB::new("c");
|
||||
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIs,
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![option_1.id.clone()],
|
||||
};
|
||||
for (options, is_visible) in vec![
|
||||
@ -237,7 +237,7 @@ mod tests {
|
||||
let option_2 = SelectOptionPB::new("B");
|
||||
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIs,
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![],
|
||||
};
|
||||
for (options, is_visible) in vec![
|
||||
@ -258,7 +258,7 @@ mod tests {
|
||||
let option_2 = SelectOptionPB::new("B");
|
||||
let option_3 = SelectOptionPB::new("C");
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIsNot,
|
||||
condition: SelectOptionConditionPB::OptionIsNot,
|
||||
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
|
||||
};
|
||||
|
||||
@ -283,7 +283,7 @@ mod tests {
|
||||
let option_3 = SelectOptionPB::new("C");
|
||||
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIs,
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
|
||||
};
|
||||
for (options, is_visible) in vec![
|
||||
@ -305,7 +305,7 @@ mod tests {
|
||||
let option_1 = SelectOptionPB::new("A");
|
||||
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition: SelectOptionCondition::OptionIs,
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![],
|
||||
};
|
||||
for (options, is_visible) in vec![(vec![option_1.clone()], true), (vec![], true)] {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
|
||||
use crate::services::cell::{
|
||||
CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString,
|
||||
CellBytes, CellBytesParser, CellDataIsEmpty, CellDataSerialize, FromCellChangeset, FromCellString, IntoCellData,
|
||||
};
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
|
||||
use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
|
||||
@ -69,7 +69,10 @@ impl std::default::Default for SelectOptionColorPB {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_selected_options(cell_data: CellData<SelectOptionIds>, options: &[SelectOptionPB]) -> Vec<SelectOptionPB> {
|
||||
pub fn make_selected_options(
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
options: &[SelectOptionPB],
|
||||
) -> Vec<SelectOptionPB> {
|
||||
if let Ok(ids) = cell_data.try_into_inner() {
|
||||
ids.iter()
|
||||
.flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
|
||||
@ -110,7 +113,7 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
|
||||
}
|
||||
|
||||
/// Return a list of options that are selected by user
|
||||
fn get_selected_options(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB {
|
||||
fn get_selected_options(&self, cell_data: IntoCellData<SelectOptionIds>) -> SelectOptionCellDataPB {
|
||||
let mut select_options = make_selected_options(cell_data, self.options());
|
||||
match self.number_of_max_options() {
|
||||
None => {}
|
||||
@ -126,7 +129,7 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
|
||||
|
||||
fn transform_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -150,7 +153,9 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
|
||||
})
|
||||
});
|
||||
|
||||
return CellBytes::from(self.get_selected_options(CellData(Some(SelectOptionIds(transformed_ids)))));
|
||||
return CellBytes::from(
|
||||
self.get_selected_options(IntoCellData(Some(SelectOptionIds(transformed_ids)))),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Ok(CellBytes::default());
|
||||
@ -165,13 +170,13 @@ pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
|
||||
fn mut_options(&mut self) -> &mut Vec<SelectOptionPB>;
|
||||
}
|
||||
|
||||
impl<T> CellDisplayable<SelectOptionIds> for T
|
||||
impl<T> CellDataSerialize<SelectOptionIds> for T
|
||||
where
|
||||
T: SelectTypeOptionSharedAction,
|
||||
{
|
||||
fn displayed_cell_bytes(
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -183,9 +188,9 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer;
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||
use crate::services::field::{
|
||||
@ -40,11 +40,11 @@ impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
|
||||
impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSelectTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellBytes, CellData};
|
||||
use crate::services::cell::{CellBytes, IntoCellData};
|
||||
use crate::services::field::{
|
||||
MultiSelectTypeOptionPB, SelectOptionColorPB, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
|
||||
SingleSelectTypeOptionPB, CHECK, UNCHECK,
|
||||
@ -57,7 +57,7 @@ impl SelectOptionTypeOptionTransformer {
|
||||
|
||||
pub fn transform_type_option_cell_data<T>(
|
||||
shared: &T,
|
||||
cell_data: CellData<SelectOptionIds>,
|
||||
cell_data: IntoCellData<SelectOptionIds>,
|
||||
decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes>
|
||||
@ -78,7 +78,7 @@ impl SelectOptionTypeOptionTransformer {
|
||||
transformed_ids.push(option.id.clone());
|
||||
}
|
||||
});
|
||||
let transformed_cell_data = CellData::from(SelectOptionIds::from(transformed_ids));
|
||||
let transformed_cell_data = IntoCellData::from(SelectOptionIds::from(transformed_ids));
|
||||
CellBytes::from(shared.get_selected_options(transformed_cell_data))
|
||||
}
|
||||
_ => Ok(CellBytes::default()),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{TextFilterCondition, TextFilterPB};
|
||||
use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
|
||||
use crate::entities::{TextFilterConditionPB, TextFilterPB};
|
||||
use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
|
||||
use crate::services::field::{RichTextTypeOptionPB, TextCellData};
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
@ -8,25 +8,25 @@ impl TextFilterPB {
|
||||
let cell_data = cell_data.as_ref().to_lowercase();
|
||||
let content = &self.content.to_lowercase();
|
||||
match self.condition {
|
||||
TextFilterCondition::Is => &cell_data == content,
|
||||
TextFilterCondition::IsNot => &cell_data != content,
|
||||
TextFilterCondition::Contains => cell_data.contains(content),
|
||||
TextFilterCondition::DoesNotContain => !cell_data.contains(content),
|
||||
TextFilterCondition::StartsWith => cell_data.starts_with(content),
|
||||
TextFilterCondition::EndsWith => cell_data.ends_with(content),
|
||||
TextFilterCondition::TextIsEmpty => cell_data.is_empty(),
|
||||
TextFilterCondition::TextIsNotEmpty => !cell_data.is_empty(),
|
||||
TextFilterConditionPB::Is => &cell_data == content,
|
||||
TextFilterConditionPB::IsNot => &cell_data != content,
|
||||
TextFilterConditionPB::Contains => cell_data.contains(content),
|
||||
TextFilterConditionPB::DoesNotContain => !cell_data.contains(content),
|
||||
TextFilterConditionPB::StartsWith => cell_data.starts_with(content),
|
||||
TextFilterConditionPB::EndsWith => cell_data.ends_with(content),
|
||||
TextFilterConditionPB::TextIsEmpty => cell_data.is_empty(),
|
||||
TextFilterConditionPB::TextIsNotEmpty => !cell_data.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CellFilterOperation<TextFilterPB> for RichTextTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_text() {
|
||||
impl CellFilterable<TextFilterPB> for RichTextTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_text() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let cell_data: CellData<TextCellData> = any_cell_data.into();
|
||||
let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
|
||||
let text_cell_data = cell_data.try_into_inner()?;
|
||||
Ok(filter.is_visible(text_cell_data))
|
||||
}
|
||||
@ -34,12 +34,12 @@ impl CellFilterOperation<TextFilterPB> for RichTextTypeOptionPB {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::all)]
|
||||
use crate::entities::{TextFilterCondition, TextFilterPB};
|
||||
use crate::entities::{TextFilterConditionPB, TextFilterPB};
|
||||
|
||||
#[test]
|
||||
fn text_filter_equal_test() {
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterCondition::Is,
|
||||
condition: TextFilterConditionPB::Is,
|
||||
content: "appflowy".to_owned(),
|
||||
};
|
||||
|
||||
@ -51,7 +51,7 @@ mod tests {
|
||||
#[test]
|
||||
fn text_filter_start_with_test() {
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterCondition::StartsWith,
|
||||
condition: TextFilterConditionPB::StartsWith,
|
||||
content: "appflowy".to_owned(),
|
||||
};
|
||||
|
||||
@ -63,7 +63,7 @@ mod tests {
|
||||
#[test]
|
||||
fn text_filter_end_with_test() {
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterCondition::EndsWith,
|
||||
condition: TextFilterConditionPB::EndsWith,
|
||||
content: "appflowy".to_owned(),
|
||||
};
|
||||
|
||||
@ -74,7 +74,7 @@ mod tests {
|
||||
#[test]
|
||||
fn text_filter_empty_test() {
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterCondition::TextIsEmpty,
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "appflowy".to_owned(),
|
||||
};
|
||||
|
||||
@ -84,7 +84,7 @@ mod tests {
|
||||
#[test]
|
||||
fn text_filter_contain_test() {
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterCondition::Contains,
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
content: "appflowy".to_owned(),
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{
|
||||
decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellData, CellDataIsEmpty,
|
||||
CellDataOperation, CellDisplayable, FromCellString,
|
||||
decode_cell_data_to_string, AnyCellChangeset, CellBytes, CellBytesParser, CellDataIsEmpty, CellDataOperation,
|
||||
CellDataSerialize, FromCellString, IntoCellData,
|
||||
};
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||
use bytes::Bytes;
|
||||
@ -39,32 +39,32 @@ pub struct RichTextTypeOptionPB {
|
||||
}
|
||||
impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
|
||||
|
||||
impl CellDisplayable<String> for RichTextTypeOptionPB {
|
||||
fn displayed_cell_bytes(
|
||||
impl CellDataSerialize<RichTextCellData> for RichTextTypeOptionPB {
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<RichTextCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
let cell_str: String = cell_data.try_into_inner()?;
|
||||
let cell_str: RichTextCellData = cell_data.try_into_inner()?;
|
||||
Ok(CellBytes::new(cell_str))
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<RichTextCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
let cell_str: String = cell_data.try_into_inner()?;
|
||||
let cell_str: RichTextCellData = cell_data.try_into_inner()?;
|
||||
Ok(cell_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation<String, String> for RichTextTypeOptionPB {
|
||||
impl CellDataOperation<RichTextCellData, String> for RichTextTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<String>,
|
||||
cell_data: IntoCellData<RichTextCellData>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
@ -77,7 +77,7 @@ impl CellDataOperation<String, String> for RichTextTypeOptionPB {
|
||||
let s = decode_cell_data_to_string(cell_data, decoded_field_type, decoded_field_type, field_rev);
|
||||
Ok(CellBytes::new(s.unwrap_or_else(|_| "".to_owned())))
|
||||
} else {
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
self.serialize_cell_data_to_bytes(cell_data, decoded_field_type, field_rev)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,3 +141,5 @@ impl CellBytesParser for TextCellDataParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type RichTextCellData = String;
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::entities::TextFilterPB;
|
||||
use crate::services::cell::{CellData, CellFilterOperation, TypeCellData};
|
||||
use crate::services::cell::{CellFilterable, IntoCellData, TypeCellData};
|
||||
use crate::services::field::{TextCellData, URLTypeOptionPB};
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
impl CellFilterOperation<TextFilterPB> for URLTypeOptionPB {
|
||||
fn apply_filter(&self, any_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
|
||||
if !any_cell_data.is_url() {
|
||||
impl CellFilterable<TextFilterPB> for URLTypeOptionPB {
|
||||
fn apply_filter(&self, type_cell_data: TypeCellData, filter: &TextFilterPB) -> FlowyResult<bool> {
|
||||
if !type_cell_data.is_url() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let cell_data: CellData<TextCellData> = any_cell_data.into();
|
||||
let cell_data: IntoCellData<TextCellData> = type_cell_data.into();
|
||||
let text_cell_data = cell_data.try_into_inner()?;
|
||||
Ok(filter.is_visible(&text_cell_data))
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellData, CellDataOperation};
|
||||
use crate::services::cell::{CellDataOperation, IntoCellData};
|
||||
use crate::services::field::{FieldBuilder, URLCellDataParser};
|
||||
use crate::services::field::{URLCellDataPB, URLTypeOptionPB};
|
||||
use crate::services::field::{URLCellData, URLTypeOptionPB};
|
||||
use grid_rev_model::FieldRevision;
|
||||
|
||||
/// The expected_str will equal to the input string, but the expected_url will be empty if there's no
|
||||
@ -175,12 +175,12 @@ mod tests {
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
|
||||
}
|
||||
|
||||
fn decode_cell_data<T: Into<CellData<URLCellDataPB>>>(
|
||||
fn decode_cell_data<T: Into<IntoCellData<URLCellData>>>(
|
||||
encoded_data: T,
|
||||
type_option: &URLTypeOptionPB,
|
||||
field_rev: &FieldRevision,
|
||||
field_type: &FieldType,
|
||||
) -> URLCellDataPB {
|
||||
) -> URLCellData {
|
||||
type_option
|
||||
.decode_cell_data(encoded_data.into(), field_type, field_rev)
|
||||
.unwrap()
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::impl_type_option;
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable};
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellDataPB};
|
||||
use crate::services::cell::{AnyCellChangeset, CellBytes, CellDataOperation, CellDataSerialize, IntoCellData};
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData, URLCellDataPB};
|
||||
use bytes::Bytes;
|
||||
use fancy_regex::Regex;
|
||||
use flowy_derive::ProtoBuf;
|
||||
@ -36,41 +36,42 @@ pub struct URLTypeOptionPB {
|
||||
}
|
||||
impl_type_option!(URLTypeOptionPB, FieldType::URL);
|
||||
|
||||
impl CellDisplayable<URLCellDataPB> for URLTypeOptionPB {
|
||||
fn displayed_cell_bytes(
|
||||
impl CellDataSerialize<URLCellData> for URLTypeOptionPB {
|
||||
fn serialize_cell_data_to_bytes(
|
||||
&self,
|
||||
cell_data: CellData<URLCellDataPB>,
|
||||
cell_data: IntoCellData<URLCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
|
||||
CellBytes::from(cell_data)
|
||||
let cell_data_pb: URLCellDataPB = cell_data.try_into_inner()?.into();
|
||||
CellBytes::from(cell_data_pb)
|
||||
}
|
||||
|
||||
fn displayed_cell_string(
|
||||
fn serialize_cell_data_to_str(
|
||||
&self,
|
||||
cell_data: CellData<URLCellDataPB>,
|
||||
cell_data: IntoCellData<URLCellData>,
|
||||
_decoded_field_type: &FieldType,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<String> {
|
||||
let cell_data: URLCellDataPB = cell_data.try_into_inner()?;
|
||||
let cell_data: URLCellData = cell_data.try_into_inner()?;
|
||||
Ok(cell_data.content)
|
||||
}
|
||||
}
|
||||
|
||||
pub type URLCellChangeset = String;
|
||||
|
||||
impl CellDataOperation<URLCellDataPB, URLCellChangeset> for URLTypeOptionPB {
|
||||
impl CellDataOperation<URLCellData, URLCellChangeset> for URLTypeOptionPB {
|
||||
fn decode_cell_data(
|
||||
&self,
|
||||
cell_data: CellData<URLCellDataPB>,
|
||||
cell_data: IntoCellData<URLCellData>,
|
||||
decoded_field_type: &FieldType,
|
||||
field_rev: &FieldRevision,
|
||||
_field_rev: &FieldRevision,
|
||||
) -> FlowyResult<CellBytes> {
|
||||
if !decoded_field_type.is_url() {
|
||||
return Ok(CellBytes::default());
|
||||
}
|
||||
self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev)
|
||||
let cell_data = cell_data.try_into_inner()?.to_json()?;
|
||||
Ok(CellBytes::new(cell_data))
|
||||
}
|
||||
|
||||
fn apply_changeset(
|
||||
@ -83,7 +84,7 @@ impl CellDataOperation<URLCellDataPB, URLCellChangeset> for URLTypeOptionPB {
|
||||
if let Ok(Some(m)) = URL_REGEX.find(&content) {
|
||||
url = auto_append_scheme(m.as_str());
|
||||
}
|
||||
URLCellDataPB { url, content }.to_json()
|
||||
URLCellData { url, content }.to_json()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
|
||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||
pub struct URLCellDataPB {
|
||||
#[pb(index = 1)]
|
||||
pub url: String,
|
||||
@ -13,7 +13,22 @@ pub struct URLCellDataPB {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl URLCellDataPB {
|
||||
impl From<URLCellData> for URLCellDataPB {
|
||||
fn from(data: URLCellData) -> Self {
|
||||
Self {
|
||||
url: data.url,
|
||||
content: data.content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct URLCellData {
|
||||
pub url: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl URLCellData {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self {
|
||||
url: "".to_string(),
|
||||
@ -26,7 +41,7 @@ impl URLCellDataPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataIsEmpty for URLCellDataPB {
|
||||
impl CellDataIsEmpty for URLCellData {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.content.is_empty()
|
||||
}
|
||||
@ -34,15 +49,18 @@ impl CellDataIsEmpty for URLCellDataPB {
|
||||
|
||||
pub struct URLCellDataParser();
|
||||
impl CellBytesParser for URLCellDataParser {
|
||||
type Object = URLCellDataPB;
|
||||
type Object = URLCellData;
|
||||
|
||||
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
|
||||
URLCellDataPB::try_from(bytes.as_ref()).map_err(internal_error)
|
||||
match String::from_utf8(bytes.to_vec()) {
|
||||
Ok(s) => URLCellData::from_cell_str(&s),
|
||||
Err(_) => Ok(URLCellData::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromCellString for URLCellDataPB {
|
||||
impl FromCellString for URLCellData {
|
||||
fn from_cell_str(s: &str) -> FlowyResult<Self> {
|
||||
serde_json::from_str::<URLCellDataPB>(s).map_err(internal_error)
|
||||
serde_json::from_str::<URLCellData>(s).map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::entities::filter_entities::*;
|
||||
use crate::entities::{FieldType, InsertedRowPB, RowPB};
|
||||
use crate::services::cell::{CellFilterOperation, TypeCellData};
|
||||
use crate::services::cell::{CellFilterable, TypeCellData};
|
||||
use crate::services::field::*;
|
||||
use crate::services::filter::{FilterChangeset, FilterMap, FilterResult, FilterResultNotification, FilterType};
|
||||
use crate::services::row::GridBlock;
|
||||
use crate::services::row::GridBlockRowRevision;
|
||||
use crate::services::view_editor::{GridViewChanged, GridViewChangedNotifier};
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
|
||||
@ -20,7 +20,7 @@ pub trait FilterDelegate: Send + Sync + 'static {
|
||||
fn get_filter_rev(&self, filter_type: FilterType) -> Fut<Option<Arc<FilterRevision>>>;
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
|
||||
fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlock>>;
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>>;
|
||||
fn get_row_rev(&self, rows_id: &str) -> Fut<Option<(usize, Arc<RowRevision>)>>;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ impl FilterController {
|
||||
notifier: GridViewChangedNotifier,
|
||||
) -> Self
|
||||
where
|
||||
T: FilterDelegate,
|
||||
T: FilterDelegate + 'static,
|
||||
{
|
||||
let mut this = Self {
|
||||
view_id: view_id.to_string(),
|
||||
@ -55,7 +55,7 @@ impl FilterController {
|
||||
task_scheduler,
|
||||
notifier,
|
||||
};
|
||||
this.cache_filters(filter_revs).await;
|
||||
this.refresh_filters(filter_revs).await;
|
||||
this
|
||||
}
|
||||
|
||||
@ -191,17 +191,14 @@ impl FilterController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn did_receive_filter_changed(
|
||||
&mut self,
|
||||
changeset: FilterChangeset,
|
||||
) -> Option<FilterChangesetNotificationPB> {
|
||||
pub async fn did_receive_changes(&mut self, changeset: FilterChangeset) -> Option<FilterChangesetNotificationPB> {
|
||||
let mut notification: Option<FilterChangesetNotificationPB> = None;
|
||||
if let Some(filter_type) = &changeset.insert_filter {
|
||||
if let Some(filter) = self.filter_from_filter_type(filter_type).await {
|
||||
notification = Some(FilterChangesetNotificationPB::from_insert(&self.view_id, vec![filter]));
|
||||
}
|
||||
if let Some(filter_rev) = self.delegate.get_filter_rev(filter_type.clone()).await {
|
||||
let _ = self.cache_filters(vec![filter_rev]).await;
|
||||
let _ = self.refresh_filters(vec![filter_rev]).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +215,7 @@ impl FilterController {
|
||||
|
||||
// Update the corresponding filter in the cache
|
||||
if let Some(filter_rev) = self.delegate.get_filter_rev(updated_filter_type.new.clone()).await {
|
||||
let _ = self.cache_filters(vec![filter_rev]).await;
|
||||
let _ = self.refresh_filters(vec![filter_rev]).await;
|
||||
}
|
||||
|
||||
if let Some(filter_id) = filter_id {
|
||||
@ -253,7 +250,7 @@ impl FilterController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn cache_filters(&mut self, filter_revs: Vec<Arc<FilterRevision>>) {
|
||||
async fn refresh_filters(&mut self, filter_revs: Vec<Arc<FilterRevision>>) {
|
||||
for filter_rev in filter_revs {
|
||||
if let Some(field_rev) = self.delegate.get_field_rev(&filter_rev.field_id).await {
|
||||
let filter_type = FilterType::from(&field_rev);
|
||||
@ -355,7 +352,7 @@ fn filter_cell(
|
||||
filter_map: &FilterMap,
|
||||
cell_rev: Option<&CellRevision>,
|
||||
) -> Option<bool> {
|
||||
let any_cell_data = match cell_rev {
|
||||
let type_cell_data = match cell_rev {
|
||||
None => TypeCellData::from_field_type(&filter_id.field_type),
|
||||
Some(cell_rev) => match TypeCellData::try_from(cell_rev) {
|
||||
Ok(cell_data) => cell_data,
|
||||
@ -365,13 +362,13 @@ fn filter_cell(
|
||||
}
|
||||
},
|
||||
};
|
||||
let cloned_cell_data = any_cell_data.data.clone();
|
||||
let cloned_type_cell_data = type_cell_data.data.clone();
|
||||
let is_visible = match &filter_id.field_type {
|
||||
FieldType::RichText => filter_map.text_filter.get(filter_id).and_then(|filter| {
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<RichTextTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -379,7 +376,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<NumberTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -387,7 +384,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<DateTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -395,7 +392,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<SingleSelectTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -403,7 +400,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<MultiSelectTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -411,7 +408,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<CheckboxTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -419,7 +416,7 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<URLTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
@ -427,14 +424,14 @@ fn filter_cell(
|
||||
Some(
|
||||
field_rev
|
||||
.get_type_option::<ChecklistTypeOptionPB>(field_rev.ty)?
|
||||
.apply_filter(any_cell_data, filter)
|
||||
.apply_filter(type_cell_data, filter)
|
||||
.ok(),
|
||||
)
|
||||
}),
|
||||
}?;
|
||||
tracing::Span::current().record(
|
||||
"cell_content",
|
||||
&format!("{} => {:?}", cloned_cell_data, is_visible.unwrap()).as_str(),
|
||||
&format!("{} => {:?}", cloned_type_cell_data, is_visible.unwrap()).as_str(),
|
||||
);
|
||||
is_visible
|
||||
}
|
||||
|
@ -71,12 +71,11 @@ pub struct FilterType {
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl FilterType {
|
||||
pub fn field_type_rev(&self) -> FieldTypeRevision {
|
||||
self.field_type.clone().into()
|
||||
impl From<FilterType> for FieldTypeRevision {
|
||||
fn from(filter_type: FilterType) -> Self {
|
||||
filter_type.field_type.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&Arc<FieldRevision>> for FilterType {
|
||||
fn from(rev: &Arc<FieldRevision>) -> Self {
|
||||
Self {
|
||||
|
@ -3,7 +3,7 @@ use crate::entities::CellPathParams;
|
||||
use crate::entities::*;
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes};
|
||||
use crate::services::cell::{apply_cell_data_changeset, decode_type_cell_data, CellBytes};
|
||||
use crate::services::field::{
|
||||
default_type_option_builder_from_type, type_option_builder_from_bytes, type_option_builder_from_json_str,
|
||||
FieldBuilder,
|
||||
@ -12,7 +12,7 @@ use crate::services::field::{
|
||||
use crate::services::filter::FilterType;
|
||||
use crate::services::grid_editor_trait_impl::GridViewEditorDelegateImpl;
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::row::{GridBlock, RowRevisionBuilder};
|
||||
use crate::services::row::{GridBlockRow, GridBlockRowRevision, RowRevisionBuilder};
|
||||
use crate::services::view_editor::{GridViewChanged, GridViewManager};
|
||||
use bytes::Bytes;
|
||||
use flowy_database::ConnectionPool;
|
||||
@ -63,8 +63,9 @@ impl GridRevisionEditor {
|
||||
let grid_pad = Arc::new(RwLock::new(grid_pad));
|
||||
|
||||
// Block manager
|
||||
let (block_event_tx, block_event_rx) = broadcast::channel(100);
|
||||
let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
|
||||
let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence).await?);
|
||||
let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence, block_event_tx).await?);
|
||||
let delegate = Arc::new(GridViewEditorDelegateImpl {
|
||||
pad: grid_pad.clone(),
|
||||
block_manager: block_manager.clone(),
|
||||
@ -72,7 +73,8 @@ impl GridRevisionEditor {
|
||||
});
|
||||
|
||||
// View manager
|
||||
let view_manager = Arc::new(GridViewManager::new(grid_id.to_owned(), user.clone(), delegate).await?);
|
||||
let view_manager =
|
||||
Arc::new(GridViewManager::new(grid_id.to_owned(), user.clone(), delegate, block_event_rx).await?);
|
||||
|
||||
let editor = Arc::new(Self {
|
||||
grid_id: grid_id.to_owned(),
|
||||
@ -391,11 +393,12 @@ impl GridRevisionEditor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_row_pbs(&self, block_id: &str) -> FlowyResult<Vec<RowPB>> {
|
||||
let rows = self.block_manager.get_row_revs(block_id).await?;
|
||||
/// Returns all the rows in this block.
|
||||
pub async fn get_row_pbs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<RowPB>> {
|
||||
let rows = self.view_manager.get_row_revs(view_id, block_id).await?;
|
||||
let rows = self
|
||||
.view_manager
|
||||
.filter_rows(block_id, rows)
|
||||
.filter_rows(view_id, block_id, rows)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|row_rev| RowPB::from(&row_rev))
|
||||
@ -428,20 +431,20 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn get_cell(&self, params: &CellPathParams) -> Option<CellPB> {
|
||||
let (field_type, cell_bytes) = self.decode_any_cell_data(params).await?;
|
||||
let (field_type, cell_bytes) = self.decode_cell_data_from(params).await?;
|
||||
Some(CellPB::new(¶ms.field_id, field_type, cell_bytes.to_vec()))
|
||||
}
|
||||
|
||||
pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellBytes> {
|
||||
let (_, cell_data) = self.decode_any_cell_data(params).await?;
|
||||
let (_, cell_data) = self.decode_cell_data_from(params).await?;
|
||||
Some(cell_data)
|
||||
}
|
||||
|
||||
async fn decode_any_cell_data(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
|
||||
async fn decode_cell_data_from(&self, params: &CellPathParams) -> Option<(FieldType, CellBytes)> {
|
||||
let field_rev = self.get_field_rev(¶ms.field_id).await?;
|
||||
let (_, row_rev) = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??;
|
||||
let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone();
|
||||
Some(decode_any_cell_data(cell_rev.data, &field_rev))
|
||||
Some(decode_type_cell_data(cell_rev.data, &field_rev))
|
||||
}
|
||||
|
||||
pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellRevision>> {
|
||||
@ -508,7 +511,7 @@ impl GridRevisionEditor {
|
||||
Ok(block_meta_revs)
|
||||
}
|
||||
|
||||
pub async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlock>> {
|
||||
pub async fn get_blocks(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockRowRevision>> {
|
||||
let block_ids = match block_ids {
|
||||
None => self
|
||||
.grid_pad
|
||||
@ -524,32 +527,28 @@ impl GridRevisionEditor {
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
pub async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<()> {
|
||||
let changesets = self.block_manager.delete_rows(row_orders).await?;
|
||||
pub async fn delete_rows(&self, block_rows: Vec<GridBlockRow>) -> FlowyResult<()> {
|
||||
let changesets = self.block_manager.delete_rows(block_rows).await?;
|
||||
for changeset in changesets {
|
||||
let _ = self.update_block(changeset).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_grid(&self) -> FlowyResult<GridPB> {
|
||||
pub async fn get_grid(&self, view_id: &str) -> FlowyResult<GridPB> {
|
||||
let pad = self.grid_pad.read().await;
|
||||
let fields = pad.get_field_revs(None)?.iter().map(FieldIdPB::from).collect();
|
||||
|
||||
let mut blocks = vec![];
|
||||
let mut all_rows = vec![];
|
||||
for block_rev in pad.get_block_meta_revs() {
|
||||
let rows = self.get_row_pbs(&block_rev.block_id).await?;
|
||||
let block = BlockPB {
|
||||
id: block_rev.block_id.clone(),
|
||||
rows,
|
||||
};
|
||||
blocks.push(block);
|
||||
if let Ok(rows) = self.get_row_pbs(view_id, &block_rev.block_id).await {
|
||||
all_rows.extend(rows);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GridPB {
|
||||
id: self.grid_id.clone(),
|
||||
fields,
|
||||
blocks,
|
||||
rows: all_rows,
|
||||
})
|
||||
}
|
||||
|
||||
@ -589,6 +588,16 @@ impl GridRevisionEditor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.delete_sort(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn create_or_update_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
|
||||
let _ = self.view_manager.create_or_update_sort(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, params: MoveRowParams) -> FlowyResult<()> {
|
||||
let MoveRowParams {
|
||||
view_id: _,
|
||||
@ -801,7 +810,7 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
async fn notify_did_update_grid(&self, changeset: GridFieldChangesetPB) -> FlowyResult<()> {
|
||||
send_dart_notification(&self.grid_id, GridDartNotification::DidUpdateGridField)
|
||||
send_dart_notification(&self.grid_id, GridDartNotification::DidUpdateGridFields)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
Ok(())
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::row::GridBlock;
|
||||
use crate::services::row::GridBlockRowRevision;
|
||||
use crate::services::view_editor::GridViewEditorDelegate;
|
||||
use flowy_sync::client_grid::GridRevisionPad;
|
||||
use flowy_task::TaskDispatcher;
|
||||
@ -51,11 +51,11 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>> {
|
||||
fn get_row_revs(&self, block_id: Option<Vec<String>>) -> Fut<Vec<Arc<RowRevision>>> {
|
||||
let block_manager = self.block_manager.clone();
|
||||
|
||||
to_fut(async move {
|
||||
let blocks = block_manager.get_blocks(None).await.unwrap();
|
||||
let blocks = block_manager.get_blocks(block_id).await.unwrap();
|
||||
blocks
|
||||
.into_iter()
|
||||
.flat_map(|block| block.row_revs)
|
||||
@ -63,7 +63,7 @@ impl GridViewEditorDelegate for GridViewEditorDelegateImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlock>> {
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>> {
|
||||
let block_manager = self.block_manager.clone();
|
||||
to_fut(async move { block_manager.get_blocks(None).await.unwrap_or_default() })
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
|
||||
use crate::services::cell::{decode_any_cell_data, CellBytesParser, CellDataIsEmpty};
|
||||
use crate::services::cell::{decode_type_cell_data, CellBytesParser, CellDataIsEmpty};
|
||||
use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions};
|
||||
use crate::services::group::configuration::GroupContext;
|
||||
use crate::services::group::entities::Group;
|
||||
@ -184,7 +184,7 @@ where
|
||||
|
||||
if let Some(cell_rev) = cell_rev {
|
||||
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev).1;
|
||||
let cell_bytes = decode_type_cell_data(cell_rev.data, field_rev).1;
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
for group in self.group_ctx.groups() {
|
||||
if self.can_group(&group.filter_content, &cell_data) {
|
||||
@ -224,7 +224,7 @@ where
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
|
||||
let cell_bytes = decode_type_cell_data(cell_rev.data.clone(), field_rev).1;
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &cell_data);
|
||||
|
||||
@ -247,7 +247,7 @@ where
|
||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
||||
// if the cell_rev is none, then the row must in the default group.
|
||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
|
||||
let cell_bytes = decode_type_cell_data(cell_rev.data.clone(), field_rev).1;
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
if !cell_data.is_empty() {
|
||||
tracing::error!("did_delete_delete_row {:?}", cell_rev.data);
|
||||
@ -280,7 +280,7 @@ where
|
||||
};
|
||||
|
||||
if let Some(cell_rev) = cell_rev {
|
||||
let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev).1;
|
||||
let cell_bytes = decode_type_cell_data(cell_rev.data, context.field_rev).1;
|
||||
let cell_data = cell_bytes.parser::<P>()?;
|
||||
Ok(self.move_row(&cell_data, context))
|
||||
} else {
|
||||
|
@ -11,4 +11,5 @@ pub mod group;
|
||||
pub mod persistence;
|
||||
pub mod row;
|
||||
pub mod setting;
|
||||
pub mod sort;
|
||||
pub mod view_editor;
|
||||
|
@ -2,4 +2,4 @@ mod row_builder;
|
||||
mod row_loader;
|
||||
|
||||
pub use row_builder::*;
|
||||
pub(crate) use row_loader::*;
|
||||
pub use row_loader::*;
|
||||
|
@ -1,42 +1,22 @@
|
||||
use crate::entities::{BlockPB, RepeatedBlockPB, RowPB};
|
||||
|
||||
use crate::entities::RowPB;
|
||||
use grid_rev_model::RowRevision;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct GridBlock {
|
||||
pub struct GridBlockRowRevision {
|
||||
pub(crate) block_id: String,
|
||||
pub row_revs: Vec<Arc<RowRevision>>,
|
||||
}
|
||||
|
||||
pub(crate) fn block_from_row_orders(row_orders: Vec<RowPB>) -> Vec<BlockPB> {
|
||||
let mut map: HashMap<String, BlockPB> = HashMap::new();
|
||||
row_orders.into_iter().for_each(|row_info| {
|
||||
// Memory Optimization: escape clone block_id
|
||||
let block_id = row_info.block_id().to_owned();
|
||||
let cloned_block_id = block_id.clone();
|
||||
map.entry(block_id)
|
||||
.or_insert_with(|| BlockPB::new(&cloned_block_id, vec![]))
|
||||
.rows
|
||||
.push(row_info);
|
||||
});
|
||||
map.into_values().collect::<Vec<_>>()
|
||||
pub struct GridBlockRow {
|
||||
pub block_id: String,
|
||||
pub row_ids: Vec<String>,
|
||||
}
|
||||
//
|
||||
// #[inline(always)]
|
||||
// fn make_cell_by_field_id(
|
||||
// field_map: &HashMap<&String, &FieldRevision>,
|
||||
// field_id: String,
|
||||
// cell_rev: CellRevision,
|
||||
// ) -> Option<(String, Cell)> {
|
||||
// let field_rev = field_map.get(&field_id)?;
|
||||
// let data = decode_cell_data(cell_rev.data, field_rev).data;
|
||||
// let cell = Cell::new(&field_id, data);
|
||||
// Some((field_id, cell))
|
||||
// }
|
||||
|
||||
pub(crate) fn make_row_pb_from_row_rev(row_revs: &[Arc<RowRevision>]) -> Vec<RowPB> {
|
||||
row_revs.iter().map(RowPB::from).collect::<Vec<_>>()
|
||||
impl GridBlockRow {
|
||||
pub fn new(block_id: String, row_ids: Vec<String>) -> Self {
|
||||
Self { block_id, row_ids }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_row_from_row_rev(row_rev: Arc<RowRevision>) -> RowPB {
|
||||
@ -52,14 +32,3 @@ pub(crate) fn make_rows_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<RowP
|
||||
|
||||
row_revs.iter().map(make_row).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn make_block_pbs(blocks: Vec<GridBlock>) -> RepeatedBlockPB {
|
||||
blocks
|
||||
.into_iter()
|
||||
.map(|block| {
|
||||
let row_pbs = make_row_pb_from_row_rev(&block.row_revs);
|
||||
BlockPB::new(&block.block_id, row_pbs)
|
||||
})
|
||||
.collect::<Vec<BlockPB>>()
|
||||
.into()
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ impl GridSettingChangesetBuilder {
|
||||
delete_filter: None,
|
||||
insert_group: None,
|
||||
delete_group: None,
|
||||
alert_sort: None,
|
||||
delete_sort: None,
|
||||
};
|
||||
Self { params }
|
||||
}
|
||||
|
58
frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
Normal file
58
frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
Normal file
@ -0,0 +1,58 @@
|
||||
#![allow(clippy::all)]
|
||||
#[allow(unused_attributes)]
|
||||
use crate::entities::{GridSortPB, SortChangesetNotificationPB};
|
||||
use crate::services::sort::{SortChangeset, SortType};
|
||||
use flowy_task::TaskDispatcher;
|
||||
use grid_rev_model::{FieldRevision, RowRevision, SortRevision};
|
||||
use lib_infra::future::Fut;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub trait SortDelegate: Send + Sync {
|
||||
fn get_sort_rev(&self, sort_type: SortType) -> Fut<Vec<Arc<SortRevision>>>;
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
|
||||
fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
|
||||
}
|
||||
|
||||
pub struct SortController {
|
||||
#[allow(dead_code)]
|
||||
view_id: String,
|
||||
#[allow(dead_code)]
|
||||
handler_id: String,
|
||||
#[allow(dead_code)]
|
||||
delegate: Box<dyn SortDelegate>,
|
||||
task_scheduler: Arc<RwLock<TaskDispatcher>>,
|
||||
#[allow(dead_code)]
|
||||
sorts: Vec<GridSortPB>,
|
||||
}
|
||||
|
||||
impl SortController {
|
||||
pub fn new<T>(view_id: &str, handler_id: &str, delegate: T, task_scheduler: Arc<RwLock<TaskDispatcher>>) -> Self
|
||||
where
|
||||
T: SortDelegate + 'static,
|
||||
{
|
||||
Self {
|
||||
view_id: view_id.to_string(),
|
||||
handler_id: handler_id.to_string(),
|
||||
delegate: Box::new(delegate),
|
||||
task_scheduler,
|
||||
sorts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn close(&self) {
|
||||
self.task_scheduler
|
||||
.write()
|
||||
.await
|
||||
.unregister_handler(&self.handler_id)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn sort_rows(&self, _rows: &mut Vec<Arc<RowRevision>>) {
|
||||
//
|
||||
}
|
||||
|
||||
pub async fn did_receive_changes(&mut self, _changeset: SortChangeset) -> Option<SortChangesetNotificationPB> {
|
||||
None
|
||||
}
|
||||
}
|
67
frontend/rust-lib/flowy-grid/src/services/sort/entities.rs
Normal file
67
frontend/rust-lib/flowy-grid/src/services/sort/entities.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::entities::{AlterSortParams, FieldType};
|
||||
use grid_rev_model::{FieldRevision, FieldTypeRevision};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
|
||||
pub struct SortType {
|
||||
pub field_id: String,
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl From<SortType> for FieldTypeRevision {
|
||||
fn from(sort_type: SortType) -> Self {
|
||||
sort_type.field_type.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&AlterSortParams> for SortType {
|
||||
fn from(params: &AlterSortParams) -> Self {
|
||||
Self {
|
||||
field_id: params.field_id.clone(),
|
||||
field_type: params.field_type.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&Arc<FieldRevision>> for SortType {
|
||||
fn from(rev: &Arc<FieldRevision>) -> Self {
|
||||
Self {
|
||||
field_id: rev.id.clone(),
|
||||
field_type: rev.ty.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct SortChangeset {
|
||||
pub(crate) insert_sort: Option<SortType>,
|
||||
pub(crate) update_sort: Option<SortType>,
|
||||
pub(crate) delete_sort: Option<SortType>,
|
||||
}
|
||||
|
||||
impl SortChangeset {
|
||||
pub fn from_insert(sort: SortType) -> Self {
|
||||
Self {
|
||||
insert_sort: Some(sort),
|
||||
update_sort: None,
|
||||
delete_sort: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_update(sort: SortType) -> Self {
|
||||
Self {
|
||||
insert_sort: None,
|
||||
update_sort: Some(sort),
|
||||
delete_sort: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_delete(sort: SortType) -> Self {
|
||||
Self {
|
||||
insert_sort: None,
|
||||
update_sort: None,
|
||||
delete_sort: Some(sort),
|
||||
}
|
||||
}
|
||||
}
|
7
frontend/rust-lib/flowy-grid/src/services/sort/mod.rs
Normal file
7
frontend/rust-lib/flowy-grid/src/services/sort/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod controller;
|
||||
mod entities;
|
||||
mod task;
|
||||
|
||||
pub use controller::*;
|
||||
pub use entities::*;
|
||||
pub use task::*;
|
30
frontend/rust-lib/flowy-grid/src/services/sort/task.rs
Normal file
30
frontend/rust-lib/flowy-grid/src/services/sort/task.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use crate::services::sort::SortController;
|
||||
use flowy_task::{TaskContent, TaskHandler};
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub struct SortTaskHandler {
|
||||
handler_id: String,
|
||||
#[allow(dead_code)]
|
||||
sort_controller: Arc<RwLock<SortController>>,
|
||||
}
|
||||
|
||||
impl SortTaskHandler {
|
||||
pub fn new(handler_id: String, sort_controller: Arc<RwLock<SortController>>) -> Self {
|
||||
Self {
|
||||
handler_id,
|
||||
sort_controller,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskHandler for SortTaskHandler {
|
||||
fn handler_id(&self) -> &str {
|
||||
&self.handler_id
|
||||
}
|
||||
|
||||
fn run(&self, _content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridDartNotification};
|
||||
use crate::entities::GridBlockChangesetPB;
|
||||
use crate::entities::GridRowsVisibilityChangesetPB;
|
||||
use crate::services::filter::FilterResultNotification;
|
||||
use async_stream::stream;
|
||||
use futures::stream::StreamExt;
|
||||
@ -28,16 +28,18 @@ impl GridViewChangedReceiverRunner {
|
||||
.for_each(|changed| async {
|
||||
match changed {
|
||||
GridViewChanged::DidReceiveFilterResult(notification) => {
|
||||
let changeset = GridBlockChangesetPB {
|
||||
block_id: notification.block_id,
|
||||
let changeset = GridRowsVisibilityChangesetPB {
|
||||
view_id: notification.view_id,
|
||||
visible_rows: notification.visible_rows,
|
||||
invisible_rows: notification.invisible_rows,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
send_dart_notification(&changeset.block_id, GridDartNotification::DidUpdateGridBlock)
|
||||
.payload(changeset)
|
||||
.send()
|
||||
send_dart_notification(
|
||||
&changeset.view_id,
|
||||
GridDartNotification::DidUpdateGridViewRowsVisibility,
|
||||
)
|
||||
.payload(changeset)
|
||||
.send()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,11 +1,13 @@
|
||||
use crate::dart_notification::{send_dart_notification, GridDartNotification};
|
||||
use crate::entities::*;
|
||||
use crate::services::block_manager::GridBlockEvent;
|
||||
use crate::services::filter::{FilterChangeset, FilterController, FilterTaskHandler, FilterType, UpdatedFilterType};
|
||||
use crate::services::group::{
|
||||
default_group_configuration, find_group_field, make_group_controller, Group, GroupConfigurationReader,
|
||||
GroupController, MoveGroupRowContext,
|
||||
};
|
||||
use crate::services::row::GridBlock;
|
||||
use crate::services::row::GridBlockRowRevision;
|
||||
use crate::services::sort::{SortChangeset, SortController, SortTaskHandler, SortType};
|
||||
use crate::services::view_editor::changed_notifier::GridViewChangedNotifier;
|
||||
use crate::services::view_editor::trait_impl::*;
|
||||
use crate::services::view_editor::GridViewChangedReceiverRunner;
|
||||
@ -16,12 +18,14 @@ use flowy_revision::RevisionManager;
|
||||
use flowy_sync::client_grid::{make_grid_view_operations, GridViewRevisionChangeset, GridViewRevisionPad};
|
||||
use flowy_task::TaskDispatcher;
|
||||
use grid_rev_model::{
|
||||
gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterRevision, LayoutRevision, RowChangeset, RowRevision,
|
||||
gen_grid_filter_id, gen_grid_sort_id, FieldRevision, FieldTypeRevision, FilterRevision, LayoutRevision,
|
||||
RowChangeset, RowRevision, SortRevision,
|
||||
};
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::future::Fut;
|
||||
use lib_infra::ref_map::RefCountValue;
|
||||
use nanoid::nanoid;
|
||||
use std::borrow::Cow;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{broadcast, RwLock};
|
||||
@ -29,13 +33,29 @@ use tokio::sync::{broadcast, RwLock};
|
||||
pub trait GridViewEditorDelegate: Send + Sync + 'static {
|
||||
/// If the field_ids is None, then it will return all the field revisions
|
||||
fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
|
||||
|
||||
/// Returns the field with the field_id
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
|
||||
|
||||
/// Returns the index of the row with row_id
|
||||
fn index_of_row(&self, row_id: &str) -> Fut<Option<usize>>;
|
||||
fn get_row_rev(&self, row_id: &str) -> Fut<Option<(usize, Arc<RowRevision>)>>;
|
||||
fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>>;
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlock>>;
|
||||
|
||||
/// Returns the `index` and `RowRevision` with row_id
|
||||
fn get_row_rev(&self, row_id: &str) -> Fut<Option<(usize, Arc<RowRevision>)>>;
|
||||
|
||||
/// Returns all the rows that the block has. If the passed-in block_ids is None, then will return all the rows
|
||||
/// The relationship between the grid and the block is:
|
||||
/// A grid has a list of blocks
|
||||
/// A block has a list of rows
|
||||
/// A row has a list of cells
|
||||
///
|
||||
fn get_row_revs(&self, block_ids: Option<Vec<String>>) -> Fut<Vec<Arc<RowRevision>>>;
|
||||
|
||||
/// Get all the blocks that the current Grid has.
|
||||
/// One grid has a list of blocks
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>>;
|
||||
|
||||
/// Returns a `TaskDispatcher` used to poll a `Task`
|
||||
fn get_task_scheduler(&self) -> Arc<RwLock<TaskDispatcher>>;
|
||||
}
|
||||
|
||||
@ -47,6 +67,7 @@ pub struct GridViewRevisionEditor {
|
||||
delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
group_controller: Arc<RwLock<Box<dyn GroupController>>>,
|
||||
filter_controller: Arc<RwLock<FilterController>>,
|
||||
sort_controller: Arc<RwLock<SortController>>,
|
||||
pub notifier: GridViewChangedNotifier,
|
||||
}
|
||||
|
||||
@ -89,6 +110,8 @@ impl GridViewRevisionEditor {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let sort_controller = make_sort_controller(&view_id, delegate.clone(), view_rev_pad.clone()).await;
|
||||
|
||||
let user_id = user_id.to_owned();
|
||||
let group_controller = Arc::new(RwLock::new(group_controller));
|
||||
let filter_controller =
|
||||
@ -101,6 +124,7 @@ impl GridViewRevisionEditor {
|
||||
delegate,
|
||||
group_controller,
|
||||
filter_controller,
|
||||
sort_controller,
|
||||
notifier,
|
||||
})
|
||||
}
|
||||
@ -110,6 +134,40 @@ impl GridViewRevisionEditor {
|
||||
self.rev_manager.generate_snapshot().await;
|
||||
self.rev_manager.close().await;
|
||||
self.filter_controller.read().await.close().await;
|
||||
self.sort_controller.read().await.close().await;
|
||||
}
|
||||
|
||||
pub async fn handle_block_event(&self, event: Cow<'_, GridBlockEvent>) {
|
||||
let changeset = match event.into_owned() {
|
||||
GridBlockEvent::InsertRow { block_id: _, row } => {
|
||||
//
|
||||
GridViewRowsChangesetPB::from_insert(self.view_id.clone(), vec![row])
|
||||
}
|
||||
GridBlockEvent::UpdateRow { block_id: _, row } => {
|
||||
//
|
||||
GridViewRowsChangesetPB::from_update(self.view_id.clone(), vec![row])
|
||||
}
|
||||
GridBlockEvent::DeleteRow { block_id: _, row_id } => {
|
||||
//
|
||||
GridViewRowsChangesetPB::from_delete(self.view_id.clone(), vec![row_id])
|
||||
}
|
||||
GridBlockEvent::Move {
|
||||
block_id: _,
|
||||
deleted_row_id,
|
||||
inserted_row,
|
||||
} => {
|
||||
//
|
||||
GridViewRowsChangesetPB::from_move(self.view_id.clone(), vec![deleted_row_id], vec![inserted_row])
|
||||
}
|
||||
};
|
||||
|
||||
send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGridViewRows)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
|
||||
self.sort_controller.read().await.sort_rows(rows)
|
||||
}
|
||||
|
||||
pub async fn filter_rows(&self, _block_id: &str, mut rows: Vec<Arc<RowRevision>>) -> Vec<Arc<RowRevision>> {
|
||||
@ -316,6 +374,70 @@ impl GridViewRevisionEditor {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn insert_view_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
|
||||
let sort_type = SortType::from(¶ms);
|
||||
let is_exist = params.sort_id.is_some();
|
||||
let sort_id = match params.sort_id {
|
||||
None => gen_grid_sort_id(),
|
||||
Some(sort_id) => sort_id,
|
||||
};
|
||||
|
||||
let sort_rev = SortRevision {
|
||||
id: sort_id,
|
||||
field_id: params.field_id.clone(),
|
||||
field_type: params.field_type,
|
||||
condition: params.condition,
|
||||
};
|
||||
|
||||
let mut sort_controller = self.sort_controller.write().await;
|
||||
let changeset = if is_exist {
|
||||
self.modify(|pad| {
|
||||
let changeset = pad.update_sort(¶ms.field_id, sort_rev)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
sort_controller
|
||||
.did_receive_changes(SortChangeset::from_update(sort_type))
|
||||
.await
|
||||
} else {
|
||||
self.modify(|pad| {
|
||||
let changeset = pad.insert_sort(¶ms.field_id, sort_rev)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
sort_controller
|
||||
.did_receive_changes(SortChangeset::from_insert(sort_type))
|
||||
.await
|
||||
};
|
||||
|
||||
if let Some(changeset) = changeset {
|
||||
self.notify_did_update_sort(changeset).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_view_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
|
||||
let sort_type = params.sort_type;
|
||||
let changeset = self
|
||||
.sort_controller
|
||||
.write()
|
||||
.await
|
||||
.did_receive_changes(SortChangeset::from_delete(sort_type.clone()))
|
||||
.await;
|
||||
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
let changeset = pad.delete_sort(¶ms.sort_id, &sort_type.field_id, sort_type.field_type)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
|
||||
if changeset.is_some() {
|
||||
self.notify_did_update_sort(changeset.unwrap()).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn insert_view_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
|
||||
let filter_type = FilterType::from(¶ms);
|
||||
@ -344,7 +466,7 @@ impl GridViewRevisionEditor {
|
||||
})
|
||||
.await?;
|
||||
filter_controller
|
||||
.did_receive_filter_changed(FilterChangeset::from_update(UpdatedFilterType::new(
|
||||
.did_receive_changes(FilterChangeset::from_update(UpdatedFilterType::new(
|
||||
old_filter_type,
|
||||
filter_type,
|
||||
)))
|
||||
@ -356,7 +478,7 @@ impl GridViewRevisionEditor {
|
||||
})
|
||||
.await?;
|
||||
filter_controller
|
||||
.did_receive_filter_changed(FilterChangeset::from_insert(filter_type))
|
||||
.did_receive_changes(FilterChangeset::from_insert(filter_type))
|
||||
.await
|
||||
};
|
||||
|
||||
@ -369,17 +491,16 @@ impl GridViewRevisionEditor {
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn delete_view_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
|
||||
let filter_type = params.filter_type;
|
||||
let field_type_rev = filter_type.field_type_rev();
|
||||
let changeset = self
|
||||
.filter_controller
|
||||
.write()
|
||||
.await
|
||||
.did_receive_filter_changed(FilterChangeset::from_delete(filter_type.clone()))
|
||||
.did_receive_changes(FilterChangeset::from_delete(filter_type.clone()))
|
||||
.await;
|
||||
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
let changeset = pad.delete_filter(¶ms.filter_id, &filter_type.field_id, &field_type_rev)?;
|
||||
let changeset = pad.delete_filter(¶ms.filter_id, &filter_type.field_id, filter_type.field_type)?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
@ -405,7 +526,7 @@ impl GridViewRevisionEditor {
|
||||
.filter_controller
|
||||
.write()
|
||||
.await
|
||||
.did_receive_filter_changed(filter_changeset)
|
||||
.did_receive_changes(filter_changeset)
|
||||
.await
|
||||
{
|
||||
self.notify_did_update_filter(changeset).await;
|
||||
@ -423,7 +544,7 @@ impl GridViewRevisionEditor {
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub async fn group_by_view_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||
if let Some(field_rev) = self.delegate.get_field_rev(field_id).await {
|
||||
let row_revs = self.delegate.get_row_revs().await;
|
||||
let row_revs = self.delegate.get_row_revs(None).await;
|
||||
let new_group_controller = new_group_controller_with_field_rev(
|
||||
self.user_id.clone(),
|
||||
self.view_id.clone(),
|
||||
@ -476,6 +597,12 @@ impl GridViewRevisionEditor {
|
||||
.send();
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_sort(&self, changeset: SortChangesetNotificationPB) {
|
||||
send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
|
||||
send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGroupView)
|
||||
.payload(changeset)
|
||||
@ -543,7 +670,7 @@ async fn new_group_controller(
|
||||
) -> FlowyResult<Box<dyn GroupController>> {
|
||||
let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
|
||||
let field_revs = delegate.get_field_revs(None).await;
|
||||
let row_revs = delegate.get_row_revs().await;
|
||||
let row_revs = delegate.get_row_revs(None).await;
|
||||
let layout = view_rev_pad.read().await.layout();
|
||||
// Read the group field or find a new group field
|
||||
let field_rev = configuration_reader
|
||||
@ -610,6 +737,31 @@ async fn make_filter_controller(
|
||||
filter_controller
|
||||
}
|
||||
|
||||
async fn make_sort_controller(
|
||||
view_id: &str,
|
||||
delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
pad: Arc<RwLock<GridViewRevisionPad>>,
|
||||
) -> Arc<RwLock<SortController>> {
|
||||
let handler_id = gen_handler_id();
|
||||
let sort_delegate = GridViewSortDelegateImpl {
|
||||
editor_delegate: delegate.clone(),
|
||||
view_revision_pad: pad,
|
||||
};
|
||||
let task_scheduler = delegate.get_task_scheduler();
|
||||
let sort_controller = Arc::new(RwLock::new(SortController::new(
|
||||
view_id,
|
||||
&handler_id,
|
||||
sort_delegate,
|
||||
task_scheduler.clone(),
|
||||
)));
|
||||
task_scheduler
|
||||
.write()
|
||||
.await
|
||||
.register_handler(SortTaskHandler::new(handler_id, sort_controller.clone()));
|
||||
|
||||
sort_controller
|
||||
}
|
||||
|
||||
fn gen_handler_id() -> String {
|
||||
nanoid!(10)
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::entities::{
|
||||
AlterFilterParams, CreateRowParams, DeleteFilterParams, DeleteGroupParams, GridSettingPB, InsertGroupParams,
|
||||
MoveGroupParams, RepeatedGroupPB, RowPB,
|
||||
AlterFilterParams, AlterSortParams, CreateRowParams, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
|
||||
GridSettingPB, InsertGroupParams, MoveGroupParams, RepeatedGroupPB, RowPB,
|
||||
};
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::block_manager::GridBlockEvent;
|
||||
use crate::services::filter::FilterType;
|
||||
use crate::services::persistence::rev_sqlite::{
|
||||
SQLiteGridRevisionSnapshotPersistence, SQLiteGridViewRevisionPersistence,
|
||||
@ -16,6 +17,7 @@ use flowy_revision::{RevisionManager, RevisionPersistence, RevisionPersistenceCo
|
||||
use grid_rev_model::{FieldRevision, FilterRevision, RowChangeset, RowRevision};
|
||||
use lib_infra::future::Fut;
|
||||
use lib_infra::ref_map::RefCountHashMap;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{broadcast, RwLock};
|
||||
|
||||
@ -23,7 +25,7 @@ pub struct GridViewManager {
|
||||
grid_id: String,
|
||||
user: Arc<dyn GridUser>,
|
||||
delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
view_editors: RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>,
|
||||
view_editors: Arc<RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>>,
|
||||
}
|
||||
|
||||
impl GridViewManager {
|
||||
@ -31,8 +33,10 @@ impl GridViewManager {
|
||||
grid_id: String,
|
||||
user: Arc<dyn GridUser>,
|
||||
delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
block_event_rx: broadcast::Receiver<GridBlockEvent>,
|
||||
) -> FlowyResult<Self> {
|
||||
let view_editors = RwLock::new(RefCountHashMap::default());
|
||||
let view_editors = Arc::new(RwLock::new(RefCountHashMap::default()));
|
||||
listen_on_grid_block_event(block_event_rx, view_editors.clone());
|
||||
Ok(Self {
|
||||
grid_id,
|
||||
user,
|
||||
@ -49,9 +53,24 @@ impl GridViewManager {
|
||||
Ok(self.get_view_editor(view_id).await?.notifier.subscribe())
|
||||
}
|
||||
|
||||
pub async fn filter_rows(&self, block_id: &str, rows: Vec<Arc<RowRevision>>) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let editor = self.get_default_view_editor().await?;
|
||||
let rows = editor.filter_rows(block_id, rows).await;
|
||||
pub async fn get_row_revs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let mut row_revs = self.delegate.get_row_revs(Some(vec![block_id.to_owned()])).await;
|
||||
if let Ok(view_editor) = self.get_view_editor(view_id).await {
|
||||
view_editor.sort_rows(&mut row_revs).await;
|
||||
}
|
||||
Ok(row_revs)
|
||||
}
|
||||
|
||||
pub async fn filter_rows(
|
||||
&self,
|
||||
view_id: &str,
|
||||
block_id: &str,
|
||||
rows: Vec<Arc<RowRevision>>,
|
||||
) -> FlowyResult<Vec<Arc<RowRevision>>> {
|
||||
let rows = match self.get_view_editor(view_id).await {
|
||||
Ok(view_editor) => view_editor.filter_rows(block_id, rows).await,
|
||||
Err(_) => rows,
|
||||
};
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
@ -117,15 +136,25 @@ impl GridViewManager {
|
||||
}
|
||||
|
||||
pub async fn create_or_update_filter(&self, params: AlterFilterParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let view_editor = self.get_view_editor(¶ms.view_id).await?;
|
||||
view_editor.insert_view_filter(params).await
|
||||
}
|
||||
|
||||
pub async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let view_editor = self.get_view_editor(¶ms.view_id).await?;
|
||||
view_editor.delete_view_filter(params).await
|
||||
}
|
||||
|
||||
pub async fn create_or_update_sort(&self, params: AlterSortParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_view_editor(¶ms.view_id).await?;
|
||||
view_editor.insert_view_sort(params).await
|
||||
}
|
||||
|
||||
pub async fn delete_sort(&self, params: DeleteSortParams) -> FlowyResult<()> {
|
||||
let view_editor = self.get_view_editor(¶ms.view_id).await?;
|
||||
view_editor.delete_view_sort(params).await
|
||||
}
|
||||
|
||||
pub async fn load_groups(&self) -> FlowyResult<RepeatedGroupPB> {
|
||||
let view_editor = self.get_default_view_editor().await?;
|
||||
let groups = view_editor.load_view_groups().await?;
|
||||
@ -225,6 +254,27 @@ impl GridViewManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn listen_on_grid_block_event(
|
||||
mut block_event_rx: broadcast::Receiver<GridBlockEvent>,
|
||||
view_editors: Arc<RwLock<RefCountHashMap<Arc<GridViewRevisionEditor>>>>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
while let Ok(event) = block_event_rx.recv().await {
|
||||
let read_guard = view_editors.read().await;
|
||||
let view_editors = read_guard.values();
|
||||
let event = if view_editors.len() == 1 {
|
||||
Cow::Owned(event)
|
||||
} else {
|
||||
Cow::Borrowed(&event)
|
||||
};
|
||||
for view_editor in view_editors.iter() {
|
||||
view_editor.handle_block_event(event.clone()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
pub async fn make_grid_view_rev_manager(
|
||||
user: &Arc<dyn GridUser>,
|
||||
view_id: &str,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::entities::{GridLayout, GridLayoutPB, GridSettingPB};
|
||||
use crate::services::filter::{FilterDelegate, FilterType};
|
||||
use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter};
|
||||
use crate::services::row::GridBlock;
|
||||
use crate::services::row::GridBlockRowRevision;
|
||||
use crate::services::sort::{SortDelegate, SortType};
|
||||
use crate::services::view_editor::GridViewEditorDelegate;
|
||||
use bytes::Bytes;
|
||||
use flowy_database::ConnectionPool;
|
||||
@ -12,7 +13,9 @@ use flowy_revision::{
|
||||
};
|
||||
use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
|
||||
use flowy_sync::util::make_operations_from_revisions;
|
||||
use grid_rev_model::{FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, RowRevision};
|
||||
use grid_rev_model::{
|
||||
FieldRevision, FieldTypeRevision, FilterRevision, GroupConfigurationRevision, RowRevision, SortRevision,
|
||||
};
|
||||
use lib_infra::future::{to_fut, Fut, FutureResult};
|
||||
use lib_ot::core::EmptyAttributes;
|
||||
use std::sync::Arc;
|
||||
@ -132,11 +135,11 @@ pub(crate) struct GridViewFilterDelegateImpl {
|
||||
}
|
||||
|
||||
impl FilterDelegate for GridViewFilterDelegateImpl {
|
||||
fn get_filter_rev(&self, filter_id: FilterType) -> Fut<Option<Arc<FilterRevision>>> {
|
||||
fn get_filter_rev(&self, filter_type: FilterType) -> Fut<Option<Arc<FilterRevision>>> {
|
||||
let pad = self.view_revision_pad.clone();
|
||||
to_fut(async move {
|
||||
let field_type_rev: FieldTypeRevision = filter_id.field_type.into();
|
||||
let mut filters = pad.read().await.get_filters(&filter_id.field_id, &field_type_rev);
|
||||
let field_type_rev: FieldTypeRevision = filter_type.field_type.into();
|
||||
let mut filters = pad.read().await.get_filters(&filter_type.field_id, &field_type_rev);
|
||||
if filters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@ -154,7 +157,7 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
|
||||
self.editor_delegate.get_field_revs(field_ids)
|
||||
}
|
||||
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlock>> {
|
||||
fn get_blocks(&self) -> Fut<Vec<GridBlockRowRevision>> {
|
||||
self.editor_delegate.get_blocks()
|
||||
}
|
||||
|
||||
@ -162,3 +165,26 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
|
||||
self.editor_delegate.get_row_rev(row_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct GridViewSortDelegateImpl {
|
||||
pub(crate) editor_delegate: Arc<dyn GridViewEditorDelegate>,
|
||||
pub(crate) view_revision_pad: Arc<RwLock<GridViewRevisionPad>>,
|
||||
}
|
||||
|
||||
impl SortDelegate for GridViewSortDelegateImpl {
|
||||
fn get_sort_rev(&self, sort_type: SortType) -> Fut<Vec<Arc<SortRevision>>> {
|
||||
let pad = self.view_revision_pad.clone();
|
||||
to_fut(async move {
|
||||
let field_type_rev: FieldTypeRevision = sort_type.field_type.into();
|
||||
pad.read().await.get_sorts(&sort_type.field_id, &field_type_rev)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>> {
|
||||
self.editor_delegate.get_field_rev(field_id)
|
||||
}
|
||||
|
||||
fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>> {
|
||||
self.editor_delegate.get_field_revs(field_ids)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
|
||||
use crate::grid::block_test::util::GridRowTestBuilder;
|
||||
use crate::grid::grid_editor::GridEditorTest;
|
||||
|
||||
use flowy_grid::entities::{CellPathParams, CreateRowParams, FieldType, GridLayout, RowPB};
|
||||
use flowy_grid::services::field::*;
|
||||
use flowy_grid::services::row::GridBlockRow;
|
||||
use grid_rev_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@ -82,28 +82,27 @@ impl GridRowTest {
|
||||
layout: GridLayout::Table,
|
||||
};
|
||||
let row_order = self.editor.create_row(params).await.unwrap();
|
||||
self.row_order_by_row_id
|
||||
.insert(row_order.row_id().to_owned(), row_order);
|
||||
self.row_by_row_id.insert(row_order.row_id().to_owned(), row_order);
|
||||
self.row_revs = self.get_row_revs().await;
|
||||
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
||||
}
|
||||
RowScript::CreateRow { row_rev } => {
|
||||
let row_orders = self.editor.insert_rows(vec![row_rev]).await.unwrap();
|
||||
for row_order in row_orders {
|
||||
self.row_order_by_row_id
|
||||
.insert(row_order.row_id().to_owned(), row_order);
|
||||
self.row_by_row_id.insert(row_order.row_id().to_owned(), row_order);
|
||||
}
|
||||
self.row_revs = self.get_row_revs().await;
|
||||
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
||||
}
|
||||
RowScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
|
||||
RowScript::DeleteRows { row_ids } => {
|
||||
let row_orders = row_ids
|
||||
let row_pbs = row_ids
|
||||
.into_iter()
|
||||
.map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
|
||||
.map(|row_id| self.row_by_row_id.get(&row_id).unwrap().clone())
|
||||
.collect::<Vec<RowPB>>();
|
||||
|
||||
self.editor.delete_rows(row_orders).await.unwrap();
|
||||
let block_rows = block_from_row_pbs(row_pbs);
|
||||
self.editor.delete_rows(block_rows).await.unwrap();
|
||||
self.row_revs = self.get_row_revs().await;
|
||||
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
||||
}
|
||||
@ -270,6 +269,19 @@ impl GridRowTest {
|
||||
}
|
||||
}
|
||||
|
||||
fn block_from_row_pbs(row_orders: Vec<RowPB>) -> Vec<GridBlockRow> {
|
||||
let mut map: HashMap<String, GridBlockRow> = HashMap::new();
|
||||
row_orders.into_iter().for_each(|row_pb| {
|
||||
let block_id = row_pb.block_id().to_owned();
|
||||
let cloned_block_id = block_id.clone();
|
||||
map.entry(block_id)
|
||||
.or_insert_with(|| GridBlockRow::new(cloned_block_id, vec![]))
|
||||
.row_ids
|
||||
.push(row_pb.id);
|
||||
});
|
||||
map.into_values().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
impl std::ops::Deref for GridRowTest {
|
||||
type Target = GridEditorTest;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::grid::filter_test::script::FilterScript::*;
|
||||
use crate::grid::filter_test::script::GridFilterTest;
|
||||
use flowy_grid::entities::CheckboxFilterCondition;
|
||||
use flowy_grid::entities::CheckboxFilterConditionPB;
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_checkbox_is_check_test() {
|
||||
@ -9,7 +9,7 @@ async fn grid_filter_checkbox_is_check_test() {
|
||||
// The initial number of checked is 2
|
||||
let scripts = vec![
|
||||
CreateCheckboxFilter {
|
||||
condition: CheckboxFilterCondition::IsChecked,
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
},
|
||||
AssertFilterChanged {
|
||||
visible_row_len: 0,
|
||||
@ -24,7 +24,7 @@ async fn grid_filter_checkbox_is_uncheck_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateCheckboxFilter {
|
||||
condition: CheckboxFilterCondition::IsUnChecked,
|
||||
condition: CheckboxFilterConditionPB::IsUnChecked,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
];
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::grid::filter_test::script::FilterScript::*;
|
||||
use crate::grid::filter_test::script::GridFilterTest;
|
||||
use flowy_grid::entities::ChecklistFilterCondition;
|
||||
use flowy_grid::entities::ChecklistFilterConditionPB;
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_checklist_is_incomplete_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterCondition::IsIncomplete,
|
||||
condition: ChecklistFilterConditionPB::IsIncomplete,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
];
|
||||
@ -19,7 +19,7 @@ async fn grid_filter_checklist_is_complete_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterCondition::IsComplete,
|
||||
condition: ChecklistFilterConditionPB::IsComplete,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 1 },
|
||||
];
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::grid::filter_test::script::FilterScript::*;
|
||||
use crate::grid::filter_test::script::GridFilterTest;
|
||||
use flowy_grid::entities::DateFilterCondition;
|
||||
use flowy_grid::entities::DateFilterConditionPB;
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_date_is_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateIs,
|
||||
condition: DateFilterConditionPB::DateIs,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
@ -22,7 +22,7 @@ async fn grid_filter_date_after_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateAfter,
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
@ -37,7 +37,7 @@ async fn grid_filter_date_on_or_after_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateOnOrAfter,
|
||||
condition: DateFilterConditionPB::DateOnOrAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
@ -52,7 +52,7 @@ async fn grid_filter_date_on_or_before_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateOnOrBefore,
|
||||
condition: DateFilterConditionPB::DateOnOrBefore,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
@ -67,7 +67,7 @@ async fn grid_filter_date_within_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateWithIn,
|
||||
condition: DateFilterConditionPB::DateWithIn,
|
||||
start: Some(1647251762),
|
||||
end: Some(1668704685),
|
||||
timestamp: None,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user