mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-11-10 10:18:57 +03:00
Merge pull request #612 from AppFlowy-IO/feat/grid_cell_data_operation
Feat/grid cell data operation
This commit is contained in:
commit
18dd4621b6
@ -153,31 +153,31 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<TextCellBloc, GridCellContext, void>(
|
||||
getIt.registerFactoryParam<TextCellBloc, GridCellController, void>(
|
||||
(context, _) => TextCellBloc(
|
||||
cellContext: context,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellContext, void>(
|
||||
getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellController, void>(
|
||||
(context, _) => SelectOptionCellBloc(
|
||||
cellContext: context,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<NumberCellBloc, GridCellContext, void>(
|
||||
getIt.registerFactoryParam<NumberCellBloc, GridCellController, void>(
|
||||
(context, _) => NumberCellBloc(
|
||||
cellContext: context,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<DateCellBloc, GridDateCellContext, void>(
|
||||
getIt.registerFactoryParam<DateCellBloc, GridDateCellController, void>(
|
||||
(context, _) => DateCellBloc(
|
||||
cellContext: context,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<CheckboxCellBloc, GridCellContext, void>(
|
||||
getIt.registerFactoryParam<CheckboxCellBloc, GridCellController, void>(
|
||||
(cellData, _) => CheckboxCellBloc(
|
||||
service: CellService(),
|
||||
cellContext: cellData,
|
||||
|
@ -9,18 +9,18 @@ import 'block_listener.dart';
|
||||
class GridBlockCacheService {
|
||||
final String gridId;
|
||||
final GridBlock block;
|
||||
late GridRowCacheService _rowCache;
|
||||
late GridRowsCache _rowCache;
|
||||
late GridBlockListener _listener;
|
||||
|
||||
List<GridRow> get rows => _rowCache.rows;
|
||||
GridRowCacheService get rowCache => _rowCache;
|
||||
GridRowsCache get rowCache => _rowCache;
|
||||
|
||||
GridBlockCacheService({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridFieldCache fieldCache,
|
||||
}) {
|
||||
_rowCache = GridRowCacheService(
|
||||
_rowCache = GridRowsCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
delegate: GridRowCacheDelegateImpl(fieldCache),
|
||||
|
@ -2,79 +2,38 @@ part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellMap = LinkedHashMap<String, GridCell>;
|
||||
|
||||
class _GridCellCacheObject {
|
||||
_GridCellCacheKey key;
|
||||
class _GridCellCacheItem {
|
||||
GridCellId key;
|
||||
dynamic object;
|
||||
_GridCellCacheObject({
|
||||
_GridCellCacheItem({
|
||||
required this.key,
|
||||
required this.object,
|
||||
});
|
||||
}
|
||||
|
||||
class _GridCellCacheKey {
|
||||
class GridCellId {
|
||||
final String fieldId;
|
||||
final String rowId;
|
||||
_GridCellCacheKey({
|
||||
GridCellId({
|
||||
required this.fieldId,
|
||||
required this.rowId,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class GridCellCacheDelegate {
|
||||
void onFieldUpdated(void Function(Field) callback);
|
||||
}
|
||||
|
||||
class GridCellCacheService {
|
||||
class GridCellsCache {
|
||||
final String gridId;
|
||||
final GridCellCacheDelegate delegate;
|
||||
|
||||
/// fieldId: {objectId: callback}
|
||||
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
|
||||
|
||||
/// fieldId: {cacheKey: cacheData}
|
||||
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
||||
GridCellCacheService({
|
||||
GridCellsCache({
|
||||
required this.gridId,
|
||||
required this.delegate,
|
||||
}) {
|
||||
delegate.onFieldUpdated((field) {
|
||||
_cellDataByFieldId.remove(field.id);
|
||||
final map = _fieldListenerByFieldId[field.id];
|
||||
if (map != null) {
|
||||
for (final callbacks in map.values) {
|
||||
for (final callback in callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
void remove(String fieldId) {
|
||||
_cellDataByFieldId.remove(fieldId);
|
||||
}
|
||||
|
||||
void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
|
||||
var map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
if (map == null) {
|
||||
_fieldListenerByFieldId[cacheKey.fieldId] = {};
|
||||
map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
map![cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
var objects = map[cacheKey.rowId];
|
||||
if (objects == null) {
|
||||
map[cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
objects.add(onFieldChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) {
|
||||
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
|
||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
||||
if (index != null && index != -1) {
|
||||
callbacks?.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
void insert<T extends _GridCellCacheObject>(T item) {
|
||||
void insert<T extends _GridCellCacheItem>(T item) {
|
||||
var map = _cellDataByFieldId[item.key.fieldId];
|
||||
if (map == null) {
|
||||
_cellDataByFieldId[item.key.fieldId] = {};
|
||||
@ -84,7 +43,7 @@ class GridCellCacheService {
|
||||
map![item.key.rowId] = item.object;
|
||||
}
|
||||
|
||||
T? get<T>(_GridCellCacheKey key) {
|
||||
T? get<T>(GridCellId key) {
|
||||
final map = _cellDataByFieldId[key.fieldId];
|
||||
if (map == null) {
|
||||
return null;
|
||||
@ -103,7 +62,6 @@ class GridCellCacheService {
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_fieldListenerByFieldId.clear();
|
||||
_cellDataByFieldId.clear();
|
||||
}
|
||||
}
|
||||
|
@ -3,23 +3,13 @@ part of 'cell_service.dart';
|
||||
abstract class IGridCellDataConfig {
|
||||
// The cell data will reload if it receives the field's change notification.
|
||||
bool get reloadOnFieldChanged;
|
||||
|
||||
// When the reloadOnCellChanged is true, it will load the cell data after user input.
|
||||
// For example: The number cell reload the cell data that carries the format
|
||||
// user input: 12
|
||||
// cell display: $12
|
||||
bool get reloadOnCellChanged;
|
||||
}
|
||||
|
||||
class GridCellDataConfig implements IGridCellDataConfig {
|
||||
@override
|
||||
final bool reloadOnCellChanged;
|
||||
|
||||
@override
|
||||
final bool reloadOnFieldChanged;
|
||||
|
||||
const GridCellDataConfig({
|
||||
this.reloadOnCellChanged = false,
|
||||
this.reloadOnFieldChanged = false,
|
||||
});
|
||||
}
|
||||
@ -72,29 +62,6 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
|
||||
}
|
||||
}
|
||||
|
||||
class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> {
|
||||
final SelectOptionService service;
|
||||
final GridCell gridCell;
|
||||
SelectOptionCellDataLoader({
|
||||
required this.gridCell,
|
||||
}) : service = SelectOptionService(gridCell: gridCell);
|
||||
@override
|
||||
Future<SelectOptionCellData?> loadData() async {
|
||||
return service.getOpitonContext().then((result) {
|
||||
return result.fold(
|
||||
(data) => data,
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
|
||||
}
|
||||
|
||||
class StringCellDataParser implements ICellDataParser<String> {
|
||||
@override
|
||||
String? parserData(List<int> data) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
abstract class _GridCellDataPersistence<D> {
|
||||
abstract class IGridCellDataPersistence<D> {
|
||||
Future<Option<FlowyError>> save(D data);
|
||||
}
|
||||
|
||||
class CellDataPersistence implements _GridCellDataPersistence<String> {
|
||||
class CellDataPersistence implements IGridCellDataPersistence<String> {
|
||||
final GridCell gridCell;
|
||||
|
||||
CellDataPersistence({
|
||||
@ -35,7 +35,7 @@ class CalendarData with _$CalendarData {
|
||||
const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
|
||||
}
|
||||
|
||||
class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> {
|
||||
class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
|
||||
final GridCell gridCell;
|
||||
DateCellDataPersistence({
|
||||
required this.gridCell,
|
||||
|
@ -0,0 +1,57 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'cell_service.dart';
|
||||
|
||||
abstract class GridFieldChangedNotifier {
|
||||
void onFieldChanged(void Function(Field) callback);
|
||||
void dispose();
|
||||
}
|
||||
|
||||
class GridCellFieldNotifier {
|
||||
/// fieldId: {objectId: callback}
|
||||
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
|
||||
|
||||
GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) {
|
||||
notifier.onFieldChanged(
|
||||
(field) {
|
||||
final map = _fieldListenerByFieldId[field.id];
|
||||
if (map != null) {
|
||||
for (final callbacks in map.values) {
|
||||
for (final callback in callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void addFieldListener(GridCellId cacheKey, VoidCallback onFieldChanged) {
|
||||
var map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
if (map == null) {
|
||||
_fieldListenerByFieldId[cacheKey.fieldId] = {};
|
||||
map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
map![cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
var objects = map[cacheKey.rowId];
|
||||
if (objects == null) {
|
||||
map[cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
objects.add(onFieldChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeFieldListener(GridCellId cacheKey, VoidCallback fn) {
|
||||
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
|
||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
||||
if (index != null && index != -1) {
|
||||
callbacks?.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_fieldListenerByFieldId.clear();
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
@ -14,9 +15,10 @@ import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'dart:convert' show utf8;
|
||||
|
||||
import 'cell_field_notifier.dart';
|
||||
part 'cell_service.freezed.dart';
|
||||
part 'cell_data_loader.dart';
|
||||
part 'context_builder.dart';
|
||||
|
@ -1,30 +1,35 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellContext = _GridCellContext<String, String>;
|
||||
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
|
||||
typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
|
||||
typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
|
||||
typedef GridCellController = IGridCellController<String, String>;
|
||||
typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellData, String>;
|
||||
typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>;
|
||||
typedef GridURLCellController = IGridCellController<URLCellData, String>;
|
||||
|
||||
class GridCellContextBuilder {
|
||||
final GridCellCacheService _cellCache;
|
||||
class GridCellControllerBuilder {
|
||||
final GridCell _gridCell;
|
||||
GridCellContextBuilder({
|
||||
required GridCellCacheService cellCache,
|
||||
required GridCell gridCell,
|
||||
}) : _cellCache = cellCache,
|
||||
final GridCellsCache _cellCache;
|
||||
final GridFieldCache _fieldCache;
|
||||
|
||||
GridCellControllerBuilder(
|
||||
{required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
|
||||
: _cellCache = cellCache,
|
||||
_fieldCache = fieldCache,
|
||||
_gridCell = gridCell;
|
||||
|
||||
_GridCellContext build() {
|
||||
IGridCellController build() {
|
||||
final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
|
||||
|
||||
switch (_gridCell.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellContext(
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
@ -34,22 +39,24 @@ class GridCellContextBuilder {
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
|
||||
return GridDateCellContext(
|
||||
return GridDateCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.Number:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
return GridCellContext(
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.RichText:
|
||||
@ -57,10 +64,11 @@ class GridCellContextBuilder {
|
||||
gridCell: _gridCell,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellContext(
|
||||
return GridCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
@ -71,10 +79,11 @@ class GridCellContextBuilder {
|
||||
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
||||
);
|
||||
|
||||
return GridSelectOptionCellContext(
|
||||
return GridSelectOptionCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
|
||||
@ -83,10 +92,11 @@ class GridCellContextBuilder {
|
||||
gridCell: _gridCell,
|
||||
parser: URLCellDataParser(),
|
||||
);
|
||||
return GridURLCellContext(
|
||||
return GridURLCellController(
|
||||
gridCell: _gridCell,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellFieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
||||
);
|
||||
}
|
||||
@ -95,37 +105,48 @@ class GridCellContextBuilder {
|
||||
}
|
||||
|
||||
// T: the type of the CellData
|
||||
// D: the type of the data that will be save to disk
|
||||
// D: the type of the data that will be saved to disk
|
||||
// ignore: must_be_immutable
|
||||
class _GridCellContext<T, D> extends Equatable {
|
||||
class IGridCellController<T, D> extends Equatable {
|
||||
final GridCell gridCell;
|
||||
final GridCellCacheService cellCache;
|
||||
final _GridCellCacheKey _cacheKey;
|
||||
final IGridCellDataLoader<T> cellDataLoader;
|
||||
final _GridCellDataPersistence<D> cellDataPersistence;
|
||||
final GridCellsCache _cellsCache;
|
||||
final GridCellId _cacheKey;
|
||||
final FieldService _fieldService;
|
||||
final GridCellFieldNotifier _cellFieldNotifier;
|
||||
// final GridCellFieldNotifier _fieldNotifier;
|
||||
final IGridCellDataLoader<T> _cellDataLoader;
|
||||
final IGridCellDataPersistence<D> _cellDataPersistence;
|
||||
|
||||
late final CellListener _cellListener;
|
||||
late final ValueNotifier<T?>? _cellDataNotifier;
|
||||
ValueNotifier<T?>? _cellDataNotifier;
|
||||
|
||||
bool isListening = false;
|
||||
VoidCallback? _onFieldChangedFn;
|
||||
Timer? _loadDataOperation;
|
||||
Timer? _saveDataOperation;
|
||||
bool isDispose = false;
|
||||
|
||||
_GridCellContext({
|
||||
IGridCellController({
|
||||
required this.gridCell,
|
||||
required this.cellCache,
|
||||
required this.cellDataLoader,
|
||||
required this.cellDataPersistence,
|
||||
}) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
|
||||
_cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
||||
required GridCellsCache cellCache,
|
||||
required GridCellFieldNotifier cellFieldNotifier,
|
||||
required IGridCellDataLoader<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);
|
||||
|
||||
_GridCellContext<T, D> clone() {
|
||||
return _GridCellContext(
|
||||
IGridCellController<T, D> clone() {
|
||||
return IGridCellController(
|
||||
gridCell: gridCell,
|
||||
cellDataLoader: cellDataLoader,
|
||||
cellCache: cellCache,
|
||||
cellDataPersistence: cellDataPersistence);
|
||||
cellDataLoader: _cellDataLoader,
|
||||
cellCache: _cellsCache,
|
||||
cellFieldNotifier: _cellFieldNotifier,
|
||||
cellDataPersistence: _cellDataPersistence);
|
||||
}
|
||||
|
||||
String get gridId => gridCell.gridId;
|
||||
@ -140,15 +161,23 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
|
||||
FieldType get fieldType => gridCell.field.fieldType;
|
||||
|
||||
VoidCallback? startListening({required void Function(T?) onCellChanged}) {
|
||||
VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
|
||||
if (isListening) {
|
||||
Log.error("Already started. It seems like you should call clone first");
|
||||
return null;
|
||||
}
|
||||
|
||||
isListening = true;
|
||||
_cellDataNotifier = ValueNotifier(cellCache.get(_cacheKey));
|
||||
|
||||
/// The cell data will be changed by two reasons:
|
||||
/// 1. User edit the cell
|
||||
/// 2. User edit the field
|
||||
/// For example: The number cell reload the cell data that carries the format
|
||||
/// user input: 12
|
||||
/// cell display: $12
|
||||
_cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
|
||||
_cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
||||
|
||||
/// 1.Listen on user edit event and load the new cell data if needed.
|
||||
_cellListener.start(onCellChanged: (result) {
|
||||
result.fold(
|
||||
(_) => _loadData(),
|
||||
@ -156,21 +185,21 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
);
|
||||
});
|
||||
|
||||
if (cellDataLoader.config.reloadOnFieldChanged) {
|
||||
_onFieldChangedFn = () {
|
||||
_loadData();
|
||||
};
|
||||
cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
}
|
||||
/// 2.Listen on the field event and load the cell data if needed.
|
||||
_onFieldChangedFn = () {
|
||||
if (onCellFieldChanged != null) {
|
||||
onCellFieldChanged();
|
||||
}
|
||||
|
||||
onCellChangedFn() {
|
||||
onCellChanged(_cellDataNotifier?.value);
|
||||
|
||||
if (cellDataLoader.config.reloadOnCellChanged) {
|
||||
if (_cellDataLoader.config.reloadOnFieldChanged) {
|
||||
_loadData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_cellFieldNotifier.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
|
||||
/// Notify the listener, the cell data was changed.
|
||||
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
|
||||
_cellDataNotifier?.addListener(onCellChangedFn);
|
||||
return onCellChangedFn;
|
||||
}
|
||||
@ -180,7 +209,7 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
}
|
||||
|
||||
T? getCellData({bool loadIfNoCache = true}) {
|
||||
final data = cellCache.get(_cacheKey);
|
||||
final data = _cellsCache.get(_cacheKey);
|
||||
if (data == null && loadIfNoCache) {
|
||||
_loadData();
|
||||
}
|
||||
@ -195,13 +224,13 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
if (deduplicate) {
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
||||
final result = await cellDataPersistence.save(data);
|
||||
final result = await _cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final result = await cellDataPersistence.save(data);
|
||||
final result = await _cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
@ -211,24 +240,55 @@ class _GridCellContext<T, D> extends Equatable {
|
||||
void _loadData() {
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
cellDataLoader.loadData().then((data) {
|
||||
_cellDataLoader.loadData().then((data) {
|
||||
_cellDataNotifier?.value = data;
|
||||
cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
|
||||
_cellsCache.insert(_GridCellCacheItem(key: _cacheKey, object: data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (isDispose) {
|
||||
Log.error("$this should only dispose once");
|
||||
return;
|
||||
}
|
||||
isDispose = true;
|
||||
_cellListener.stop();
|
||||
_loadDataOperation?.cancel();
|
||||
_saveDataOperation?.cancel();
|
||||
_cellDataNotifier = null;
|
||||
|
||||
if (_onFieldChangedFn != null) {
|
||||
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
_cellFieldNotifier.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
||||
_onFieldChangedFn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [cellCache.get(_cacheKey) ?? "", cellId];
|
||||
List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId];
|
||||
}
|
||||
|
||||
class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
|
||||
final GridFieldCache _cache;
|
||||
FieldChangesetCallback? _onChangesetFn;
|
||||
|
||||
_GridFieldChangedNotifierImpl(GridFieldCache cache) : _cache = cache;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_onChangesetFn != null) {
|
||||
_cache.removeListener(onChangsetListener: _onChangesetFn!);
|
||||
_onChangesetFn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onFieldChanged(void Function(Field p1) callback) {
|
||||
_onChangesetFn = (GridFieldChangeset changeset) {
|
||||
for (final updatedField in changeset.updatedFields) {
|
||||
callback(updatedField);
|
||||
}
|
||||
};
|
||||
_cache.addListener(onChangeset: _onChangesetFn);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'checkbox_cell_bloc.freezed.dart';
|
||||
|
||||
class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
final GridCellContext cellContext;
|
||||
final GridCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
CheckboxCellBloc({
|
||||
@ -67,7 +67,7 @@ class CheckboxCellState with _$CheckboxCellState {
|
||||
required bool isSelected,
|
||||
}) = _CheckboxCellState;
|
||||
|
||||
factory CheckboxCellState.initial(GridCellContext context) {
|
||||
factory CheckboxCellState.initial(GridCellController context) {
|
||||
return CheckboxCellState(isSelected: _isSelected(context.getCellData()));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
part 'date_cal_bloc.freezed.dart';
|
||||
|
||||
class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
|
||||
final GridDateCellContext cellContext;
|
||||
final GridDateCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
DateCalBloc({
|
||||
|
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'date_cell_bloc.freezed.dart';
|
||||
|
||||
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
final GridDateCellContext cellContext;
|
||||
final GridDateCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) {
|
||||
@ -60,7 +60,7 @@ class DateCellState with _$DateCellState {
|
||||
required Field field,
|
||||
}) = _DateCellState;
|
||||
|
||||
factory DateCellState.initial(GridDateCellContext context) {
|
||||
factory DateCellState.initial(GridDateCellController context) {
|
||||
final cellData = context.getCellData();
|
||||
|
||||
return DateCellState(
|
||||
|
@ -8,7 +8,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'number_cell_bloc.freezed.dart';
|
||||
|
||||
class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
|
||||
final GridCellContext cellContext;
|
||||
final GridCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
NumberCellBloc({
|
||||
@ -72,7 +72,7 @@ class NumberCellState with _$NumberCellState {
|
||||
required Either<String, FlowyError> content,
|
||||
}) = _NumberCellState;
|
||||
|
||||
factory NumberCellState.initial(GridCellContext context) {
|
||||
factory NumberCellState.initial(GridCellController context) {
|
||||
final cellContent = context.getCellData() ?? "";
|
||||
return NumberCellState(
|
||||
content: left(cellContent),
|
||||
|
@ -7,7 +7,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_serv
|
||||
part 'select_option_cell_bloc.freezed.dart';
|
||||
|
||||
class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
|
||||
final GridSelectOptionCellContext cellContext;
|
||||
final GridSelectOptionCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
|
||||
SelectOptionCellBloc({
|
||||
@ -66,7 +66,7 @@ class SelectOptionCellState with _$SelectOptionCellState {
|
||||
required List<SelectOption> selectedOptions,
|
||||
}) = _SelectOptionCellState;
|
||||
|
||||
factory SelectOptionCellState.initial(GridSelectOptionCellContext context) {
|
||||
factory SelectOptionCellState.initial(GridSelectOptionCellController context) {
|
||||
final data = context.getCellData();
|
||||
|
||||
return SelectOptionCellState(
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
|
||||
@ -13,16 +12,13 @@ part 'select_option_editor_bloc.freezed.dart';
|
||||
|
||||
class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
|
||||
final SelectOptionService _selectOptionService;
|
||||
final GridSelectOptionCellContext cellContext;
|
||||
late final GridFieldsListener _fieldListener;
|
||||
void Function()? _onCellChangedFn;
|
||||
final GridSelectOptionCellController cellController;
|
||||
Timer? _delayOperation;
|
||||
|
||||
SelectOptionCellEditorBloc({
|
||||
required this.cellContext,
|
||||
}) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell),
|
||||
_fieldListener = GridFieldsListener(gridId: cellContext.gridId),
|
||||
super(SelectOptionEditorState.initial(cellContext)) {
|
||||
required this.cellController,
|
||||
}) : _selectOptionService = SelectOptionService(gridCell: cellController.gridCell),
|
||||
super(SelectOptionEditorState.initial(cellController)) {
|
||||
on<SelectOptionEditorEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
@ -64,13 +60,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
if (_onCellChangedFn != null) {
|
||||
cellContext.removeListener(_onCellChangedFn!);
|
||||
_onCellChangedFn = null;
|
||||
}
|
||||
_delayOperation?.cancel();
|
||||
await _fieldListener.stop();
|
||||
cellContext.dispose();
|
||||
cellController.dispose();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@ -157,24 +148,16 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_onCellChangedFn = cellContext.startListening(
|
||||
cellController.startListening(
|
||||
onCellChanged: ((selectOptionContext) {
|
||||
if (!isClosed) {
|
||||
_loadOptions();
|
||||
}
|
||||
}),
|
||||
onCellFieldChanged: () {
|
||||
_loadOptions();
|
||||
},
|
||||
);
|
||||
|
||||
_fieldListener.start(onFieldsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
if (changeset.updatedFields.isNotEmpty) {
|
||||
_loadOptions();
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,7 +183,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
required Option<String> filter,
|
||||
}) = _SelectOptionEditorState;
|
||||
|
||||
factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
|
||||
factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
|
||||
final data = context.getCellData(loadIfNoCache: false);
|
||||
return SelectOptionEditorState(
|
||||
options: data?.options ?? [],
|
||||
|
@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'text_cell_bloc.freezed.dart';
|
||||
|
||||
class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
final GridCellContext cellContext;
|
||||
final GridCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
TextCellBloc({
|
||||
required this.cellContext,
|
||||
@ -63,7 +63,7 @@ class TextCellState with _$TextCellState {
|
||||
required String content,
|
||||
}) = _TextCellState;
|
||||
|
||||
factory TextCellState.initial(GridCellContext context) => TextCellState(
|
||||
factory TextCellState.initial(GridCellController context) => TextCellState(
|
||||
content: context.getCellData() ?? "",
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'url_cell_bloc.freezed.dart';
|
||||
|
||||
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
||||
final GridURLCellContext cellContext;
|
||||
final GridURLCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
URLCellBloc({
|
||||
required this.cellContext,
|
||||
@ -67,7 +67,7 @@ class URLCellState with _$URLCellState {
|
||||
required String url,
|
||||
}) = _URLCellState;
|
||||
|
||||
factory URLCellState.initial(GridURLCellContext context) {
|
||||
factory URLCellState.initial(GridURLCellController context) {
|
||||
final cellData = context.getCellData();
|
||||
return URLCellState(
|
||||
content: cellData?.content ?? "",
|
||||
|
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
|
||||
part 'url_cell_editor_bloc.freezed.dart';
|
||||
|
||||
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
|
||||
final GridURLCellContext cellContext;
|
||||
final GridURLCellController cellContext;
|
||||
void Function()? _onCellChangedFn;
|
||||
URLCellEditorBloc({
|
||||
required this.cellContext,
|
||||
@ -64,7 +64,7 @@ class URLCellEditorState with _$URLCellEditorState {
|
||||
required String content,
|
||||
}) = _URLCellEditorState;
|
||||
|
||||
factory URLCellEditorState.initial(GridURLCellContext context) {
|
||||
factory URLCellEditorState.initial(GridURLCellController context) {
|
||||
final cellData = context.getCellData();
|
||||
return URLCellEditorState(
|
||||
content: cellData?.content ?? "",
|
||||
|
@ -68,7 +68,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
GridRowCacheService? getRowCache(String blockId, String rowId) {
|
||||
GridRowsCache? getRowCache(String blockId, String rowId) {
|
||||
final GridBlockCacheService? blockCache = _blocks[blockId];
|
||||
return blockCache?.rowCache;
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
|
||||
}
|
||||
|
||||
@override
|
||||
void onFieldUpdated(void Function(Field) callback) {
|
||||
void onFieldChanged(void Function(Field) callback) {
|
||||
_onChangesetFn = (GridFieldChangeset changeset) {
|
||||
for (final updatedField in changeset.updatedFields) {
|
||||
callback(updatedField);
|
||||
|
@ -11,12 +11,12 @@ part 'row_bloc.freezed.dart';
|
||||
|
||||
class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
final RowService _rowService;
|
||||
final GridRowCacheService _rowCache;
|
||||
final GridRowsCache _rowCache;
|
||||
void Function()? _rowListenFn;
|
||||
|
||||
RowBloc({
|
||||
required GridRow rowData,
|
||||
required GridRowCacheService rowCache,
|
||||
required GridRowsCache rowCache,
|
||||
}) : _rowService = RowService(
|
||||
gridId: rowData.gridId,
|
||||
blockId: rowData.blockId,
|
||||
|
@ -8,12 +8,12 @@ part 'row_detail_bloc.freezed.dart';
|
||||
|
||||
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
final GridRow rowData;
|
||||
final GridRowCacheService _rowCache;
|
||||
final GridRowsCache _rowCache;
|
||||
void Function()? _rowListenFn;
|
||||
|
||||
RowDetailBloc({
|
||||
required this.rowData,
|
||||
required GridRowCacheService rowCache,
|
||||
required GridRowsCache rowCache,
|
||||
}) : _rowCache = rowCache,
|
||||
super(RowDetailState.initial()) {
|
||||
on<RowDetailEvent>(
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
@ -15,34 +14,36 @@ part 'row_service.freezed.dart';
|
||||
|
||||
typedef RowUpdateCallback = void Function();
|
||||
|
||||
abstract class GridRowCacheDelegate with GridCellCacheDelegate {
|
||||
abstract class GridRowCacheDelegate {
|
||||
UnmodifiableListView<Field> get fields;
|
||||
void onFieldsChanged(void Function() callback);
|
||||
void onFieldsChanged(VoidCallback callback);
|
||||
void onFieldChanged(void Function(Field) callback);
|
||||
void dispose();
|
||||
}
|
||||
|
||||
class GridRowCacheService {
|
||||
class GridRowsCache {
|
||||
final String gridId;
|
||||
final GridBlock block;
|
||||
final _Notifier _notifier;
|
||||
List<GridRow> _rows = [];
|
||||
final HashMap<String, Row> _rowByRowId;
|
||||
final GridRowCacheDelegate _delegate;
|
||||
final GridCellCacheService _cellCache;
|
||||
final GridCellsCache _cellCache;
|
||||
|
||||
List<GridRow> get rows => _rows;
|
||||
GridCellCacheService get cellCache => _cellCache;
|
||||
GridCellsCache get cellCache => _cellCache;
|
||||
|
||||
GridRowCacheService({
|
||||
GridRowsCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridRowCacheDelegate delegate,
|
||||
}) : _cellCache = GridCellCacheService(gridId: gridId, delegate: delegate),
|
||||
}) : _cellCache = GridCellsCache(gridId: gridId),
|
||||
_rowByRowId = HashMap(),
|
||||
_notifier = _Notifier(),
|
||||
_delegate = delegate {
|
||||
//
|
||||
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
|
||||
delegate.onFieldChanged((field) => _cellCache.remove(field.id));
|
||||
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList();
|
||||
}
|
||||
|
||||
|
@ -228,12 +228,14 @@ class _GridRowsState extends State<_GridRows> {
|
||||
Animation<double> animation,
|
||||
) {
|
||||
final rowCache = context.read<GridBloc>().getRowCache(rowData.blockId, rowData.rowId);
|
||||
final fieldCache = context.read<GridBloc>().fieldCache;
|
||||
if (rowCache != null) {
|
||||
return SizeTransition(
|
||||
sizeFactor: animation,
|
||||
child: GridRowWidget(
|
||||
rowData: rowData,
|
||||
rowCache: rowCache,
|
||||
fieldCache: fieldCache,
|
||||
key: ValueKey(rowData.rowId),
|
||||
),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -12,28 +13,41 @@ import 'select_option_cell/select_option_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
import 'url_cell/url_cell.dart';
|
||||
|
||||
GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCacheService cellCache, {GridCellStyle? style}) {
|
||||
final key = ValueKey(gridCell.cellId());
|
||||
class GridCellBuilder {
|
||||
final GridCellsCache cellCache;
|
||||
final GridFieldCache fieldCache;
|
||||
GridCellBuilder({
|
||||
required this.cellCache,
|
||||
required this.fieldCache,
|
||||
});
|
||||
|
||||
final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache);
|
||||
GridCellWidget build(GridCell cell, {GridCellStyle? style}) {
|
||||
final key = ValueKey(cell.cellId());
|
||||
|
||||
switch (gridCell.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxCell(cellContextBuilder: cellContextBuilder, key: key);
|
||||
case FieldType.DateTime:
|
||||
return DateCell(cellContextBuilder: cellContextBuilder, key: key, style: style);
|
||||
case FieldType.SingleSelect:
|
||||
return SingleSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
case FieldType.MultiSelect:
|
||||
return MultiSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
case FieldType.Number:
|
||||
return NumberCell(cellContextBuilder: cellContextBuilder, key: key);
|
||||
case FieldType.RichText:
|
||||
return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
case FieldType.URL:
|
||||
return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
|
||||
final cellControllerBuilder = GridCellControllerBuilder(
|
||||
gridCell: cell,
|
||||
cellCache: cellCache,
|
||||
fieldCache: fieldCache,
|
||||
);
|
||||
|
||||
switch (cell.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key);
|
||||
case FieldType.DateTime:
|
||||
return DateCell(cellControllerBuilder: cellControllerBuilder, key: key, style: style);
|
||||
case FieldType.SingleSelect:
|
||||
return SingleSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
|
||||
case FieldType.MultiSelect:
|
||||
return MultiSelectCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
|
||||
case FieldType.Number:
|
||||
return NumberCell(cellContorllerBuilder: cellControllerBuilder, key: key);
|
||||
case FieldType.RichText:
|
||||
return GridTextCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
|
||||
case FieldType.URL:
|
||||
return GridURLCell(cellContorllerBuilder: cellControllerBuilder, style: style, key: key);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
class BlankCell extends StatelessWidget {
|
||||
|
@ -7,9 +7,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class CheckboxCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellControllerBuilder;
|
||||
CheckboxCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -22,7 +22,7 @@ class _CheckboxCellState extends GridCellState<CheckboxCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build();
|
||||
final cellContext = widget.cellControllerBuilder.build();
|
||||
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
@ -19,12 +19,12 @@ abstract class GridCellDelegate {
|
||||
}
|
||||
|
||||
class DateCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellControllerBuilder;
|
||||
late final DateCellStyle? cellStyle;
|
||||
|
||||
DateCell({
|
||||
GridCellStyle? style,
|
||||
required this.cellContextBuilder,
|
||||
required this.cellControllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
if (style != null) {
|
||||
@ -43,7 +43,7 @@ class _DateCellState extends GridCellState<DateCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build();
|
||||
final cellContext = widget.cellControllerBuilder.build();
|
||||
_cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class DateCellEditor with FlowyOverlayDelegate {
|
||||
|
||||
Future<void> show(
|
||||
BuildContext context, {
|
||||
required GridDateCellContext cellContext,
|
||||
required GridDateCellController cellContext,
|
||||
}) async {
|
||||
DateCellEditor.remove(context);
|
||||
|
||||
@ -75,7 +75,7 @@ class DateCellEditor with FlowyOverlayDelegate {
|
||||
}
|
||||
|
||||
class _CellCalendarWidget extends StatelessWidget {
|
||||
final GridDateCellContext cellContext;
|
||||
final GridDateCellController cellContext;
|
||||
final DateTypeOption dateTypeOption;
|
||||
|
||||
const _CellCalendarWidget({
|
||||
|
@ -7,10 +7,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class NumberCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
|
||||
NumberCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -25,7 +25,7 @@ class _NumberCellState extends GridFocusNodeCellState<NumberCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build();
|
||||
final cellContext = widget.cellContorllerBuilder.build();
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)..add(const NumberCellEvent.initial());
|
||||
_controller = TextEditingController(text: contentFromState(_cellBloc.state));
|
||||
super.initState();
|
||||
|
@ -21,11 +21,11 @@ class SelectOptionCellStyle extends GridCellStyle {
|
||||
}
|
||||
|
||||
class SingleSelectCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
late final SelectOptionCellStyle? cellStyle;
|
||||
|
||||
SingleSelectCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
@ -45,7 +45,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext;
|
||||
final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
|
||||
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
@ -60,7 +60,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
selectOptions: state.selectedOptions,
|
||||
cellStyle: widget.cellStyle,
|
||||
onFocus: (value) => widget.onCellEditing.value = value,
|
||||
cellContextBuilder: widget.cellContextBuilder);
|
||||
cellContorllerBuilder: widget.cellContorllerBuilder);
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -75,11 +75,11 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
|
||||
//----------------------------------------------------------------
|
||||
class MultiSelectCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
late final SelectOptionCellStyle? cellStyle;
|
||||
|
||||
MultiSelectCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
@ -99,7 +99,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridSelectOptionCellContext;
|
||||
final cellContext = widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
|
||||
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)..add(const SelectOptionCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
@ -114,7 +114,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
|
||||
selectOptions: state.selectedOptions,
|
||||
cellStyle: widget.cellStyle,
|
||||
onFocus: (value) => widget.onCellEditing.value = value,
|
||||
cellContextBuilder: widget.cellContextBuilder);
|
||||
cellContorllerBuilder: widget.cellContorllerBuilder);
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -131,12 +131,12 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
final List<SelectOption> selectOptions;
|
||||
final void Function(bool) onFocus;
|
||||
final SelectOptionCellStyle? cellStyle;
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
const _SelectOptionCell({
|
||||
required this.selectOptions,
|
||||
required this.onFocus,
|
||||
required this.cellStyle,
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -172,7 +172,7 @@ class _SelectOptionCell extends StatelessWidget {
|
||||
InkWell(
|
||||
onTap: () {
|
||||
onFocus(true);
|
||||
final cellContext = cellContextBuilder.build() as GridSelectOptionCellContext;
|
||||
final cellContext = cellContorllerBuilder.build() as GridSelectOptionCellController;
|
||||
SelectOptionCellEditor.show(context, cellContext, () => onFocus(false));
|
||||
},
|
||||
),
|
||||
|
@ -24,11 +24,11 @@ import 'text_field.dart';
|
||||
const double _editorPannelWidth = 300;
|
||||
|
||||
class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final GridSelectOptionCellContext cellContext;
|
||||
final GridSelectOptionCellController cellController;
|
||||
final VoidCallback onDismissed;
|
||||
|
||||
const SelectOptionCellEditor({
|
||||
required this.cellContext,
|
||||
required this.cellController,
|
||||
required this.onDismissed,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -37,7 +37,7 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SelectOptionCellEditorBloc(
|
||||
cellContext: cellContext,
|
||||
cellController: cellController,
|
||||
)..add(const SelectOptionEditorEvent.initial()),
|
||||
child: BlocBuilder<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||
builder: (context, state) {
|
||||
@ -59,12 +59,12 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
|
||||
|
||||
static void show(
|
||||
BuildContext context,
|
||||
GridSelectOptionCellContext cellContext,
|
||||
GridSelectOptionCellController cellContext,
|
||||
VoidCallback onDismissed,
|
||||
) {
|
||||
SelectOptionCellEditor.remove(context);
|
||||
final editor = SelectOptionCellEditor(
|
||||
cellContext: cellContext,
|
||||
cellController: cellContext,
|
||||
onDismissed: onDismissed,
|
||||
);
|
||||
|
||||
|
@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle {
|
||||
}
|
||||
|
||||
class GridTextCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
late final GridTextCellStyle? cellStyle;
|
||||
GridTextCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
@ -39,7 +39,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build();
|
||||
final cellContext = widget.cellContorllerBuilder.build();
|
||||
_cellBloc = getIt<TextCellBloc>(param1: cellContext);
|
||||
_cellBloc.add(const TextCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
|
@ -7,7 +7,7 @@ import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final GridURLCellContext cellContext;
|
||||
final GridURLCellController cellContext;
|
||||
final VoidCallback completed;
|
||||
const URLCellEditor({required this.cellContext, required this.completed, Key? key}) : super(key: key);
|
||||
|
||||
@ -16,7 +16,7 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||
|
||||
static void show(
|
||||
BuildContext context,
|
||||
GridURLCellContext cellContext,
|
||||
GridURLCellController cellContext,
|
||||
VoidCallback completed,
|
||||
) {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
|
@ -31,10 +31,10 @@ enum GridURLCellAccessoryType {
|
||||
}
|
||||
|
||||
class GridURLCell extends GridCellWidget {
|
||||
final GridCellContextBuilder cellContextBuilder;
|
||||
final GridCellControllerBuilder cellContorllerBuilder;
|
||||
late final GridURLCellStyle? cellStyle;
|
||||
GridURLCell({
|
||||
required this.cellContextBuilder,
|
||||
required this.cellContorllerBuilder,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key) {
|
||||
@ -51,11 +51,11 @@ class GridURLCell extends GridCellWidget {
|
||||
GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
|
||||
switch (ty) {
|
||||
case GridURLCellAccessoryType.edit:
|
||||
final cellContext = cellContextBuilder.build() as GridURLCellContext;
|
||||
final cellContext = cellContorllerBuilder.build() as GridURLCellController;
|
||||
return _EditURLAccessory(cellContext: cellContext, anchorContext: buildContext.anchorContext);
|
||||
|
||||
case GridURLCellAccessoryType.copyURL:
|
||||
final cellContext = cellContextBuilder.build() as GridURLCellContext;
|
||||
final cellContext = cellContorllerBuilder.build() as GridURLCellController;
|
||||
return _CopyURLAccessory(cellContext: cellContext);
|
||||
}
|
||||
}
|
||||
@ -83,7 +83,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
||||
final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
|
||||
_cellBloc = URLCellBloc(cellContext: cellContext);
|
||||
_cellBloc.add(const URLCellEvent.initial());
|
||||
super.initState();
|
||||
@ -132,7 +132,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
||||
final cellContext = widget.cellContorllerBuilder.build() as GridURLCellController;
|
||||
widget.onCellEditing.value = true;
|
||||
URLCellEditor.show(context, cellContext, () {
|
||||
widget.onCellEditing.value = false;
|
||||
@ -155,7 +155,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
}
|
||||
|
||||
class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
final GridURLCellContext cellContext;
|
||||
final GridURLCellController cellContext;
|
||||
final BuildContext anchorContext;
|
||||
const _EditURLAccessory({
|
||||
required this.cellContext,
|
||||
@ -176,7 +176,7 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
}
|
||||
|
||||
class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
final GridURLCellContext cellContext;
|
||||
final GridURLCellController cellContext;
|
||||
const _CopyURLAccessory({required this.cellContext, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -10,19 +10,25 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'row_action_sheet.dart';
|
||||
|
||||
import 'row_action_sheet.dart';
|
||||
import 'row_detail.dart';
|
||||
|
||||
class GridRowWidget extends StatefulWidget {
|
||||
final GridRow rowData;
|
||||
final GridRowCacheService rowCache;
|
||||
final GridRowsCache rowCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
|
||||
const GridRowWidget({
|
||||
GridRowWidget({
|
||||
required this.rowData,
|
||||
required this.rowCache,
|
||||
required GridFieldCache fieldCache,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
}) : cellBuilder = GridCellBuilder(
|
||||
cellCache: rowCache.cellCache,
|
||||
fieldCache: fieldCache,
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
State<GridRowWidget> createState() => _GridRowWidgetState();
|
||||
@ -52,7 +58,11 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
return Row(
|
||||
children: [
|
||||
const _RowLeading(),
|
||||
Expanded(child: _RowCells(cellCache: widget.rowCache.cellCache, onExpand: () => _expandRow(context))),
|
||||
Expanded(
|
||||
child: _RowCells(
|
||||
builder: widget.cellBuilder,
|
||||
onExpand: () => _expandRow(context),
|
||||
)),
|
||||
const _RowTrailing(),
|
||||
],
|
||||
);
|
||||
@ -72,6 +82,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
|
||||
final page = RowDetailPage(
|
||||
rowData: widget.rowData,
|
||||
rowCache: widget.rowCache,
|
||||
cellBuilder: widget.cellBuilder,
|
||||
);
|
||||
page.show(context);
|
||||
}
|
||||
@ -146,9 +157,13 @@ class _DeleteRowButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _RowCells extends StatelessWidget {
|
||||
final GridCellCacheService cellCache;
|
||||
final VoidCallback onExpand;
|
||||
const _RowCells({required this.cellCache, required this.onExpand, Key? key}) : super(key: key);
|
||||
final GridCellBuilder builder;
|
||||
const _RowCells({
|
||||
required this.builder,
|
||||
required this.onExpand,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -169,8 +184,7 @@ class _RowCells extends StatelessWidget {
|
||||
List<Widget> _makeCells(BuildContext context, GridCellMap gridCellMap) {
|
||||
return gridCellMap.values.map(
|
||||
(gridCell) {
|
||||
final GridCellWidget child = buildGridCellWidget(gridCell, cellCache);
|
||||
|
||||
final GridCellWidget child = builder.build(gridCell);
|
||||
accessoryBuilder(GridCellAccessoryBuildContext buildContext) {
|
||||
final builder = child.accessoryBuilder;
|
||||
List<GridCellAccessory> accessories = [];
|
||||
|
@ -22,11 +22,13 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final GridRow rowData;
|
||||
final GridRowCacheService rowCache;
|
||||
final GridRowsCache rowCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
|
||||
const RowDetailPage({
|
||||
required this.rowData,
|
||||
required this.rowCache,
|
||||
required this.cellBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -74,7 +76,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
||||
children: const [Spacer(), _CloseButton()],
|
||||
),
|
||||
),
|
||||
Expanded(child: _PropertyList(cellCache: widget.rowCache.cellCache)),
|
||||
Expanded(child: _PropertyList(cellBuilder: widget.cellBuilder)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -98,10 +100,10 @@ class _CloseButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _PropertyList extends StatelessWidget {
|
||||
final GridCellCacheService cellCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
final ScrollController _scrollController;
|
||||
_PropertyList({
|
||||
required this.cellCache,
|
||||
required this.cellBuilder,
|
||||
Key? key,
|
||||
}) : _scrollController = ScrollController(),
|
||||
super(key: key);
|
||||
@ -121,7 +123,7 @@ class _PropertyList extends StatelessWidget {
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _RowDetailCell(
|
||||
gridCell: state.gridCells[index],
|
||||
cellCache: cellCache,
|
||||
cellBuilder: cellBuilder,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
@ -136,10 +138,10 @@ class _PropertyList extends StatelessWidget {
|
||||
|
||||
class _RowDetailCell extends StatelessWidget {
|
||||
final GridCell gridCell;
|
||||
final GridCellCacheService cellCache;
|
||||
final GridCellBuilder cellBuilder;
|
||||
const _RowDetailCell({
|
||||
required this.gridCell,
|
||||
required this.cellCache,
|
||||
required this.cellBuilder,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -147,7 +149,7 @@ class _RowDetailCell extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final style = _customCellStyle(theme, gridCell.field.fieldType);
|
||||
final cell = buildGridCellWidget(gridCell, cellCache, style: style);
|
||||
final cell = cellBuilder.build(gridCell, style: style);
|
||||
|
||||
final gesture = GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
Loading…
Reference in New Issue
Block a user