field_controller.dart 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import 'dart:collection';
  2. import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
  3. import 'package:app_flowy/plugins/grid/application/grid_service.dart';
  4. import 'package:app_flowy/plugins/grid/application/setting/setting_listener.dart';
  5. import 'package:app_flowy/plugins/grid/application/setting/setting_service.dart';
  6. import 'package:dartz/dartz.dart';
  7. import 'package:flowy_sdk/log.dart';
  8. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  9. import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
  10. import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
  11. import 'package:flutter/foundation.dart';
  12. import '../row/row_cache.dart';
  13. class _GridFieldNotifier extends ChangeNotifier {
  14. List<GridFieldContext> _fieldContexts = [];
  15. set fieldContexts(List<GridFieldContext> fieldContexts) {
  16. _fieldContexts = fieldContexts;
  17. notifyListeners();
  18. }
  19. void notify() {
  20. notifyListeners();
  21. }
  22. List<GridFieldContext> get fieldContexts => _fieldContexts;
  23. }
  24. typedef OnChangeset = void Function(FieldChangesetPB);
  25. typedef OnReceiveFields = void Function(List<GridFieldContext>);
  26. class GridFieldController {
  27. final String gridId;
  28. final GridFieldsListener _fieldListener;
  29. final SettingListener _settingListener;
  30. final Map<OnReceiveFields, VoidCallback> _fieldCallbackMap = {};
  31. final Map<OnChangeset, OnChangeset> _changesetCallbackMap = {};
  32. _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
  33. List<String> _groupFieldIds = [];
  34. final GridFFIService _gridFFIService;
  35. final SettingFFIService _settingFFIService;
  36. List<GridFieldContext> get fieldContexts =>
  37. [..._fieldNotifier?.fieldContexts ?? []];
  38. GridFieldController({required this.gridId})
  39. : _fieldListener = GridFieldsListener(gridId: gridId),
  40. _gridFFIService = GridFFIService(gridId: gridId),
  41. _settingFFIService = SettingFFIService(viewId: gridId),
  42. _settingListener = SettingListener(gridId: gridId) {
  43. //Listen on field's changes
  44. _fieldListener.start(onFieldsChanged: (result) {
  45. result.fold(
  46. (changeset) {
  47. _deleteFields(changeset.deletedFields);
  48. _insertFields(changeset.insertedFields);
  49. _updateFields(changeset.updatedFields);
  50. for (final listener in _changesetCallbackMap.values) {
  51. listener(changeset);
  52. }
  53. },
  54. (err) => Log.error(err),
  55. );
  56. });
  57. //Listen on setting changes
  58. _settingListener.start(onSettingUpdated: (result) {
  59. result.fold(
  60. (setting) => _updateFieldsWhenSettingChanged(setting),
  61. (r) => Log.error(r),
  62. );
  63. });
  64. _settingFFIService.getSetting().then((result) {
  65. result.fold(
  66. (setting) => _updateFieldsWhenSettingChanged(setting),
  67. (err) => Log.error(err),
  68. );
  69. });
  70. }
  71. void _updateFieldsWhenSettingChanged(GridSettingPB setting) {
  72. _groupFieldIds = setting.groupConfigurations.items
  73. .map((item) => item.groupFieldId)
  74. .toList();
  75. _updateFieldContexts();
  76. }
  77. void _updateFieldContexts() {
  78. if (_fieldNotifier != null) {
  79. for (var field in _fieldNotifier!.fieldContexts) {
  80. if (_groupFieldIds.contains(field.id)) {
  81. field._isGroupField = true;
  82. } else {
  83. field._isGroupField = false;
  84. }
  85. }
  86. _fieldNotifier?.notify();
  87. }
  88. }
  89. Future<void> dispose() async {
  90. await _fieldListener.stop();
  91. _fieldNotifier?.dispose();
  92. _fieldNotifier = null;
  93. }
  94. Future<Either<Unit, FlowyError>> loadFields(
  95. {required List<FieldIdPB> fieldIds}) async {
  96. final result = await _gridFFIService.getFields(fieldIds: fieldIds);
  97. return Future(
  98. () => result.fold(
  99. (newFields) {
  100. _fieldNotifier?.fieldContexts = newFields.items
  101. .map((field) => GridFieldContext(field: field))
  102. .toList();
  103. _updateFieldContexts();
  104. return left(unit);
  105. },
  106. (err) => right(err),
  107. ),
  108. );
  109. }
  110. void addListener({
  111. OnReceiveFields? onFields,
  112. OnChangeset? onChangeset,
  113. bool Function()? listenWhen,
  114. }) {
  115. if (onChangeset != null) {
  116. callback(c) {
  117. if (listenWhen != null && listenWhen() == false) {
  118. return;
  119. }
  120. onChangeset(c);
  121. }
  122. _changesetCallbackMap[onChangeset] = callback;
  123. }
  124. if (onFields != null) {
  125. callback() {
  126. if (listenWhen != null && listenWhen() == false) {
  127. return;
  128. }
  129. onFields(fieldContexts);
  130. }
  131. _fieldCallbackMap[onFields] = callback;
  132. _fieldNotifier?.addListener(callback);
  133. }
  134. }
  135. void removeListener({
  136. OnReceiveFields? onFieldsListener,
  137. OnChangeset? onChangesetListener,
  138. }) {
  139. if (onFieldsListener != null) {
  140. final callback = _fieldCallbackMap.remove(onFieldsListener);
  141. if (callback != null) {
  142. _fieldNotifier?.removeListener(callback);
  143. }
  144. }
  145. if (onChangesetListener != null) {
  146. _changesetCallbackMap.remove(onChangesetListener);
  147. }
  148. }
  149. void _deleteFields(List<FieldIdPB> deletedFields) {
  150. if (deletedFields.isEmpty) {
  151. return;
  152. }
  153. final List<GridFieldContext> newFields = fieldContexts;
  154. final Map<String, FieldIdPB> deletedFieldMap = {
  155. for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
  156. };
  157. newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
  158. _fieldNotifier?.fieldContexts = newFields;
  159. }
  160. void _insertFields(List<IndexFieldPB> insertedFields) {
  161. if (insertedFields.isEmpty) {
  162. return;
  163. }
  164. final List<GridFieldContext> newFields = fieldContexts;
  165. for (final indexField in insertedFields) {
  166. final gridField = GridFieldContext(field: indexField.field_1);
  167. if (newFields.length > indexField.index) {
  168. newFields.insert(indexField.index, gridField);
  169. } else {
  170. newFields.add(gridField);
  171. }
  172. }
  173. _fieldNotifier?.fieldContexts = newFields;
  174. }
  175. void _updateFields(List<FieldPB> updatedFields) {
  176. if (updatedFields.isEmpty) {
  177. return;
  178. }
  179. final List<GridFieldContext> newFields = fieldContexts;
  180. for (final updatedField in updatedFields) {
  181. final index =
  182. newFields.indexWhere((field) => field.id == updatedField.id);
  183. if (index != -1) {
  184. newFields.removeAt(index);
  185. final gridField = GridFieldContext(field: updatedField);
  186. newFields.insert(index, gridField);
  187. }
  188. }
  189. _fieldNotifier?.fieldContexts = newFields;
  190. }
  191. }
  192. class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
  193. final GridFieldController _cache;
  194. OnChangeset? _onChangesetFn;
  195. OnReceiveFields? _onFieldFn;
  196. GridRowFieldNotifierImpl(GridFieldController cache) : _cache = cache;
  197. @override
  198. UnmodifiableListView<GridFieldContext> get fields =>
  199. UnmodifiableListView(_cache.fieldContexts);
  200. @override
  201. void onRowFieldsChanged(VoidCallback callback) {
  202. _onFieldFn = (_) => callback();
  203. _cache.addListener(onFields: _onFieldFn);
  204. }
  205. @override
  206. void onRowFieldChanged(void Function(FieldPB) callback) {
  207. _onChangesetFn = (FieldChangesetPB changeset) {
  208. for (final updatedField in changeset.updatedFields) {
  209. callback(updatedField);
  210. }
  211. };
  212. _cache.addListener(onChangeset: _onChangesetFn);
  213. }
  214. @override
  215. void onRowDispose() {
  216. if (_onFieldFn != null) {
  217. _cache.removeListener(onFieldsListener: _onFieldFn!);
  218. _onFieldFn = null;
  219. }
  220. if (_onChangesetFn != null) {
  221. _cache.removeListener(onChangesetListener: _onChangesetFn!);
  222. _onChangesetFn = null;
  223. }
  224. }
  225. }
  226. class GridFieldContext {
  227. final FieldPB _field;
  228. bool _isGroupField = false;
  229. String get id => _field.id;
  230. FieldType get fieldType => _field.fieldType;
  231. bool get visibility => _field.visibility;
  232. double get width => _field.width.toDouble();
  233. bool get isPrimary => _field.isPrimary;
  234. String get name => _field.name;
  235. FieldPB get field => _field;
  236. bool get isGroupField => _isGroupField;
  237. GridFieldContext({required FieldPB field}) : _field = field;
  238. }