mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #616 from AppFlowy-IO/feat/add_more_grid_documentation
Feat/add more grid documentation
This commit is contained in:
commit
58892e3914
@ -6,24 +6,25 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
|
||||
import 'block_listener.dart';
|
||||
|
||||
class GridBlockCacheService {
|
||||
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
|
||||
class GridBlockCache {
|
||||
final String gridId;
|
||||
final GridBlock block;
|
||||
late GridRowsCache _rowCache;
|
||||
late GridRowCache _rowCache;
|
||||
late GridBlockListener _listener;
|
||||
|
||||
List<GridRow> get rows => _rowCache.rows;
|
||||
GridRowsCache get rowCache => _rowCache;
|
||||
GridRowCache get rowCache => _rowCache;
|
||||
|
||||
GridBlockCacheService({
|
||||
GridBlockCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridFieldCache fieldCache,
|
||||
}) {
|
||||
_rowCache = GridRowsCache(
|
||||
_rowCache = GridRowCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
delegate: GridRowCacheDelegateImpl(fieldCache),
|
||||
notifier: GridRowCacheFieldNotifierImpl(fieldCache),
|
||||
);
|
||||
|
||||
_listener = GridBlockListener(blockId: block.id);
|
@ -1,67 +0,0 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellMap = LinkedHashMap<String, GridCell>;
|
||||
|
||||
class _GridCellCacheItem {
|
||||
GridCellId key;
|
||||
dynamic object;
|
||||
_GridCellCacheItem({
|
||||
required this.key,
|
||||
required this.object,
|
||||
});
|
||||
}
|
||||
|
||||
class GridCellId {
|
||||
final String fieldId;
|
||||
final String rowId;
|
||||
GridCellId({
|
||||
required this.fieldId,
|
||||
required this.rowId,
|
||||
});
|
||||
}
|
||||
|
||||
class GridCellsCache {
|
||||
final String gridId;
|
||||
|
||||
/// fieldId: {cacheKey: cacheData}
|
||||
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
||||
GridCellsCache({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
void remove(String fieldId) {
|
||||
_cellDataByFieldId.remove(fieldId);
|
||||
}
|
||||
|
||||
void insert<T extends _GridCellCacheItem>(T item) {
|
||||
var map = _cellDataByFieldId[item.key.fieldId];
|
||||
if (map == null) {
|
||||
_cellDataByFieldId[item.key.fieldId] = {};
|
||||
map = _cellDataByFieldId[item.key.fieldId];
|
||||
}
|
||||
|
||||
map![item.key.rowId] = item.object;
|
||||
}
|
||||
|
||||
T? get<T>(GridCellId key) {
|
||||
final map = _cellDataByFieldId[key.fieldId];
|
||||
if (map == null) {
|
||||
return null;
|
||||
} else {
|
||||
final object = map[key.rowId];
|
||||
if (object is T) {
|
||||
return object;
|
||||
} else {
|
||||
if (object != null) {
|
||||
Log.error("Cache data type does not match the cache data type");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_cellDataByFieldId.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellMap = LinkedHashMap<String, GridCellIdentifier>;
|
||||
|
||||
class GridCell {
|
||||
dynamic object;
|
||||
GridCell({
|
||||
required this.object,
|
||||
});
|
||||
}
|
||||
|
||||
/// Use to index the cell in the grid.
|
||||
/// We use [fieldId + rowId] to identify the cell.
|
||||
class GridCellCacheKey {
|
||||
final String fieldId;
|
||||
final String rowId;
|
||||
GridCellCacheKey({
|
||||
required this.fieldId,
|
||||
required this.rowId,
|
||||
});
|
||||
}
|
||||
|
||||
/// GridCellCache is used to cache cell data of each block.
|
||||
/// We use GridCellCacheKey to index the cell in the cache.
|
||||
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid
|
||||
/// for more information
|
||||
class GridCellCache {
|
||||
final String gridId;
|
||||
|
||||
/// fieldId: {cacheKey: GridCell}
|
||||
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
||||
GridCellCache({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
void remove(String fieldId) {
|
||||
_cellDataByFieldId.remove(fieldId);
|
||||
}
|
||||
|
||||
void insert<T extends GridCell>(GridCellCacheKey key, T value) {
|
||||
var map = _cellDataByFieldId[key.fieldId];
|
||||
if (map == null) {
|
||||
_cellDataByFieldId[key.fieldId] = {};
|
||||
map = _cellDataByFieldId[key.fieldId];
|
||||
}
|
||||
|
||||
map![key.rowId] = value.object;
|
||||
}
|
||||
|
||||
T? get<T>(GridCellCacheKey key) {
|
||||
final map = _cellDataByFieldId[key.fieldId];
|
||||
if (map == null) {
|
||||
return null;
|
||||
} else {
|
||||
final value = map[key.rowId];
|
||||
if (value is T) {
|
||||
return value;
|
||||
} else {
|
||||
if (value != null) {
|
||||
Log.error("Expected value type: $T, but receive $value");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_cellDataByFieldId.clear();
|
||||
}
|
||||
}
|
@ -5,46 +5,24 @@ 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 cellId;
|
||||
final ICellDataParser<T> parser;
|
||||
|
||||
@override
|
||||
final IGridCellDataConfig config;
|
||||
final bool reloadOnFieldChanged;
|
||||
|
||||
GridCellDataLoader({
|
||||
required this.gridCell,
|
||||
required this.cellId,
|
||||
required this.parser,
|
||||
this.config = const GridCellDataConfig(),
|
||||
this.reloadOnFieldChanged = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<T?> loadData() {
|
||||
final fut = service.getCell(
|
||||
gridId: gridCell.gridId,
|
||||
fieldId: gridCell.field.id,
|
||||
rowId: gridCell.rowId,
|
||||
);
|
||||
final fut = service.getCell(cellId: cellId);
|
||||
return fut.then(
|
||||
(result) => result.fold((Cell cell) {
|
||||
try {
|
||||
|
@ -1,25 +1,22 @@
|
||||
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 cellId;
|
||||
|
||||
CellDataPersistence({
|
||||
required this.gridCell,
|
||||
required this.cellId,
|
||||
});
|
||||
final CellService _cellService = CellService();
|
||||
|
||||
@override
|
||||
Future<Option<FlowyError>> save(String data) async {
|
||||
final fut = _cellService.updateCell(
|
||||
gridId: gridCell.gridId,
|
||||
fieldId: gridCell.field.id,
|
||||
rowId: gridCell.rowId,
|
||||
data: data,
|
||||
);
|
||||
final fut = _cellService.updateCell(cellId: cellId, data: data);
|
||||
|
||||
return fut.then((result) {
|
||||
return result.fold(
|
||||
@ -36,14 +33,14 @@ class CalendarData with _$CalendarData {
|
||||
}
|
||||
|
||||
class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier cellId;
|
||||
DateCellDataPersistence({
|
||||
required this.gridCell,
|
||||
required this.cellId,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Option<FlowyError>> save(CalendarData data) {
|
||||
var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell);
|
||||
var payload = DateChangesetPayload.create()..cellIdentifier = _makeCellIdPayload(cellId);
|
||||
|
||||
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
|
||||
payload.date = date;
|
||||
@ -61,9 +58,9 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
|
||||
}
|
||||
}
|
||||
|
||||
CellIdentifierPayload _cellIdentifier(GridCell gridCell) {
|
||||
CellIdentifierPayload _makeCellIdPayload(GridCellIdentifier cellId) {
|
||||
return CellIdentifierPayload.create()
|
||||
..gridId = gridCell.gridId
|
||||
..fieldId = gridCell.field.id
|
||||
..rowId = gridCell.rowId;
|
||||
..gridId = cellId.gridId
|
||||
..fieldId = cellId.fieldId
|
||||
..rowId = cellId.rowId;
|
||||
}
|
||||
|
@ -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,11 +18,12 @@ 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';
|
||||
part 'context_builder.dart';
|
||||
part 'cache.dart';
|
||||
part 'cell_cache.dart';
|
||||
part 'cell_data_persistence.dart';
|
||||
|
||||
// key: rowId
|
||||
@ -31,44 +32,46 @@ class CellService {
|
||||
CellService();
|
||||
|
||||
Future<Either<void, FlowyError>> updateCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
required GridCellIdentifier cellId,
|
||||
required String data,
|
||||
}) {
|
||||
final payload = CellChangeset.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId
|
||||
..gridId = cellId.gridId
|
||||
..fieldId = cellId.fieldId
|
||||
..rowId = cellId.rowId
|
||||
..content = data;
|
||||
return GridEventUpdateCell(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Cell, FlowyError>> getCell({
|
||||
required String gridId,
|
||||
required String fieldId,
|
||||
required String rowId,
|
||||
required GridCellIdentifier cellId,
|
||||
}) {
|
||||
final payload = CellIdentifierPayload.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
..gridId = cellId.gridId
|
||||
..fieldId = cellId.fieldId
|
||||
..rowId = cellId.rowId;
|
||||
return GridEventGetCell(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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}";
|
||||
String get fieldId => field.id;
|
||||
|
||||
FieldType get fieldType => field.fieldType;
|
||||
|
||||
ValueKey key() {
|
||||
return ValueKey(rowId + fieldId + "${field.fieldType}");
|
||||
}
|
||||
}
|
||||
|
@ -6,115 +6,120 @@ typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>
|
||||
typedef GridURLCellController = IGridCellController<URLCellData, String>;
|
||||
|
||||
class GridCellControllerBuilder {
|
||||
final GridCell _gridCell;
|
||||
final GridCellsCache _cellCache;
|
||||
final GridCellIdentifier _cellId;
|
||||
final GridCellCache _cellCache;
|
||||
final GridFieldCache _fieldCache;
|
||||
|
||||
GridCellControllerBuilder(
|
||||
{required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
|
||||
: _cellCache = cellCache,
|
||||
GridCellControllerBuilder({
|
||||
required GridCellIdentifier cellId,
|
||||
required GridCellCache cellCache,
|
||||
required GridFieldCache fieldCache,
|
||||
}) : _cellCache = cellCache,
|
||||
_fieldCache = fieldCache,
|
||||
_gridCell = gridCell;
|
||||
_cellId = cellId;
|
||||
|
||||
IGridCellController build() {
|
||||
final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
|
||||
|
||||
switch (_gridCell.field.fieldType) {
|
||||
switch (_cellId.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: DateCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridDateCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.Number:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.RichText:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.SingleSelect:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: SelectOptionCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridSelectOptionCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
|
||||
case FieldType.URL:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
parser: URLCellDataParser(),
|
||||
);
|
||||
return GridURLCellController(
|
||||
gridCell: _gridCell,
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 GridCellsCache _cellsCache;
|
||||
final GridCellId _cacheKey;
|
||||
final GridCellIdentifier cellId;
|
||||
final GridCellCache _cellsCache;
|
||||
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,42 +129,39 @@ 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 this.cellId,
|
||||
required GridCellCache cellCache,
|
||||
required GridCellFieldNotifier fieldNotifier,
|
||||
required GridCellDataLoader<T> cellDataLoader,
|
||||
required IGridCellDataPersistence<D> cellDataPersistence,
|
||||
// required GridFieldChangedNotifier notifierDelegate,
|
||||
}) : _cellsCache = cellCache,
|
||||
_cellDataLoader = cellDataLoader,
|
||||
_cellDataPersistence = cellDataPersistence,
|
||||
_cellFieldNotifier = cellFieldNotifier,
|
||||
_fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
|
||||
_cacheKey = GridCellId(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
||||
_fieldNotifier = fieldNotifier,
|
||||
_fieldService = FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
|
||||
_cacheKey = GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
|
||||
|
||||
IGridCellController<T, D> clone() {
|
||||
return IGridCellController(
|
||||
gridCell: gridCell,
|
||||
cellId: cellId,
|
||||
cellDataLoader: _cellDataLoader,
|
||||
cellCache: _cellsCache,
|
||||
cellFieldNotifier: _cellFieldNotifier,
|
||||
fieldNotifier: _fieldNotifier,
|
||||
cellDataPersistence: _cellDataPersistence);
|
||||
}
|
||||
|
||||
String get gridId => gridCell.gridId;
|
||||
String get gridId => cellId.gridId;
|
||||
|
||||
String get rowId => gridCell.rowId;
|
||||
String get rowId => cellId.rowId;
|
||||
|
||||
String get cellId => gridCell.rowId + gridCell.field.id;
|
||||
String get fieldId => cellId.field.id;
|
||||
|
||||
String get fieldId => gridCell.field.id;
|
||||
Field get field => cellId.field;
|
||||
|
||||
Field get field => gridCell.field;
|
||||
|
||||
FieldType get fieldType => gridCell.field.fieldType;
|
||||
FieldType get fieldType => cellId.field.fieldType;
|
||||
|
||||
VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
|
||||
if (isListening) {
|
||||
@ -175,7 +177,7 @@ class IGridCellController<T, D> extends Equatable {
|
||||
/// user input: 12
|
||||
/// cell display: $12
|
||||
_cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
|
||||
_cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
||||
_cellListener = CellListener(rowId: cellId.rowId, fieldId: cellId.field.id);
|
||||
|
||||
/// 1.Listen on user edit event and load the new cell data if needed.
|
||||
_cellListener.start(onCellChanged: (result) {
|
||||
@ -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,34 +258,36 @@ 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(_cacheKey, GridCell(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;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId];
|
||||
List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
|
||||
}
|
||||
|
||||
class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
|
||||
|
@ -17,7 +17,7 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
|
||||
SelectOptionCellEditorBloc({
|
||||
required this.cellController,
|
||||
}) : _selectOptionService = SelectOptionService(gridCell: cellController.gridCell),
|
||||
}) : _selectOptionService = SelectOptionService(cellId: cellController.cellId),
|
||||
super(SelectOptionEditorState.initial(cellController)) {
|
||||
on<SelectOptionEditorEvent>(
|
||||
(event, emit) async {
|
||||
@ -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,12 +7,12 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
|
||||
import 'cell_service/cell_service.dart';
|
||||
|
||||
class SelectOptionService {
|
||||
final GridCell gridCell;
|
||||
SelectOptionService({required this.gridCell});
|
||||
final GridCellIdentifier cellId;
|
||||
SelectOptionService({required this.cellId});
|
||||
|
||||
String get gridId => gridCell.gridId;
|
||||
String get fieldId => gridCell.field.id;
|
||||
String get rowId => gridCell.rowId;
|
||||
String get gridId => cellId.gridId;
|
||||
String get fieldId => cellId.field.id;
|
||||
String get rowId => cellId.rowId;
|
||||
|
||||
Future<Either<Unit, FlowyError>> create({required String name}) {
|
||||
return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then(
|
||||
|
@ -9,7 +9,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
FieldEditorBloc({
|
||||
required String gridId,
|
||||
required String fieldName,
|
||||
required IFieldContextLoader fieldContextLoader,
|
||||
required IFieldTypeOptionLoader fieldContextLoader,
|
||||
}) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) {
|
||||
on<FieldEditorEvent>(
|
||||
(event, emit) async {
|
||||
@ -53,7 +53,7 @@ class FieldEditorState with _$FieldEditorState {
|
||||
required Option<GridFieldContext> fieldContext,
|
||||
}) = _FieldEditorState;
|
||||
|
||||
factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState(
|
||||
factory FieldEditorState.initial(String gridId, String fieldName, IFieldTypeOptionLoader loader) => FieldEditorState(
|
||||
gridId: gridId,
|
||||
fieldContext: none(),
|
||||
errorText: '',
|
||||
|
@ -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;
|
||||
@ -137,7 +141,7 @@ class GridFieldCellContext with _$GridFieldCellContext {
|
||||
}) = _GridFieldCellContext;
|
||||
}
|
||||
|
||||
abstract class IFieldContextLoader {
|
||||
abstract class IFieldTypeOptionLoader {
|
||||
String get gridId;
|
||||
Future<Either<FieldTypeOptionData, FlowyError>> load();
|
||||
|
||||
@ -151,10 +155,10 @@ abstract class IFieldContextLoader {
|
||||
}
|
||||
}
|
||||
|
||||
class NewFieldContextLoader extends IFieldContextLoader {
|
||||
class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
|
||||
@override
|
||||
final String gridId;
|
||||
NewFieldContextLoader({
|
||||
NewFieldTypeOptionLoader({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
@ -168,12 +172,12 @@ class NewFieldContextLoader extends IFieldContextLoader {
|
||||
}
|
||||
}
|
||||
|
||||
class FieldContextLoader extends IFieldContextLoader {
|
||||
class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
|
||||
@override
|
||||
final String gridId;
|
||||
final Field field;
|
||||
|
||||
FieldContextLoader({
|
||||
FieldTypeOptionLoader({
|
||||
required this.gridId,
|
||||
required this.field,
|
||||
});
|
||||
@ -191,14 +195,14 @@ class FieldContextLoader extends IFieldContextLoader {
|
||||
|
||||
class GridFieldContext {
|
||||
final String gridId;
|
||||
final IFieldContextLoader _loader;
|
||||
final IFieldTypeOptionLoader _loader;
|
||||
|
||||
late FieldTypeOptionData _data;
|
||||
ValueNotifier<Field>? _fieldNotifier;
|
||||
|
||||
GridFieldContext({
|
||||
required this.gridId,
|
||||
required IFieldContextLoader loader,
|
||||
required IFieldTypeOptionLoader loader,
|
||||
}) : _loader = loader;
|
||||
|
||||
Future<Either<Unit, FlowyError>> loadData() async {
|
||||
|
@ -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,
|
||||
|
@ -7,7 +7,7 @@ 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_service.dart';
|
||||
import 'block/block_cache.dart';
|
||||
import 'grid_service.dart';
|
||||
import 'row/row_service.dart';
|
||||
import 'dart:collection';
|
||||
@ -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 = [];
|
||||
@ -68,8 +68,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
GridRowsCache? getRowCache(String blockId, String rowId) {
|
||||
final GridBlockCacheService? blockCache = _blocks[blockId];
|
||||
GridRowCache? getRowCache(String blockId, String rowId) {
|
||||
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,
|
||||
|
@ -61,13 +61,12 @@ typedef FieldsCallback = void Function(List<Field>);
|
||||
|
||||
class GridFieldCache {
|
||||
final String gridId;
|
||||
late final GridFieldsListener _fieldListener;
|
||||
final GridFieldsListener _fieldListener;
|
||||
FieldsNotifier? _fieldNotifier = FieldsNotifier();
|
||||
final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
|
||||
final Map<FieldChangesetCallback, FieldChangesetCallback> _changesetCallbackMap = {};
|
||||
|
||||
GridFieldCache({required this.gridId}) {
|
||||
_fieldListener = GridFieldsListener(gridId: gridId);
|
||||
GridFieldCache({required this.gridId}) : _fieldListener = GridFieldsListener(gridId: gridId) {
|
||||
_fieldListener.start(onFieldsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
@ -186,11 +185,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;
|
||||
|
@ -15,7 +15,7 @@ class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState>
|
||||
: _rowService = RowService(
|
||||
gridId: rowData.gridId,
|
||||
blockId: rowData.blockId,
|
||||
rowId: rowData.rowId,
|
||||
rowId: rowData.id,
|
||||
),
|
||||
super(RowActionSheetState.initial(rowData)) {
|
||||
on<RowActionSheetEvent>(
|
||||
|
@ -11,19 +11,19 @@ part 'row_bloc.freezed.dart';
|
||||
|
||||
class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
final RowService _rowService;
|
||||
final GridRowsCache _rowCache;
|
||||
final GridRowCache _rowCache;
|
||||
void Function()? _rowListenFn;
|
||||
|
||||
RowBloc({
|
||||
required GridRow rowData,
|
||||
required GridRowsCache rowCache,
|
||||
required GridRowCache rowCache,
|
||||
}) : _rowService = RowService(
|
||||
gridId: rowData.gridId,
|
||||
blockId: rowData.blockId,
|
||||
rowId: rowData.rowId,
|
||||
rowId: rowData.id,
|
||||
),
|
||||
_rowCache = rowCache,
|
||||
super(RowState.initial(rowData, rowCache.loadGridCells(rowData.rowId))) {
|
||||
super(RowState.initial(rowData, rowCache.loadGridCells(rowData.id))) {
|
||||
on<RowEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
@ -58,7 +58,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_rowListenFn = _rowCache.addListener(
|
||||
rowId: state.rowData.rowId,
|
||||
rowId: state.rowData.id,
|
||||
onCellUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
|
@ -8,12 +8,12 @@ part 'row_detail_bloc.freezed.dart';
|
||||
|
||||
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
final GridRow rowData;
|
||||
final GridRowsCache _rowCache;
|
||||
final GridRowCache _rowCache;
|
||||
void Function()? _rowListenFn;
|
||||
|
||||
RowDetailBloc({
|
||||
required this.rowData,
|
||||
required GridRowsCache rowCache,
|
||||
required GridRowCache rowCache,
|
||||
}) : _rowCache = rowCache,
|
||||
super(RowDetailState.initial()) {
|
||||
on<RowDetailEvent>(
|
||||
@ -41,14 +41,14 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_rowListenFn = _rowCache.addListener(
|
||||
rowId: rowData.rowId,
|
||||
rowId: rowData.id,
|
||||
onCellUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadCellData() async {
|
||||
final cellDataMap = _rowCache.loadGridCells(rowData.rowId);
|
||||
final cellDataMap = _rowCache.loadGridCells(rowData.id);
|
||||
if (!isClosed) {
|
||||
add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList()));
|
||||
}
|
||||
@ -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,42 +14,53 @@ 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);
|
||||
void dispose();
|
||||
}
|
||||
|
||||
class GridRowsCache {
|
||||
/// Cache the rows in memory
|
||||
/// Insert / delete / update row
|
||||
///
|
||||
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information.
|
||||
|
||||
class GridRowCache {
|
||||
final String gridId;
|
||||
final GridBlock block;
|
||||
final _Notifier _notifier;
|
||||
|
||||
/// _rows containers the current block's rows
|
||||
/// Use List to reverse the order of the GridRow.
|
||||
List<GridRow> _rows = [];
|
||||
|
||||
/// Use Map for faster access the raw row data.
|
||||
final HashMap<String, Row> _rowByRowId;
|
||||
final GridRowCacheDelegate _delegate;
|
||||
final GridCellsCache _cellCache;
|
||||
|
||||
List<GridRow> get rows => _rows;
|
||||
GridCellsCache get cellCache => _cellCache;
|
||||
final GridCellCache _cellCache;
|
||||
final GridRowCacheFieldNotifier _fieldNotifier;
|
||||
final _GridRowChangesetNotifier _rowChangeReasonNotifier;
|
||||
|
||||
GridRowsCache({
|
||||
UnmodifiableListView<GridRow> get rows => UnmodifiableListView(_rows);
|
||||
GridCellCache get cellCache => _cellCache;
|
||||
|
||||
GridRowCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridRowCacheDelegate delegate,
|
||||
}) : _cellCache = GridCellsCache(gridId: gridId),
|
||||
required GridRowCacheFieldNotifier notifier,
|
||||
}) : _cellCache = GridCellCache(gridId: gridId),
|
||||
_rowByRowId = HashMap(),
|
||||
_notifier = _Notifier(),
|
||||
_delegate = delegate {
|
||||
_rowChangeReasonNotifier = _GridRowChangesetNotifier(),
|
||||
_fieldNotifier = notifier {
|
||||
//
|
||||
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
||||
delegate.onFieldChanged((field) => _cellCache.remove(field.id));
|
||||
notifier.onFieldsChanged(() => _rowChangeReasonNotifier.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();
|
||||
_notifier.dispose();
|
||||
_fieldNotifier.dispose();
|
||||
_rowChangeReasonNotifier.dispose();
|
||||
await _cellCache.dispose();
|
||||
}
|
||||
|
||||
@ -73,14 +84,15 @@ class GridRowsCache {
|
||||
final Map<String, String> deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId};
|
||||
|
||||
_rows.asMap().forEach((index, row) {
|
||||
if (deletedRowByRowId[row.rowId] == null) {
|
||||
if (deletedRowByRowId[row.id] == null) {
|
||||
newRows.add(row);
|
||||
} else {
|
||||
_rowByRowId.remove(row.id);
|
||||
deletedIndex.add(DeletedIndex(index: index, row: row));
|
||||
}
|
||||
});
|
||||
_rows = newRows;
|
||||
_notifier.receive(GridRowChangeReason.delete(deletedIndex));
|
||||
_rowChangeReasonNotifier.receive(GridRowChangeReason.delete(deletedIndex));
|
||||
}
|
||||
|
||||
void _insertRows(List<InsertedRow> insertRows) {
|
||||
@ -89,17 +101,16 @@ class GridRowsCache {
|
||||
}
|
||||
|
||||
InsertedIndexs insertIndexs = [];
|
||||
final List<GridRow> newRows = _rows;
|
||||
for (final insertRow in insertRows) {
|
||||
final insertIndex = InsertedIndex(
|
||||
index: insertRow.index,
|
||||
rowId: insertRow.rowId,
|
||||
);
|
||||
insertIndexs.add(insertIndex);
|
||||
newRows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
|
||||
_rows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
|
||||
}
|
||||
|
||||
_notifier.receive(GridRowChangeReason.insert(insertIndexs));
|
||||
_rowChangeReasonNotifier.receive(GridRowChangeReason.insert(insertIndexs));
|
||||
}
|
||||
|
||||
void _updateRows(List<UpdatedRow> updatedRows) {
|
||||
@ -108,20 +119,19 @@ class GridRowsCache {
|
||||
}
|
||||
|
||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||
final List<GridRow> newRows = _rows;
|
||||
for (final updatedRow in updatedRows) {
|
||||
final rowId = updatedRow.rowId;
|
||||
final index = newRows.indexWhere((row) => row.rowId == rowId);
|
||||
final index = _rows.indexWhere((row) => row.id == rowId);
|
||||
if (index != -1) {
|
||||
_rowByRowId[rowId] = updatedRow.row;
|
||||
|
||||
newRows.removeAt(index);
|
||||
newRows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
|
||||
_rows.removeAt(index);
|
||||
_rows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
|
||||
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
|
||||
}
|
||||
}
|
||||
|
||||
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
|
||||
_rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
|
||||
}
|
||||
|
||||
void _hideRows(List<String> hideRows) {}
|
||||
@ -131,8 +141,8 @@ class GridRowsCache {
|
||||
void onRowsChanged(
|
||||
void Function(GridRowChangeReason) onRowChanged,
|
||||
) {
|
||||
_notifier.addListener(() {
|
||||
onRowChanged(_notifier._reason);
|
||||
_rowChangeReasonNotifier.addListener(() {
|
||||
onRowChanged(_rowChangeReasonNotifier.reason);
|
||||
});
|
||||
}
|
||||
|
||||
@ -151,12 +161,12 @@ class GridRowsCache {
|
||||
final row = _rowByRowId[rowId];
|
||||
if (row != null) {
|
||||
final GridCellMap cellDataMap = _makeGridCells(rowId, row);
|
||||
onCellUpdated(cellDataMap, _notifier._reason);
|
||||
onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_notifier._reason.whenOrNull(
|
||||
_rowChangeReasonNotifier.reason.whenOrNull(
|
||||
update: (indexs) {
|
||||
if (indexs[rowId] != null) notifyUpdate();
|
||||
},
|
||||
@ -164,12 +174,12 @@ class GridRowsCache {
|
||||
);
|
||||
}
|
||||
|
||||
_notifier.addListener(listenrHandler);
|
||||
_rowChangeReasonNotifier.addListener(listenrHandler);
|
||||
return listenrHandler;
|
||||
}
|
||||
|
||||
void removeRowListener(VoidCallback callback) {
|
||||
_notifier.removeListener(callback);
|
||||
_rowChangeReasonNotifier.removeListener(callback);
|
||||
}
|
||||
|
||||
GridCellMap loadGridCells(String rowId) {
|
||||
@ -195,9 +205,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,
|
||||
@ -215,19 +225,19 @@ class GridRowsCache {
|
||||
updatedRow.freeze();
|
||||
|
||||
_rowByRowId[updatedRow.id] = updatedRow;
|
||||
final index = _rows.indexWhere((gridRow) => gridRow.rowId == updatedRow.id);
|
||||
final index = _rows.indexWhere((gridRow) => gridRow.id == updatedRow.id);
|
||||
if (index != -1) {
|
||||
// update the corresponding row in _rows if they are not the same
|
||||
if (_rows[index].data != updatedRow) {
|
||||
final row = _rows.removeAt(index).copyWith(data: updatedRow);
|
||||
if (_rows[index].rawRow != updatedRow) {
|
||||
final row = _rows.removeAt(index).copyWith(rawRow: updatedRow);
|
||||
_rows.insert(index, row);
|
||||
|
||||
// Calculate the update index
|
||||
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
|
||||
updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId);
|
||||
updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id);
|
||||
|
||||
//
|
||||
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
|
||||
_rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,20 +246,20 @@ class GridRowsCache {
|
||||
return GridRow(
|
||||
gridId: gridId,
|
||||
blockId: block.id,
|
||||
fields: _delegate.fields,
|
||||
rowId: rowId,
|
||||
fields: _fieldNotifier.fields,
|
||||
id: rowId,
|
||||
height: rowHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Notifier extends ChangeNotifier {
|
||||
GridRowChangeReason _reason = const InitialListState();
|
||||
class _GridRowChangesetNotifier extends ChangeNotifier {
|
||||
GridRowChangeReason reason = const InitialListState();
|
||||
|
||||
_Notifier();
|
||||
_GridRowChangesetNotifier();
|
||||
|
||||
void receive(GridRowChangeReason reason) {
|
||||
_reason = reason;
|
||||
void receive(GridRowChangeReason newReason) {
|
||||
reason = newReason;
|
||||
reason.map(
|
||||
insert: (_) => notifyListeners(),
|
||||
delete: (_) => notifyListeners(),
|
||||
@ -319,10 +329,10 @@ class GridRow with _$GridRow {
|
||||
const factory GridRow({
|
||||
required String gridId,
|
||||
required String blockId,
|
||||
required String rowId,
|
||||
required String id,
|
||||
required UnmodifiableListView<Field> fields,
|
||||
required double height,
|
||||
Row? data,
|
||||
Row? rawRow,
|
||||
}) = _GridRow;
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
GridRow rowData,
|
||||
Animation<double> animation,
|
||||
) {
|
||||
final rowCache = context.read<GridBloc>().getRowCache(rowData.blockId, rowData.rowId);
|
||||
final rowCache = context.read<GridBloc>().getRowCache(rowData.blockId, rowData.id);
|
||||
final fieldCache = context.read<GridBloc>().fieldCache;
|
||||
if (rowCache != null) {
|
||||
return SizeTransition(
|
||||
@ -236,7 +236,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
rowData: rowData,
|
||||
rowCache: rowCache,
|
||||
fieldCache: fieldCache,
|
||||
key: ValueKey(rowData.rowId),
|
||||
key: ValueKey(rowData.id),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -14,23 +14,21 @@ import 'text_cell.dart';
|
||||
import 'url_cell/url_cell.dart';
|
||||
|
||||
class GridCellBuilder {
|
||||
final GridCellsCache cellCache;
|
||||
final GridCellCache cellCache;
|
||||
final GridFieldCache fieldCache;
|
||||
GridCellBuilder({
|
||||
required this.cellCache,
|
||||
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,
|
||||
cellId: cell,
|
||||
cellCache: cellCache,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
|
||||
switch (cell.field.fieldType) {
|
||||
final key = cell.key();
|
||||
switch (cell.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);
|
||||
case FieldType.DateTime:
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class GridFieldCell extends StatelessWidget {
|
||||
FieldEditor(
|
||||
gridId: state.gridId,
|
||||
fieldName: field.name,
|
||||
contextLoader: FieldContextLoader(
|
||||
contextLoader: FieldTypeOptionLoader(
|
||||
gridId: state.gridId,
|
||||
field: field,
|
||||
),
|
||||
|
@ -14,7 +14,7 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final String gridId;
|
||||
final String fieldName;
|
||||
|
||||
final IFieldContextLoader contextLoader;
|
||||
final IFieldTypeOptionLoader contextLoader;
|
||||
const FieldEditor({
|
||||
required this.gridId,
|
||||
required this.fieldName,
|
||||
|
@ -151,7 +151,7 @@ class CreateFieldButton extends StatelessWidget {
|
||||
onTap: () => FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldName: "",
|
||||
contextLoader: NewFieldContextLoader(gridId: gridId),
|
||||
contextLoader: NewFieldTypeOptionLoader(gridId: gridId),
|
||||
).show(context),
|
||||
leftIcon: svgWidget("home/add"),
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -16,7 +16,7 @@ import 'row_detail.dart';
|
||||
|
||||
class GridRowWidget extends StatefulWidget {
|
||||
final GridRow rowData;
|
||||
final GridRowsCache rowCache;
|
||||
final GridRowCache rowCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
|
||||
GridRowWidget({
|
||||
@ -183,12 +183,12 @@ class _RowCells extends StatelessWidget {
|
||||
|
||||
List<Widget> _makeCells(BuildContext context, GridCellMap gridCellMap) {
|
||||
return gridCellMap.values.map(
|
||||
(gridCell) {
|
||||
final GridCellWidget child = builder.build(gridCell);
|
||||
(cellId) {
|
||||
final GridCellWidget child = builder.build(cellId);
|
||||
accessoryBuilder(GridCellAccessoryBuildContext buildContext) {
|
||||
final builder = child.accessoryBuilder;
|
||||
List<GridCellAccessory> accessories = [];
|
||||
if (gridCell.field.isPrimary) {
|
||||
if (cellId.field.isPrimary) {
|
||||
accessories.add(PrimaryCellAccessory(
|
||||
onTapCallback: onExpand,
|
||||
isCellEditing: buildContext.isCellEditing,
|
||||
@ -202,7 +202,7 @@ class _RowCells extends StatelessWidget {
|
||||
}
|
||||
|
||||
return CellContainer(
|
||||
width: gridCell.field.width.toDouble(),
|
||||
width: cellId.field.width.toDouble(),
|
||||
child: child,
|
||||
rowStateNotifier: Provider.of<RegionStateNotifier>(context, listen: false),
|
||||
accessoryBuilder: accessoryBuilder,
|
||||
|
@ -22,7 +22,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final GridRow rowData;
|
||||
final GridRowsCache rowCache;
|
||||
final GridRowCache rowCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
|
||||
const RowDetailPage({
|
||||
@ -122,7 +122,7 @@ class _PropertyList extends StatelessWidget {
|
||||
itemCount: state.gridCells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _RowDetailCell(
|
||||
gridCell: state.gridCells[index],
|
||||
cellId: state.gridCells[index],
|
||||
cellBuilder: cellBuilder,
|
||||
);
|
||||
},
|
||||
@ -137,10 +137,10 @@ class _PropertyList extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _RowDetailCell extends StatelessWidget {
|
||||
final GridCell gridCell;
|
||||
final GridCellIdentifier cellId;
|
||||
final GridCellBuilder cellBuilder;
|
||||
const _RowDetailCell({
|
||||
required this.gridCell,
|
||||
required this.cellId,
|
||||
required this.cellBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -148,8 +148,8 @@ class _RowDetailCell extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final style = _customCellStyle(theme, gridCell.field.fieldType);
|
||||
final cell = cellBuilder.build(gridCell, style: style);
|
||||
final style = _customCellStyle(theme, cellId.fieldType);
|
||||
final cell = cellBuilder.build(cellId, style: style);
|
||||
|
||||
final gesture = GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
@ -169,7 +169,7 @@ class _RowDetailCell extends StatelessWidget {
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)),
|
||||
child: FieldCellButton(field: cellId.field, onTap: () => _showFieldEditor(context)),
|
||||
),
|
||||
const HSpace(10),
|
||||
Expanded(child: gesture),
|
||||
@ -181,11 +181,11 @@ class _RowDetailCell extends StatelessWidget {
|
||||
|
||||
void _showFieldEditor(BuildContext context) {
|
||||
FieldEditor(
|
||||
gridId: gridCell.gridId,
|
||||
fieldName: gridCell.field.name,
|
||||
contextLoader: FieldContextLoader(
|
||||
gridId: gridCell.gridId,
|
||||
field: gridCell.field,
|
||||
gridId: cellId.gridId,
|
||||
fieldName: cellId.field.name,
|
||||
contextLoader: FieldTypeOptionLoader(
|
||||
gridId: cellId.gridId,
|
||||
field: cellId.field,
|
||||
),
|
||||
).show(context);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget {
|
||||
FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldName: field.name,
|
||||
contextLoader: FieldContextLoader(gridId: gridId, field: field),
|
||||
contextLoader: FieldTypeOptionLoader(gridId: gridId, field: field),
|
||||
).show(context, anchorDirection: AnchorDirection.bottomRight);
|
||||
},
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user