database_controller.dart 9.9 KB

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