From 9c495957dcaf50d3fa26edb3957d9e6798794078 Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Tue, 9 Aug 2022 20:19:43 +0800 Subject: [PATCH] refactor: RowDetailBloc --- .../cell/cell_service/context_builder.dart | 13 ++-- .../application/row/row_data_controller.dart | 32 ++++++--- .../grid/application/row/row_detail_bloc.dart | 39 +++++------ .../plugins/grid/presentation/grid_page.dart | 67 ++++++++++++++----- .../widgets/cell/cell_builder.dart | 24 ++++--- .../presentation/widgets/row/grid_row.dart | 54 +++++++-------- .../presentation/widgets/row/row_detail.dart | 11 ++- 7 files changed, 133 insertions(+), 107 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/context_builder.dart index 12cadcca40..09564b46b0 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/context_builder.dart @@ -7,23 +7,24 @@ typedef GridDateCellController = IGridCellController<DateCellDataPB, CalendarData>; typedef GridURLCellController = IGridCellController<URLCellDataPB, String>; +abstract class GridCellControllerBuilderDelegate { + GridCellFieldNotifier buildFieldNotifier(); +} + class GridCellControllerBuilder { final GridCellIdentifier _cellId; final GridCellCache _cellCache; - final GridFieldCache _fieldCache; + final GridCellControllerBuilderDelegate delegate; GridCellControllerBuilder({ + required this.delegate, required GridCellIdentifier cellId, required GridCellCache cellCache, - required GridFieldCache fieldCache, }) : _cellCache = cellCache, - _fieldCache = fieldCache, _cellId = cellId; IGridCellController build() { - final cellFieldNotifier = - GridCellFieldNotifier(notifier: GridCellFieldNotifierImpl(_fieldCache)); - + final cellFieldNotifier = delegate.buildFieldNotifier(); switch (_cellId.fieldType) { case FieldType.Checkbox: final cellDataLoader = GridCellDataLoader( diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart index 6975bd6a51..3f25e414f1 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart @@ -1,13 +1,15 @@ +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart'; import 'package:flutter/material.dart'; +import '../../presentation/widgets/cell/cell_builder.dart'; import '../cell/cell_service/cell_service.dart'; import '../field/field_cache.dart'; import 'row_cache.dart'; typedef OnRowChanged = void Function(GridCellMap, GridRowChangeReason); -class GridRowDataController { - final String rowId; - VoidCallback? _onRowChangedListener; +class GridRowDataController extends GridCellBuilderDelegate { + final GridRowInfo rowInfo; + final List<VoidCallback> _onRowChangedListeners = []; final GridFieldCache _fieldCache; final GridRowCache _rowCache; @@ -16,26 +18,36 @@ class GridRowDataController { GridRowCache get rowCache => _rowCache; GridRowDataController({ - required this.rowId, + required this.rowInfo, required GridFieldCache fieldCache, required GridRowCache rowCache, }) : _fieldCache = fieldCache, _rowCache = rowCache; GridCellMap loadData() { - return _rowCache.loadGridCells(rowId); + return _rowCache.loadGridCells(rowInfo.id); } void addListener({OnRowChanged? onRowChanged}) { - _onRowChangedListener = _rowCache.addListener( - rowId: rowId, + _onRowChangedListeners.add(_rowCache.addListener( + rowId: rowInfo.id, onCellUpdated: onRowChanged, - ); + )); } void dispose() { - if (_onRowChangedListener != null) { - _rowCache.removeRowListener(_onRowChangedListener!); + for (final fn in _onRowChangedListeners) { + _rowCache.removeRowListener(fn); } } + + // GridCellBuilderDelegate implementation + @override + GridCellFieldNotifier buildFieldNotifier() { + return GridCellFieldNotifier( + notifier: GridCellFieldNotifierImpl(_fieldCache)); + } + + @override + GridCellCache get cellCache => rowCache.cellCache; } diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart index 17f6ef4990..6f59682f4d 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart @@ -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:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; -import 'row_cache.dart'; +import 'row_data_controller.dart'; part 'row_detail_bloc.freezed.dart'; class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> { - final GridRowInfo rowInfo; - final GridRowCache _rowCache; - void Function()? _rowListenFn; + final GridRowDataController dataController; RowDetailBloc({ - required this.rowInfo, - required GridRowCache rowCache, - }) : _rowCache = rowCache, - super(RowDetailState.initial()) { + required this.dataController, + }) : super(RowDetailState.initial()) { on<RowDetailEvent>( (event, emit) async { await event.map( initial: (_Initial value) async { await _startListening(); - _loadCellData(); + final cells = dataController.loadData(); + if (!isClosed) { + add(RowDetailEvent.didReceiveCellDatas(cells.values.toList())); + } }, didReceiveCellDatas: (_DidReceiveCellDatas value) { emit(state.copyWith(gridCells: value.gridCells)); @@ -32,27 +31,19 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> { @override Future<void> close() async { - if (_rowListenFn != null) { - _rowCache.removeRowListener(_rowListenFn!); - } + dataController.dispose(); return super.close(); } Future<void> _startListening() async { - _rowListenFn = _rowCache.addListener( - rowId: rowInfo.id, - onCellUpdated: (cellDatas, reason) => - add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())), - listenWhen: () => !isClosed, + dataController.addListener( + onRowChanged: (cells, reason) { + if (!isClosed) { + add(RowDetailEvent.didReceiveCellDatas(cells.values.toList())); + } + }, ); } - - Future<void> _loadCellData() async { - final cellDataMap = _rowCache.loadGridCells(rowInfo.id); - if (!isClosed) { - add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList())); - } - } } @freezed diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart index c4fbbf4e92..3d3be83c79 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart @@ -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/startup/startup.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 'layout/layout.dart'; import 'layout/sizes.dart'; +import 'widgets/cell/cell_builder.dart'; import 'widgets/row/grid_row.dart'; import 'widgets/footer/grid_footer.dart'; import 'widgets/header/grid_header.dart'; +import 'widgets/row/row_detail.dart'; import 'widgets/shortcuts.dart'; import 'widgets/toolbar/grid_toolbar.dart'; @@ -239,25 +242,53 @@ class _GridRowsState extends State<_GridRows> { final rowCache = context.read<GridBloc>().getRowCache(rowInfo.blockId, rowInfo.id); - final fieldCache = context.read<GridBloc>().dataController.fieldCache; - if (rowCache != null) { - final dataController = GridRowDataController( - rowId: rowInfo.id, - fieldCache: fieldCache, - rowCache: rowCache, - ); + /// Return placeholder widget if the rowCache is null. + if (rowCache == null) return const SizedBox(); - return SizeTransition( - sizeFactor: animation, - child: GridRowWidget( - rowData: rowInfo, - dataController: dataController, - key: ValueKey(rowInfo.id), - ), - ); - } else { - return const SizedBox(); - } + final fieldCache = context.read<GridBloc>().dataController.fieldCache; + final dataController = GridRowDataController( + rowInfo: rowInfo, + fieldCache: fieldCache, + rowCache: rowCache, + ); + + return SizeTransition( + sizeFactor: animation, + 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); } } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart index 1220d39a9d..6c3fa38bc1 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart @@ -1,5 +1,4 @@ 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:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -13,23 +12,26 @@ import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; import 'url_cell/url_cell.dart'; +abstract class GridCellBuilderDelegate + extends GridCellControllerBuilderDelegate { + GridCellCache get cellCache; +} + class GridCellBuilder { - final GridCellCache cellCache; - final GridFieldCache fieldCache; + final GridCellBuilderDelegate delegate; GridCellBuilder({ - required this.cellCache, - required this.fieldCache, + required this.delegate, }); - GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) { + GridCellWidget build(GridCellIdentifier cellId, {GridCellStyle? style}) { final cellControllerBuilder = GridCellControllerBuilder( - cellId: cell, - cellCache: cellCache, - fieldCache: fieldCache, + cellId: cellId, + cellCache: delegate.cellCache, + delegate: delegate, ); - final key = cell.key(); - switch (cell.fieldType) { + final key = cellId.key(); + switch (cellId.fieldType) { case FieldType.Checkbox: return GridCheckboxCell( cellControllerBuilder: cellControllerBuilder, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart index 7ea59ab7e9..044ddbb5ee 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart @@ -14,22 +14,20 @@ import '../cell/cell_accessory.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 GridRowInfo rowInfo; final GridRowDataController dataController; final GridCellBuilder cellBuilder; + final void Function(BuildContext, GridCellBuilder) openDetailPage; - GridRowWidget({ - required this.rowData, + const GridRowWidget({ + required this.rowInfo, required this.dataController, + required this.cellBuilder, + required this.openDetailPage, Key? key, - }) : cellBuilder = GridCellBuilder( - cellCache: dataController.rowCache.cellCache, - fieldCache: dataController.fieldCache, - ), - super(key: key); + }) : super(key: key); @override State<GridRowWidget> createState() => _GridRowWidgetState(); @@ -41,7 +39,7 @@ class _GridRowWidgetState extends State<GridRowWidget> { @override void initState() { _rowBloc = RowBloc( - rowInfo: widget.rowData, + rowInfo: widget.rowInfo, dataController: widget.dataController, ); _rowBloc.add(const RowEvent.initial()); @@ -56,17 +54,20 @@ class _GridRowWidgetState extends State<GridRowWidget> { child: BlocBuilder<RowBloc, RowState>( buildWhen: (p, c) => p.rowInfo.height != c.rowInfo.height, builder: (context, state) { - return Row( - children: [ - const _RowLeading(), - Expanded( - child: _RowCells( + final children = [ + const _RowLeading(), + Expanded( + child: RowContent( builder: widget.cellBuilder, - onExpand: () => _expandRow(context), - )), - const _RowTrailing(), - ], - ); + onExpand: () => widget.openDetailPage( + context, + widget.cellBuilder, + ), + ), + ), + const _RowTrailing(), + ]; + return Row(children: children); }, ), ), @@ -78,15 +79,6 @@ class _GridRowWidgetState extends State<GridRowWidget> { _rowBloc.close(); 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 { @@ -159,10 +151,10 @@ class _DeleteRowButton extends StatelessWidget { } } -class _RowCells extends StatelessWidget { +class RowContent extends StatelessWidget { final VoidCallback onExpand; final GridCellBuilder builder; - const _RowCells({ + const RowContent({ required this.builder, required this.onExpand, Key? key, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart index 6394b98874..bfaef332a9 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart @@ -1,5 +1,6 @@ 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/row/row_data_controller.dart'; import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart'; import 'package:flowy_infra/image.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_bloc/flutter_bloc.dart'; -import '../../../application/row/row_cache.dart'; import '../../layout/sizes.dart'; import '../cell/cell_accessory.dart'; import '../cell/prelude.dart'; @@ -21,13 +21,11 @@ import '../header/field_cell.dart'; import '../header/field_editor.dart'; class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate { - final GridRowInfo rowInfo; - final GridRowCache rowCache; + final GridRowDataController dataController; final GridCellBuilder cellBuilder; const RowDetailPage({ - required this.rowInfo, - required this.rowCache, + required this.dataController, required this.cellBuilder, Key? key, }) : super(key: key); @@ -63,8 +61,7 @@ class _RowDetailPageState extends State<RowDetailPage> { return BlocProvider( create: (context) { final bloc = RowDetailBloc( - rowInfo: widget.rowInfo, - rowCache: widget.rowCache, + dataController: widget.dataController, ); bloc.add(const RowDetailEvent.initial()); return bloc;