Merge pull request #800 from AppFlowy-IO/fix/create_column

fix: create column errors
This commit is contained in:
Nathan.fooo 2022-08-09 19:01:48 +08:00 committed by GitHub
commit fd1bbaa6fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 501 additions and 197 deletions

View File

@ -1,17 +1,161 @@
// ignore_for_file: unused_field
import 'package:appflowy_board/appflowy_board.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
class BoardPage extends StatelessWidget {
class BoardPage extends StatefulWidget {
final ViewPB _view;
const BoardPage({required ViewPB view, Key? key})
: _view = view,
super(key: key);
@override
State<BoardPage> createState() => _BoardPageState();
}
class _BoardPageState extends State<BoardPage> {
final BoardDataController boardDataController = BoardDataController(
onMoveColumn: (fromIndex, toIndex) {
debugPrint('Move column from $fromIndex to $toIndex');
},
onMoveColumnItem: (columnId, fromIndex, toIndex) {
debugPrint('Move $columnId:$fromIndex to $columnId:$toIndex');
},
onMoveColumnItemToColumn: (fromColumnId, fromIndex, toColumnId, toIndex) {
debugPrint('Move $fromColumnId:$fromIndex to $toColumnId:$toIndex');
},
);
@override
void initState() {
final column1 = BoardColumnData(id: "To Do", items: [
TextItem("Card 1"),
TextItem("Card 2"),
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 4"),
]);
final column2 = BoardColumnData(id: "In Progress", items: [
RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 6"),
]);
final column3 = BoardColumnData(id: "Done", items: []);
boardDataController.addColumn(column1);
boardDataController.addColumn(column2);
boardDataController.addColumn(column3);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container();
final config = BoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
);
return Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Board(
dataController: boardDataController,
footBuilder: (context, columnData) {
return AppFlowyColumnFooter(
icon: const Icon(Icons.add, size: 20),
title: const Text('New'),
height: 50,
margin: config.columnItemPadding,
);
},
headerBuilder: (context, columnData) {
return AppFlowyColumnHeader(
icon: const Icon(Icons.lightbulb_circle),
title: Text(columnData.id),
addIcon: const Icon(Icons.add, size: 20),
moreIcon: const Icon(Icons.more_horiz, size: 20),
height: 50,
margin: config.columnItemPadding,
);
},
cardBuilder: (context, item) {
return AppFlowyColumnItemCard(
key: ObjectKey(item),
child: _buildCard(item),
);
},
columnConstraints: const BoxConstraints.tightFor(width: 240),
config: BoardConfig(
columnBackgroundColor: HexColor.fromHex('#F7F8FC'),
),
),
),
);
}
Widget _buildCard(ColumnItem item) {
if (item is TextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(item.s),
),
);
}
if (item is RichTextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
Text(
item.subtitle,
style: const TextStyle(fontSize: 12, color: Colors.grey),
)
],
),
),
);
}
throw UnimplementedError();
}
}
class TextItem extends ColumnItem {
final String s;
TextItem(this.s);
@override
String get id => s;
}
class RichTextItem extends ColumnItem {
final String title;
final String subtitle;
RichTextItem({required this.title, required this.subtitle});
@override
String get id => title;
}
extension HexColor on Color {
static Color fromHex(String hexString) {
final buffer = StringBuffer();
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
buffer.write(hexString.replaceFirst('#', ''));
return Color(int.parse(buffer.toString(), radix: 16));
}
}

View File

