refactor: RowDetailBloc

This commit is contained in:
appflowy 2022-08-09 20:19:43 +08:00
parent 9930706d9a
commit 9c495957dc
7 changed files with 133 additions and 107 deletions

View File

@ -7,23 +7,24 @@ typedef GridDateCellController
= IGridCellController<DateCellDataPB, CalendarData>; = IGridCellController<DateCellDataPB, CalendarData>;
typedef GridURLCellController = IGridCellController<URLCellDataPB, String>; typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
abstract class GridCellControllerBuilderDelegate {
GridCellFieldNotifier buildFieldNotifier();
}
class GridCellControllerBuilder { class GridCellControllerBuilder {
final GridCellIdentifier _cellId; final GridCellIdentifier _cellId;
final GridCellCache _cellCache; final GridCellCache _cellCache;
final GridFieldCache _fieldCache; final GridCellControllerBuilderDelegate delegate;
GridCellControllerBuilder({ GridCellControllerBuilder({
required this.delegate,
required GridCellIdentifier cellId, required GridCellIdentifier cellId,
required GridCellCache cellCache, required GridCellCache cellCache,
required GridFieldCache fieldCache,
}) : _cellCache = cellCache, }) : _cellCache = cellCache,
_fieldCache = fieldCache,
_cellId = cellId; _cellId = cellId;
IGridCellController build() { IGridCellController build() {
final cellFieldNotifier = final cellFieldNotifier = delegate.buildFieldNotifier();
GridCellFieldNotifier(notifier: GridCellFieldNotifierImpl(_fieldCache));
switch (_cellId.fieldType) { switch (_cellId.fieldType) {
case FieldType.Checkbox: case FieldType.Checkbox:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(

View File

@ -1,13 +1,15 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../presentation/widgets/cell/cell_builder.dart';
import '../cell/cell_service/cell_service.dart'; import '../cell/cell_service/cell_service.dart';
import '../field/field_cache.dart'; import '../field/field_cache.dart';
import 'row_cache.dart'; import 'row_cache.dart';
typedef OnRowChanged = void Function(GridCellMap, GridRowChangeReason); typedef OnRowChanged = void Function(GridCellMap, GridRowChangeReason);
class GridRowDataController { class GridRowDataController extends GridCellBuilderDelegate {
final String rowId; final GridRowInfo rowInfo;
VoidCallback? _onRowChangedListener; final List<VoidCallback> _onRowChangedListeners = [];
final GridFieldCache _fieldCache; final GridFieldCache _fieldCache;
final GridRowCache _rowCache; final GridRowCache _rowCache;
@ -16,26 +18,36 @@ class GridRowDataController {
GridRowCache get rowCache => _rowCache; GridRowCache get rowCache => _rowCache;
GridRowDataController({ GridRowDataController({
required this.rowId, required this.rowInfo,
required GridFieldCache fieldCache, required GridFieldCache fieldCache,
required GridRowCache rowCache, required GridRowCache rowCache,
}) : _fieldCache = fieldCache, }) : _fieldCache = fieldCache,
_rowCache = rowCache; _rowCache = rowCache;
GridCellMap loadData() { GridCellMap loadData() {
return _rowCache.loadGridCells(rowId); return _rowCache.loadGridCells(rowInfo.id);
} }
void addListener({OnRowChanged? onRowChanged}) { void addListener({OnRowChanged? onRowChanged}) {
_onRowChangedListener = _rowCache.addListener( _onRowChangedListeners.add(_rowCache.addListener(
rowId: rowId, rowId: rowInfo.id,
onCellUpdated: onRowChanged, onCellUpdated: onRowChanged,
); ));
} }
void dispose() { void dispose() {
if (_onRowChangedListener != null) { for (final fn in _onRowChangedListeners) {
_rowCache.removeRowListener(_onRowChangedListener!); _rowCache.removeRowListener(fn);
} }
} }
// GridCellBuilderDelegate implementation
@override
GridCellFieldNotifier buildFieldNotifier() {
return GridCellFieldNotifier(
notifier: GridCellFieldNotifierImpl(_fieldCache));
}
@override
GridCellCache get cellCache => rowCache.cellCache;
} }

View File

@ -2,25 +2,24 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
import 'row_cache.dart'; import 'row_data_controller.dart';
part 'row_detail_bloc.freezed.dart'; part 'row_detail_bloc.freezed.dart';
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> { class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
final GridRowInfo rowInfo; final GridRowDataController dataController;
final GridRowCache _rowCache;
void Function()? _rowListenFn;
RowDetailBloc({ RowDetailBloc({
required this.rowInfo, required this.dataController,
required GridRowCache rowCache, }) : super(RowDetailState.initial()) {
}) : _rowCache = rowCache,
super(RowDetailState.initial()) {
on<RowDetailEvent>( on<RowDetailEvent>(
(event, emit) async { (event, emit) async {
await event.map( await event.map(
initial: (_Initial value) async { initial: (_Initial value) async {
await _startListening(); await _startListening();
_loadCellData(); final cells = dataController.loadData();
if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
}
}, },
didReceiveCellDatas: (_DidReceiveCellDatas value) { didReceiveCellDatas: (_DidReceiveCellDatas value) {
emit(state.copyWith(gridCells: value.gridCells)); emit(state.copyWith(gridCells: value.gridCells));
@ -32,27 +31,19 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
@override @override
Future<void> close() async { Future<void> close() async {
if (_rowListenFn != null) { dataController.dispose();
_rowCache.removeRowListener(_rowListenFn!);
}
return super.close(); return super.close();
} }
Future<void> _startListening() async { Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener( dataController.addListener(
rowId: rowInfo.id, onRowChanged: (cells, reason) {
onCellUpdated: (cellDatas, reason) => if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())), add(RowDetailEvent.didReceiveCellDatas(cells.values.toList()));
listenWhen: () => !isClosed, }
},
); );
} }
Future<void> _loadCellData() async {
final cellDataMap = _rowCache.loadGridCells(rowInfo.id);
if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList()));
}
}
} }
@freezed @freezed

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart'; import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/startup/startup.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/grid_bloc.dart';
@ -15,9 +16,11 @@ import '../application/row/row_cache.dart';
import 'controller/grid_scroll.dart'; import 'controller/grid_scroll.dart';
import 'layout/layout.dart'; import 'layout/layout.dart';
import 'layout/sizes.dart'; import 'layout/sizes.dart';
import 'widgets/cell/cell_builder.dart';
import 'widgets/row/grid_row.dart'; import 'widgets/row/grid_row.dart';
import 'widgets/footer/grid_footer.dart'; import 'widgets/footer/grid_footer.dart';
import 'widgets/header/grid_header.dart'; import 'widgets/header/grid_header.dart';
import 'widgets/row/row_detail.dart';
import 'widgets/shortcuts.dart'; import 'widgets/shortcuts.dart';
import 'widgets/toolbar/grid_toolbar.dart'; import 'widgets/toolbar/grid_toolbar.dart';
@ -239,25 +242,53 @@ class _GridRowsState extends State<_GridRows> {
final rowCache = final rowCache =
context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id); context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id);
final fieldCache = context.read<GridBloc>().dataController.fieldCache; /// Return placeholder widget if the rowCache is null.
if (rowCache != null) { if (rowCache == null) return const SizedBox();
final dataController = GridRowDataController(
rowId: rowInfo.id,
fieldCache: fieldCache,
rowCache: rowCache,
);
return SizeTransition( final fieldCache = context.read<GridBloc>().dataController.fieldCache;
sizeFactor: animation, final dataController = GridRowDataController(
child: GridRowWidget( rowInfo: rowInfo,
rowData: rowInfo, fieldCache: fieldCache,
dataController: dataController, rowCache: rowCache,
key: ValueKey(rowInfo.id), );
),
); return SizeTransition(
} else { sizeFactor: animation,
return const SizedBox(); child: GridRowWidget(
} rowInfo: rowInfo,
dataController: dataController,
cellBuilder: GridCellBuilder(delegate: dataController),
openDetailPage: (context, cellBuilder) {
_openRowDetailPage(
context,
rowInfo,
fieldCache,
rowCache,
cellBuilder,
);
},
key: ValueKey(rowInfo.id),
),
);
}
void _openRowDetailPage(
BuildContext context,
GridRowInfo rowInfo,
GridFieldCache fieldCache,
GridRowCache rowCache,
GridCellBuilder cellBuilder,
) {
final dataController = GridRowDataController(
rowInfo: rowInfo,
fieldCache: fieldCache,
rowCache: rowCache,
);
RowDetailPage(
cellBuilder: cellBuilder,
dataController: dataController,
).show(context);
} }
} }

View File

@ -1,5 +1,4 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -13,23 +12,26 @@ import 'select_option_cell/select_option_cell.dart';
import 'text_cell.dart'; import 'text_cell.dart';
import 'url_cell/url_cell.dart'; import 'url_cell/url_cell.dart';
abstract class GridCellBuilderDelegate
extends GridCellControllerBuilderDelegate {
GridCellCache get cellCache;
}
class GridCellBuilder { class GridCellBuilder {
final GridCellCache cellCache; final GridCellBuilderDelegate delegate;
final GridFieldCache fieldCache;
GridCellBuilder({ GridCellBuilder({
required this.cellCache, required this.delegate,
required this.fieldCache,
}); });
GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) { GridCellWidget build(GridCellIdentifier cellId, {GridCellStyle? style}) {
final cellControllerBuilder = GridCellControllerBuilder( final cellControllerBuilder = GridCellControllerBuilder(
cellId: cell, cellId: cellId,
cellCache: cellCache, cellCache: delegate.cellCache,
fieldCache: fieldCache, delegate: delegate,
); );
final key = cell.key(); final key = cellId.key();
switch (cell.fieldType) { switch (cellId.fieldType) {
case FieldType.Checkbox: case FieldType.Checkbox:
return GridCheckboxCell( return GridCheckboxCell(
cellControllerBuilder: cellControllerBuilder, cellControllerBuilder: cellControllerBuilder,

View File

@ -14,22 +14,20 @@ import '../cell/cell_accessory.dart';
import '../cell/cell_container.dart'; import '../cell/cell_container.dart';
import '../cell/prelude.dart'; import '../cell/prelude.dart';
import 'row_action_sheet.dart'; import 'row_action_sheet.dart';
import 'row_detail.dart';
class GridRowWidget extends StatefulWidget { class GridRowWidget extends StatefulWidget {
final GridRowInfo rowData; final GridRowInfo rowInfo;
final GridRowDataController dataController; final GridRowDataController dataController;
final GridCellBuilder cellBuilder; final GridCellBuilder cellBuilder;
final void Function(BuildContext, GridCellBuilder) openDetailPage;
GridRowWidget({ const GridRowWidget({
required this.rowData, required this.rowInfo,
required this.dataController, required this.dataController,
required this.cellBuilder,
required this.openDetailPage,
Key? key, Key? key,
}) : cellBuilder = GridCellBuilder( }) : super(key: key);
cellCache: dataController.rowCache.cellCache,
fieldCache: dataController.fieldCache,
),
super(key: key);
@override @override
State<GridRowWidget> createState() => _GridRowWidgetState(); State<GridRowWidget> createState() => _GridRowWidgetState();
@ -41,7 +39,7 @@ class _GridRowWidgetState extends State<GridRowWidget> {
@override @override
void initState() { void initState() {
_rowBloc = RowBloc( _rowBloc = RowBloc(
rowInfo: widget.rowData, rowInfo: widget.rowInfo,
dataController: widget.dataController, dataController: widget.dataController,
); );
_rowBloc.add(const RowEvent.initial()); _rowBloc.add(const RowEvent.initial());
@ -56,17 +54,20 @@ class _GridRowWidgetState extends State<GridRowWidget> {
child: BlocBuilder<RowBloc, RowState>( child: BlocBuilder<RowBloc, RowState>(
buildWhen: (p, c) => p.rowInfo.height != c.rowInfo.height, buildWhen: (p, c) => p.rowInfo.height != c.rowInfo.height,
builder: (context, state) { builder: (context, state) {
return Row( final children = [
children: [ const _RowLeading(),
const _RowLeading(), Expanded(
Expanded( child: RowContent(
child: _RowCells(
builder: widget.cellBuilder, builder: widget.cellBuilder,
onExpand: () => _expandRow(context), onExpand: () => widget.openDetailPage(
)), context,
const _RowTrailing(), widget.cellBuilder,
], ),
); ),
),
const _RowTrailing(),
];
return Row(children: children);
}, },
), ),
), ),
@ -78,15 +79,6 @@ class _GridRowWidgetState extends State<GridRowWidget> {
_rowBloc.close(); _rowBloc.close();
super.dispose(); super.dispose();
} }
void _expandRow(BuildContext context) {
final page = RowDetailPage(
rowInfo: widget.rowData,
rowCache: widget.dataController.rowCache,
cellBuilder: widget.cellBuilder,
);
page.show(context);
}
} }
class _RowLeading extends StatelessWidget { class _RowLeading extends StatelessWidget {
@ -159,10 +151,10 @@ class _DeleteRowButton extends StatelessWidget {
} }
} }
class _RowCells extends StatelessWidget { class RowContent extends StatelessWidget {
final VoidCallback onExpand; final VoidCallback onExpand;
final GridCellBuilder builder; final GridCellBuilder builder;
const _RowCells({ const RowContent({
required this.builder, required this.builder,
required this.onExpand, required this.onExpand,
Key? key, Key? key,

View File

@ -1,5 +1,6 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart'; import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart'; import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra/theme.dart';
@ -13,7 +14,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../application/row/row_cache.dart';
import '../../layout/sizes.dart'; import '../../layout/sizes.dart';
import '../cell/cell_accessory.dart'; import '../cell/cell_accessory.dart';
import '../cell/prelude.dart'; import '../cell/prelude.dart';
@ -21,13 +21,11 @@ import '../header/field_cell.dart';
import '../header/field_editor.dart'; import '../header/field_editor.dart';
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate { class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
final GridRowInfo rowInfo; final GridRowDataController dataController;
final GridRowCache rowCache;
final GridCellBuilder cellBuilder; final GridCellBuilder cellBuilder;
const RowDetailPage({ const RowDetailPage({
required this.rowInfo, required this.dataController,
required this.rowCache,
required this.cellBuilder, required this.cellBuilder,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -63,8 +61,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
return BlocProvider( return BlocProvider(
create: (context) { create: (context) {
final bloc = RowDetailBloc( final bloc = RowDetailBloc(
rowInfo: widget.rowInfo, dataController: widget.dataController,
rowCache: widget.rowCache,
); );
bloc.add(const RowDetailEvent.initial()); bloc.add(const RowDetailEvent.initial());
return bloc; return bloc;