mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add documentation
This commit is contained in:
parent
e01a0cf8a5
commit
a73987456e
@ -6,7 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
|
||||
import 'block_listener.dart';
|
||||
|
||||
class GridBlockCacheService {
|
||||
class GridBlockCache {
|
||||
final String gridId;
|
||||
final GridBlock block;
|
||||
late GridRowsCache _rowCache;
|
||||
@ -15,7 +15,7 @@ class GridBlockCacheService {
|
||||
List<GridRow> get rows => _rowCache.rows;
|
||||
GridRowsCache get rowCache => _rowCache;
|
||||
|
||||
GridBlockCacheService({
|
||||
GridBlockCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridFieldCache fieldCache,
|
||||
@ -23,7 +23,7 @@ class GridBlockCacheService {
|
||||
_rowCache = GridRowsCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
delegate: GridRowCacheDelegateImpl(fieldCache),
|
||||
notifier: GridRowCacheFieldNotifierImpl(fieldCache),
|
||||
);
|
||||
|
||||
_listener = GridBlockListener(blockId: block.id);
|
||||
|
@ -1,25 +1,29 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellMap = LinkedHashMap<String, GridCell>;
|
||||
typedef GridCellMap = LinkedHashMap<String, GridCellIdentifier>;
|
||||
|
||||
class _GridCellCacheItem {
|
||||
GridCellId key;
|
||||
class _GridCellCacheValue {
|
||||
GridCellCacheKey key;
|
||||
dynamic object;
|
||||
_GridCellCacheItem({
|
||||
_GridCellCacheValue({
|
||||
required this.key,
|
||||
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 rowId;
|
||||
GridCellId({
|
||||
GridCellCacheKey({
|
||||
required this.fieldId,
|
||||
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 {
|
||||
final String gridId;
|
||||
|
||||
@ -33,29 +37,28 @@ class GridCellsCache {
|
||||
_cellDataByFieldId.remove(fieldId);
|
||||
}
|
||||
|
||||
void insert<T extends _GridCellCacheItem>(T item) {
|
||||
var map = _cellDataByFieldId[item.key.fieldId];
|
||||
void insert<T extends _GridCellCacheValue>(T value) {
|
||||
var map = _cellDataByFieldId[value.key.fieldId];
|
||||
if (map == null) {
|
||||
_cellDataByFieldId[item.key.fieldId] = {};
|
||||
map = _cellDataByFieldId[item.key.fieldId];
|
||||
_cellDataByFieldId[value.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];
|
||||
if (map == null) {
|
||||
return null;
|
||||
} else {
|
||||
final object = map[key.rowId];
|
||||
if (object is T) {
|
||||
return object;
|
||||
final value = map[key.rowId];
|
||||
if (value is T) {
|
||||
return value;
|
||||
} else {
|
||||
if (object != null) {
|
||||
Log.error("Cache data type does not match the cache data type");
|
||||
if (value != null) {
|
||||
Log.error("Expected value type: $T, but receive $value");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -5,40 +5,22 @@ abstract class IGridCellDataConfig {
|
||||
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> {
|
||||
T? parserData(List<int> data);
|
||||
}
|
||||
|
||||
class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
|
||||
class GridCellDataLoader<T> {
|
||||
final CellService service = CellService();
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
final ICellDataParser<T> parser;
|
||||
|
||||
@override
|
||||
final IGridCellDataConfig config;
|
||||
final bool reloadOnFieldChanged;
|
||||
|
||||
GridCellDataLoader({
|
||||
required this.gridCell,
|
||||
required this.parser,
|
||||
this.config = const GridCellDataConfig(),
|
||||
this.reloadOnFieldChanged = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<T?> loadData() {
|
||||
final fut = service.getCell(
|
||||
gridId: gridCell.gridId,
|
||||
|
@ -1,11 +1,13 @@
|
||||
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> {
|
||||
Future<Option<FlowyError>> save(D data);
|
||||
}
|
||||
|
||||
class CellDataPersistence implements IGridCellDataPersistence<String> {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
|
||||
CellDataPersistence({
|
||||
required this.gridCell,
|
||||
@ -36,7 +38,7 @@ class CalendarData with _$CalendarData {
|
||||
}
|
||||
|
||||
class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
DateCellDataPersistence({
|
||||
required this.gridCell,
|
||||
});
|
||||
@ -61,7 +63,7 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
|
||||
}
|
||||
}
|
||||
|
||||
CellIdentifierPayload _cellIdentifier(GridCell gridCell) {
|
||||
CellIdentifierPayload _cellIdentifier(GridCellIdentifier gridCell) {
|
||||
return CellIdentifierPayload.create()
|
||||
..gridId = gridCell.gridId
|
||||
..fieldId = gridCell.field.id
|
||||
|
@ -8,6 +8,8 @@ abstract class GridFieldChangedNotifier {
|
||||
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 {
|
||||
/// fieldId: {objectId: callback}
|
||||
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];
|
||||
if (map == null) {
|
||||
_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];
|
||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
||||
if (index != null && index != -1) {
|
||||
|
@ -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 'dart:convert' show utf8;
|
||||
|
||||
import '../../field/type_option/type_option_service.dart';
|
||||
import 'cell_field_notifier.dart';
|
||||
part 'cell_service.freezed.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
|
||||
class GridCell with _$GridCell {
|
||||
const factory GridCell({
|
||||
class GridCellIdentifier with _$GridCellIdentifier {
|
||||
const factory GridCellIdentifier({
|
||||
required String gridId,
|
||||
required String rowId,
|
||||
required Field field,
|
||||
}) = _GridCell;
|
||||
}) = _GridCellIdentifier;
|
||||
|
||||
// ignore: unused_element
|
||||
const GridCell._();
|
||||
const GridCellIdentifier._();
|
||||
|
||||
String cellId() {
|
||||
return rowId + field.id + "${field.fieldType}";
|
||||
ValueKey key() {
|
||||
return ValueKey(rowId + field.id + "${field.fieldType}");
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>
|
||||
typedef GridURLCellController = IGridCellController<URLCellData, String>;
|
||||
|
||||
class GridCellControllerBuilder {
|
||||
final GridCell _gridCell;
|
||||
final GridCellIdentifier _gridCell;
|
||||
final GridCellsCache _cellCache;
|
||||
final GridFieldCache _fieldCache;
|
||||
|
||||
GridCellControllerBuilder(
|
||||
{required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
|
||||
{required GridCellIdentifier gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
|
||||
: _cellCache = cellCache,
|
||||
_fieldCache = fieldCache,
|
||||
_gridCell = gridCell;
|
||||
@ -29,34 +29,33 @@ class GridCellControllerBuilder {
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: DateCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridDateCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.Number:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.RichText:
|
||||
@ -68,7 +67,7 @@ class GridCellControllerBuilder {
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
@ -76,14 +75,14 @@ class GridCellControllerBuilder {
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: SelectOptionCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridSelectOptionCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
|
||||
@ -96,7 +95,7 @@ class GridCellControllerBuilder {
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
}
|
||||
@ -104,17 +103,21 @@ class GridCellControllerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// T: the type of the CellData
|
||||
// D: the type of the data that will be saved to disk
|
||||
/// IGridCellController is used to manipulate the cell and receive notifications.
|
||||
/// * 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
|
||||
class IGridCellController<T, D> extends Equatable {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
final GridCellsCache _cellsCache;
|
||||
final GridCellId _cacheKey;
|
||||
final GridCellCacheKey _cacheKey;
|
||||
final FieldService _fieldService;
|
||||
final GridCellFieldNotifier _cellFieldNotifier;
|
||||
// final GridCellFieldNotifier _fieldNotifier;
|
||||
final IGridCellDataLoader<T> _cellDataLoader;
|
||||
final GridCellFieldNotifier _fieldNotifier;
|
||||
final GridCellDataLoader<T> _cellDataLoader;
|
||||
final IGridCellDataPersistence<D> _cellDataPersistence;
|
||||
|
||||
late final CellListener _cellListener;
|
||||
@ -124,28 +127,27 @@ class IGridCellController<T, D> extends Equatable {
|
||||
VoidCallback? _onFieldChangedFn;
|
||||
Timer? _loadDataOperation;
|
||||
Timer? _saveDataOperation;
|
||||
bool isDispose = false;
|
||||
bool _isDispose = false;
|
||||
|
||||
IGridCellController({
|
||||
required this.gridCell,
|
||||
required GridCellsCache cellCache,
|
||||
required GridCellFieldNotifier cellFieldNotifier,
|
||||
required IGridCellDataLoader<T> cellDataLoader,
|
||||
required GridCellFieldNotifier fieldNotifier,
|
||||
required GridCellDataLoader<T> cellDataLoader,
|
||||
required IGridCellDataPersistence<D> cellDataPersistence,
|
||||
// required GridFieldChangedNotifier notifierDelegate,
|
||||
}) : _cellsCache = cellCache,
|
||||
_cellDataLoader = cellDataLoader,
|
||||
_cellDataPersistence = cellDataPersistence,
|
||||
_cellFieldNotifier = cellFieldNotifier,
|
||||
_fieldNotifier = fieldNotifier,
|
||||
_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() {
|
||||
return IGridCellController(
|
||||
gridCell: gridCell,
|
||||
cellDataLoader: _cellDataLoader,
|
||||
cellCache: _cellsCache,
|
||||
cellFieldNotifier: _cellFieldNotifier,
|
||||
fieldNotifier: _fieldNotifier,
|
||||
cellDataPersistence: _cellDataPersistence);
|
||||
}
|
||||
|
||||
@ -191,16 +193,18 @@ class IGridCellController<T, D> extends Equatable {
|
||||
onCellFieldChanged();
|
||||
}
|
||||
|
||||
if (_cellDataLoader.config.reloadOnFieldChanged) {
|
||||
if (_cellDataLoader.reloadOnFieldChanged) {
|
||||
_loadData();
|
||||
}
|
||||
};
|
||||
|
||||
_cellFieldNotifier.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
_fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
|
||||
|
||||
/// Notify the listener, the cell data was changed.
|
||||
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
|
||||
_cellDataNotifier?.addListener(onCellChangedFn);
|
||||
|
||||
// Return the function pointer that can be used when calling removeListener.
|
||||
return onCellChangedFn;
|
||||
}
|
||||
|
||||
@ -208,22 +212,38 @@ class IGridCellController<T, D> extends Equatable {
|
||||
_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);
|
||||
if (data == null && loadIfNoCache) {
|
||||
if (data == null && loadIfNotExist) {
|
||||
_loadData();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<Either<FieldTypeOptionData, FlowyError>> getTypeOptionData() {
|
||||
return _fieldService.getFieldTypeOptionData(fieldType: fieldType);
|
||||
/// Return the FieldTypeOptionData that can be parsed into corresponding class using the [parser].
|
||||
/// [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 {
|
||||
if (deduplicate) {
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
||||
|
||||
_saveDataOperation?.cancel();
|
||||
_saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
||||
final result = await _cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
@ -238,28 +258,30 @@ class IGridCellController<T, D> extends Equatable {
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
_saveDataOperation?.cancel();
|
||||
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_cellDataLoader.loadData().then((data) {
|
||||
_cellDataNotifier?.value = data;
|
||||
_cellsCache.insert(_GridCellCacheItem(key: _cacheKey, object: data));
|
||||
_cellsCache.insert(_GridCellCacheValue(key: _cacheKey, object: data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (isDispose) {
|
||||
if (_isDispose) {
|
||||
Log.error("$this should only dispose once");
|
||||
return;
|
||||
}
|
||||
isDispose = true;
|
||||
_isDispose = true;
|
||||
_cellListener.stop();
|
||||
_loadDataOperation?.cancel();
|
||||
_saveDataOperation?.cancel();
|
||||
_cellDataNotifier = null;
|
||||
|
||||
if (_onFieldChangedFn != null) {
|
||||
_cellFieldNotifier.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
_fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
|
||||
_onFieldChangedFn = null;
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
}) = _SelectOptionEditorState;
|
||||
|
||||
factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
|
||||
final data = context.getCellData(loadIfNoCache: false);
|
||||
final data = context.getCellData(loadIfNotExist: false);
|
||||
return SelectOptionEditorState(
|
||||
options: data?.options ?? [],
|
||||
allOptions: data?.options ?? [],
|
||||
|
@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
|
||||
import 'cell_service/cell_service.dart';
|
||||
|
||||
class SelectOptionService {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
SelectOptionService({required this.gridCell});
|
||||
|
||||
String get gridId => gridCell.gridId;
|
||||
|
@ -9,6 +9,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:protobuf/protobuf.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 {
|
||||
final String gridId;
|
||||
final String fieldId;
|
||||
|
@ -8,7 +8,7 @@ part 'date_bloc.freezed.dart';
|
||||
|
||||
typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>;
|
||||
|
||||
class DateTypeOptionDataParser extends TypeOptionWidgetDataParser<DateTypeOption> {
|
||||
class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
|
||||
@override
|
||||
DateTypeOption fromBuffer(List<int> buffer) {
|
||||
return DateTypeOption.fromBuffer(buffer);
|
||||
|
@ -18,7 +18,7 @@ class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTy
|
||||
gridId: fieldContext.gridId,
|
||||
fieldId: fieldContext.field.id,
|
||||
),
|
||||
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
|
||||
super(dataParser: dataBuilder, fieldContext: fieldContext);
|
||||
|
||||
@override
|
||||
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
|
||||
MultiSelectTypeOption fromBuffer(List<int> buffer) {
|
||||
return MultiSelectTypeOption.fromBuffer(buffer);
|
||||
|
@ -10,7 +10,7 @@ part 'number_bloc.freezed.dart';
|
||||
|
||||
typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
|
||||
|
||||
class NumberTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<NumberTypeOption> {
|
||||
class NumberTypeOptionWidgetDataParser extends TypeOptionDataParser<NumberTypeOption> {
|
||||
@override
|
||||
NumberTypeOption fromBuffer(List<int> buffer) {
|
||||
return NumberTypeOption.fromBuffer(buffer);
|
||||
|
@ -18,7 +18,7 @@ class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelect
|
||||
gridId: fieldContext.gridId,
|
||||
fieldId: fieldContext.field.id,
|
||||
),
|
||||
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
|
||||
super(dataParser: dataBuilder, fieldContext: fieldContext);
|
||||
|
||||
@override
|
||||
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
|
||||
SingleSelectTypeOption fromBuffer(List<int> buffer) {
|
||||
return SingleSelectTypeOption.fromBuffer(buffer);
|
||||
|
@ -33,17 +33,17 @@ class TypeOptionService {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypeOptionWidgetDataParser<T> {
|
||||
abstract class TypeOptionDataParser<T> {
|
||||
T fromBuffer(List<int> buffer);
|
||||
}
|
||||
|
||||
class TypeOptionWidgetContext<T extends GeneratedMessage> {
|
||||
T? _typeOptionObject;
|
||||
final GridFieldContext _fieldContext;
|
||||
final TypeOptionWidgetDataParser<T> dataBuilder;
|
||||
final TypeOptionDataParser<T> dataParser;
|
||||
|
||||
TypeOptionWidgetContext({
|
||||
required this.dataBuilder,
|
||||
required this.dataParser,
|
||||
required GridFieldContext fieldContext,
|
||||
}) : _fieldContext = fieldContext;
|
||||
|
||||
@ -56,7 +56,7 @@ class TypeOptionWidgetContext<T extends GeneratedMessage> {
|
||||
return _typeOptionObject!;
|
||||
}
|
||||
|
||||
final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData);
|
||||
final T object = dataParser.fromBuffer(_fieldContext.typeOptionData);
|
||||
_typeOptionObject = object;
|
||||
return object;
|
||||
}
|
||||
@ -77,7 +77,7 @@ class TypeOptionContext2<T> {
|
||||
final Field field;
|
||||
final FieldService _fieldService;
|
||||
T? _data;
|
||||
final TypeOptionWidgetDataParser dataBuilder;
|
||||
final TypeOptionDataParser dataBuilder;
|
||||
|
||||
TypeOptionContext2({
|
||||
required this.gridId,
|
||||
|
@ -20,7 +20,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
final GridFieldCache fieldCache;
|
||||
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCacheService> _blocks;
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
|
||||
List<GridRow> get rows {
|
||||
final List<GridRow> rows = [];
|
||||
@ -69,7 +69,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
}
|
||||
|
||||
GridRowsCache? getRowCache(String blockId, String rowId) {
|
||||
final GridBlockCacheService? blockCache = _blocks[blockId];
|
||||
final GridBlockCache? blockCache = _blocks[blockId];
|
||||
return blockCache?.rowCache;
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
return;
|
||||
}
|
||||
|
||||
final cache = GridBlockCacheService(
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldCache: fieldCache,
|
||||
|
@ -186,11 +186,11 @@ class GridFieldCache {
|
||||
}
|
||||
}
|
||||
|
||||
class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
|
||||
class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
|
||||
final GridFieldCache _cache;
|
||||
FieldChangesetCallback? _onChangesetFn;
|
||||
FieldsCallback? _onFieldFn;
|
||||
GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
|
||||
GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
|
||||
|
||||
@override
|
||||
UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;
|
||||
|
@ -58,13 +58,13 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
@freezed
|
||||
class RowDetailEvent with _$RowDetailEvent {
|
||||
const factory RowDetailEvent.initial() = _Initial;
|
||||
const factory RowDetailEvent.didReceiveCellDatas(List<GridCell> gridCells) = _DidReceiveCellDatas;
|
||||
const factory RowDetailEvent.didReceiveCellDatas(List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowDetailState with _$RowDetailState {
|
||||
const factory RowDetailState({
|
||||
required List<GridCell> gridCells,
|
||||
required List<GridCellIdentifier> gridCells,
|
||||
}) = _RowDetailState;
|
||||
|
||||
factory RowDetailState.initial() => RowDetailState(
|
||||
|
@ -14,7 +14,7 @@ part 'row_service.freezed.dart';
|
||||
|
||||
typedef RowUpdateCallback = void Function();
|
||||
|
||||
abstract class GridRowCacheDelegate {
|
||||
abstract class GridRowCacheFieldNotifier {
|
||||
UnmodifiableListView<Field> get fields;
|
||||
void onFieldsChanged(VoidCallback callback);
|
||||
void onFieldChanged(void Function(Field) callback);
|
||||
@ -27,7 +27,7 @@ class GridRowsCache {
|
||||
final _Notifier _notifier;
|
||||
List<GridRow> _rows = [];
|
||||
final HashMap<String, Row> _rowByRowId;
|
||||
final GridRowCacheDelegate _delegate;
|
||||
final GridRowCacheFieldNotifier _fieldNotifier;
|
||||
final GridCellsCache _cellCache;
|
||||
|
||||
List<GridRow> get rows => _rows;
|
||||
@ -36,19 +36,19 @@ class GridRowsCache {
|
||||
GridRowsCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridRowCacheDelegate delegate,
|
||||
required GridRowCacheFieldNotifier notifier,
|
||||
}) : _cellCache = GridCellsCache(gridId: gridId),
|
||||
_rowByRowId = HashMap(),
|
||||
_notifier = _Notifier(),
|
||||
_delegate = delegate {
|
||||
_fieldNotifier = notifier {
|
||||
//
|
||||
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
||||
delegate.onFieldChanged((field) => _cellCache.remove(field.id));
|
||||
notifier.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
||||
notifier.onFieldChanged((field) => _cellCache.remove(field.id));
|
||||
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_delegate.dispose();
|
||||
_fieldNotifier.dispose();
|
||||
_notifier.dispose();
|
||||
await _cellCache.dispose();
|
||||
}
|
||||
@ -195,9 +195,9 @@ class GridRowsCache {
|
||||
|
||||
GridCellMap _makeGridCells(String rowId, Row? row) {
|
||||
var cellDataMap = GridCellMap.new();
|
||||
for (final field in _delegate.fields) {
|
||||
for (final field in _fieldNotifier.fields) {
|
||||
if (field.visibility) {
|
||||
cellDataMap[field.id] = GridCell(
|
||||
cellDataMap[field.id] = GridCellIdentifier(
|
||||
rowId: rowId,
|
||||
gridId: gridId,
|
||||
field: field,
|
||||
@ -236,7 +236,7 @@ class GridRowsCache {
|
||||
return GridRow(
|
||||
gridId: gridId,
|
||||
blockId: block.id,
|
||||
fields: _delegate.fields,
|
||||
fields: _fieldNotifier.fields,
|
||||
rowId: rowId,
|
||||
height: rowHeight,
|
||||
);
|
||||
|
@ -21,15 +21,13 @@ class GridCellBuilder {
|
||||
required this.fieldCache,
|
||||
});
|
||||
|
||||
GridCellWidget build(GridCell cell, {GridCellStyle? style}) {
|
||||
final key = ValueKey(cell.cellId());
|
||||
|
||||
GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) {
|
||||
final cellControllerBuilder = GridCellControllerBuilder(
|
||||
gridCell: cell,
|
||||
cellCache: cellCache,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
|
||||
final key = cell.key();
|
||||
switch (cell.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);
|
||||
|
@ -80,7 +80,7 @@ class _DateCellState extends GridCellState<DateCell> {
|
||||
final calendar = DateCellEditor(onDismissed: () => widget.onCellEditing.value = false);
|
||||
calendar.show(
|
||||
context,
|
||||
cellContext: bloc.cellContext.clone(),
|
||||
cellController: bloc.cellContext.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,16 +31,16 @@ class DateCellEditor with FlowyOverlayDelegate {
|
||||
|
||||
Future<void> show(
|
||||
BuildContext context, {
|
||||
required GridDateCellController cellContext,
|
||||
required GridDateCellController cellController,
|
||||
}) async {
|
||||
DateCellEditor.remove(context);
|
||||
|
||||
final result = await cellContext.getTypeOptionData();
|
||||
final result = await cellController.getFieldTypeOption(DateTypeOptionDataParser());
|
||||
result.fold(
|
||||
(data) {
|
||||
(dateTypeOption) {
|
||||
final calendar = _CellCalendarWidget(
|
||||
cellContext: cellContext,
|
||||
dateTypeOption: DateTypeOption.fromBuffer(data.typeOptionData),
|
||||
cellContext: cellController,
|
||||
dateTypeOption: dateTypeOption,
|
||||
);
|
||||
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
|
@ -7,9 +7,9 @@ import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final GridURLCellController cellContext;
|
||||
final GridURLCellController cellController;
|
||||
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
|
||||
State<URLCellEditor> createState() => _URLCellEditorState();
|
||||
@ -21,7 +21,7 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||
) {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
final editor = URLCellEditor(
|
||||
cellContext: cellContext,
|
||||
cellController: cellContext,
|
||||
completed: completed,
|
||||
);
|
||||
|
||||
@ -62,7 +62,7 @@ class _URLCellEditorState extends State<URLCellEditor> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = URLCellEditorBloc(cellContext: widget.cellContext);
|
||||
_cellBloc = URLCellEditorBloc(cellContext: widget.cellController);
|
||||
_cellBloc.add(const URLCellEditorEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
|
||||
|
@ -187,7 +187,7 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? "";
|
||||
final content = cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
|
||||
Clipboard.setData(ClipboardData(text: content));
|
||||
showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
|
||||
}
|
||||
|
@ -51,13 +51,13 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
|
||||
case FieldType.Checkbox:
|
||||
final context = CheckboxTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: CheckboxTypeOptionWidgetDataParser(),
|
||||
dataParser: CheckboxTypeOptionWidgetDataParser(),
|
||||
);
|
||||
return CheckboxTypeOptionWidgetBuilder(context);
|
||||
case FieldType.DateTime:
|
||||
final context = DateTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: DateTypeOptionDataParser(),
|
||||
dataParser: DateTypeOptionDataParser(),
|
||||
);
|
||||
return DateTypeOptionWidgetBuilder(
|
||||
context,
|
||||
@ -84,7 +84,7 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
|
||||
case FieldType.Number:
|
||||
final context = NumberTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: NumberTypeOptionWidgetDataParser(),
|
||||
dataParser: NumberTypeOptionWidgetDataParser(),
|
||||
);
|
||||
return NumberTypeOptionWidgetBuilder(
|
||||
context,
|
||||
@ -93,14 +93,14 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
|
||||
case FieldType.RichText:
|
||||
final context = RichTextTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: RichTextTypeOptionWidgetDataParser(),
|
||||
dataParser: RichTextTypeOptionWidgetDataParser(),
|
||||
);
|
||||
return RichTextTypeOptionWidgetBuilder(context);
|
||||
|
||||
case FieldType.URL:
|
||||
final context = URLTypeOptionContext(
|
||||
fieldContext: fieldContext,
|
||||
dataBuilder: URLTypeOptionWidgetDataParser(),
|
||||
dataParser: URLTypeOptionWidgetDataParser(),
|
||||
);
|
||||
return URLTypeOptionWidgetBuilder(context);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import 'builder.dart';
|
||||
|
||||
typedef CheckboxTypeOptionContext = TypeOptionWidgetContext<CheckboxTypeOption>;
|
||||
|
||||
class CheckboxTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<CheckboxTypeOption> {
|
||||
class CheckboxTypeOptionWidgetDataParser extends TypeOptionDataParser<CheckboxTypeOption> {
|
||||
@override
|
||||
CheckboxTypeOption fromBuffer(List<int> buffer) {
|
||||
return CheckboxTypeOption.fromBuffer(buffer);
|
||||
|
@ -5,7 +5,7 @@ import 'builder.dart';
|
||||
|
||||
typedef RichTextTypeOptionContext = TypeOptionWidgetContext<RichTextTypeOption>;
|
||||
|
||||
class RichTextTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<RichTextTypeOption> {
|
||||
class RichTextTypeOptionWidgetDataParser extends TypeOptionDataParser<RichTextTypeOption> {
|
||||
@override
|
||||
RichTextTypeOption fromBuffer(List<int> buffer) {
|
||||
return RichTextTypeOption.fromBuffer(buffer);
|
||||
|
@ -5,7 +5,7 @@ import 'builder.dart';
|
||||
|
||||
typedef URLTypeOptionContext = TypeOptionWidgetContext<URLTypeOption>;
|
||||
|
||||
class URLTypeOptionWidgetDataParser extends TypeOptionWidgetDataParser<URLTypeOption> {
|
||||
class URLTypeOptionWidgetDataParser extends TypeOptionDataParser<URLTypeOption> {
|
||||
@override
|
||||
URLTypeOption fromBuffer(List<int> buffer) {
|
||||
return URLTypeOption.fromBuffer(buffer);
|
||||
|
@ -137,7 +137,7 @@ class _PropertyList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _RowDetailCell extends StatelessWidget {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier gridCell;
|
||||
final GridCellBuilder cellBuilder;
|
||||
const _RowDetailCell({
|
||||
required this.gridCell,
|
||||
|
Loading…
Reference in New Issue
Block a user