chore: refactor row cache

This commit is contained in:
appflowy 2022-04-19 11:41:37 +08:00
parent 5ee4b8a2bc
commit b3a99be7f8
6 changed files with 133 additions and 102 deletions

View File

@ -51,13 +51,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
void _startListening() {
fieldCache.addListener(
onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
listenWhen: () => !isClosed,
onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
);
rowCache.addListener(
onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)),
listenWhen: () => !isClosed,
onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)),
);
}

View File

@ -53,23 +53,17 @@ class RowBloc extends Bloc<RowEvent, RowState> {
void _handleRowUpdate(Row row, Emitter<RowState> emit) {
final CellDataMap cellDataMap = _makeCellDatas(row, state.rowData.fields);
emit(state.copyWith(
row: Future(() => Some(row)),
cellDataMap: Some(cellDataMap),
));
emit(state.copyWith(cellDataMap: Some(cellDataMap)));
}
Future<void> _handleFieldUpdate(Emitter<RowState> emit) async {
final optionRow = await state.row;
final CellDataMap cellDataMap = optionRow.fold(
() => CellDataMap.identity(),
(row) => _makeCellDatas(row, _fieldCache.unmodifiableFields),
);
final data = state.rowData.data;
if (data == null) {
return;
}
emit(state.copyWith(
rowData: state.rowData.copyWith(fields: _fieldCache.unmodifiableFields),
cellDataMap: Some(cellDataMap),
));
final CellDataMap cellDataMap = _makeCellDatas(data, state.rowData.fields);
emit(state.copyWith(cellDataMap: Some(cellDataMap)));
}
@override
@ -98,11 +92,12 @@ class RowBloc extends Bloc<RowEvent, RowState> {
}
Future<void> _loadRow(Emitter<RowState> emit) async {
final data = await _rowCache.getRowData(state.rowData.rowId);
if (isClosed) {
return;
}
data.foldRight(null, (data, _) => add(RowEvent.didLoadRow(data)));
final data = _rowCache.loadRow(state.rowData.rowId);
data.foldRight(null, (data, _) {
if (!isClosed) {
add(RowEvent.didLoadRow(data));
}
});
}
CellDataMap _makeCellDatas(Row row, List<Field> fields) {
@ -134,13 +129,11 @@ class RowEvent with _$RowEvent {
class RowState with _$RowState {
const factory RowState({
required GridRow rowData,
required Future<Option<Row>> row,
required Option<CellDataMap> cellDataMap,
}) = _RowState;
factory RowState.initial(GridRow rowData) => RowState(
rowData: rowData,
row: Future(() => none()),
cellDataMap: none(),
);
}

View File

@ -1,4 +1,5 @@
import 'dart:collection';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
@ -7,37 +8,31 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/grid_listener.dart';
part 'row_service.freezed.dart';
class RowsNotifier extends ChangeNotifier {
List<GridRow> _rows = [];
GridRowChangeReason _changeReason = const InitialListState();
void updateRows(List<GridRow> rows, GridRowChangeReason changeReason) {
_rows = rows;
_changeReason = changeReason;
changeReason.map(
insert: (_) => notifyListeners(),
delete: (_) => notifyListeners(),
update: (_) => notifyListeners(),
initial: (_) {},
);
}
List<GridRow> get rows => _rows;
}
class GridRowCache {
final String gridId;
final GridRowListener _rowsListener;
final RowsNotifier _rowNotifier = RowsNotifier();
final HashMap<String, Row> _rowDataMap = HashMap();
late final _RowsNotifier _rowNotifier;
UnmodifiableListView<Field> _fields = UnmodifiableListView([]);
List<GridRow> get clonedRows => _rowNotifier.clonedRows;
GridRowCache({required this.gridId}) : _rowsListener = GridRowListener(gridId: gridId) {
_rowNotifier = _RowsNotifier(
rowBuilder: (rowOrder) {
return GridRow(
gridId: gridId,
fields: _fields,
rowId: rowOrder.rowId,
height: rowOrder.height.toDouble(),
);
},
);
_rowsListener.rowsUpdateNotifier.addPublishListener((result) {
result.fold(
(changesets) {
@ -58,8 +53,6 @@ class GridRowCache {
_rowNotifier.dispose();
}
List<GridRow> get clonedRows => [..._rowNotifier.rows];
void addListener({
void Function(List<GridRow>, GridRowChangeReason)? onChanged,
bool Function()? listenWhen,
@ -80,7 +73,7 @@ class GridRowCache {
void Function(Row)? onUpdated,
bool Function()? listenWhen,
}) {
f() {
listenrHandler() {
if (onUpdated == null) {
return;
}
@ -90,57 +83,76 @@ class GridRowCache {
}
_rowNotifier._changeReason.whenOrNull(update: (indexs) {
final row = _rowDataMap[rowId];
final row = _rowNotifier.rowDataWithId(rowId);
if (indexs[rowId] != null && row != null) {
onUpdated(row);
}
});
}
_rowNotifier.addListener(f);
return f;
_rowNotifier.addListener(listenrHandler);
return listenrHandler;
}
void removeRowListener(VoidCallback callback) {
_rowNotifier.removeListener(callback);
}
Future<Option<Row>> getRowData(String rowId) async {
final Row? data = _rowDataMap[rowId];
Option<Row> loadRow(String rowId) {
final Row? data = _rowNotifier.rowDataWithId(rowId);
if (data != null) {
return Future(() => Some(data));
return Some(data);
}
final payload = RowIdentifierPayload.create()
..gridId = gridId
..rowId = rowId;
final result = await GridEventGetRow(payload).send();
return Future(() {
return result.fold(
(data) {
data.freeze();
_rowDataMap[data.id] = data;
return Some(data);
},
(err) {
Log.error(err);
return none();
},
GridEventGetRow(payload).send().then((result) {
result.fold(
(rowData) => _rowNotifier.rowData = rowData,
(err) => Log.error(err),
);
});
return none();
}
void updateWithBlock(List<GridBlockOrder> blocks, UnmodifiableListView<Field> fields) {
_fields = fields;
final newRows = blocks.expand((block) => block.rowOrders).map((rowOrder) {
return GridRow.fromBlockRow(gridId, rowOrder, _fields);
}).toList();
_rowNotifier.updateRows(newRows, const GridRowChangeReason.initial());
final rowOrders = blocks.expand((block) => block.rowOrders).toList();
_rowNotifier.reset(rowOrders);
}
void _deleteRows(List<RowOrder> deletedRows) {
_rowNotifier.deleteRows(deletedRows);
}
void _insertRows(List<IndexRowOrder> createdRows) {
_rowNotifier.insertRows(createdRows);
}
void _updateRows(List<RowOrder> rowOrders) {
_rowNotifier.updateRows(rowOrders);
}
}
class _RowsNotifier extends ChangeNotifier {
List<GridRow> _rows = [];
HashMap<String, Row> _rowDataMap = HashMap();
GridRowChangeReason _changeReason = const InitialListState();
final GridRow Function(RowOrder) rowBuilder;
_RowsNotifier({
required this.rowBuilder,
});
void reset(List<RowOrder> rowOrders) {
_rowDataMap = HashMap();
final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList();
_update(rows, const GridRowChangeReason.initial());
}
void deleteRows(List<RowOrder> deletedRows) {
if (deletedRows.isEmpty) {
return;
}
@ -149,7 +161,7 @@ class GridRowCache {
final DeletedIndexs deletedIndex = [];
final Map<String, RowOrder> deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder};
_rowNotifier.rows.asMap().forEach((index, row) {
_rows.asMap().forEach((index, row) {
if (deletedRowMap[row.rowId] == null) {
newRows.add(row);
} else {
@ -157,48 +169,81 @@ class GridRowCache {
}
});
_rowNotifier.updateRows(newRows, GridRowChangeReason.delete(deletedIndex));
_update(newRows, GridRowChangeReason.delete(deletedIndex));
}
void _insertRows(List<IndexRowOrder> createdRows) {
void insertRows(List<IndexRowOrder> createdRows) {
if (createdRows.isEmpty) {
return;
}
InsertedIndexs insertIndexs = [];
final List<GridRow> newRows = _rowNotifier.rows;
final List<GridRow> newRows = _rows;
for (final createdRow in createdRows) {
final gridRow = GridRow.fromBlockRow(gridId, createdRow.rowOrder, _fields);
insertIndexs.add(
InsertedIndex(
index: createdRow.index,
rowId: gridRow.rowId,
),
);
newRows.insert(createdRow.index, gridRow);
final rowOrder = createdRow.rowOrder;
final insertIndex = InsertedIndex(index: createdRow.index, rowId: rowOrder.rowId);
insertIndexs.add(insertIndex);
newRows.insert(createdRow.index, (rowBuilder(rowOrder)));
}
_rowNotifier.updateRows(newRows, GridRowChangeReason.insert(insertIndexs));
_update(newRows, GridRowChangeReason.insert(insertIndexs));
}
void _updateRows(List<RowOrder> updatedRows) {
void updateRows(List<RowOrder> updatedRows) {
if (updatedRows.isEmpty) {
return;
}
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
final List<GridRow> newRows = _rowNotifier.rows;
final List<GridRow> newRows = _rows;
for (final rowOrder in updatedRows) {
final index = newRows.indexWhere((row) => row.rowId == rowOrder.rowId);
if (index != -1) {
newRows.removeAt(index);
newRows.insert(index, GridRow.fromBlockRow(gridId, rowOrder, _fields));
// Remove the cache data
_rowDataMap.remove(rowOrder.rowId);
newRows.insert(index, rowBuilder(rowOrder));
updatedIndexs[rowOrder.rowId] = UpdatedIndex(index: index, rowId: rowOrder.rowId);
}
}
_rowNotifier.updateRows(newRows, GridRowChangeReason.update(updatedIndexs));
_update(newRows, GridRowChangeReason.update(updatedIndexs));
}
void _update(List<GridRow> rows, GridRowChangeReason changeReason) {
_rows = rows;
_changeReason = changeReason;
changeReason.map(
insert: (_) => notifyListeners(),
delete: (_) => notifyListeners(),
update: (_) => notifyListeners(),
initial: (_) {},
);
}
set rowData(Row rowData) {
rowData.freeze();
_rowDataMap[rowData.id] = rowData;
final index = _rows.indexWhere((row) => row.rowId == rowData.id);
if (index != -1) {
if (_rows[index].data != rowData) {
final row = _rows.removeAt(index).copyWith(data: rowData);
_rows.insert(index, row);
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId);
_changeReason = GridRowChangeReason.update(updatedIndexs);
notifyListeners();
}
}
}
Row? rowDataWithId(String rowId) {
return _rowDataMap[rowId];
}
List<GridRow> get clonedRows => [..._rows];
}
class RowService {
@ -258,7 +303,7 @@ class GridCellIdentifier with _$GridCellIdentifier {
required String rowId,
required Field field,
Cell? cell,
}) = _CellData;
}) = _GridCellIdentifier;
}
@freezed
@ -268,18 +313,8 @@ class GridRow with _$GridRow {
required String rowId,
required List<Field> fields,
required double height,
required Future<Option<Row>> data,
Row? data,
}) = _GridRow;
factory GridRow.fromBlockRow(String gridId, RowOrder row, List<Field> fields) {
return GridRow(
gridId: gridId,
fields: fields,
rowId: row.rowId,
data: Future(() => none()),
height: row.height.toDouble(),
);
}
}
typedef InsertedIndexs = List<InsertedIndex>;

View File

@ -222,7 +222,11 @@ class _GridRowsState extends State<_GridRows> {
);
}
Widget _renderRow(BuildContext context, GridRow rowData, Animation<double> animation) {
Widget _renderRow(
BuildContext context,
GridRow rowData,
Animation<double> animation,
) {
final bloc = context.read<GridBloc>();
final fieldCache = bloc.fieldCache;
final rowCache = bloc.rowCache;

View File

@ -47,7 +47,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
() => widget.setFocus(context, false),
);
},
child: Row(children: children),
child: ClipRRect(child: Row(children: children)),
),
);
},
@ -103,7 +103,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
() => widget.setFocus(context, false),
);
},
child: Row(children: children),
child: ClipRRect(child: Row(children: children)),
),
);
},

View File

@ -7,7 +7,6 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'row_action_sheet.dart';
class GridRowWidget extends StatefulWidget {