grid_service.dart 6.5 KB

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