@ -11,7 +11,9 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
class DocumentBanner extends StatelessWidget {
final void Function() onRestore;
final void Function() onDelete;
const DocumentBanner({required this.onRestore, required this.onDelete, Key? key}) : super(key: key);
const DocumentBanner(
{required this.onRestore, required this.onDelete, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -26,7 +28,8 @@ class DocumentBanner extends StatelessWidget {
fit: BoxFit.scaleDown,
child: Row(
children: [
FlowyText.medium(LocaleKeys.deletePagePrompt_text.tr(), color: Colors.white),
FlowyText.medium(LocaleKeys.deletePagePrompt_text.tr(),
color: Colors.white),
const HSpace(20),
BaseStyledButton(
minWidth: 160,
@ -37,7 +40,10 @@ class DocumentBanner extends StatelessWidget {
downColor: theme.main1,
outlineColor: Colors.white,
borderRadius: Corners.s8Border,
child: FlowyText.medium(LocaleKeys.deletePagePrompt_restore.tr(), color: Colors.white, fontSize: 14),
child: FlowyText.medium(
LocaleKeys.deletePagePrompt_restore.tr(),
color: Colors.white,
fontSize: 14),
onPressed: onRestore),
const HSpace(20),
BaseStyledButton(
@ -49,8 +55,10 @@ class DocumentBanner extends StatelessWidget {
downColor: theme.main1,
outlineColor: Colors.white,
borderRadius: Corners.s8Border,
child: FlowyText.medium(LocaleKeys.deletePagePrompt_deletePermanent.tr(),
color: Colors.white, fontSize: 14),
child: FlowyText.medium(
LocaleKeys.deletePagePrompt_deletePermanent.tr(),
color: Colors.white,
fontSize: 14),
onPressed: onDelete),
],
),

View File

@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
import 'cell_service.dart';
abstract class GridFieldChangedNotifier {
abstract class IGridFieldChangedNotifier {
void onFieldChanged(void Function(GridFieldPB) callback);
void dispose();
}
@ -12,9 +12,10 @@ abstract class GridFieldChangedNotifier {
/// 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 = {};
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId =
{};
GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) {
GridCellFieldNotifier({required IGridFieldChangedNotifier notifier}) {
notifier.onFieldChanged(
(field) {
final map = _fieldListenerByFieldId[field.id];

View File

@ -246,7 +246,7 @@ class IGridCellController<T, D> extends Equatable {
}
/// Save the cell data to disk
/// You can set [dedeplicate] to true (default is false) to reduce the save operation.
/// You can set [deduplicate] 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,
@ -304,7 +304,7 @@ class IGridCellController<T, D> extends Equatable {
[_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
}
class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
class _GridFieldChangedNotifierImpl extends IGridFieldChangedNotifier {
final GridFieldCache _cache;
FieldChangesetCallback? _onChangesetFn;

View File

@ -1,40 +1,23 @@
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'block/block_cache.dart';
import 'grid_service.dart';
import 'grid_data_controller.dart';
import 'row/row_service.dart';
import 'dart:collection';
part 'grid_bloc.freezed.dart';
class GridBloc extends Bloc<GridEvent, GridState> {
final String gridId;
final GridService _gridService;
final GridFieldCache fieldCache;
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
List<GridRowInfo> get rowInfos {
final List<GridRowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
final GridDataController dataController;
GridBloc({required ViewPB view})
: gridId = view.id,
_blocks = LinkedHashMap.identity(),
_gridService = GridService(gridId: view.id),
fieldCache = GridFieldCache(gridId: view.id),
: dataController = GridDataController(view: view),
super(GridState.initial(view.id)) {
on<GridEvent>(
(event, emit) async {
@ -44,13 +27,21 @@ class GridBloc extends Bloc<GridEvent, GridState> {
await _loadGrid(emit);
},
createRow: () {
_gridService.createRow();
dataController.createRow();
},
didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith(rowInfos: newRowInfos, reason: reason));
didReceiveGridUpdate: (grid) {
emit(state.copyWith(grid: Some(grid)));
},
didReceiveFieldUpdate: (fields) {
emit(state.copyWith(rowInfos: rowInfos, fields: GridFieldEquatable(fields)));
emit(state.copyWith(
fields: GridFieldEquatable(fields),
));
},
didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith(
rowInfos: newRowInfos,
reason: reason,
));
},
);
},
@ -59,89 +50,63 @@ class GridBloc extends Bloc<GridEvent, GridState> {
@override
Future<void> close() async {
await _gridService.closeGrid();
await fieldCache.dispose();
for (final blockCache in _blocks.values) {
blockCache.dispose();
}
await dataController.dispose();
return super.close();
}
GridRowCache? getRowCache(String blockId, String rowId) {
final GridBlockCache? blockCache = _blocks[blockId];
final GridBlockCache? blockCache = dataController.blocks[blockId];
return blockCache?.rowCache;
}
void _startListening() {
fieldCache.addListener(
listenWhen: () => !isClosed,
onFields: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
dataController.addListener(
onGridChanged: (grid) {
if (!isClosed) {
add(GridEvent.didReceiveGridUpdate(grid));
}
},
onRowsChanged: (rowInfos, reason) {
if (!isClosed) {
add(GridEvent.didReceiveRowUpdate(rowInfos, reason));
}
},
onFieldsChanged: (fields) {
if (!isClosed) {
add(GridEvent.didReceiveFieldUpdate(fields));
}
},
);
}
Future<void> _loadGrid(Emitter<GridState> emit) async {
final result = await _gridService.loadGrid();
return Future(
() => result.fold(
(grid) async {
_initialBlocks(grid.blocks);
await _loadFields(grid, emit);
},
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
final result = await dataController.loadData();
result.fold(
(grid) => emit(
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
),
(err) => emit(
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
),
);
}
Future<void> _loadFields(GridPB grid, Emitter<GridState> emit) async {
final result = await _gridService.getFields(fieldIds: grid.fields);
return Future(
() => result.fold(
(fields) {
fieldCache.fields = fields.items;
emit(state.copyWith(
grid: Some(grid),
fields: GridFieldEquatable(fieldCache.fields),
rowInfos: rowInfos,
loadingState: GridLoadingState.finish(left(unit)),
));
},
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
),
);
}
void _initialBlocks(List<GridBlockPB> blocks) {
for (final block in blocks) {
if (_blocks[block.id] != null) {
Log.warn("Intial duplicate block's cache: ${block.id}");
return;
}
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldCache: fieldCache,
);
cache.addListener(
listenWhen: () => !isClosed,
onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rowInfos, reason)),
);
_blocks[block.id] = cache;
}
}
}
@freezed
class GridEvent with _$GridEvent {
const factory GridEvent.initial() = InitialGrid;
const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.didReceiveRowUpdate(List<GridRowInfo> rows, GridRowChangeReason listState) =
_DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridEvent.didReceiveRowUpdate(
List<GridRowInfo> rows,
GridRowChangeReason listState,
) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(
UnmodifiableListView<GridFieldPB> fields,
) = _DidReceiveFieldUpdate;
const factory GridEvent.didReceiveGridUpdate(
GridPB grid,
) = _DidReceiveGridUpdate;
}
@freezed
@ -156,7 +121,7 @@ class GridState with _$GridState {
}) = _GridState;
factory GridState.initial(String gridId) => GridState(
fields: const GridFieldEquatable([]),
fields: GridFieldEquatable(UnmodifiableListView([])),
rowInfos: [],
grid: none(),
gridId: gridId,
@ -168,18 +133,27 @@ class GridState with _$GridState {
@freezed
class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish;
}
class GridFieldEquatable extends Equatable {
final List<GridFieldPB> _fields;
const GridFieldEquatable(List<GridFieldPB> fields) : _fields = fields;
final UnmodifiableListView<GridFieldPB> _fields;
const GridFieldEquatable(
UnmodifiableListView<GridFieldPB> fields,
) : _fields = fields;
@override
List<Object?> get props {
if (_fields.isEmpty) {
return [];
}
return [
_fields.length,
_fields.map((field) => field.width).reduce((value, element) => value + element),
_fields
.map((field) => field.width)
.reduce((value, element) => value + element),
];
}

View File

@ -0,0 +1,128 @@
import 'dart:collection';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'block/block_cache.dart';
import 'prelude.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<GridFieldPB>);
typedef OnGridChanged = void Function(GridPB);
typedef OnRowsChanged = void Function(
List<GridRowInfo> rowInfos,
GridRowChangeReason,
);
typedef ListenONRowChangedCondition = bool Function();
class GridDataController {
final String gridId;
final GridService _gridFFIService;
final GridFieldCache fieldCache;
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
UnmodifiableMapView<String, GridBlockCache> get blocks =>
UnmodifiableMapView(_blocks);
OnRowsChanged? _onRowChanged;
OnFieldsChanged? _onFieldsChanged;
OnGridChanged? _onGridChanged;
List<GridRowInfo> get rowInfos {
final List<GridRowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
GridDataController({required ViewPB view})
: gridId = view.id,
_blocks = LinkedHashMap.identity(),
_gridFFIService = GridService(gridId: view.id),
fieldCache = GridFieldCache(gridId: view.id);
void addListener({
required OnGridChanged onGridChanged,
required OnRowsChanged onRowsChanged,
required OnFieldsChanged onFieldsChanged,
}) {
_onGridChanged = onGridChanged;
_onRowChanged = onRowsChanged;
_onFieldsChanged = onFieldsChanged;
fieldCache.addListener(onFields: (fields) {
_onFieldsChanged?.call(UnmodifiableListView(fields));
});
}
Future<Either<Unit, FlowyError>> loadData() async {
final result = await _gridFFIService.loadGrid();
return Future(
() => result.fold(
(grid) async {
_initialBlocks(grid.blocks);
_onGridChanged?.call(grid);
return await _loadFields(grid);
},
(err) => right(err),
),
);
}
void createRow() {
_gridFFIService.createRow();
}
Future<void> dispose() async {
await _gridFFIService.closeGrid();
await fieldCache.dispose();
for (final blockCache in _blocks.values) {
blockCache.dispose();
}
}
void _initialBlocks(List<GridBlockPB> blocks) {
for (final block in blocks) {
if (_blocks[block.id] != null) {
Log.warn("Initial duplicate block's cache: ${block.id}");
return;
}
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldCache: fieldCache,
);
cache.addListener(
onChangeReason: (reason) {
_onRowChanged?.call(rowInfos, reason);
},
);
_blocks[block.id] = cache;
}
}
Future<Either<Unit, FlowyError>> _loadFields(GridPB grid) async {
final result = await _gridFFIService.getFields(fieldIds: grid.fields);
return Future(
() => result.fold(
(fields) {
fieldCache.fields = fields.items;
_onFieldsChanged?.call(UnmodifiableListView(fieldCache.fields));
return left(unit);
},
(err) => right(err),
),
);
}
}

View File

@ -5,25 +5,25 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'row_data_controller.dart';
import 'row_service.dart';
part 'row_bloc.freezed.dart';
class RowBloc extends Bloc<RowEvent, RowState> {
final RowService _rowService;
final GridRowCache _rowCache;
void Function()? _rowListenFn;
final GridRowDataController _dataController;
RowBloc({
required GridRowInfo rowInfo,
required GridRowCache rowCache,
required GridRowDataController dataController,
}) : _rowService = RowService(
gridId: rowInfo.gridId,
blockId: rowInfo.blockId,
rowId: rowInfo.id,
),
_rowCache = rowCache,
super(RowState.initial(rowInfo, rowCache.loadGridCells(rowInfo.id))) {
_dataController = dataController,
super(RowState.initial(rowInfo, dataController.loadData())) {
on<RowEvent>(
(event, emit) async {
await event.map(
@ -33,7 +33,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
createRow: (_CreateRow value) {
_rowService.createRow();
},
didReceiveCellDatas: (_DidReceiveCellDatas value) async {
didReceiveCells: (_DidReceiveCells value) async {
final fields = value.gridCellMap.values
.map((e) => GridCellEquatable(e.field))
.toList();
@ -51,19 +51,17 @@ class RowBloc extends Bloc<RowEvent, RowState> {
@override
Future<void> close() async {
if (_rowListenFn != null) {
_rowCache.removeRowListener(_rowListenFn!);
}
_dataController.dispose();
return super.close();
}
Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener(
rowId: state.rowInfo.id,
onCellUpdated: (cellDatas, reason) =>
add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
listenWhen: () => !isClosed,
_dataController.addListener(
onRowChanged: (cells, reason) {
if (!isClosed) {
add(RowEvent.didReceiveCells(cells, reason));
}
},
);
}
}
@ -72,9 +70,8 @@ class RowBloc extends Bloc<RowEvent, RowState> {
class RowEvent with _$RowEvent {
const factory RowEvent.initial() = _InitialRow;
const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.didReceiveCellDatas(
GridCellMap gridCellMap, GridRowChangeReason reason) =
_DidReceiveCellDatas;
const factory RowEvent.didReceiveCells(
GridCellMap gridCellMap, GridRowChangeReason reason) = _DidReceiveCells;
}
@freezed

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import '../cell/cell_service/cell_service.dart';
import '../grid_service.dart';
import 'row_service.dart';
typedef OnRowChanged = void Function(GridCellMap, GridRowChangeReason);
class GridRowDataController {
final String rowId;
VoidCallback? _onRowChangedListener;
final GridFieldCache _fieldCache;
final GridRowCache _rowCache;
GridFieldCache get fieldCache => _fieldCache;
GridRowCache get rowCache => _rowCache;
GridRowDataController({
required this.rowId,
required GridFieldCache fieldCache,
required GridRowCache rowCache,
}) : _fieldCache = fieldCache,
_rowCache = rowCache;
GridCellMap loadData() {
return _rowCache.loadGridCells(rowId);
}
void addListener({OnRowChanged? onRowChanged}) {
_onRowChangedListener = _rowCache.addListener(
rowId: rowId,
onCellUpdated: onRowChanged,
);
}
void dispose() {
if (_onRowChangedListener != null) {
_rowCache.removeRowListener(_onRowChangedListener!);
}
}
}

View File

@ -158,7 +158,7 @@ class GridRowCache {
void Function(GridCellMap, GridRowChangeReason)? onCellUpdated,
bool Function()? listenWhen,
}) {
listenrHandler() async {
listenerHandler() async {
if (listenWhen != null && listenWhen() == false) {
return;
}
@ -181,8 +181,8 @@ class GridRowCache {
);
}
_rowChangeReasonNotifier.addListener(listenrHandler);
return listenrHandler;
_rowChangeReasonNotifier.addListener(listenerHandler);
return listenerHandler;
}
void removeRowListener(VoidCallback callback) {

View File

@ -2,19 +2,20 @@ import 'package:flutter/material.dart';
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
class GridScrollController {
final LinkedScrollControllerGroup _scrollGroupContorller;
final LinkedScrollControllerGroup _scrollGroupController;
final ScrollController verticalController;
final ScrollController horizontalController;
final List<ScrollController> _linkHorizontalControllers = [];
GridScrollController({required LinkedScrollControllerGroup scrollGroupContorller})
: _scrollGroupContorller = scrollGroupContorller,
GridScrollController(
{required LinkedScrollControllerGroup scrollGroupController})
: _scrollGroupController = scrollGroupController,
verticalController = ScrollController(),
horizontalController = scrollGroupContorller.addAndGet();
horizontalController = scrollGroupController.addAndGet();
ScrollController linkHorizontalController() {
final controller = _scrollGroupContorller.addAndGet();
final controller = _scrollGroupController.addAndGet();
_linkHorizontalControllers.add(controller);
return controller;
}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/grid/application/grid_bloc.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
@ -79,7 +80,7 @@ class FlowyGrid extends StatefulWidget {
class _FlowyGridState extends State<FlowyGrid> {
final _scrollController = GridScrollController(
scrollGroupContorller: LinkedScrollControllerGroup());
scrollGroupController: LinkedScrollControllerGroup());
late ScrollController headerScrollController;
@override
@ -153,7 +154,7 @@ class _FlowyGridState extends State<FlowyGrid> {
}
Widget _gridHeader(BuildContext context, String gridId) {
final fieldCache = context.read<GridBloc>().fieldCache;
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
return GridHeaderSliverAdaptor(
gridId: gridId,
fieldCache: fieldCache,
@ -169,7 +170,7 @@ class _GridToolbarAdaptor extends StatelessWidget {
Widget build(BuildContext context) {
return BlocSelector<GridBloc, GridState, GridToolbarContext>(
selector: (state) {
final fieldCache = context.read<GridBloc>().fieldCache;
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
return GridToolbarContext(
gridId: state.gridId,
fieldCache: fieldCache,
@ -237,14 +238,20 @@ class _GridRowsState extends State<_GridRows> {
) {
final rowCache =
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id);
final fieldCache = context.read<GridBloc>().fieldCache;
final fieldCache = context.read<GridBloc>().dataController.fieldCache;
if (rowCache != null) {
final dataController = GridRowDataController(
rowId: rowInfo.id,
fieldCache: fieldCache,
rowCache: rowCache,
);
return SizeTransition(
sizeFactor: animation,
child: GridRowWidget(
rowData: rowInfo,
rowCache: rowCache,
fieldCache: fieldCache,
dataController: dataController,
key: ValueKey(rowInfo.id),
),
);

View File

@ -27,39 +27,49 @@ class GridCellBuilder {
cellCache: cellCache,
fieldCache: fieldCache,
);
final key = cell.key();
switch (cell.fieldType) {
case FieldType.Checkbox:
return GridCheckboxCell(
cellControllerBuilder: cellControllerBuilder, key: key);
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.DateTime:
return GridDateCell(
cellControllerBuilder: cellControllerBuilder,
key: key,
style: style);
cellControllerBuilder: cellControllerBuilder,
key: key,
style: style,
);
case FieldType.SingleSelect:
return GridSingleSelectCell(
cellContorllerBuilder: cellControllerBuilder,
style: style,
key: key);
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.MultiSelect:
return GridMultiSelectCell(
cellContorllerBuilder: cellControllerBuilder,
style: style,
key: key);
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.Number:
return GridNumberCell(
cellContorllerBuilder: cellControllerBuilder, key: key);
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.RichText:
return GridTextCell(
cellContorllerBuilder: cellControllerBuilder,
style: style,
key: key);
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
case FieldType.URL:
return GridURLCell(
cellContorllerBuilder: cellControllerBuilder,
style: style,
key: key);
cellControllerBuilder: cellControllerBuilder,
style: style,
key: key,
);
}
throw UnimplementedError;
}
@ -93,7 +103,7 @@ abstract class GridCellWidget extends StatefulWidget
@override
final ValueNotifier<bool> onCellFocus = ValueNotifier<bool>(false);
// When the cell is focused, we assume that the accessory alse be hovered.
// When the cell is focused, we assume that the accessory also be hovered.
@override
ValueNotifier<bool> get onAccessoryHover => onCellFocus;
@ -150,7 +160,7 @@ abstract class GridCellState<T extends GridCellWidget> extends State<T> {
abstract class GridFocusNodeCellState<T extends GridCellWidget>
extends GridCellState<T> {
SingleListenrFocusNode focusNode = SingleListenrFocusNode();
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
@override
void initState() {
@ -219,7 +229,7 @@ class GridCellFocusListener extends ChangeNotifier {
abstract class GridCellStyle {}
class SingleListenrFocusNode extends FocusNode {
class SingleListenerFocusNode extends FocusNode {
VoidCallback? _listener;
void setListener(VoidCallback listener) {

View File

@ -7,10 +7,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
class GridNumberCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
GridNumberCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
Key? key,
}) : super(key: key);
@ -25,7 +25,7 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build();
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)
..add(const NumberCellEvent.initial());
_controller =

View File

@ -22,11 +22,11 @@ class SelectOptionCellStyle extends GridCellStyle {
}
class GridSingleSelectCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final SelectOptionCellStyle? cellStyle;
GridSingleSelectCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -47,7 +47,7 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
@override
void initState() {
final cellContext =
widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
widget.cellControllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)
..add(const SelectOptionCellEvent.initial());
super.initState();
@ -63,7 +63,7 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
selectOptions: state.selectedOptions,
cellStyle: widget.cellStyle,
onFocus: (value) => widget.onCellEditing.value = value,
cellContorllerBuilder: widget.cellContorllerBuilder);
cellControllerBuilder: widget.cellControllerBuilder);
},
),
);
@ -78,11 +78,11 @@ class _SingleSelectCellState extends State<GridSingleSelectCell> {
//----------------------------------------------------------------
class GridMultiSelectCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final SelectOptionCellStyle? cellStyle;
GridMultiSelectCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -103,7 +103,7 @@ class _MultiSelectCellState extends State<GridMultiSelectCell> {
@override
void initState() {
final cellContext =
widget.cellContorllerBuilder.build() as GridSelectOptionCellController;
widget.cellControllerBuilder.build() as GridSelectOptionCellController;
_cellBloc = getIt<SelectOptionCellBloc>(param1: cellContext)
..add(const SelectOptionCellEvent.initial());
super.initState();
@ -119,7 +119,7 @@ class _MultiSelectCellState extends State<GridMultiSelectCell> {
selectOptions: state.selectedOptions,
cellStyle: widget.cellStyle,
onFocus: (value) => widget.onCellEditing.value = value,
cellContorllerBuilder: widget.cellContorllerBuilder);
cellControllerBuilder: widget.cellControllerBuilder);
},
),
);
@ -136,12 +136,12 @@ class _SelectOptionCell extends StatelessWidget {
final List<SelectOptionPB> selectOptions;
final void Function(bool) onFocus;
final SelectOptionCellStyle? cellStyle;
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
const _SelectOptionCell({
required this.selectOptions,
required this.onFocus,
required this.cellStyle,
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
Key? key,
}) : super(key: key);
@ -179,7 +179,7 @@ class _SelectOptionCell extends StatelessWidget {
onTap: () {
onFocus(true);
final cellContext =
cellContorllerBuilder.build() as GridSelectOptionCellController;
cellControllerBuilder.build() as GridSelectOptionCellController;
SelectOptionCellEditor.show(
context, cellContext, () => onFocus(false));
},

View File

@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle {
}
class GridTextCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final GridTextCellStyle? cellStyle;
GridTextCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -39,7 +39,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
@override
void initState() {
final cellContext = widget.cellContorllerBuilder.build();
final cellContext = widget.cellControllerBuilder.build();
_cellBloc = getIt<TextCellBloc>(param1: cellContext);
_cellBloc.add(const TextCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);

View File

@ -31,10 +31,10 @@ enum GridURLCellAccessoryType {
}
class GridURLCell extends GridCellWidget {
final GridCellControllerBuilder cellContorllerBuilder;
final GridCellControllerBuilder cellControllerBuilder;
late final GridURLCellStyle? cellStyle;
GridURLCell({
required this.cellContorllerBuilder,
required this.cellControllerBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -53,14 +53,14 @@ class GridURLCell extends GridCellWidget {
switch (ty) {
case GridURLCellAccessoryType.edit:
final cellContext =
cellContorllerBuilder.build() as GridURLCellController;
cellControllerBuilder.build() as GridURLCellController;
return _EditURLAccessory(
cellContext: cellContext,
anchorContext: buildContext.anchorContext);
case GridURLCellAccessoryType.copyURL:
final cellContext =
cellContorllerBuilder.build() as GridURLCellController;
cellControllerBuilder.build() as GridURLCellController;
return _CopyURLAccessory(cellContext: cellContext);
}
}
@ -91,7 +91,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
@override
void initState() {
final cellContext =
widget.cellContorllerBuilder.build() as GridURLCellController;
widget.cellControllerBuilder.build() as GridURLCellController;
_cellBloc = URLCellBloc(cellContext: cellContext);
_cellBloc.add(const URLCellEvent.initial());
super.initState();
@ -141,7 +141,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
await launchUrl(uri);
} else {
final cellContext =
widget.cellContorllerBuilder.build() as GridURLCellController;
widget.cellControllerBuilder.build() as GridURLCellController;
widget.onCellEditing.value = true;
URLCellEditor.show(context, cellContext, () {
widget.onCellEditing.value = false;

View File

@ -1,4 +1,5 @@
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
@ -9,24 +10,23 @@ import 'package:provider/provider.dart';
import '../../layout/sizes.dart';
import '../cell/cell_accessory.dart';
import '../cell/cell_cotainer.dart';
import '../cell/cell_container.dart';
import '../cell/prelude.dart';
import 'row_action_sheet.dart';
import 'row_detail.dart';
class GridRowWidget extends StatefulWidget {
final GridRowInfo rowData;
final GridRowCache rowCache;
final GridRowDataController dataController;
final GridCellBuilder cellBuilder;
GridRowWidget({
required this.rowData,
required this.rowCache,
required GridFieldCache fieldCache,
required this.dataController,
Key? key,
}) : cellBuilder = GridCellBuilder(
cellCache: rowCache.cellCache,
fieldCache: fieldCache,
cellCache: dataController.rowCache.cellCache,
fieldCache: dataController.fieldCache,
),
super(key: key);
@ -41,7 +41,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
void initState() {
_rowBloc = RowBloc(
rowInfo: widget.rowData,
rowCache: widget.rowCache,
dataController: widget.dataController,
);
_rowBloc.add(const RowEvent.initial());
super.initState();
@ -81,7 +81,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
void _expandRow(BuildContext context) {
final page = RowDetailPage(
rowInfo: widget.rowData,
rowCache: widget.rowCache,
rowCache: widget.dataController.rowCache,
cellBuilder: widget.cellBuilder,
);
page.show(context);

View File

@ -62,8 +62,10 @@ class _RowDetailPageState extends State<RowDetailPage> {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) {
final bloc =
RowDetailBloc(rowInfo: widget.rowInfo, rowCache: widget.rowCache);
final bloc = RowDetailBloc(
rowInfo: widget.rowInfo,
rowCache: widget.rowCache,
);
bloc.add(const RowDetailEvent.initial());
return bloc;
},

View File

@ -164,18 +164,11 @@ pub struct CreateFieldPayloadPB {
pub grid_id: String,
#[pb(index = 2)]
pub field_id: String,
#[pb(index = 3)]
pub field_type: FieldType,
#[pb(index = 4)]
pub create_if_not_exist: bool,
}
pub struct CreateFieldParams {
pub grid_id: String,
pub field_id: String,
pub field_type: FieldType,
}
@ -184,10 +177,8 @@ impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
fn try_into(self) -> Result<CreateFieldParams, Self::Error> {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
Ok(CreateFieldParams {
grid_id: grid_id.0,
field_id: field_id.0,
field_type: self.field_type,
})
}