field_controller.dart 8.8 KB

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