chore: add documentation

This commit is contained in:
appflowy 2022-07-16 11:53:39 +08:00
parent e01a0cf8a5
commit a73987456e
29 changed files with 161 additions and 144 deletions

View File

@ -6,7 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'block_listener.dart'; import 'block_listener.dart';
class GridBlockCacheService { class GridBlockCache {
final String gridId; final String gridId;
final GridBlock block; final GridBlock block;
late GridRowsCache _rowCache; late GridRowsCache _rowCache;
@ -15,7 +15,7 @@ class GridBlockCacheService {
List<GridRow> get rows => _rowCache.rows; List<GridRow> get rows => _rowCache.rows;
GridRowsCache get rowCache => _rowCache; GridRowsCache get rowCache => _rowCache;
GridBlockCacheService({ GridBlockCache({
required this.gridId, required this.gridId,
required this.block, required this.block,
required GridFieldCache fieldCache, required GridFieldCache fieldCache,
@ -23,7 +23,7 @@ class GridBlockCacheService {
_rowCache = GridRowsCache( _rowCache = GridRowsCache(
gridId: gridId, gridId: gridId,
block: block, block: block,
delegate: GridRowCacheDelegateImpl(fieldCache), notifier: GridRowCacheFieldNotifierImpl(fieldCache),
); );
_listener = GridBlockListener(blockId: block.id); _listener = GridBlockListener(blockId: block.id);

View File

@ -1,25 +1,29 @@
part of 'cell_service.dart'; part of 'cell_service.dart';
typedef GridCellMap = LinkedHashMap<String, GridCell>; typedef GridCellMap = LinkedHashMap<String, GridCellIdentifier>;
class _GridCellCacheItem { class _GridCellCacheValue {
GridCellId key; GridCellCacheKey key;
dynamic object; dynamic object;
_GridCellCacheItem({ _GridCellCacheValue({
required this.key, required this.key,
required this.object, required this.object,
}); });
} }
class GridCellId { /// Use to index the cell in the grid.
/// We use [fieldId + rowId] to identify the cell.
class GridCellCacheKey {
final String fieldId; final String fieldId;
final String rowId; final String rowId;
GridCellId({ GridCellCacheKey({
required this.fieldId, required this.fieldId,
required this.rowId, required this.rowId,
}); });
} }
/// GridCellsCache is used to cache cell data of each Grid.
/// We use GridCellCacheKey to index the cell in the cache.
class GridCellsCache { class GridCellsCache {
final String gridId; final String gridId;
@ -33,29 +37,28 @@ class GridCellsCache {
_cellDataByFieldId.remove(fieldId); _cellDataByFieldId.remove(fieldId);
} }
void insert<T extends _GridCellCacheItem>(T item) { void insert<T extends _GridCellCacheValue>(T value) {
var map = _cellDataByFieldId[item.key.fieldId]; var map = _cellDataByFieldId[value.key.fieldId];
if (map == null) { if (map == null) {
_cellDataByFieldId[item.key.fieldId] = {}; _cellDataByFieldId[value.key.fieldId] = {};
map = _cellDataByFieldId[item.key.fieldId]; map = _cellDataByFieldId[value.key.fieldId];
} }
map![item.key.rowId] = item.object; map![value.key.rowId] = value.object;
} }
T? get<T>(GridCellId key) { T? get<T>(GridCellCacheKey key) {
final map = _cellDataByFieldId[key.fieldId]; final map = _cellDataByFieldId[key.fieldId];
if (map == null) { if (map == null) {
return null; return null;
} else { } else {
final object = map[key.rowId]; final value = map[key.rowId];
if (object is T) { if (value is T) {
return object; return value;
} else { } else {
if (object != null) { if (value != null) {
Log.error("Cache data type does not match the cache data type"); Log.error("Expected value type: $T, but receive $value");
} }
return null; return null;
} }
} }

View File

@ -5,40 +5,22 @@ abstract class IGridCellDataConfig {
bool get reloadOnFieldChanged; bool get reloadOnFieldChanged;
} }
class GridCellDataConfig implements IGridCellDataConfig {
@override
final bool reloadOnFieldChanged;
const GridCellDataConfig({
this.reloadOnFieldChanged = false,
});
}
abstract class IGridCellDataLoader<T> {
Future<T?> loadData();
IGridCellDataConfig get config;
}
abstract class ICellDataParser<T> { abstract class ICellDataParser<T> {
T? parserData(List<int> data); T? parserData(List<int> data);
} }
class GridCellDataLoader<T> extends IGridCellDataLoader<T> { class GridCellDataLoader<T> {
final CellService service = CellService(); final CellService service = CellService();
final GridCell gridCell; final GridCellIdentifier gridCell;
final ICellDataParser<T> parser; final ICellDataParser<T> parser;
final bool reloadOnFieldChanged;
@override
final IGridCellDataConfig config;
GridCellDataLoader({ GridCellDataLoader({
required this.gridCell, required this.gridCell,
required this.parser, required this.parser,
this.config = const GridCellDataConfig(), this.reloadOnFieldChanged = false,
}); });
@override
Future<T?> loadData() { Future<T?> loadData() {
final fut = service.getCell( final fut = service.getCell(
gridId: gridCell.gridId, gridId: gridCell.gridId,

View File

@ -1,11 +1,13 @@
part of 'cell_service.dart'; part of 'cell_service.dart';
/// Save the cell data to disk
/// You can extend this class to do custom operations. For example, the DateCellDataPersistence.
abstract class IGridCellDataPersistence<D> { abstract class IGridCellDataPersistence<D> {
Future<Option<FlowyError>> save(D data); Future<Option<FlowyError>> save(D data);
} }
class CellDataPersistence implements IGridCellDataPersistence<String> { class CellDataPersistence implements IGridCellDataPersistence<String> {
final GridCell gridCell; final GridCellIdentifier gridCell;
CellDataPersistence({ CellDataPersistence({
required this.gridCell, required this.gridCell,
@ -36,7 +38,7 @@ class CalendarData with _$CalendarData {
} }
class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> { class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
final GridCell gridCell; final GridCellIdentifier gridCell;
DateCellDataPersistence({ DateCellDataPersistence({
required this.gridCell, required this.gridCell,
}); });
@ -61,7 +63,7 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
} }
} }
CellIdentifierPayload _cellIdentifier(GridCell gridCell) { CellIdentifierPayload _cellIdentifier(GridCellIdentifier gridCell) {
return CellIdentifierPayload.create() return CellIdentifierPayload.create()
..gridId = gridCell.gridId ..gridId = gridCell.gridId
..fieldId = gridCell.field.id ..fieldId = gridCell.field.id

View File

@ -8,6 +8,8 @@ abstract class GridFieldChangedNotifier {
void dispose(); void dispose();
} }
/// Grid's cell helper wrapper that enables each cell will get notified when the corresponding field was changed.
/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
class GridCellFieldNotifier { class GridCellFieldNotifier {
/// fieldId: {objectId: callback} /// fieldId: {objectId: callback}
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {}; final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
@ -27,7 +29,8 @@ class GridCellFieldNotifier {
); );
} }
void addFieldListener(GridCellId cacheKey, VoidCallback onFieldChanged) { ///
void register(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
var map = _fieldListenerByFieldId[cacheKey.fieldId]; var map = _fieldListenerByFieldId[cacheKey.fieldId];
if (map == null) { if (map == null) {
_fieldListenerByFieldId[cacheKey.fieldId] = {}; _fieldListenerByFieldId[cacheKey.fieldId] = {};
@ -43,7 +46,7 @@ class GridCellFieldNotifier {
} }
} }
void removeFieldListener(GridCellId cacheKey, VoidCallback fn) { void unregister(GridCellCacheKey cacheKey, VoidCallback fn) {
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId]; var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
final index = callbacks?.indexWhere((callback) => callback == fn); final index = callbacks?.indexWhere((callback) => callback == fn);
if (index != null && index != -1) { if (index != null && index != -1) {

View File

@ -18,6 +18,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'dart:convert' show utf8; import 'dart:convert' show utf8;
import '../../field/type_option/type_option_service.dart';
import 'cell_field_notifier.dart'; import 'cell_field_notifier.dart';
part 'cell_service.freezed.dart'; part 'cell_service.freezed.dart';
part 'cell_data_loader.dart'; part 'cell_data_loader.dart';
@ -57,18 +58,20 @@ class CellService {
} }
} }
/// Id of the cell
/// We can locate the cell by using gridId + rowId + field.id.
@freezed @freezed
class GridCell with _$GridCell { class GridCellIdentifier with _$GridCellIdentifier {
const factory GridCell({ const factory GridCellIdentifier({
required String gridId, required String gridId,
required String rowId, required String rowId,
required Field field, required Field field,
}) = _GridCell; }) = _GridCellIdentifier;
// ignore: unused_element // ignore: unused_element
const GridCell._(); const GridCellIdentifier._();
String cellId() { ValueKey key() {
return rowId + field.id + "${field.fieldType}"; return ValueKey(rowId + field.id + "${field.fieldType}");
} }
} }

View File

@ -6,12 +6,12 @@ typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>
typedef GridURLCellController = IGridCellController<URLCellData, String>; typedef GridURLCellController = IGridCellController<URLCellData, String>;
class GridCellControllerBuilder { class GridCellControllerBuilder {
final GridCell _gridCell; final GridCellIdentifier _gridCell;
final GridCellsCache _cellCache; final GridCellsCache _cellCache;
final GridFieldCache _fieldCache; final GridFieldCache _fieldCache;
GridCellControllerBuilder( GridCellControllerBuilder(
{required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache}) {required GridCellIdentifier gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
: _cellCache = cellCache, : _cellCache = cellCache,
_fieldCache = fieldCache, _fieldCache = fieldCache,
_gridCell = gridCell; _gridCell = gridCell;
@ -29,34 +29,33 @@ class GridCellControllerBuilder {
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
); );
case FieldType.DateTime: case FieldType.DateTime:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, gridCell: _gridCell,
parser: DateCellDataParser(), parser: DateCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true), reloadOnFieldChanged: true,
); );
return GridDateCellController( return GridDateCellController(
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
); );
case FieldType.Number: case FieldType.Number:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, gridCell: _gridCell,
parser: StringCellDataParser(), parser: StringCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true),
); );
return GridCellController( return GridCellController(
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
); );
case FieldType.RichText: case FieldType.RichText:
@ -68,7 +67,7 @@ class GridCellControllerBuilder {
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
); );
case FieldType.MultiSelect: case FieldType.MultiSelect:
@ -76,14 +75,14 @@ class GridCellControllerBuilder {
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, gridCell: _gridCell,
parser: SelectOptionCellDataParser(), parser: SelectOptionCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true), reloadOnFieldChanged: true,
); );
return GridSelectOptionCellController( return GridSelectOptionCellController(
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
); );
@ -96,7 +95,7 @@ class GridCellControllerBuilder {
gridCell: _gridCell, gridCell: _gridCell,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellFieldNotifier: cellFieldNotifier, fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
); );
} }
@ -104,17 +103,21 @@ class GridCellControllerBuilder {
} }
} }
// T: the type of the CellData /// IGridCellController is used to manipulate the cell and receive notifications.
// D: the type of the data that will be saved to disk /// * Read/Write cell data
/// * Listen on field/cell notifications.
///
/// Generic T represents the type of the cell data.
/// Generic D represents the type of data that will be saved to the disk
///
// ignore: must_be_immutable // ignore: must_be_immutable
class IGridCellController<T, D> extends Equatable { class IGridCellController<T, D> extends Equatable {
final GridCell gridCell; final GridCellIdentifier gridCell;
final GridCellsCache _cellsCache; final GridCellsCache _cellsCache;
final GridCellId _cacheKey; final GridCellCacheKey _cacheKey;
final FieldService _fieldService; final FieldService _fieldService;
final GridCellFieldNotifier _cellFieldNotifier; final GridCellFieldNotifier _fieldNotifier;
// final GridCellFieldNotifier _fieldNotifier; final GridCellDataLoader<T> _cellDataLoader;
final IGridCellDataLoader<T> _cellDataLoader;
final IGridCellDataPersistence<D> _cellDataPersistence; final IGridCellDataPersistence<D> _cellDataPersistence;
late final CellListener _cellListener; late final CellListener _cellListener;
@ -124,28 +127,27 @@ class IGridCellController<T, D> extends Equatable {
VoidCallback? _onFieldChangedFn; VoidCallback? _onFieldChangedFn;
Timer? _loadDataOperation; Timer? _loadDataOperation;
Timer? _saveDataOperation; Timer? _saveDataOperation;
bool isDispose = false; bool _isDispose = false;
IGridCellController({ IGridCellController({
required this.gridCell, required this.gridCell,
required GridCellsCache cellCache, required GridCellsCache cellCache,
required GridCellFieldNotifier cellFieldNotifier, required GridCellFieldNotifier fieldNotifier,
required IGridCellDataLoader<T> cellDataLoader, required GridCellDataLoader<T> cellDataLoader,
required IGridCellDataPersistence<D> cellDataPersistence, required IGridCellDataPersistence<D> cellDataPersistence,
// required GridFieldChangedNotifier notifierDelegate,
}) : _cellsCache = cellCache, }) : _cellsCache = cellCache,
_cellDataLoader = cellDataLoader, _cellDataLoader = cellDataLoader,
_cellDataPersistence = cellDataPersistence, _cellDataPersistence = cellDataPersistence,
_cellFieldNotifier = cellFieldNotifier, _fieldNotifier = fieldNotifier,
_fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id), _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
_cacheKey = GridCellId(rowId: gridCell.rowId, fieldId: gridCell.field.id); _cacheKey = GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
IGridCellController<T, D> clone() { IGridCellController<T, D> clone() {
return IGridCellController( return IGridCellController(
gridCell: gridCell, gridCell: gridCell,
cellDataLoader: _cellDataLoader, cellDataLoader: _cellDataLoader,
cellCache: _cellsCache, cellCache: _cellsCache,
cellFieldNotifier: _cellFieldNotifier, fieldNotifier: _fieldNotifier,
cellDataPersistence: _cellDataPersistence); cellDataPersistence: _cellDataPersistence);
} }
@ -191,16 +193,18 @@ class IGridCellController<T, D> extends Equatable {
onCellFieldChanged(); onCellFieldChanged();
} }
if (_cellDataLoader.config.reloadOnFieldChanged) { if (_cellDataLoader.reloadOnFieldChanged) {
_loadData(); _loadData();
} }
}; };
_cellFieldNotifier.addFieldListener(_cacheKey, _onFieldChangedFn!); _fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
/// Notify the listener, the cell data was changed. /// Notify the listener, the cell data was changed.
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value); onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_cellDataNotifier?.addListener(onCellChangedFn); _cellDataNotifier?.addListener(onCellChangedFn);
// Return the function pointer that can be used when calling removeListener.
return onCellChangedFn; return onCellChangedFn;
} }
@ -208,22 +212,38 @@ class IGridCellController<T, D> extends Equatable {
_cellDataNotifier?.removeListener(fn); _cellDataNotifier?.removeListener(fn);
} }
T? getCellData({bool loadIfNoCache = true}) { /// Return the cell data.
/// The cell data will be read from the Cache first, and load from disk if it does not exist.
/// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data.
T? getCellData({bool loadIfNotExist = true}) {
final data = _cellsCache.get(_cacheKey); final data = _cellsCache.get(_cacheKey);
if (data == null && loadIfNoCache) { if (data == null && loadIfNotExist) {
_loadData(); _loadData();
} }
return data; return data;
} }
Future<Either<FieldTypeOptionData, FlowyError>> getTypeOptionData() { /// Return the FieldTypeOptionData that can be parsed into corresponding class using the [parser].
return _fieldService.getFieldTypeOptionData(fieldType: fieldType); /// [PD] is the type that the parser return.
Future<Either<PD, FlowyError>> getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
return _fieldService.getFieldTypeOptionData(fieldType: fieldType).then((result) {
return result.fold(
(data) => parser.fromBuffer(data.typeOptionData),
(err) => right(err),
);
});
} }
/// Save the cell data to disk
/// You can set [dedeplicate] to true (default is false) to reduce the save operation.
/// It's useful when you call this method when user editing the [TextField].
/// The default debounce interval is 300 milliseconds.
void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async { void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) { if (deduplicate) {
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
_saveDataOperation?.cancel();
_saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
final result = await _cellDataPersistence.save(data); final result = await _cellDataPersistence.save(data);
if (resultCallback != null) { if (resultCallback != null) {
resultCallback(result); resultCallback(result);
@ -238,28 +258,30 @@ class IGridCellController<T, D> extends Equatable {
} }
void _loadData() { void _loadData() {
_saveDataOperation?.cancel();
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 10), () { _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
_cellDataLoader.loadData().then((data) { _cellDataLoader.loadData().then((data) {
_cellDataNotifier?.value = data; _cellDataNotifier?.value = data;
_cellsCache.insert(_GridCellCacheItem(key: _cacheKey, object: data)); _cellsCache.insert(_GridCellCacheValue(key: _cacheKey, object: data));
}); });
}); });
} }
void dispose() { void dispose() {
if (isDispose) { if (_isDispose) {
Log.error("$this should only dispose once"); Log.error("$this should only dispose once");
return; return;
} }
isDispose = true; _isDispose = true;
_cellListener.stop(); _cellListener.stop();
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_saveDataOperation?.cancel(); _saveDataOperation?.cancel();
_cellDataNotifier = null; _cellDataNotifier = null;
if (_onFieldChangedFn != null) { if (_onFieldChangedFn != null) {
_cellFieldNotifier.removeFieldListener(_cacheKey, _onFieldChangedFn!); _fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
_onFieldChangedFn = null; _onFieldChangedFn = null;
} }
} }

