grid_service.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import 'dart:collection';
  2. import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
  3. import 'package:dartz/dartz.dart';
  4. import 'package:flowy_sdk/dispatch/dispatch.dart';
  5. import 'package:flowy_sdk/log.dart';
  6. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  7. import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
  8. import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
  9. import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
  10. import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
  12. import 'package:flutter/foundation.dart';
  13. import 'row/row_service.dart';
  14. class GridService {
  15. final String gridId;
  16. GridService({
  17. required this.gridId,
  18. });
  19. Future<Either<GridPB, FlowyError>> loadGrid() async {
  20. await FolderEventSetLatestView(ViewIdPB(value: gridId)).send();
  21. final payload = GridIdPB(value: gridId);
  22. return GridEventGetGrid(payload).send();
  23. }
  24. Future<Either<GridRowPB, FlowyError>> createRow(
  25. {Option<String>? startRowId}) {
  26. CreateRowPayloadPB payload = CreateRowPayloadPB.create()..gridId = gridId;
  27. startRowId?.fold(() => null, (id) => payload.startRowId = id);
  28. return GridEventCreateRow(payload).send();
  29. }
  30. Future<Either<RepeatedGridFieldPB, FlowyError>> getFields(
  31. {required List<GridFieldIdPB> fieldIds}) {
  32. final payload = QueryFieldPayloadPB.create()
  33. ..gridId = gridId
  34. ..fieldIds = RepeatedGridFieldIdPB(items: fieldIds);
  35. return GridEventGetFields(payload).send();
  36. }
  37. Future<Either<Unit, FlowyError>> closeGrid() {
  38. final request = ViewIdPB(value: gridId);
  39. return FolderEventCloseView(request).send();
  40. }
  41. }
  42. class FieldsNotifier extends ChangeNotifier {
  43. List<GridFieldPB> _fields = [];
  44. set fields(List<GridFieldPB> fields) {
  45. _fields = fields;
  46. notifyListeners();
  47. }
  48. List<GridFieldPB> get fields => _fields;
  49. }
  50. typedef FieldChangesetCallback = void Function(GridFieldChangesetPB);
  51. typedef FieldsCallback = void Function(List<GridFieldPB>);
  52. class GridFieldCache {
  53. final String gridId;
  54. final GridFieldsListener _fieldListener;
  55. FieldsNotifier? _fieldNotifier = FieldsNotifier();
  56. final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
  57. final Map<FieldChangesetCallback, FieldChangesetCallback>
  58. _changesetCallbackMap = {};
  59. GridFieldCache({required this.gridId})
  60. : _fieldListener = GridFieldsListener(gridId: gridId) {
  61. _fieldListener.start(onFieldsChanged: (result) {
  62. result.fold(
  63. (changeset) {
  64. _deleteFields(changeset.deletedFields);
  65. _insertFields(changeset.insertedFields);
  66. _updateFields(changeset.updatedFields);
  67. for (final listener in _changesetCallbackMap.values) {
  68. listener(changeset);
  69. }
  70. },
  71. (err) => Log.error(err),
  72. );
  73. });
  74. }
  75. Future<void> dispose() async {
  76. await _fieldListener.stop();
  77. _fieldNotifier?.dispose();
  78. _fieldNotifier = null;
  79. }
  80. UnmodifiableListView<GridFieldPB> get unmodifiableFields =>
  81. UnmodifiableListView(_fieldNotifier?.fields ?? []);
  82. List<GridFieldPB> get fields => [..._fieldNotifier?.fields ?? []];
  83. set fields(List<GridFieldPB> fields) {
  84. _fieldNotifier?.fields = [...fields];
  85. }
  86. void addListener({
  87. FieldsCallback? onFields,
  88. FieldChangesetCallback? onChangeset,
  89. bool Function()? listenWhen,
  90. }) {
  91. if (onChangeset != null) {
  92. fn(c) {
  93. if (listenWhen != null && listenWhen() == false) {
  94. return;
  95. }
  96. onChangeset(c);
  97. }
  98. _changesetCallbackMap[onChangeset] = fn;
  99. }
  100. if (onFields != null) {
  101. fn() {
  102. if (listenWhen != null && listenWhen() == false) {
  103. return;
  104. }
  105. onFields(fields);
  106. }
  107. _fieldsCallbackMap[onFields] = fn;
  108. _fieldNotifier?.addListener(fn);
  109. }
  110. }
  111. void removeListener({
  112. FieldsCallback? onFieldsListener,
  113. FieldChangesetCallback? onChangesetListener,
  114. }) {
  115. if (onFieldsListener != null) {
  116. final fn = _fieldsCallbackMap.remove(onFieldsListener);
  117. if (fn != null) {
  118. _fieldNotifier?.removeListener(fn);
  119. }
  120. }
  121. if (onChangesetListener != null) {
  122. _changesetCallbackMap.remove(onChangesetListener);
  123. }
  124. }
  125. void _deleteFields(List<GridFieldIdPB> deletedFields) {
  126. if (deletedFields.isEmpty) {
  127. return;
  128. }
  129. final List<GridFieldPB> newFields = fields;
  130. final Map<String, GridFieldIdPB> deletedFieldMap = {
  131. for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
  132. };
  133. newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
  134. _fieldNotifier?.fields = newFields;
  135. }
  136. void _insertFields(List<IndexFieldPB> insertedFields) {
  137. if (insertedFields.isEmpty) {
  138. return;
  139. }
  140. final List<GridFieldPB> newFields = fields;
  141. for (final indexField in insertedFields) {
  142. if (newFields.length > indexField.index) {
  143. newFields.insert(indexField.index, indexField.field_1);
  144. } else {
  145. newFields.add(indexField.field_1);
  146. }
  147. }
  148. _fieldNotifier?.fields = newFields;
  149. }
  150. void _updateFields(List<GridFieldPB> updatedFields) {
  151. if (updatedFields.isEmpty) {
  152. return;
  153. }
  154. final List<GridFieldPB> newFields = fields;
  155. for (final updatedField in updatedFields) {
  156. final index =
  157. newFields.indexWhere((field) => field.id == updatedField.id);
  158. if (index != -1) {
  159. newFields.removeAt(index);
  160. newFields.insert(index, updatedField);
  161. }
  162. }
  163. _fieldNotifier?.fields = newFields;
  164. }
  165. }
  166. class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
  167. final GridFieldCache _cache;
  168. FieldChangesetCallback? _onChangesetFn;
  169. FieldsCallback? _onFieldFn;
  170. GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
  171. @override
  172. UnmodifiableListView<GridFieldPB> get fields => _cache.unmodifiableFields;
  173. @override
  174. void onFieldsChanged(VoidCallback callback) {
  175. _onFieldFn = (_) => callback();
  176. _cache.addListener(onFields: _onFieldFn);
  177. }
  178. @override
  179. void onFieldChanged(void Function(GridFieldPB) callback) {
  180. _onChangesetFn = (GridFieldChangesetPB changeset) {
  181. for (final updatedField in changeset.updatedFields) {
  182. callback(updatedField);
  183. }
  184. };
  185. _cache.addListener(onChangeset: _onChangesetFn);
  186. }
  187. @override
  188. void dispose() {
  189. if (_onFieldFn != null) {
  190. _cache.removeListener(onFieldsListener: _onFieldFn!);
  191. _onFieldFn = null;
  192. }
  193. if (_onChangesetFn != null) {
  194. _cache.removeListener(onChangesetListener: _onChangesetFn!);
  195. _onChangesetFn = null;
  196. }
  197. }
  198. }