database_controller.dart 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
  2. import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
  3. import 'package:appflowy_backend/log.dart';
  4. import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
  5. import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
  6. import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
  7. import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
  8. import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
  9. import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
  10. import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
  11. import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
  12. import 'package:collection/collection.dart';
  13. import 'dart:async';
  14. import 'package:dartz/dartz.dart';
  15. import 'database_service.dart';
  16. import 'defines.dart';
  17. import 'layout/layout_setting_listener.dart';
  18. import 'row/row_cache.dart';
  19. import 'group/group_listener.dart';
  20. typedef OnRowsChanged = void Function(
  21. List<RowInfo> rowInfos,
  22. RowsChangedReason,
  23. );
  24. typedef OnGroupByField = void Function(List<GroupPB>);
  25. typedef OnUpdateGroup = void Function(List<GroupPB>);
  26. typedef OnDeleteGroup = void Function(List<String>);
  27. typedef OnInsertGroup = void Function(InsertedGroupPB);
  28. class GroupCallbacks {
  29. final OnGroupByField? onGroupByField;
  30. final OnUpdateGroup? onUpdateGroup;
  31. final OnDeleteGroup? onDeleteGroup;
  32. final OnInsertGroup? onInsertGroup;
  33. GroupCallbacks({
  34. this.onGroupByField,
  35. this.onUpdateGroup,
  36. this.onDeleteGroup,
  37. this.onInsertGroup,
  38. });
  39. }
  40. class LayoutCallbacks {
  41. final void Function(LayoutSettingPB) onLayoutChanged;
  42. final void Function(LayoutSettingPB) onLoadLayout;
  43. LayoutCallbacks({
  44. required this.onLayoutChanged,
  45. required this.onLoadLayout,
  46. });
  47. }
  48. class DatabaseCallbacks {
  49. OnDatabaseChanged? onDatabaseChanged;
  50. OnRowsChanged? onRowsChanged;
  51. OnFieldsChanged? onFieldsChanged;
  52. OnFiltersChanged? onFiltersChanged;
  53. DatabaseCallbacks({
  54. this.onDatabaseChanged,
  55. this.onRowsChanged,
  56. this.onFieldsChanged,
  57. this.onFiltersChanged,
  58. });
  59. }
  60. class DatabaseController {
  61. final String viewId;
  62. final DatabaseBackendService _databaseBackendSvc;
  63. final FieldController fieldController;
  64. late DatabaseViewCache _viewCache;
  65. final LayoutTypePB layoutType;
  66. // Callbacks
  67. DatabaseCallbacks? _databaseCallbacks;
  68. GroupCallbacks? _groupCallbacks;
  69. LayoutCallbacks? _layoutCallbacks;
  70. // Getters
  71. List<RowInfo> get rowInfos => _viewCache.rowInfos;
  72. RowCache get rowCache => _viewCache.rowCache;
  73. // Listener
  74. final DatabaseGroupListener groupListener;
  75. final DatabaseLayoutListener layoutListener;
  76. DatabaseController({required ViewPB view, required this.layoutType})
  77. : viewId = view.id,
  78. _databaseBackendSvc = DatabaseBackendService(viewId: view.id),
  79. fieldController = FieldController(viewId: view.id),
  80. groupListener = DatabaseGroupListener(view.id),
  81. layoutListener = DatabaseLayoutListener(view.id) {
  82. _viewCache = DatabaseViewCache(
  83. viewId: viewId,
  84. fieldController: fieldController,
  85. );
  86. _listenOnRowsChanged();
  87. _listenOnFieldsChanged();
  88. _listenOnGroupChanged();
  89. _listenOnLayoutChanged();
  90. }
  91. void addListener({
  92. DatabaseCallbacks? onDatabaseChanged,
  93. LayoutCallbacks? onLayoutChanged,
  94. GroupCallbacks? onGroupChanged,
  95. }) {
  96. _layoutCallbacks = onLayoutChanged;
  97. _databaseCallbacks = onDatabaseChanged;
  98. _groupCallbacks = onGroupChanged;
  99. }
  100. Future<Either<Unit, FlowyError>> open() async {
  101. return _databaseBackendSvc.openGrid().then((result) {
  102. return result.fold(
  103. (database) async {
  104. _databaseCallbacks?.onDatabaseChanged?.call(database);
  105. _viewCache.rowCache.setInitialRows(database.rows);
  106. return await fieldController
  107. .loadFields(
  108. fieldIds: database.fields,
  109. )
  110. .then(
  111. (result) {
  112. return result.fold(
  113. (l) => Future(() async {
  114. await _loadGroups();
  115. await _loadLayoutSetting();
  116. return left(l);
  117. }),
  118. (err) => right(err),
  119. );
  120. },
  121. );
  122. },
  123. (err) => right(err),
  124. );
  125. });
  126. }
  127. Future<Either<RowPB, FlowyError>> createRow({
  128. String? startRowId,
  129. String? groupId,
  130. void Function(RowDataBuilder builder)? withCells,
  131. }) {
  132. Map<String, String>? cellDataByFieldId;
  133. if (withCells != null) {
  134. final rowBuilder = RowDataBuilder();
  135. withCells(rowBuilder);
  136. cellDataByFieldId = rowBuilder.build();
  137. }
  138. return _databaseBackendSvc.createRow(
  139. startRowId: startRowId,
  140. groupId: groupId,
  141. cellDataByFieldId: cellDataByFieldId,
  142. );
  143. }
  144. Future<Either<Unit, FlowyError>> moveRow(RowPB fromRow,
  145. {RowPB? toRow, String? groupId}) {
  146. return _databaseBackendSvc.moveRow(
  147. fromRowId: fromRow.id,
  148. toGroupId: groupId,
  149. toRowId: toRow?.id,
  150. );
  151. }
  152. Future<Either<Unit, FlowyError>> moveGroup(
  153. {required String fromGroupId, required String toGroupId}) {
  154. return _databaseBackendSvc.moveGroup(
  155. fromGroupId: fromGroupId,
  156. toGroupId: toGroupId,
  157. );
  158. }
  159. Future<void> updateCalenderLayoutSetting(
  160. CalendarLayoutSettingsPB layoutSetting) async {
  161. await _databaseBackendSvc
  162. .updateLayoutSetting(calendarLayoutSetting: layoutSetting)
  163. .then((result) {
  164. result.fold((l) => null, (r) => Log.error(r));
  165. });
  166. }
  167. Future<void> dispose() async {
  168. await _databaseBackendSvc.closeView();
  169. await fieldController.dispose();
  170. await groupListener.stop();
  171. }
  172. Future<void> _loadGroups() async {
  173. final result = await _databaseBackendSvc.loadGroups();
  174. return Future(
  175. () => result.fold(
  176. (groups) {
  177. _groupCallbacks?.onGroupByField?.call(groups.items);
  178. },
  179. (err) => Log.error(err),
  180. ),
  181. );
  182. }
  183. Future<void> _loadLayoutSetting() async {
  184. _databaseBackendSvc.getLayoutSetting(layoutType).then((result) {
  185. result.fold(
  186. (l) {
  187. _layoutCallbacks?.onLoadLayout(l);
  188. },
  189. (r) => Log.error(r),
  190. );
  191. });
  192. }
  193. void _listenOnRowsChanged() {
  194. _viewCache.addListener(onRowsChanged: (reason) {
  195. _databaseCallbacks?.onRowsChanged?.call(rowInfos, reason);
  196. });
  197. }
  198. void _listenOnFieldsChanged() {
  199. fieldController.addListener(
  200. onReceiveFields: (fields) {
  201. _databaseCallbacks?.onFieldsChanged?.call(UnmodifiableListView(fields));
  202. },
  203. onFilters: (filters) {
  204. _databaseCallbacks?.onFiltersChanged?.call(filters);
  205. },
  206. );
  207. }
  208. void _listenOnGroupChanged() {
  209. groupListener.start(
  210. onNumOfGroupsChanged: (result) {
  211. result.fold((changeset) {
  212. if (changeset.updateGroups.isNotEmpty) {
  213. _groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups);
  214. }
  215. if (changeset.deletedGroups.isNotEmpty) {
  216. _groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups);
  217. }
  218. for (final insertedGroup in changeset.insertedGroups) {
  219. _groupCallbacks?.onInsertGroup?.call(insertedGroup);
  220. }
  221. }, (r) => Log.error(r));
  222. },
  223. onGroupByNewField: (result) {
  224. result.fold((groups) {
  225. _groupCallbacks?.onGroupByField?.call(groups);
  226. }, (r) => Log.error(r));
  227. },
  228. );
  229. }
  230. void _listenOnLayoutChanged() {
  231. layoutListener.start(onLayoutChanged: (result) {
  232. result.fold((l) {
  233. _layoutCallbacks?.onLayoutChanged(l);
  234. }, (r) => Log.error(r));
  235. });
  236. }
  237. }
  238. class RowDataBuilder {
  239. final _cellDataByFieldId = <String, String>{};
  240. void insertText(FieldInfo fieldInfo, String text) {
  241. assert(fieldInfo.fieldType == FieldType.RichText);
  242. _cellDataByFieldId[fieldInfo.field.id] = text;
  243. }
  244. void insertNumber(FieldInfo fieldInfo, int num) {
  245. assert(fieldInfo.fieldType == FieldType.Number);
  246. _cellDataByFieldId[fieldInfo.field.id] = num.toString();
  247. }
  248. void insertDate(FieldInfo fieldInfo, DateTime date) {
  249. assert(fieldInfo.fieldType == FieldType.DateTime);
  250. final timestamp = (date.millisecondsSinceEpoch ~/ 1000);
  251. _cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
  252. }
  253. Map<String, String> build() {
  254. return _cellDataByFieldId;
  255. }
  256. }