View File

@ -184,7 +184,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
}) = _SelectOptionEditorState; }) = _SelectOptionEditorState;
factory SelectOptionEditorState.initial(GridSelectOptionCellController context) { factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
final data = context.getCellData(loadIfNoCache: false); final data = context.getCellData(loadIfNotExist: false);
return SelectOptionEditorState( return SelectOptionEditorState(
options: data?.options ?? [], options: data?.options ?? [],
allOptions: data?.options ?? [], allOptions: data?.options ?? [],

View File

@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'cell_service/cell_service.dart'; import 'cell_service/cell_service.dart';
class SelectOptionService { class SelectOptionService {
final GridCell gridCell; final GridCellIdentifier gridCell;
SelectOptionService({required this.gridCell}); SelectOptionService({required this.gridCell});
String get gridId => gridCell.gridId; String get gridId => gridCell.gridId;

View File

@ -9,6 +9,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
part 'field_service.freezed.dart'; part 'field_service.freezed.dart';
/// FieldService consists of lots of event functions. We define the events in the backend(Rust),
/// you can find the corresponding event implementation in event_map.rs of the corresponding crate.
///
/// You could check out the rust-lib/flowy-grid/event_map.rs for more information.
class FieldService { class FieldService {
final String gridId; final String gridId;
final String fieldId; final String fieldId;

View File

@ -8,7 +8,7 @@ part 'date_bloc.freezed.dart';
typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>; typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>;
class DateTypeOptionDataParser extends TypeOptionWidgetDataParser<DateTypeOption> { class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
@override @override
DateTypeOption fromBuffer(List<int> buffer) { DateTypeOption fromBuffer(List<int> buffer) {
return DateTypeOption.fromBuffer(buffer); return DateTypeOption.fromBuffer(buffer);

View File

@ -18,7 +18,7 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
gridId: fieldContext.gridId, gridId: fieldContext.gridId,
fieldId: fieldContext.field.id, fieldId: fieldContext.field.id,
), ),
super(dataBuilder: dataBuilder, fieldContext: fieldContext); super(dataParser: dataBuilder, fieldContext: fieldContext);
@override @override
List<SelectOption> Function(SelectOption) get deleteOption { List<SelectOption> Function(SelectOption) get deleteOption {
@ -71,7 +71,7 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
} }
} }
class MultiSelectTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<MultiSelectTypeOption> { class MultiSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<MultiSelectTypeOption> {
@override @override
MultiSelectTypeOption fromBuffer(List<int> buffer) { MultiSelectTypeOption fromBuffer(List<int> buffer) {
return MultiSelectTypeOption.fromBuffer(buffer); return MultiSelectTypeOption.fromBuffer(buffer);

View File

@ -10,7 +10,7 @@ part 'number_bloc.freezed.dart';
typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>; typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
class NumberTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<NumberTypeOption> { class NumberTypeOptionWidgetDataParser extends TypeOptionDataParser<NumberTypeOption> {
@override @override
NumberTypeOption fromBuffer(List<int> buffer) { NumberTypeOption fromBuffer(List<int> buffer) {
return NumberTypeOption.fromBuffer(buffer); return NumberTypeOption.fromBuffer(buffer);

View File

@ -18,7 +18,7 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
gridId: fieldContext.gridId, gridId: fieldContext.gridId,
fieldId: fieldContext.field.id, fieldId: fieldContext.field.id,
), ),
super(dataBuilder: dataBuilder, fieldContext: fieldContext); super(dataParser: dataBuilder, fieldContext: fieldContext);
@override @override
List<SelectOption> Function(SelectOption) get deleteOption { List<SelectOption> Function(SelectOption) get deleteOption {
@ -71,7 +71,7 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
} }
} }
class SingleSelectTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<SingleSelectTypeOption> { class SingleSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<SingleSelectTypeOption> {
@override @override
SingleSelectTypeOption fromBuffer(List<int> buffer) { SingleSelectTypeOption fromBuffer(List<int> buffer) {
return SingleSelectTypeOption.fromBuffer(buffer); return SingleSelectTypeOption.fromBuffer(buffer);

View File

@ -33,17 +33,17 @@ class TypeOptionService {
} }
} }
abstract class TypeOptionWidgetDataParser<T> { abstract class TypeOptionDataParser<T> {
T fromBuffer(List<int> buffer); T fromBuffer(List<int> buffer);
} }
class TypeOptionWidgetContext<T extends GeneratedMessage> { class TypeOptionWidgetContext<T extends GeneratedMessage> {
T? _typeOptionObject; T? _typeOptionObject;
final GridFieldContext _fieldContext; final GridFieldContext _fieldContext;
final TypeOptionWidgetDataParser<T> dataBuilder; final TypeOptionDataParser<T> dataParser;
TypeOptionWidgetContext({ TypeOptionWidgetContext({
required this.dataBuilder, required this.dataParser,
required GridFieldContext fieldContext, required GridFieldContext fieldContext,
}) : _fieldContext = fieldContext; }) : _fieldContext = fieldContext;
@ -56,7 +56,7 @@ class TypeOptionWidgetContext<T extends GeneratedMessage> {
return _typeOptionObject!; return _typeOptionObject!;
} }
final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData); final T object = dataParser.fromBuffer(_fieldContext.typeOptionData);
_typeOptionObject = object; _typeOptionObject = object;
return object; return object;
} }
@ -77,7 +77,7 @@ class TypeOptionContext2<T> {
final Field field; final Field field;
final FieldService _fieldService; final FieldService _fieldService;
T? _data; T? _data;
final TypeOptionWidgetDataParser dataBuilder; final TypeOptionDataParser dataBuilder;
TypeOptionContext2({ TypeOptionContext2({
required this.gridId, required this.gridId,

View File

@ -20,7 +20,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
final GridFieldCache fieldCache; final GridFieldCache fieldCache;
// key: the block id // key: the block id
final LinkedHashMap<String, GridBlockCacheService> _blocks; final LinkedHashMap<String, GridBlockCache> _blocks;
List<GridRow> get rows { List<GridRow> get rows {
final List<GridRow> rows = []; final List<GridRow> rows = [];
@ -69,7 +69,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
} }
GridRowsCache? getRowCache(String blockId, String rowId) { GridRowsCache? getRowCache(String blockId, String rowId) {
final GridBlockCacheService? blockCache = _blocks[blockId]; final GridBlockCache? blockCache = _blocks[blockId];
return blockCache?.rowCache; return blockCache?.rowCache;
} }
@ -119,7 +119,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
return; return;
} }
final cache = GridBlockCacheService( final cache = GridBlockCache(
gridId: gridId, gridId: gridId,
block: block, block: block,
fieldCache: fieldCache, fieldCache: fieldCache,

View File

@ -186,11 +186,11 @@ class GridFieldCache {
} }
} }
class GridRowCacheDelegateImpl extends GridRowCacheDelegate { class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
final GridFieldCache _cache; final GridFieldCache _cache;
FieldChangesetCallback? _onChangesetFn; FieldChangesetCallback? _onChangesetFn;
FieldsCallback? _onFieldFn; FieldsCallback? _onFieldFn;
GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache; GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
@override @override
UnmodifiableListView<Field> get fields => _cache.unmodifiableFields; UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;

View File

@ -58,13 +58,13 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
@freezed @freezed
class RowDetailEvent with _$RowDetailEvent { class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial; const factory RowDetailEvent.initial() = _Initial;
const factory RowDetailEvent.didReceiveCellDatas(List<GridCell> gridCells) = _DidReceiveCellDatas; const factory RowDetailEvent.didReceiveCellDatas(List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
} }
@freezed @freezed
class RowDetailState with _$RowDetailState { class RowDetailState with _$RowDetailState {
const factory RowDetailState({ const factory RowDetailState({
required List<GridCell> gridCells, required List<GridCellIdentifier> gridCells,
}) = _RowDetailState; }) = _RowDetailState;
factory RowDetailState.initial() => RowDetailState( factory RowDetailState.initial() => RowDetailState(

View File

@ -14,7 +14,7 @@ part 'row_service.freezed.dart';
typedef RowUpdateCallback = void Function(); typedef RowUpdateCallback = void Function();
abstract class GridRowCacheDelegate { abstract class GridRowCacheFieldNotifier {
UnmodifiableListView<Field> get fields; UnmodifiableListView<Field> get fields;
void onFieldsChanged(VoidCallback callback); void onFieldsChanged(VoidCallback callback);
void onFieldChanged(void Function(Field) callback); void onFieldChanged(void Function(Field) callback);
@ -27,7 +27,7 @@ class GridRowsCache {
final _Notifier _notifier; final _Notifier _notifier;
List<GridRow> _rows = []; List<GridRow> _rows = [];
final HashMap<String, Row> _rowByRowId; final HashMap<String, Row> _rowByRowId;
final GridRowCacheDelegate _delegate; final GridRowCacheFieldNotifier _fieldNotifier;
final GridCellsCache _cellCache; final GridCellsCache _cellCache;
List<GridRow> get rows => _rows; List<GridRow> get rows => _rows;
@ -36,19 +36,19 @@ class GridRowsCache {
GridRowsCache({ GridRowsCache({
required this.gridId, required this.gridId,
required this.block, required this.block,
required GridRowCacheDelegate delegate, required GridRowCacheFieldNotifier notifier,
}) : _cellCache = GridCellsCache(gridId: gridId), }) : _cellCache = GridCellsCache(gridId: gridId),
_rowByRowId = HashMap(), _rowByRowId = HashMap(),
_notifier = _Notifier(), _notifier = _Notifier(),
_delegate = delegate { _fieldNotifier = notifier {
// //
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange())); notifier.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
delegate.onFieldChanged((field) => _cellCache.remove(field.id)); notifier.onFieldChanged((field) => _cellCache.remove(field.id));
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList(); _rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList();
} }
Future<void> dispose() async { Future<void> dispose() async {
_delegate.dispose(); _fieldNotifier.dispose();
_notifier.dispose(); _notifier.dispose();
await _cellCache.dispose(); await _cellCache.dispose();
} }
@ -195,9 +195,9 @@ class GridRowsCache {
GridCellMap _makeGridCells(String rowId, Row? row) { GridCellMap _makeGridCells(String rowId, Row? row) {
var cellDataMap = GridCellMap.new(); var cellDataMap = GridCellMap.new();
for (final field in _delegate.fields) { for (final field in _fieldNotifier.fields) {
if (field.visibility) { if (field.visibility) {
cellDataMap[field.id] = GridCell( cellDataMap[field.id] = GridCellIdentifier(
rowId: rowId, rowId: rowId,
gridId: gridId, gridId: gridId,
field: field, field: field,
@ -236,7 +236,7 @@ class GridRowsCache {
return GridRow( return GridRow(
gridId: gridId, gridId: gridId,
blockId: block.id, blockId: block.id,
fields: _delegate.fields, fields: _fieldNotifier.fields,
rowId: rowId, rowId: rowId,
height: rowHeight, height: rowHeight,
); );

View File

@ -21,15 +21,13 @@ class GridCellBuilder {
required this.fieldCache, required this.fieldCache,
}); });
GridCellWidget build(GridCell cell, {GridCellStyle? style}) { GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) {
final key = ValueKey(cell.cellId());
final cellControllerBuilder = GridCellControllerBuilder( final cellControllerBuilder = GridCellControllerBuilder(
gridCell: cell, gridCell: cell,
cellCache: cellCache, cellCache: cellCache,
fieldCache: fieldCache, fieldCache: fieldCache,
); );
final key = cell.key();
switch (cell.field.fieldType) { switch (cell.field.fieldType) {
case FieldType.Checkbox: case FieldType.Checkbox:
return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key); return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);

View File

@ -80,7 +80,7 @@ class _DateCellState extends GridCellState<DateCell> {
final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false); final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false);
calendar.show( calendar.show(
context, context,
cellContext: bloc.cellContext.clone(), cellController: bloc.cellContext.clone(),
); );
} }

View File

@ -31,16 +31,16 @@ class DateCellEditor with FlowyOverlayDelegate {
Future<void> show( Future<void> show(
BuildContext context, { BuildContext context, {
required GridDateCellController cellContext, required GridDateCellController cellController,
}) async { }) async {
DateCellEditor.remove(context); DateCellEditor.remove(context);
final result = await cellContext.getTypeOptionData(); final result = await cellController.getFieldTypeOption(DateTypeOptionDataParser());
result.fold( result.fold(
(data) { (dateTypeOption) {
final calendar = _CellCalendarWidget( final calendar = _CellCalendarWidget(
cellContext: cellContext, cellContext: cellController,
dateTypeOption: DateTypeOption.fromBuffer(data.typeOptionData), dateTypeOption: dateTypeOption,
); );
FlowyOverlay.of(context).insertWithAnchor( FlowyOverlay.of(context).insertWithAnchor(

View File

@ -7,9 +7,9 @@ import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
final GridURLCellController cellContext; final GridURLCellController cellController;
final VoidCallback completed; final VoidCallback completed;
const URLCellEditor({required this.cellContext, required this.completed, Key? key}) : super(key: key); const URLCellEditor({required this.cellController, required this.completed, Key? key}) : super(key: key);
@override @override
State<URLCellEditor> createState() => _URLCellEditorState(); State<URLCellEditor> createState() => _URLCellEditorState();
@ -21,7 +21,7 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
) { ) {
FlowyOverlay.of(context).remove(identifier()); FlowyOverlay.of(context).remove(identifier());
final editor = URLCellEditor( final editor = URLCellEditor(
cellContext: cellContext, cellController: cellContext,
completed: completed, completed: completed,
); );
@ -62,7 +62,7 @@ class _URLCellEditorState extends State<URLCellEditor> {
@override @override
void initState() { void initState() {
_cellBloc = URLCellEditorBloc(cellContext: widget.cellContext); _cellBloc = URLCellEditorBloc(cellContext: widget.cellController);
_cellBloc.add(const URLCellEditorEvent.initial()); _cellBloc.add(const URLCellEditorEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content); _controller = TextEditingController(text: _cellBloc.state.content);

View File

@ -187,7 +187,7 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
@override @override
void onTap() { void onTap() {
final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? ""; final content = cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
Clipboard.setData(ClipboardData(text: content)); Clipboard.setData(ClipboardData(text: content));
showMessageToast(LocaleKeys.grid_row_copyProperty.tr()); showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
} }

View File

@ -51,13 +51,13 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
case FieldType.Checkbox: case FieldType.Checkbox:
final context = CheckboxTypeOptionContext( final context = CheckboxTypeOptionContext(
fieldContext: fieldContext, fieldContext: fieldContext,
dataBuilder: CheckboxTypeOptionWidgetDataParser(), dataParser: CheckboxTypeOptionWidgetDataParser(),
); );
return CheckboxTypeOptionWidgetBuilder(context); return CheckboxTypeOptionWidgetBuilder(context);
case FieldType.DateTime: case FieldType.DateTime:
final context = DateTypeOptionContext( final context = DateTypeOptionContext(
fieldContext: fieldContext, fieldContext: fieldContext,
dataBuilder: DateTypeOptionDataParser(), dataParser: DateTypeOptionDataParser(),
); );
return DateTypeOptionWidgetBuilder( return DateTypeOptionWidgetBuilder(
context, context,
@ -84,7 +84,7 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
case FieldType.Number: case FieldType.Number:
final context = NumberTypeOptionContext( final context = NumberTypeOptionContext(
fieldContext: fieldContext, fieldContext: fieldContext,
dataBuilder: NumberTypeOptionWidgetDataParser(), dataParser: NumberTypeOptionWidgetDataParser(),
); );
return NumberTypeOptionWidgetBuilder( return NumberTypeOptionWidgetBuilder(
context, context,
@ -93,14 +93,14 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
case FieldType.RichText: case FieldType.RichText:
final context = RichTextTypeOptionContext( final context = RichTextTypeOptionContext(
fieldContext: fieldContext, fieldContext: fieldContext,
dataBuilder: RichTextTypeOptionWidgetDataParser(), dataParser: RichTextTypeOptionWidgetDataParser(),
); );
return RichTextTypeOptionWidgetBuilder(context); return RichTextTypeOptionWidgetBuilder(context);
case FieldType.URL: case FieldType.URL:
final context = URLTypeOptionContext( final context = URLTypeOptionContext(
fieldContext: fieldContext, fieldContext: fieldContext,
dataBuilder: URLTypeOptionWidgetDataParser(), dataParser: URLTypeOptionWidgetDataParser(),
); );
return URLTypeOptionWidgetBuilder(context); return URLTypeOptionWidgetBuilder(context);
} }

View File

@ -5,7 +5,7 @@ import 'builder.dart';
typedef CheckboxTypeOptionContext = TypeOptionWidgetContext<CheckboxTypeOption>; typedef CheckboxTypeOptionContext = TypeOptionWidgetContext<CheckboxTypeOption>;
class CheckboxTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<CheckboxTypeOption> { class CheckboxTypeOptionWidgetDataParser extends TypeOptionDataParser<CheckboxTypeOption> {
@override @override
CheckboxTypeOption fromBuffer(List<int> buffer) { CheckboxTypeOption fromBuffer(List<int> buffer) {
return CheckboxTypeOption.fromBuffer(buffer); return CheckboxTypeOption.fromBuffer(buffer);

View File

@ -5,7 +5,7 @@ import 'builder.dart';
typedef RichTextTypeOptionContext = TypeOptionWidgetContext<RichTextTypeOption>; typedef RichTextTypeOptionContext = TypeOptionWidgetContext<RichTextTypeOption>;
class RichTextTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<RichTextTypeOption> { class RichTextTypeOptionWidgetDataParser extends TypeOptionDataParser<RichTextTypeOption> {
@override @override
RichTextTypeOption fromBuffer(List<int> buffer) { RichTextTypeOption fromBuffer(List<int> buffer) {
return RichTextTypeOption.fromBuffer(buffer); return RichTextTypeOption.fromBuffer(buffer);

View File

@ -5,7 +5,7 @@ import 'builder.dart';
typedef URLTypeOptionContext = TypeOptionWidgetContext<URLTypeOption>; typedef URLTypeOptionContext = TypeOptionWidgetContext<URLTypeOption>;
class URLTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<URLTypeOption> { class URLTypeOptionWidgetDataParser extends TypeOptionDataParser<URLTypeOption> {
@override @override
URLTypeOption fromBuffer(List<int> buffer) { URLTypeOption fromBuffer(List<int> buffer) {
return URLTypeOption.fromBuffer(buffer); return URLTypeOption.fromBuffer(buffer);

View File

@ -137,7 +137,7 @@ class _PropertyList extends StatelessWidget {
} }
class _RowDetailCell extends StatelessWidget { class _RowDetailCell extends StatelessWidget {
final GridCell gridCell; final GridCellIdentifier gridCell;
final GridCellBuilder cellBuilder; final GridCellBuilder cellBuilder;
const _RowDetailCell({ const _RowDetailCell({
required this.gridCell, required this.gridCell,