field_controller.dart 8.8 KB

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