grid_bloc.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import 'dart:async';
  2. import 'package:dartz/dartz.dart';
  3. import 'package:equatable/equatable.dart';
  4. import 'package:flowy_sdk/log.dart';
  5. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  6. import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
  7. import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
  8. import 'package:flutter_bloc/flutter_bloc.dart';
  9. import 'package:freezed_annotation/freezed_annotation.dart';
  10. import 'block/block_service.dart';
  11. import 'grid_service.dart';
  12. import 'row/row_service.dart';
  13. import 'dart:collection';
  14. part 'grid_bloc.freezed.dart';
  15. class GridBloc extends Bloc<GridEvent, GridState> {
  16. final String gridId;
  17. final GridService _gridService;
  18. final GridFieldCache fieldCache;
  19. // key: the block id
  20. final LinkedHashMap<String, GridBlockCacheService> _blocks;
  21. List<GridRow> get rows {
  22. final List<GridRow> rows = [];
  23. for (var block in _blocks.values) {
  24. rows.addAll(block.rows);
  25. }
  26. return rows;
  27. }
  28. GridBloc({required View view})
  29. : gridId = view.id,
  30. _blocks = LinkedHashMap.identity(),
  31. _gridService = GridService(gridId: view.id),
  32. fieldCache = GridFieldCache(gridId: view.id),
  33. super(GridState.initial(view.id)) {
  34. on<GridEvent>(
  35. (event, emit) async {
  36. await event.when(
  37. initial: () async {
  38. _startListening();
  39. await _loadGrid(emit);
  40. },
  41. createRow: () {
  42. _gridService.createRow();
  43. },
  44. didReceiveRowUpdate: (rows, reason) {
  45. emit(state.copyWith(rows: rows, reason: reason));
  46. },
  47. didReceiveFieldUpdate: (fields) {
  48. emit(state.copyWith(rows: rows, fields: GridFieldEquatable(fields)));
  49. },
  50. );
  51. },
  52. );
  53. }
  54. @override
  55. Future<void> close() async {
  56. await _gridService.closeGrid();
  57. await fieldCache.dispose();
  58. for (final blockCache in _blocks.values) {
  59. blockCache.dispose();
  60. }
  61. return super.close();
  62. }
  63. GridRowCacheService? getRowCache(String blockId, String rowId) {
  64. final GridBlockCacheService? blockCache = _blocks[blockId];
  65. return blockCache?.rowCache;
  66. }
  67. void _startListening() {
  68. fieldCache.addListener(
  69. listenWhen: () => !isClosed,
  70. onFields: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
  71. );
  72. }
  73. Future<void> _loadGrid(Emitter<GridState> emit) async {
  74. final result = await _gridService.loadGrid();
  75. return Future(
  76. () => result.fold(
  77. (grid) async {
  78. _initialBlocks(grid.blocks);
  79. await _loadFields(grid, emit);
  80. },
  81. (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
  82. ),
  83. );
  84. }
  85. Future<void> _loadFields(Grid grid, Emitter<GridState> emit) async {
  86. final result = await _gridService.getFields(fieldOrders: grid.fieldOrders);
  87. return Future(
  88. () => result.fold(
  89. (fields) {
  90. fieldCache.fields = fields.items;
  91. emit(state.copyWith(
  92. grid: Some(grid),
  93. fields: GridFieldEquatable(fieldCache.fields),
  94. rows: rows,
  95. loadingState: GridLoadingState.finish(left(unit)),
  96. ));
  97. },
  98. (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
  99. ),
  100. );
  101. }
  102. void _initialBlocks(List<GridBlock> blocks) {
  103. for (final block in blocks) {
  104. if (_blocks[block.id] != null) {
  105. Log.warn("Intial duplicate block's cache: ${block.id}");
  106. return;
  107. }
  108. final cache = GridBlockCacheService(
  109. gridId: gridId,
  110. block: block,
  111. fieldCache: fieldCache,
  112. );
  113. cache.addListener(
  114. listenWhen: () => !isClosed,
  115. onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rows, reason)),
  116. );
  117. _blocks[block.id] = cache;
  118. }
  119. }
  120. }
  121. @freezed
  122. class GridEvent with _$GridEvent {
  123. const factory GridEvent.initial() = InitialGrid;
  124. const factory GridEvent.createRow() = _CreateRow;
  125. const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridRowChangeReason listState) = _DidReceiveRowUpdate;
  126. const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
  127. }
  128. @freezed
  129. class GridState with _$GridState {
  130. const factory GridState({
  131. required String gridId,
  132. required Option<Grid> grid,
  133. required GridFieldEquatable fields,
  134. required List<GridRow> rows,
  135. required GridLoadingState loadingState,
  136. required GridRowChangeReason reason,
  137. }) = _GridState;
  138. factory GridState.initial(String gridId) => GridState(
  139. fields: const GridFieldEquatable([]),
  140. rows: [],
  141. grid: none(),
  142. gridId: gridId,
  143. loadingState: const _Loading(),
  144. reason: const InitialListState(),
  145. );
  146. }
  147. @freezed
  148. class GridLoadingState with _$GridLoadingState {
  149. const factory GridLoadingState.loading() = _Loading;
  150. const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
  151. }
  152. class GridFieldEquatable extends Equatable {
  153. final List<Field> _fields;
  154. const GridFieldEquatable(List<Field> fields) : _fields = fields;
  155. @override
  156. List<Object?> get props {
  157. return [
  158. _fields.length,
  159. _fields.map((field) => field.width).reduce((value, element) => value + element),
  160. ];
  161. }
  162. UnmodifiableListView<Field> get value => UnmodifiableListView(_fields);
  163. }