浏览代码

Merge branch 'upstream-main' into feat/appflowy_tauri_3

# Conflicts:
#	frontend/appflowy_tauri/package.json
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/Breadcrumbs.tsx
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/MainPanel.tsx
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.hooks.ts
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PageItem.hooks.ts
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/PageItem.tsx
#	frontend/appflowy_tauri/src/appflowy_app/components/layout/Screen.tsx
ascarbek 2 年之前
父节点
当前提交
a9c8bad599
共有 100 个文件被更改,包括 1611 次插入1297 次删除
  1. 2 1
      frontend/appflowy_flutter/assets/translations/en.json
  2. 3 3
      frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart
  3. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart
  4. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart
  5. 9 5
      frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart
  6. 290 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/database_controller.dart
  7. 67 11
      frontend/appflowy_flutter/lib/plugins/database_view/application/database_service.dart
  8. 15 15
      frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_listener.dart
  9. 51 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/layout/layout_setting_listener.dart
  10. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart
  11. 8 6
      frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart
  12. 0 51
      frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_service.dart
  13. 47 66
      frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart
  14. 0 144
      frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart
  15. 0 39
      frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart
  16. 49 3
      frontend/appflowy_flutter/lib/plugins/database_view/board/application/group_controller.dart
  17. 0 51
      frontend/appflowy_flutter/lib/plugins/database_view/board/application/group_listener.dart
  18. 27 17
      frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart
  19. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_toolbar.dart
  20. 179 54
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_bloc.dart
  21. 0 115
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_data_controller.dart
  22. 0 65
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_listener.dart
  23. 1 2
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/calendar.dart
  24. 251 34
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_page.dart
  25. 6 5
      frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart
  26. 0 83
      frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_data_controller.dart
  27. 2 2
      frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart
  28. 2 2
      frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart
  29. 16 10
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart
  30. 0 7
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/prelude.dart
  31. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart
  32. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart
  33. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart
  34. 0 2
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart
  35. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart
  36. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart
  37. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart
  38. 0 3
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart
  39. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart
  40. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart
  41. 0 2
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart
  42. 0 2
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart
  43. 1 3
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart
  44. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart
  45. 5 7
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart
  46. 10 12
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart
  47. 0 3
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart
  48. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart
  49. 0 3
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart
  50. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart
  51. 0 1
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart
  52. 16 16
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/checkbox_card_cell_bloc.dart
  53. 14 14
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/date_card_cell_bloc.dart
  54. 15 15
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/number_card_cell_bloc.dart
  55. 15 15
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/select_option_card_cell_bloc.dart
  56. 16 16
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/text_card_cell_bloc.dart
  57. 15 15
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/url_card_cell_bloc.dart
  58. 8 8
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/board_number_cell.dart
  59. 40 30
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart
  60. 23 17
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart
  61. 32 37
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart
  62. 32 7
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart
  63. 13 13
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/checkbox_card_cell.dart
  64. 10 9
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/checklist_card_cell.dart
  65. 12 13
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/date_card_cell.dart
  66. 68 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/number_card_cell.dart
  67. 26 26
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/select_option_card_cell.dart
  68. 21 23
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/text_card_cell.dart
  69. 12 13
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/url_card_cell.dart
  70. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/container/accessory.dart
  71. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/container/card_container.dart
  72. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/define.dart
  73. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart
  74. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_decoration.dart
  75. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_shortcuts.dart
  76. 10 10
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart
  77. 5 5
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/cell_container.dart
  78. 6 5
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checkbox_cell/checkbox_cell.dart
  79. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checkbox_cell/checkbox_cell_bloc.dart
  80. 5 5
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell.dart
  81. 4 3
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart
  82. 3 7
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart
  83. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart
  84. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart
  85. 77 65
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cal_bloc.dart
  86. 4 5
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart
  87. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell_bloc.dart
  88. 37 31
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart
  89. 6 6
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/number_cell/number_cell.dart
  90. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/number_cell/number_cell_bloc.dart
  91. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart
  92. 5 6
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_cell.dart
  93. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_cell_bloc.dart
  94. 4 7
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart
  95. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart
  96. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart
  97. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart
  98. 6 6
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/text_cell/text_cell.dart
  99. 0 0
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart
  100. 1 1
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/cell_editor.dart

+ 2 - 1
frontend/appflowy_flutter/assets/translations/en.json

@@ -372,6 +372,7 @@
   },
   "calendar": {
     "menuName": "Calendar",
+    "defaultNewCalendarTitle": "Untitled",
     "navigation": {
       "today": "Today",
       "jumpToday": "Jump to Today",
@@ -379,4 +380,4 @@
       "nextMonth": "Next Month"
     }
   }
-}
+}

+ 3 - 3
frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart

@@ -2,9 +2,9 @@ part of 'cell_service.dart';
 
 typedef CellByFieldId = LinkedHashMap<String, CellIdentifier>;
 
-class GridBaseCell {
+class DatabaseCell {
   dynamic object;
-  GridBaseCell({
+  DatabaseCell({
     required this.object,
   });
 }
@@ -44,7 +44,7 @@ class CellCache {
     }
   }
 
-  void insert<T extends GridBaseCell>(CellCacheKey key, T value) {
+  void insert<T extends DatabaseCell>(CellCacheKey key, T value) {
     var map = _cellDataByFieldId[key.fieldId];
     if (map == null) {
       _cellDataByFieldId[key.fieldId] = {};

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart

@@ -170,7 +170,7 @@ class CellController<T, D> extends Equatable {
     _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
       _cellDataLoader.loadData().then((data) {
         if (data != null) {
-          _cellCache.insert(_cacheKey, GridBaseCell(object: data));
+          _cellCache.insert(_cacheKey, DatabaseCell(object: data));
         } else {
           _cellCache.remove(_cacheKey);
         }

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart

@@ -13,7 +13,7 @@ typedef SelectOptionCellController
     = CellController<SelectOptionCellDataPB, String>;
 typedef ChecklistCellController
     = CellController<SelectOptionCellDataPB, String>;
-typedef DateCellController = CellController<DateCellDataPB, CalendarData>;
+typedef DateCellController = CellController<DateCellDataPB, DateCellData>;
 typedef URLCellController = CellController<URLCellDataPB, String>;
 
 class CellControllerBuilder {

+ 9 - 5
frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart

@@ -27,24 +27,28 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
 }
 
 @freezed
-class CalendarData with _$CalendarData {
-  const factory CalendarData({required DateTime date, String? time}) =
-      _CalendarData;
+class DateCellData with _$DateCellData {
+  const factory DateCellData({
+    required DateTime date,
+    String? time,
+    required bool includeTime,
+  }) = _DateCellData;
 }
 
-class DateCellDataPersistence implements CellDataPersistence<CalendarData> {
+class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
   final CellIdentifier cellId;
   DateCellDataPersistence({
     required this.cellId,
   });
 
   @override
-  Future<Option<FlowyError>> save(CalendarData data) {
+  Future<Option<FlowyError>> save(DateCellData data) {
     var payload = DateChangesetPB.create()..cellPath = _makeCellPath(cellId);
 
     final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
     payload.date = date;
     payload.isUtc = data.date.isUtc;
+    payload.includeTime = data.includeTime;
 
     if (data.time != null) {
       payload.time = data.time!;

+ 290 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/database_controller.dart

@@ -0,0 +1,290 @@
+import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
+import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
+import 'package:collection/collection.dart';
+import 'dart:async';
+import 'package:dartz/dartz.dart';
+import 'database_service.dart';
+import 'defines.dart';
+import 'layout/layout_setting_listener.dart';
+import 'row/row_cache.dart';
+import 'group/group_listener.dart';
+
+typedef OnRowsChanged = void Function(
+  List<RowInfo> rowInfos,
+  RowsChangedReason,
+);
+
+typedef OnGroupByField = void Function(List<GroupPB>);
+typedef OnUpdateGroup = void Function(List<GroupPB>);
+typedef OnDeleteGroup = void Function(List<String>);
+typedef OnInsertGroup = void Function(InsertedGroupPB);
+
+class GroupCallbacks {
+  final OnGroupByField? onGroupByField;
+  final OnUpdateGroup? onUpdateGroup;
+  final OnDeleteGroup? onDeleteGroup;
+  final OnInsertGroup? onInsertGroup;
+
+  GroupCallbacks({
+    this.onGroupByField,
+    this.onUpdateGroup,
+    this.onDeleteGroup,
+    this.onInsertGroup,
+  });
+}
+
+class LayoutCallbacks {
+  final void Function(LayoutSettingPB) onLayoutChanged;
+  final void Function(LayoutSettingPB) onLoadLayout;
+
+  LayoutCallbacks({
+    required this.onLayoutChanged,
+    required this.onLoadLayout,
+  });
+}
+
+class DatabaseCallbacks {
+  OnDatabaseChanged? onDatabaseChanged;
+  OnRowsChanged? onRowsChanged;
+  OnFieldsChanged? onFieldsChanged;
+  OnFiltersChanged? onFiltersChanged;
+  DatabaseCallbacks({
+    this.onDatabaseChanged,
+    this.onRowsChanged,
+    this.onFieldsChanged,
+    this.onFiltersChanged,
+  });
+}
+
+class DatabaseController {
+  final String viewId;
+  final DatabaseBackendService _databaseBackendSvc;
+  final FieldController fieldController;
+  late DatabaseViewCache _viewCache;
+  final LayoutTypePB layoutType;
+
+  // Callbacks
+  DatabaseCallbacks? _databaseCallbacks;
+  GroupCallbacks? _groupCallbacks;
+  LayoutCallbacks? _layoutCallbacks;
+
+  // Getters
+  List<RowInfo> get rowInfos => _viewCache.rowInfos;
+  RowCache get rowCache => _viewCache.rowCache;
+
+  // Listener
+  final DatabaseGroupListener groupListener;
+  final DatabaseLayoutListener layoutListener;
+
+  DatabaseController({required ViewPB view, required this.layoutType})
+      : viewId = view.id,
+        _databaseBackendSvc = DatabaseBackendService(viewId: view.id),
+        fieldController = FieldController(viewId: view.id),
+        groupListener = DatabaseGroupListener(view.id),
+        layoutListener = DatabaseLayoutListener(view.id) {
+    _viewCache = DatabaseViewCache(
+      viewId: viewId,
+      fieldController: fieldController,
+    );
+    _listenOnRowsChanged();
+    _listenOnFieldsChanged();
+    _listenOnGroupChanged();
+    _listenOnLayoutChanged();
+  }
+
+  void addListener({
+    DatabaseCallbacks? onDatabaseChanged,
+    LayoutCallbacks? onLayoutChanged,
+    GroupCallbacks? onGroupChanged,
+  }) {
+    _layoutCallbacks = onLayoutChanged;
+    _databaseCallbacks = onDatabaseChanged;
+    _groupCallbacks = onGroupChanged;
+  }
+
+  Future<Either<Unit, FlowyError>> open() async {
+    return _databaseBackendSvc.openGrid().then((result) {
+      return result.fold(
+        (database) async {
+          _databaseCallbacks?.onDatabaseChanged?.call(database);
+          _viewCache.rowCache.setInitialRows(database.rows);
+          return await fieldController
+              .loadFields(
+            fieldIds: database.fields,
+          )
+              .then(
+            (result) {
+              return result.fold(
+                (l) => Future(() async {
+                  await _loadGroups();
+                  await _loadLayoutSetting();
+                  return left(l);
+                }),
+                (err) => right(err),
+              );
+            },
+          );
+        },
+        (err) => right(err),
+      );
+    });
+  }
+
+  Future<Either<RowPB, FlowyError>> createRow({
+    String? startRowId,
+    String? groupId,
+    void Function(RowDataBuilder builder)? withCells,
+  }) {
+    Map<String, String>? cellDataByFieldId;
+
+    if (withCells != null) {
+      final rowBuilder = RowDataBuilder();
+      withCells(rowBuilder);
+      cellDataByFieldId = rowBuilder.build();
+    }
+
+    return _databaseBackendSvc.createRow(
+      startRowId: startRowId,
+      groupId: groupId,
+      cellDataByFieldId: cellDataByFieldId,
+    );
+  }
+
+  Future<Either<Unit, FlowyError>> moveRow(RowPB fromRow,
+      {RowPB? toRow, String? groupId}) {
+    return _databaseBackendSvc.moveRow(
+      fromRowId: fromRow.id,
+      toGroupId: groupId,
+      toRowId: toRow?.id,
+    );
+  }
+
+  Future<Either<Unit, FlowyError>> moveGroup(
+      {required String fromGroupId, required String toGroupId}) {
+    return _databaseBackendSvc.moveGroup(
+      fromGroupId: fromGroupId,
+      toGroupId: toGroupId,
+    );
+  }
+
+  Future<void> updateCalenderLayoutSetting(
+      CalendarLayoutSettingsPB layoutSetting) async {
+    await _databaseBackendSvc
+        .updateLayoutSetting(calendarLayoutSetting: layoutSetting)
+        .then((result) {
+      result.fold((l) => null, (r) => Log.error(r));
+    });
+  }
+
+  Future<void> dispose() async {
+    await _databaseBackendSvc.closeView();
+    await fieldController.dispose();
+    await groupListener.stop();
+  }
+
+  Future<void> _loadGroups() async {
+    final result = await _databaseBackendSvc.loadGroups();
+    return Future(
+      () => result.fold(
+        (groups) {
+          _groupCallbacks?.onGroupByField?.call(groups.items);
+        },
+        (err) => Log.error(err),
+      ),
+    );
+  }
+
+  Future<void> _loadLayoutSetting() async {
+    _databaseBackendSvc.getLayoutSetting(layoutType).then((result) {
+      result.fold(
+        (l) {
+          _layoutCallbacks?.onLoadLayout(l);
+        },
+        (r) => Log.error(r),
+      );
+    });
+  }
+
+  void _listenOnRowsChanged() {
+    _viewCache.addListener(onRowsChanged: (reason) {
+      _databaseCallbacks?.onRowsChanged?.call(rowInfos, reason);
+    });
+  }
+
+  void _listenOnFieldsChanged() {
+    fieldController.addListener(
+      onReceiveFields: (fields) {
+        _databaseCallbacks?.onFieldsChanged?.call(UnmodifiableListView(fields));
+      },
+      onFilters: (filters) {
+        _databaseCallbacks?.onFiltersChanged?.call(filters);
+      },
+    );
+  }
+
+  void _listenOnGroupChanged() {
+    groupListener.start(
+      onNumOfGroupsChanged: (result) {
+        result.fold((changeset) {
+          if (changeset.updateGroups.isNotEmpty) {
+            _groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups);
+          }
+
+          if (changeset.deletedGroups.isNotEmpty) {
+            _groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups);
+          }
+
+          for (final insertedGroup in changeset.insertedGroups) {
+            _groupCallbacks?.onInsertGroup?.call(insertedGroup);
+          }
+        }, (r) => Log.error(r));
+      },
+      onGroupByNewField: (result) {
+        result.fold((groups) {
+          _groupCallbacks?.onGroupByField?.call(groups);
+        }, (r) => Log.error(r));
+      },
+    );
+  }
+
+  void _listenOnLayoutChanged() {
+    layoutListener.start(onLayoutChanged: (result) {
+      result.fold((l) {
+        _layoutCallbacks?.onLayoutChanged(l);
+      }, (r) => Log.error(r));
+    });
+  }
+}
+
+class RowDataBuilder {
+  final _cellDataByFieldId = <String, String>{};
+
+  void insertText(FieldInfo fieldInfo, String text) {
+    assert(fieldInfo.fieldType == FieldType.RichText);
+    _cellDataByFieldId[fieldInfo.field.id] = text;
+  }
+
+  void insertNumber(FieldInfo fieldInfo, int num) {
+    assert(fieldInfo.fieldType == FieldType.Number);
+    _cellDataByFieldId[fieldInfo.field.id] = num.toString();
+  }
+
+  void insertDate(FieldInfo fieldInfo, DateTime date) {
+    assert(fieldInfo.fieldType == FieldType.DateTime);
+    final timestamp = (date.millisecondsSinceEpoch ~/ 1000);
+    _cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
+  }
+
+  Map<String, String> build() {
+    return _cellDataByFieldId;
+  }
+}

+ 67 - 11
frontend/appflowy_flutter/lib/plugins/database_view/application/database_service.dart

@@ -1,4 +1,7 @@
+import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
 import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/dispatch/dispatch.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
@@ -20,25 +23,56 @@ class DatabaseBackendService {
     return DatabaseEventGetDatabase(payload).send();
   }
 
-  Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) {
+  Future<Either<RowPB, FlowyError>> createRow({
+    String? startRowId,
+    String? groupId,
+    Map<String, String>? cellDataByFieldId,
+  }) {
     var payload = CreateRowPayloadPB.create()..viewId = viewId;
-    startRowId?.fold(() => null, (id) => payload.startRowId = id);
+    if (startRowId != null) {
+      payload.startRowId = startRowId;
+    }
+
+    if (groupId != null) {
+      payload.groupId = groupId;
+    }
+
+    if (cellDataByFieldId != null && cellDataByFieldId.isNotEmpty) {
+      payload.data = RowDataPB(cellDataByFieldId: cellDataByFieldId);
+    }
+
     return DatabaseEventCreateRow(payload).send();
   }
 
-  Future<Either<RowPB, FlowyError>> createBoardCard(
-    String groupId,
-    String? startRowId,
-  ) {
-    CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
+  Future<Either<Unit, FlowyError>> moveRow({
+    required String fromRowId,
+    required String? toGroupId,
+    required String? toRowId,
+  }) {
+    var payload = MoveGroupRowPayloadPB.create()
       ..viewId = viewId
-      ..groupId = groupId;
+      ..fromRowId = fromRowId;
+    if (toGroupId != null) {
+      payload.toGroupId = toGroupId;
+    }
 
-    if (startRowId != null) {
-      payload.startRowId = startRowId;
+    if (toRowId != null) {
+      payload.toRowId = toRowId;
     }
 
-    return DatabaseEventCreateBoardCard(payload).send();
+    return DatabaseEventMoveGroupRow(payload).send();
+  }
+
+  Future<Either<Unit, FlowyError>> moveGroup({
+    required String fromGroupId,
+    required String toGroupId,
+  }) {
+    final payload = MoveGroupPayloadPB.create()
+      ..viewId = viewId
+      ..fromGroupId = fromGroupId
+      ..toGroupId = toGroupId;
+
+    return DatabaseEventMoveGroup(payload).send();
   }
 
   Future<Either<List<FieldPB>, FlowyError>> getFields(
@@ -53,6 +87,28 @@ class DatabaseBackendService {
     });
   }
 
+  Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
+      LayoutTypePB layoutType) {
+    final payload = DatabaseLayoutIdPB.create()
+      ..viewId = viewId
+      ..layout = layoutType;
+    return DatabaseEventGetLayoutSetting(payload).send();
+  }
+
+  Future<Either<Unit, FlowyError>> updateLayoutSetting(
+      {CalendarLayoutSettingsPB? calendarLayoutSetting}) {
+    final layoutSetting = LayoutSettingPB.create();
+    if (calendarLayoutSetting != null) {
+      layoutSetting.calendar = calendarLayoutSetting;
+    }
+
+    final payload = UpdateLayoutSettingPB.create()
+      ..viewId = viewId
+      ..layoutSetting = layoutSetting;
+
+    return DatabaseEventSetLayoutSetting(payload).send();
+  }
+
   Future<Either<Unit, FlowyError>> closeView() {
     final request = ViewIdPB(value: viewId);
     return FolderEventCloseView(request).send();

+ 15 - 15
frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_listener.dart → frontend/appflowy_flutter/lib/plugins/database_view/application/group/group_listener.dart

@@ -11,20 +11,20 @@ import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart
 typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
 typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
 
-class BoardListener {
+class DatabaseGroupListener {
   final String viewId;
-  PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
-  PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
+  PublishNotifier<GroupUpdateValue>? _numOfGroupsNotifier = PublishNotifier();
+  PublishNotifier<GroupByNewFieldValue>? _groupByFieldNotifier =
       PublishNotifier();
   DatabaseNotificationListener? _listener;
-  BoardListener(this.viewId);
+  DatabaseGroupListener(this.viewId);
 
   void start({
-    required void Function(GroupUpdateValue) onBoardChanged,
+    required void Function(GroupUpdateValue) onNumOfGroupsChanged,
     required void Function(GroupByNewFieldValue) onGroupByNewField,
   }) {
-    _groupUpdateNotifier?.addPublishListener(onBoardChanged);
-    _groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
+    _numOfGroupsNotifier?.addPublishListener(onNumOfGroupsChanged);
+    _groupByFieldNotifier?.addPublishListener(onGroupByNewField);
     _listener = DatabaseNotificationListener(
       objectId: viewId,
       handler: _handler,
@@ -38,16 +38,16 @@ class BoardListener {
     switch (ty) {
       case DatabaseNotification.DidUpdateGroups:
         result.fold(
-          (payload) => _groupUpdateNotifier?.value =
+          (payload) => _numOfGroupsNotifier?.value =
               left(GroupChangesetPB.fromBuffer(payload)),
-          (error) => _groupUpdateNotifier?.value = right(error),
+          (error) => _numOfGroupsNotifier?.value = right(error),
         );
         break;
       case DatabaseNotification.DidGroupByField:
         result.fold(
-          (payload) => _groupByNewFieldNotifier?.value =
+          (payload) => _groupByFieldNotifier?.value =
               left(GroupChangesetPB.fromBuffer(payload).initialGroups),
-          (error) => _groupByNewFieldNotifier?.value = right(error),
+          (error) => _groupByFieldNotifier?.value = right(error),
         );
         break;
       default:
@@ -57,10 +57,10 @@ class BoardListener {
 
   Future<void> stop() async {
     await _listener?.stop();
-    _groupUpdateNotifier?.dispose();
-    _groupUpdateNotifier = null;
+    _numOfGroupsNotifier?.dispose();
+    _numOfGroupsNotifier = null;
 
-    _groupByNewFieldNotifier?.dispose();
-    _groupByNewFieldNotifier = null;
+    _groupByFieldNotifier?.dispose();
+    _groupByFieldNotifier = null;
   }
 }

+ 51 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/layout/layout_setting_listener.dart

@@ -0,0 +1,51 @@
+import 'dart:typed_data';
+
+import 'package:appflowy/core/grid_notification.dart';
+import 'package:flowy_infra/notifier.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
+import 'package:dartz/dartz.dart';
+
+typedef LayoutSettingsValue<T> = Either<T, FlowyError>;
+
+class DatabaseLayoutListener {
+  final String viewId;
+  PublishNotifier<LayoutSettingsValue<LayoutSettingPB>>? _settingNotifier =
+      PublishNotifier();
+  DatabaseNotificationListener? _listener;
+  DatabaseLayoutListener(this.viewId);
+
+  void start({
+    required void Function(LayoutSettingsValue<LayoutSettingPB>)
+        onLayoutChanged,
+  }) {
+    _settingNotifier?.addPublishListener(onLayoutChanged);
+    _listener = DatabaseNotificationListener(
+      objectId: viewId,
+      handler: _handler,
+    );
+  }
+
+  void _handler(
+    DatabaseNotification ty,
+    Either<Uint8List, FlowyError> result,
+  ) {
+    switch (ty) {
+      case DatabaseNotification.DidUpdateLayoutSettings:
+        result.fold(
+          (payload) => _settingNotifier?.value =
+              left(LayoutSettingPB.fromBuffer(payload)),
+          (error) => _settingNotifier?.value = right(error),
+        );
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> stop() async {
+    await _listener?.stop();
+    _settingNotifier?.dispose();
+    _settingNotifier = null;
+  }
+}

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart

@@ -61,7 +61,7 @@ class RowCache {
     });
   }
 
-  void initializeRows(List<RowPB> rows) {
+  void setInitialRows(List<RowPB> rows) {
     for (final row in rows) {
       final rowInfo = buildGridRow(row);
       _rowList.add(rowInfo);

+ 8 - 6
frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart

@@ -4,25 +4,27 @@ import 'row_cache.dart';
 
 typedef OnRowChanged = void Function(CellByFieldId, RowsChangedReason);
 
-class RowDataController {
-  final RowInfo rowInfo;
+class RowController {
+  final String rowId;
+  final String viewId;
   final List<VoidCallback> _onRowChangedListeners = [];
   final RowCache _rowCache;
 
   get cellCache => _rowCache.cellCache;
 
-  RowDataController({
-    required this.rowInfo,
+  RowController({
+    required this.rowId,
+    required this.viewId,
     required RowCache rowCache,
   }) : _rowCache = rowCache;
 
   CellByFieldId loadData() {
-    return _rowCache.loadGridCells(rowInfo.rowPB.id);
+    return _rowCache.loadGridCells(rowId);
   }
 
   void addListener({OnRowChanged? onRowChanged}) {
     _onRowChangedListeners.add(_rowCache.addListener(
-      rowId: rowInfo.rowPB.id,
+      rowId: rowId,
       onCellUpdated: onRowChanged,
     ));
   }

+ 0 - 51
frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_service.dart

@@ -1,8 +1,6 @@
-import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
 import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/dispatch/dispatch.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
 
 class RowBackendService {
@@ -44,52 +42,3 @@ class RowBackendService {
     return DatabaseEventDuplicateRow(payload).send();
   }
 }
-
-class GroupBackendService {
-  final String viewId;
-
-  GroupBackendService({
-    required this.viewId,
-  });
-
-  Future<Either<Unit, FlowyError>> moveRow({
-    required String fromRowId,
-    required String toRowId,
-  }) {
-    var payload = MoveRowPayloadPB.create()
-      ..viewId = viewId
-      ..fromRowId = fromRowId
-      ..toRowId = toRowId;
-
-    return DatabaseEventMoveRow(payload).send();
-  }
-
-  Future<Either<Unit, FlowyError>> moveGroupRow({
-    required String fromRowId,
-    required String toGroupId,
-    required String? toRowId,
-  }) {
-    var payload = MoveGroupRowPayloadPB.create()
-      ..viewId = viewId
-      ..fromRowId = fromRowId
-      ..toGroupId = toGroupId;
-
-    if (toRowId != null) {
-      payload.toRowId = toRowId;
-    }
-
-    return DatabaseEventMoveGroupRow(payload).send();
-  }
-
-  Future<Either<Unit, FlowyError>> moveGroup({
-    required String fromGroupId,
-    required String toGroupId,
-  }) {
-    final payload = MoveGroupPayloadPB.create()
-      ..viewId = viewId
-      ..fromGroupId = fromGroupId
-      ..toGroupId = toGroupId;
-
-    return DatabaseEventMoveGroup(payload).send();
-  }
-}

+ 47 - 66
frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart

@@ -13,25 +13,25 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 
 import '../../application/field/field_controller.dart';
 import '../../application/row/row_cache.dart';
-import '../../application/row/row_service.dart';
-import 'board_data_controller.dart';
+import '../../application/database_controller.dart';
 import 'group_controller.dart';
 
 part 'board_bloc.freezed.dart';
 
 class BoardBloc extends Bloc<BoardEvent, BoardState> {
-  final BoardDataController _boardDataController;
+  final DatabaseController _databaseController;
   late final AppFlowyBoardController boardController;
-  final GroupBackendService _groupBackendSvc;
   final LinkedHashMap<String, GroupController> groupControllers =
       LinkedHashMap();
 
-  FieldController get fieldController => _boardDataController.fieldController;
-  String get viewId => _boardDataController.viewId;
+  FieldController get fieldController => _databaseController.fieldController;
+  String get viewId => _databaseController.viewId;
 
   BoardBloc({required ViewPB view})
-      : _groupBackendSvc = GroupBackendService(viewId: view.id),
-        _boardDataController = BoardDataController(view: view),
+      : _databaseController = DatabaseController(
+          view: view,
+          layoutType: LayoutTypePB.Board,
+        ),
         super(BoardState.initial(view.id)) {
     boardController = AppFlowyBoardController(
       onMoveGroup: (
@@ -40,7 +40,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
         toGroupId,
         toIndex,
       ) {
-        _moveGroup(fromGroupId, toGroupId);
+        _databaseController.moveGroup(
+          fromGroupId: fromGroupId,
+          toGroupId: toGroupId,
+        );
       },
       onMoveGroupItem: (
         groupId,
@@ -49,7 +52,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
       ) {
         final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
         final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
-        _moveRow(fromRow, groupId, toRow);
+        if (fromRow != null) {
+          _databaseController.moveRow(
+            fromRow,
+            toRow: toRow,
+            groupId: groupId,
+          );
+        }
       },
       onMoveGroupItemToGroup: (
         fromGroupId,
@@ -59,7 +68,13 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
       ) {
         final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
         final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
-        _moveRow(fromRow, toGroupId, toRow);
+        if (fromRow != null) {
+          _databaseController.moveRow(
+            fromRow,
+            toRow: toRow,
+            groupId: toGroupId,
+          );
+        }
       },
     );
 
@@ -72,8 +87,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
           },
           createBottomRow: (groupId) async {
             final startRowId = groupControllers[groupId]?.lastRow()?.id;
-            final result = await _boardDataController.createBoardCard(
-              groupId,
+            final result = await _databaseController.createRow(
+              groupId: groupId,
               startRowId: startRowId,
             );
             result.fold(
@@ -82,7 +97,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
             );
           },
           createHeaderRow: (String groupId) async {
-            final result = await _boardDataController.createBoardCard(groupId);
+            final result =
+                await _databaseController.createRow(groupId: groupId);
             result.fold(
               (_) {},
               (err) => Log.error(err),
@@ -141,44 +157,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
     }
 
     boardController.enableGroupDragging(!isEdit);
-    // boardController.updateGroupItem(
-    //   group.groupId,
-    //   GroupItem(
-    //     row: row,
-    //     fieldInfo: fieldInfo,
-    //     isDraggable: !isEdit,
-    //   ),
-    // );
-  }
-
-  void _moveRow(RowPB? fromRow, String columnId, RowPB? toRow) {
-    if (fromRow != null) {
-      _groupBackendSvc
-          .moveGroupRow(
-        fromRowId: fromRow.id,
-        toGroupId: columnId,
-        toRowId: toRow?.id,
-      )
-          .then((result) {
-        result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
-      });
-    }
-  }
-
-  void _moveGroup(String fromGroupId, String toGroupId) {
-    _groupBackendSvc
-        .moveGroup(
-      fromGroupId: fromGroupId,
-      toGroupId: toGroupId,
-    )
-        .then((result) {
-      result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
-    });
   }
 
   @override
   Future<void> close() async {
-    await _boardDataController.dispose();
+    await _databaseController.dispose();
     for (final controller in groupControllers.values) {
       controller.dispose();
     }
@@ -204,34 +187,36 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   }
 
   RowCache? getRowCache(String blockId) {
-    return _boardDataController.rowCache;
+    return _databaseController.rowCache;
   }
 
   void _startListening() {
-    _boardDataController.addListener(
-      onDatabaseChanged: (grid) {
+    final onDatabaseChanged = DatabaseCallbacks(
+      onDatabaseChanged: (database) {
         if (!isClosed) {
-          add(BoardEvent.didReceiveGridUpdate(grid));
+          add(BoardEvent.didReceiveGridUpdate(database));
         }
       },
-      didLoadGroups: (groups) {
+    );
+    final onGroupChanged = GroupCallbacks(
+      onGroupByField: (groups) {
         if (isClosed) return;
         initializeGroups(groups);
         add(BoardEvent.didReceiveGroups(groups));
       },
-      onDeletedGroup: (groupIds) {
+      onDeleteGroup: (groupIds) {
         if (isClosed) return;
         boardController.removeGroups(groupIds);
       },
-      onInsertedGroup: (insertedGroup) {
+      onInsertGroup: (insertGroups) {
         if (isClosed) return;
-        final group = insertedGroup.group;
+        final group = insertGroups.group;
         final newGroup = initializeGroupData(group);
         final controller = initializeGroupController(group);
         groupControllers[controller.group.groupId] = (controller);
         boardController.addGroup(newGroup);
       },
-      onUpdatedGroup: (updatedGroups) {
+      onUpdateGroup: (updatedGroups) {
         if (isClosed) return;
         for (final group in updatedGroups) {
           final columnController =
@@ -239,15 +224,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
           columnController?.updateGroupName(group.desc);
         }
       },
-      onError: (err) {
-        Log.error(err);
-      },
-      onResetGroups: (groups) {
-        if (isClosed) return;
+    );
 
-        initializeGroups(groups);
-        add(BoardEvent.didReceiveGroups(groups));
-      },
+    _databaseController.addListener(
+      onDatabaseChanged: onDatabaseChanged,
+      onGroupChanged: onGroupChanged,
     );
   }
 
@@ -264,7 +245,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   }
 
   Future<void> _openGrid(Emitter<BoardState> emit) async {
-    final result = await _boardDataController.openGrid();
+    final result = await _databaseController.open();
     result.fold(
       (grid) => emit(
         state.copyWith(loadingState: GridLoadingState.finish(left(unit))),

+ 0 - 144
frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart

@@ -1,144 +0,0 @@
-import 'dart:collection';
-
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
-import 'dart:async';
-import 'package:dartz/dartz.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
-
-import '../../application/database_service.dart';
-import '../../application/defines.dart';
-import '../../application/field/field_controller.dart';
-import '../../application/row/row_cache.dart';
-import '../../application/view/view_cache.dart';
-import 'board_listener.dart';
-
-typedef DidLoadGroups = void Function(List<GroupPB>);
-typedef OnUpdatedGroup = void Function(List<GroupPB>);
-typedef OnDeletedGroup = void Function(List<String>);
-typedef OnInsertedGroup = void Function(InsertedGroupPB);
-typedef OnResetGroups = void Function(List<GroupPB>);
-
-class BoardDataController {
-  final String viewId;
-  final DatabaseBackendService _databaseSvc;
-  final FieldController fieldController;
-  final BoardListener _listener;
-  late DatabaseViewCache _viewCache;
-
-  OnFieldsChanged? _onFieldsChanged;
-  OnDatabaseChanged? _onDatabaseChanged;
-  DidLoadGroups? _didLoadGroup;
-  OnRowsChanged? _onRowsChanged;
-  OnError? _onError;
-
-  List<RowInfo> get rowInfos => _viewCache.rowInfos;
-  RowCache get rowCache => _viewCache.rowCache;
-
-  BoardDataController({required ViewPB view})
-      : viewId = view.id,
-        _listener = BoardListener(view.id),
-        _databaseSvc = DatabaseBackendService(viewId: view.id),
-        fieldController = FieldController(viewId: view.id) {
-    //
-    _viewCache = DatabaseViewCache(
-      viewId: view.id,
-      fieldController: fieldController,
-    );
-    _viewCache.addListener(onRowsChanged: (reason) {
-      _onRowsChanged?.call(rowInfos, reason);
-    });
-  }
-
-  void addListener({
-    required OnDatabaseChanged onDatabaseChanged,
-    OnFieldsChanged? onFieldsChanged,
-    required DidLoadGroups didLoadGroups,
-    OnRowsChanged? onRowsChanged,
-    required OnUpdatedGroup onUpdatedGroup,
-    required OnDeletedGroup onDeletedGroup,
-    required OnInsertedGroup onInsertedGroup,
-    required OnResetGroups onResetGroups,
-    required OnError? onError,
-  }) {
-    _onDatabaseChanged = onDatabaseChanged;
-    _onFieldsChanged = onFieldsChanged;
-    _didLoadGroup = didLoadGroups;
-    _onRowsChanged = onRowsChanged;
-    _onError = onError;
-
-    fieldController.addListener(onReceiveFields: (fields) {
-      _onFieldsChanged?.call(UnmodifiableListView(fields));
-    });
-
-    _listener.start(
-      onBoardChanged: (result) {
-        result.fold(
-          (changeset) {
-            if (changeset.updateGroups.isNotEmpty) {
-              onUpdatedGroup.call(changeset.updateGroups);
-            }
-
-            if (changeset.deletedGroups.isNotEmpty) {
-              onDeletedGroup.call(changeset.deletedGroups);
-            }
-
-            for (final insertedGroup in changeset.insertedGroups) {
-              onInsertedGroup.call(insertedGroup);
-            }
-          },
-          (e) => _onError?.call(e),
-        );
-      },
-      onGroupByNewField: (result) {
-        result.fold(
-          (groups) => onResetGroups(groups),
-          (e) => _onError?.call(e),
-        );
-      },
-    );
-  }
-
-  Future<Either<Unit, FlowyError>> openGrid() async {
-    final result = await _databaseSvc.openGrid();
-    return result.fold(
-      (grid) async {
-        _onDatabaseChanged?.call(grid);
-        return fieldController.loadFields(fieldIds: grid.fields).then((result) {
-          return result.fold(
-            (l) => Future(() async {
-              await _loadGroups();
-              _viewCache.rowCache.initializeRows(grid.rows);
-              return left(l);
-            }),
-            (err) => right(err),
-          );
-        });
-      },
-      (err) => right(err),
-    );
-  }
-
-  Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
-      {String? startRowId}) {
-    return _databaseSvc.createBoardCard(groupId, startRowId);
-  }
-
-  Future<void> dispose() async {
-    await _viewCache.dispose();
-    await _databaseSvc.closeView();
-    await fieldController.dispose();
-  }
-
-  Future<void> _loadGroups() async {
-    final result = await _databaseSvc.loadGroups();
-    return Future(
-      () => result.fold(
-        (groups) {
-          _didLoadGroup?.call(groups.items);
-        },
-        (err) => _onError?.call(err),
-      ),
-    );
-  }
-}

+ 0 - 39
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart

@@ -1,39 +0,0 @@
-import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
-import 'package:flutter/foundation.dart';
-
-import '../../../application/cell/cell_service.dart';
-import '../../../application/row/row_cache.dart';
-import '../../presentation/card/card_cell_builder.dart';
-
-typedef OnCardChanged = void Function(CellByFieldId, RowsChangedReason);
-
-class CardDataController extends BoardCellBuilderDelegate {
-  final RowPB rowPB;
-  final RowCache _rowCache;
-  final List<VoidCallback> _onCardChangedListeners = [];
-
-  CardDataController({
-    required this.rowPB,
-    required RowCache rowCache,
-  }) : _rowCache = rowCache;
-
-  CellByFieldId loadData() {
-    return _rowCache.loadGridCells(rowPB.id);
-  }
-
-  void addListener({OnCardChanged? onRowChanged}) {
-    _onCardChangedListeners.add(_rowCache.addListener(
-      rowId: rowPB.id,
-      onCellUpdated: onRowChanged,
-    ));
-  }
-
-  void dispose() {
-    for (final fn in _onCardChangedListeners) {
-      _rowCache.removeRowListener(fn);
-    }
-  }
-
-  @override
-  CellCache get cellCache => _rowCache.cellCache;
-}

+ 49 - 3
frontend/appflowy_flutter/lib/plugins/database_view/board/application/group_controller.dart

@@ -1,7 +1,11 @@
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
-import 'group_listener.dart';
+import 'dart:typed_data';
+
+import 'package:appflowy/core/grid_notification.dart';
+import 'package:flowy_infra/notifier.dart';
+import 'package:dartz/dartz.dart';
 
 typedef OnGroupError = void Function(FlowyError);
 
@@ -14,14 +18,14 @@ abstract class GroupControllerDelegate {
 
 class GroupController {
   final GroupPB group;
-  final GroupListener _listener;
+  final SingleGroupListener _listener;
   final GroupControllerDelegate delegate;
 
   GroupController({
     required String viewId,
     required this.group,
     required this.delegate,
-  }) : _listener = GroupListener(group);
+  }) : _listener = SingleGroupListener(group);
 
   RowPB? rowAtIndex(int index) {
     if (index < group.rows.length) {
@@ -81,3 +85,45 @@ class GroupController {
     _listener.stop();
   }
 }
+
+typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
+
+class SingleGroupListener {
+  final GroupPB group;
+  PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
+  DatabaseNotificationListener? _listener;
+  SingleGroupListener(this.group);
+
+  void start({
+    required void Function(UpdateGroupNotifiedValue) onGroupChanged,
+  }) {
+    _groupNotifier?.addPublishListener(onGroupChanged);
+    _listener = DatabaseNotificationListener(
+      objectId: group.groupId,
+      handler: _handler,
+    );
+  }
+
+  void _handler(
+    DatabaseNotification ty,
+    Either<Uint8List, FlowyError> result,
+  ) {
+    switch (ty) {
+      case DatabaseNotification.DidUpdateGroupRow:
+        result.fold(
+          (payload) => _groupNotifier?.value =
+              left(GroupRowsNotificationPB.fromBuffer(payload)),
+          (error) => _groupNotifier?.value = right(error),
+        );
+        break;
+      default:
+        break;
+    }
+  }
+
+  Future<void> stop() async {
+    await _listener?.stop();
+    _groupNotifier?.dispose();
+    _groupNotifier = null;
+  }
+}

+ 0 - 51
frontend/appflowy_flutter/lib/plugins/database_view/board/application/group_listener.dart

@@ -1,51 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:appflowy/core/grid_notification.dart';
-import 'package:flowy_infra/notifier.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
-import 'package:dartz/dartz.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
-
-typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
-
-class GroupListener {
-  final GroupPB group;
-  PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
-  DatabaseNotificationListener? _listener;
-  GroupListener(this.group);
-
-  void start({
-    required void Function(UpdateGroupNotifiedValue) onGroupChanged,
-  }) {
-    _groupNotifier?.addPublishListener(onGroupChanged);
-    _listener = DatabaseNotificationListener(
-      objectId: group.groupId,
-      handler: _handler,
-    );
-  }
-
-  void _handler(
-    DatabaseNotification ty,
-    Either<Uint8List, FlowyError> result,
-  ) {
-    switch (ty) {
-      case DatabaseNotification.DidUpdateGroupRow:
-        result.fold(
-          (payload) => _groupNotifier?.value =
-              left(GroupRowsNotificationPB.fromBuffer(payload)),
-          (error) => _groupNotifier?.value = right(error),
-        );
-        break;
-      default:
-        break;
-    }
-  }
-
-  Future<void> stop() async {
-    await _listener?.stop();
-    _groupNotifier?.dispose();
-    _groupNotifier = null;
-  }
-}

+ 27 - 17
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart

@@ -6,8 +6,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
 import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
 import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row_detail.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
@@ -17,13 +16,14 @@ import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
-import 'package:flutter/material.dart';
+import 'package:flutter/material.dart' hide Card;
 import 'package:flutter_bloc/flutter_bloc.dart';
 
+import '../../widgets/card/cells/card_cell.dart';
+import '../../widgets/card/card_cell_builder.dart';
+import '../../widgets/row/cell_builder.dart';
 import '../application/board_bloc.dart';
-import '../application/card/card_data_controller.dart';
-import 'card/card.dart';
-import 'card/card_cell_builder.dart';
+import '../../widgets/card/card.dart';
 import 'toolbar/board_toolbar.dart';
 
 class BoardPage extends StatelessWidget {
@@ -78,6 +78,7 @@ class BoardContent extends StatefulWidget {
 
 class _BoardContentState extends State<BoardContent> {
   late AppFlowyBoardScrollController scrollManager;
+  final cardConfiguration = CardConfiguration<String>();
 
   final config = AppFlowyBoardConfig(
     groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
@@ -86,6 +87,16 @@ class _BoardContentState extends State<BoardContent> {
   @override
   void initState() {
     scrollManager = AppFlowyBoardScrollController();
+    cardConfiguration.addSelectOptionHook((options, groupId) {
+      // The cell should hide if the option id is equal to the groupId.
+      final isInGroup =
+          options.where((element) => element.id == groupId).isNotEmpty;
+      if (isInGroup || options.isEmpty) {
+        return const SizedBox();
+      }
+      return null;
+    });
+
     super.initState();
   }
 
@@ -225,15 +236,11 @@ class _BoardContentState extends State<BoardContent> {
 
     /// Return placeholder widget if the rowCache is null.
     if (rowCache == null) return SizedBox(key: ObjectKey(groupItem));
-
+    final cellCache = rowCache.cellCache;
     final fieldController = context.read<BoardBloc>().fieldController;
     final viewId = context.read<BoardBloc>().viewId;
-    final cardController = CardDataController(
-      rowCache: rowCache,
-      rowPB: rowPB,
-    );
 
-    final cellBuilder = BoardCellBuilder(cardController);
+    final cellBuilder = CardCellBuilder<String>(cellCache);
     bool isEditing = false;
     context.read<BoardBloc>().state.editingRow.fold(
       () => null,
@@ -247,13 +254,15 @@ class _BoardContentState extends State<BoardContent> {
       key: ValueKey(groupItemId),
       margin: config.cardPadding,
       decoration: _makeBoxDecoration(context),
-      child: BoardCard(
+      child: Card<String>(
+        row: rowPB,
         viewId: viewId,
-        groupId: groupData.group.groupId,
+        rowCache: rowCache,
+        cardData: groupData.group.groupId,
         fieldId: groupItem.fieldInfo.id,
         isEditing: isEditing,
         cellBuilder: cellBuilder,
-        dataController: cardController,
+        configuration: cardConfiguration,
         openCard: (context) => _openCard(
           viewId,
           fieldController,
@@ -303,8 +312,9 @@ class _BoardContentState extends State<BoardContent> {
       rowPB: rowPB,
     );
 
-    final dataController = RowDataController(
-      rowInfo: rowInfo,
+    final dataController = RowController(
+      rowId: rowInfo.rowPB.id,
+      viewId: rowInfo.viewId,
       rowCache: rowCache,
     );
 

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_toolbar.dart

@@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flutter/material.dart';
 
 import 'board_setting.dart';

+ 179 - 54
frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_bloc.dart

@@ -1,5 +1,6 @@
+import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
 import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
-import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
@@ -9,20 +10,24 @@ import 'package:dartz/dartz.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
-import 'calendar_data_controller.dart';
+import '../../application/database_controller.dart';
+import '../../application/row/row_cache.dart';
 
 part 'calendar_bloc.freezed.dart';
 
 class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
-  final CalendarDataController _databaseDataController;
-  final EventController calendarEventsController = EventController();
+  final DatabaseController _databaseController;
 
-  FieldController get fieldController =>
-      _databaseDataController.fieldController;
-  String get databaseId => _databaseDataController.databaseId;
+  // Getters
+  String get viewId => _databaseController.viewId;
+  CellCache get cellCache => _databaseController.rowCache.cellCache;
+  RowCache get rowCache => _databaseController.rowCache;
 
   CalendarBloc({required ViewPB view})
-      : _databaseDataController = CalendarDataController(view: view),
+      : _databaseController = DatabaseController(
+          view: view,
+          layoutType: LayoutTypePB.Calendar,
+        ),
         super(CalendarState.initial(view.id)) {
     on<CalendarEvent>(
       (event, emit) async {
@@ -30,23 +35,57 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
           initial: () async {
             _startListening();
             await _openDatabase(emit);
+            _loadAllEvents();
           },
-          didReceiveCalendarSettings: (CalendarSettingsPB settings) {
+          didReceiveCalendarSettings: (CalendarLayoutSettingsPB settings) {
             emit(state.copyWith(settings: Some(settings)));
           },
           didReceiveDatabaseUpdate: (DatabasePB database) {
             emit(state.copyWith(database: Some(database)));
           },
-          didReceiveError: (FlowyError error) {
-            emit(state.copyWith(noneOrError: Some(error)));
+          didLoadAllEvents: (events) {
+            emit(state.copyWith(events: events));
+          },
+          createEvent: (DateTime date, String title) async {
+            await _createEvent(date, title);
+          },
+          didReceiveEvent: (CalendarEventData<CalendarCardData> newEvent) {
+            emit(state.copyWith(events: [...state.events, newEvent]));
+          },
+          didUpdateFieldInfos: (Map<String, FieldInfo> fieldInfoByFieldId) {
+            emit(state.copyWith(fieldInfoByFieldId: fieldInfoByFieldId));
           },
         );
       },
     );
   }
 
+  FieldInfo? _getCalendarFieldInfo(String fieldId) {
+    final fieldInfos = _databaseController.fieldController.fieldInfos;
+    final index = fieldInfos.indexWhere(
+      (element) => element.field.id == fieldId,
+    );
+    if (index != -1) {
+      return fieldInfos[index];
+    } else {
+      return null;
+    }
+  }
+
+  FieldInfo? _getTitleFieldInfo() {
+    final fieldInfos = _databaseController.fieldController.fieldInfos;
+    final index = fieldInfos.indexWhere(
+      (element) => element.field.isPrimary,
+    );
+    if (index != -1) {
+      return fieldInfos[index];
+    } else {
+      return null;
+    }
+  }
+
   Future<void> _openDatabase(Emitter<CalendarState> emit) async {
-    final result = await _databaseDataController.openDatabase();
+    final result = await _databaseController.open();
     result.fold(
       (database) => emit(
         state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
@@ -57,60 +96,145 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
     );
   }
 
-  RowCache? getRowCache(String blockId) {
-    return _databaseDataController.rowCache;
+  Future<void> _createEvent(DateTime date, String title) async {
+    state.settings.fold(
+      () => null,
+      (settings) async {
+        final dateField = _getCalendarFieldInfo(settings.layoutFieldId);
+        final titleField = _getTitleFieldInfo();
+        if (dateField != null && titleField != null) {
+          final result = await _databaseController.createRow(
+            withCells: (builder) {
+              builder.insertDate(dateField, date);
+              builder.insertText(titleField, title);
+            },
+          );
+
+          result.fold(
+            (newRow) => _loadEvent(newRow.id),
+            (err) => Log.error(err),
+          );
+        }
+      },
+    );
+  }
+
+  Future<void> _loadEvent(String rowId) async {
+    final payload = RowIdPB(viewId: viewId, rowId: rowId);
+    DatabaseEventGetCalendarEvent(payload).send().then((result) {
+      result.fold(
+        (eventPB) {
+          final calendarEvent = _calendarEventDataFromEventPB(eventPB);
+          if (calendarEvent != null) {
+            add(CalendarEvent.didReceiveEvent(calendarEvent));
+          }
+        },
+        (r) => Log.error(r),
+      );
+    });
+  }
+
+  Future<void> _loadAllEvents() async {
+    final payload = CalendarEventRequestPB.create()..viewId = viewId;
+    DatabaseEventGetAllCalendarEvents(payload).send().then((result) {
+      result.fold(
+        (events) {
+          if (!isClosed) {
+            final calendarEvents = <CalendarEventData<CalendarCardData>>[];
+            for (final eventPB in events.items) {
+              final calendarEvent = _calendarEventDataFromEventPB(eventPB);
+              if (calendarEvent != null) {
+                calendarEvents.add(calendarEvent);
+              }
+            }
+
+            add(CalendarEvent.didLoadAllEvents(calendarEvents));
+          }
+        },
+        (r) => Log.error(r),
+      );
+    });
+  }
+
+  CalendarEventData<CalendarCardData>? _calendarEventDataFromEventPB(
+      CalendarEventPB eventPB) {
+    final fieldInfo = state.fieldInfoByFieldId[eventPB.titleFieldId];
+    if (fieldInfo != null) {
+      final cellId = CellIdentifier(
+        viewId: viewId,
+        rowId: eventPB.rowId,
+        fieldInfo: fieldInfo,
+      );
+
+      final eventData = CalendarCardData(
+        event: eventPB,
+        cellId: cellId,
+      );
+
+      final date = DateTime.fromMillisecondsSinceEpoch(
+        eventPB.timestamp.toInt() * 1000,
+        isUtc: true,
+      );
+      return CalendarEventData(
+        title: eventPB.title,
+        date: date,
+        event: eventData,
+      );
+    } else {
+      return null;
+    }
   }
 
   void _startListening() {
-    _databaseDataController.addListener(
+    final onDatabaseChanged = DatabaseCallbacks(
       onDatabaseChanged: (database) {
-        if (!isClosed) return;
-
-        add(CalendarEvent.didReceiveDatabaseUpdate(database));
-      },
-      onSettingsChanged: (CalendarSettingsPB settings) {
         if (isClosed) return;
-        add(CalendarEvent.didReceiveCalendarSettings(settings));
       },
-      onArrangeWithNewField: (field) {
+      onFieldsChanged: (fieldInfos) {
         if (isClosed) return;
-        _initializeEvents(field);
-        // add(CalendarEvent.)
-      },
-      onError: (err) {
-        Log.error(err);
+        final fieldInfoByFieldId = {
+          for (var fieldInfo in fieldInfos) fieldInfo.field.id: fieldInfo
+        };
+        add(CalendarEvent.didUpdateFieldInfos(fieldInfoByFieldId));
       },
     );
-  }
-
-  void _initializeEvents(FieldPB dateField) {
-    calendarEventsController.removeWhere((element) => true);
-
-    const events = <CalendarEventData<CalendarData>>[];
 
-    // final List<CalendarEventData<CalendarData>> events = rows.map((row) {
-    // final event = CalendarEventData(
-    //   title: "",
-    //   date: row -> dateField -> value,
-    //   event: row,
-    // );
+    final onLayoutChanged = LayoutCallbacks(
+      onLayoutChanged: _didReceiveLayoutSetting,
+      onLoadLayout: _didReceiveLayoutSetting,
+    );
 
-    // return event;
-    // }).toList();
+    _databaseController.addListener(
+      onDatabaseChanged: onDatabaseChanged,
+      onLayoutChanged: onLayoutChanged,
+    );
+  }
 
-    calendarEventsController.addAll(events);
+  void _didReceiveLayoutSetting(LayoutSettingPB layoutSetting) {
+    if (layoutSetting.hasCalendar()) {
+      if (isClosed) return;
+      add(CalendarEvent.didReceiveCalendarSettings(layoutSetting.calendar));
+    }
   }
 }
 
+typedef Events = List<CalendarEventData<CalendarCardData>>;
+
 @freezed
 class CalendarEvent with _$CalendarEvent {
   const factory CalendarEvent.initial() = _InitialCalendar;
   const factory CalendarEvent.didReceiveCalendarSettings(
-      CalendarSettingsPB settings) = _DidReceiveCalendarSettings;
-  const factory CalendarEvent.didReceiveError(FlowyError error) =
-      _DidReceiveError;
+      CalendarLayoutSettingsPB settings) = _ReceiveCalendarSettings;
+  const factory CalendarEvent.didLoadAllEvents(Events events) =
+      _ReceiveCalendarEvents;
+  const factory CalendarEvent.didReceiveEvent(
+      CalendarEventData<CalendarCardData> event) = _ReceiveEvent;
+  const factory CalendarEvent.didUpdateFieldInfos(
+      Map<String, FieldInfo> fieldInfoByFieldId) = _DidUpdateFieldInfos;
+  const factory CalendarEvent.createEvent(DateTime date, String title) =
+      _CreateEvent;
   const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
-      _DidReceiveDatabaseUpdate;
+      _ReceiveDatabaseUpdate;
 }
 
 @freezed
@@ -118,9 +242,9 @@ class CalendarState with _$CalendarState {
   const factory CalendarState({
     required String databaseId,
     required Option<DatabasePB> database,
-    required Option<FieldPB> dateField,
-    required Option<List<RowInfo>> unscheduledRows,
-    required Option<CalendarSettingsPB> settings,
+    required Events events,
+    required Map<String, FieldInfo> fieldInfoByFieldId,
+    required Option<CalendarLayoutSettingsPB> settings,
     required DatabaseLoadingState loadingState,
     required Option<FlowyError> noneOrError,
   }) = _CalendarState;
@@ -128,8 +252,8 @@ class CalendarState with _$CalendarState {
   factory CalendarState.initial(String databaseId) => CalendarState(
         database: none(),
         databaseId: databaseId,
-        dateField: none(),
-        unscheduledRows: none(),
+        fieldInfoByFieldId: {},
+        events: [],
         settings: none(),
         noneOrError: none(),
         loadingState: const _Loading(),
@@ -153,7 +277,8 @@ class CalendarEditingRow {
   });
 }
 
-class CalendarData {
-  final RowInfo rowInfo;
-  CalendarData(this.rowInfo);
+class CalendarCardData {
+  final CalendarEventPB event;
+  final CellIdentifier cellId;
+  CalendarCardData({required this.cellId, required this.event});
 }

+ 0 - 115
frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_data_controller.dart

@@ -1,115 +0,0 @@
-import 'dart:async';
-import 'dart:collection';
-
-import 'package:appflowy/plugins/database_view/application/database_service.dart';
-import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
-import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
-import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
-import 'package:dartz/dartz.dart';
-
-import 'calendar_listener.dart';
-
-typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
-typedef OnDatabaseChanged = void Function(DatabasePB);
-typedef OnSettingsChanged = void Function(CalendarSettingsPB);
-typedef OnArrangeWithNewField = void Function(FieldPB);
-
-typedef OnRowsChanged = void Function(List<RowInfo>, RowsChangedReason);
-typedef OnError = void Function(FlowyError);
-
-class CalendarDataController {
-  final String databaseId;
-  final DatabaseBackendService _databaseBackendSvc;
-  final FieldController fieldController;
-  final CalendarListener _listener;
-  late DatabaseViewCache _viewCache;
-
-  OnFieldsChanged? _onFieldsChanged;
-  OnDatabaseChanged? _onDatabaseChanged;
-  OnRowsChanged? _onRowsChanged;
-  OnSettingsChanged? _onSettingsChanged;
-  OnArrangeWithNewField? _onArrangeWithNewField;
-  OnError? _onError;
-
-  List<RowInfo> get rowInfos => _viewCache.rowInfos;
-  RowCache get rowCache => _viewCache.rowCache;
-
-  CalendarDataController({required ViewPB view})
-      : databaseId = view.id,
-        _listener = CalendarListener(view.id),
-        _databaseBackendSvc = DatabaseBackendService(viewId: view.id),
-        fieldController = FieldController(viewId: view.id) {
-    _viewCache = DatabaseViewCache(
-      viewId: view.id,
-      fieldController: fieldController,
-    );
-    _viewCache.addListener(onRowsChanged: (reason) {
-      _onRowsChanged?.call(rowInfos, reason);
-    });
-  }
-
-  void addListener({
-    required OnDatabaseChanged onDatabaseChanged,
-    OnFieldsChanged? onFieldsChanged,
-    OnRowsChanged? onRowsChanged,
-    required OnSettingsChanged? onSettingsChanged,
-    required OnArrangeWithNewField? onArrangeWithNewField,
-    required OnError? onError,
-  }) {
-    _onDatabaseChanged = onDatabaseChanged;
-    _onFieldsChanged = onFieldsChanged;
-    _onRowsChanged = onRowsChanged;
-    _onSettingsChanged = onSettingsChanged;
-    _onArrangeWithNewField = onArrangeWithNewField;
-    _onError = onError;
-
-    fieldController.addListener(onReceiveFields: (fields) {
-      _onFieldsChanged?.call(UnmodifiableListView(fields));
-    });
-
-    _listener.start(
-      onCalendarSettingsChanged: (result) {
-        result.fold(
-          (settings) => _onSettingsChanged?.call(settings),
-          (e) => _onError?.call(e),
-        );
-      },
-      onArrangeWithNewField: (result) {
-        result.fold(
-          (settings) => _onArrangeWithNewField?.call(settings),
-          (e) => _onError?.call(e),
-        );
-      },
-    );
-  }
-
-  Future<Either<Unit, FlowyError>> openDatabase() async {
-    final result = await _databaseBackendSvc.openGrid();
-    return result.fold(
-      (database) async {
-        _onDatabaseChanged?.call(database);
-        return fieldController
-            .loadFields(fieldIds: database.fields)
-            .then((result) {
-          return result.fold(
-            (l) => Future(() async {
-              _viewCache.rowCache.initializeRows(database.rows);
-              return left(l);
-            }),
-            (err) => right(err),
-          );
-        });
-      },
-      (err) => right(err),
-    );
-  }
-
-  Future<void> dispose() async {
-    await _viewCache.dispose();
-    await _databaseBackendSvc.closeView();
-    await fieldController.dispose();
-  }
-}

+ 0 - 65
frontend/appflowy_flutter/lib/plugins/database_view/calendar/application/calendar_listener.dart

@@ -1,65 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:appflowy/core/grid_notification.dart';
-import 'package:flowy_infra/notifier.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
-import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
-import 'package:dartz/dartz.dart';
-
-typedef CalendarSettingsValue = Either<CalendarSettingsPB, FlowyError>;
-typedef ArrangeWithNewField = Either<FieldPB, FlowyError>;
-
-class CalendarListener {
-  final String viewId;
-  PublishNotifier<CalendarSettingsValue>? _calendarSettingsNotifier =
-      PublishNotifier();
-  PublishNotifier<ArrangeWithNewField>? _arrangeWithNewFieldNotifier =
-      PublishNotifier();
-  DatabaseNotificationListener? _listener;
-  CalendarListener(this.viewId);
-
-  void start({
-    required void Function(CalendarSettingsValue) onCalendarSettingsChanged,
-    required void Function(ArrangeWithNewField) onArrangeWithNewField,
-  }) {
-    _calendarSettingsNotifier?.addPublishListener(onCalendarSettingsChanged);
-    _arrangeWithNewFieldNotifier?.addPublishListener(onArrangeWithNewField);
-    _listener = DatabaseNotificationListener(
-      objectId: viewId,
-      handler: _handler,
-    );
-  }
-
-  void _handler(
-    DatabaseNotification ty,
-    Either<Uint8List, FlowyError> result,
-  ) {
-    switch (ty) {
-      case DatabaseNotification.DidUpdateCalendarSettings:
-        result.fold(
-          (payload) => _calendarSettingsNotifier?.value =
-              left(CalendarSettingsPB.fromBuffer(payload)),
-          (error) => _calendarSettingsNotifier?.value = right(error),
-        );
-        break;
-      case DatabaseNotification.DidArrangeCalendarWithNewField:
-        result.fold(
-          (payload) => _arrangeWithNewFieldNotifier?.value =
-              left(FieldPB.fromBuffer(payload)),
-          (error) => _arrangeWithNewFieldNotifier?.value = right(error),
-        );
-        break;
-      default:
-        break;
-    }
-  }
-
-  Future<void> stop() async {
-    await _listener?.stop();
-    _calendarSettingsNotifier?.dispose();
-    _calendarSettingsNotifier = null;
-
-    _arrangeWithNewFieldNotifier?.dispose();
-    _arrangeWithNewFieldNotifier = null;
-  }
-}

+ 1 - 2
frontend/appflowy_flutter/lib/plugins/database_view/calendar/calendar.dart

@@ -77,8 +77,7 @@ class CalendarPluginDisplay extends PluginDisplay {
       });
     });
 
-    return CalendarPage(key: ValueKey(view.id));
-    // return CalendarPage(key: ValueKey(view.id), view: view);
+    return CalendarPage(key: ValueKey(view.id), view: view);
   }
 
   @override

+ 251 - 34
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_page.dart

@@ -1,56 +1,83 @@
 import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
+import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
+import 'package:appflowy/plugins/database_view/widgets/card/card_cell_builder.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
 import 'package:calendar_view/calendar_view.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
-import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flutter/material.dart';
-import 'package:styled_widget/styled_widget.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:provider/provider.dart';
 
 import '../../grid/presentation/layout/sizes.dart';
+import '../../widgets/row/cell_builder.dart';
+import '../../widgets/row/row_detail.dart';
 import 'layout/sizes.dart';
 import 'toolbar/calendar_toolbar.dart';
 
-class CalendarPage extends StatelessWidget {
-  const CalendarPage({super.key});
+class CalendarPage extends StatefulWidget {
+  final ViewPB view;
+  const CalendarPage({required this.view, super.key});
 
   @override
-  Widget build(BuildContext context) {
-    return const CalendarContent();
-  }
-}
-
-class CalendarContent extends StatefulWidget {
-  const CalendarContent({super.key});
-
-  @override
-  State<CalendarContent> createState() => _CalendarContentState();
+  State<CalendarPage> createState() => _CalendarPageState();
 }
 
-class _CalendarContentState extends State<CalendarContent> {
-  late EventController _eventController;
+class _CalendarPageState extends State<CalendarPage> {
+  final _eventController = EventController<CalendarCardData>();
   GlobalKey<MonthViewState>? _calendarState;
+  late CalendarBloc _calendarBloc;
 
   @override
   void initState() {
-    _eventController = EventController();
     _calendarState = GlobalKey<MonthViewState>();
+    _calendarBloc = CalendarBloc(view: widget.view)
+      ..add(const CalendarEvent.initial());
+
     super.initState();
   }
 
+  @override
+  void dispose() {
+    _calendarBloc.close();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     return CalendarControllerProvider(
       controller: _eventController,
-      child: Column(
-        children: [
-          // const _ToolbarBlocAdaptor(),
-          _toolbar(),
-          _buildCalendar(_eventController),
+      child: MultiBlocProvider(
+        providers: [
+          BlocProvider<CalendarBloc>.value(
+            value: _calendarBloc,
+          )
         ],
+        child: BlocListener<CalendarBloc, CalendarState>(
+          listenWhen: (previous, current) => previous.events != current.events,
+          listener: (context, state) {
+            if (state.events.isNotEmpty) {
+              _eventController.removeWhere((element) => true);
+              _eventController.addAll(state.events);
+            }
+          },
+          child: BlocBuilder<CalendarBloc, CalendarState>(
+            builder: (context, state) {
+              return Column(
+                children: [
+                  // const _ToolbarBlocAdaptor(),
+                  _toolbar(),
+                  _buildCalendar(_eventController),
+                ],
+              );
+            },
+          ),
+        ),
       ),
     );
   }
@@ -125,9 +152,190 @@ class _CalendarContentState extends State<CalendarContent> {
     );
   }
 
-  Widget _calendarDayBuilder(date, event, isToday, isInMonth) {
+  Widget _calendarDayBuilder(
+    DateTime date,
+    List<CalendarEventData<CalendarCardData>> calenderEvents,
+    isToday,
+    isInMonth,
+  ) {
+    final builder = CardCellBuilder(_calendarBloc.cellCache);
+    final cells = calenderEvents.map((value) => value.event!).map((event) {
+      final child = builder.buildCell(cellId: event.cellId);
+
+      return FlowyHover(
+        child: GestureDetector(
+          onTap: () {
+            final dataController = RowController(
+              rowId: event.cellId.rowId,
+              viewId: widget.view.id,
+              rowCache: _calendarBloc.rowCache,
+            );
+
+            FlowyOverlay.show(
+              context: context,
+              builder: (BuildContext context) {
+                return RowDetailPage(
+                  cellBuilder:
+                      GridCellBuilder(cellCache: _calendarBloc.cellCache),
+                  dataController: dataController,
+                );
+              },
+            );
+          },
+          child: Container(
+            padding: const EdgeInsets.symmetric(horizontal: 8),
+            child: child,
+          ),
+        ),
+      );
+    }).toList();
+
+    return _CalendarCard(
+      isToday: isToday,
+      isInMonth: isInMonth,
+      date: date,
+      children: cells,
+      onCreateEvent: (date) {
+        _calendarBloc.add(
+          CalendarEvent.createEvent(
+            date,
+            LocaleKeys.calendar_defaultNewCalendarTitle.tr(),
+          ),
+        );
+      },
+    );
+  }
+}
+
+class _CalendarCard extends StatelessWidget {
+  final bool isToday;
+  final bool isInMonth;
+  final DateTime date;
+  final List<Widget> children;
+  final void Function(DateTime) onCreateEvent;
+
+  const _CalendarCard({
+    required this.isToday,
+    required this.isInMonth,
+    required this.date,
+    required this.children,
+    required this.onCreateEvent,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    Color backgroundColor = Theme.of(context).colorScheme.surface;
+    if (!isInMonth) {
+      backgroundColor = AFThemeExtension.of(context).lightGreyHover;
+    }
+
+    return ChangeNotifierProvider(
+      create: (_) => _CardEnterNotifier(),
+      builder: ((context, child) {
+        return Container(
+          color: backgroundColor,
+          child: MouseRegion(
+            cursor: SystemMouseCursors.click,
+            onEnter: (p) => notifyEnter(context, true),
+            onExit: (p) => notifyEnter(context, false),
+            child: Padding(
+              padding: const EdgeInsets.all(8.0),
+              child: Column(
+                children: [
+                  _Header(
+                    date: date,
+                    isInMonth: isInMonth,
+                    isToday: isToday,
+                    onCreate: () => onCreateEvent(date),
+                  ),
+                  ...children
+                ],
+              ),
+            ),
+          ),
+        );
+      }),
+    );
+  }
+
+  notifyEnter(BuildContext context, bool isEnter) {
+    Provider.of<_CardEnterNotifier>(
+      context,
+      listen: false,
+    ).onEnter = isEnter;
+  }
+}
+
+class _Header extends StatelessWidget {
+  final bool isToday;
+  final bool isInMonth;
+  final DateTime date;
+  final VoidCallback onCreate;
+  const _Header({
+    required this.isToday,
+    required this.isInMonth,
+    required this.date,
+    required this.onCreate,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Consumer<_CardEnterNotifier>(
+      builder: (context, notifier, _) {
+        final badge = _DayBadge(
+          isToday: isToday,
+          isInMonth: isInMonth,
+          date: date,
+        );
+        return Row(
+          children: [
+            if (notifier.onEnter) _NewEventButton(onClick: onCreate),
+            const Spacer(),
+            badge,
+          ],
+        );
+      },
+    );
+  }
+}
+
+class _NewEventButton extends StatelessWidget {
+  final VoidCallback onClick;
+  const _NewEventButton({
+    required this.onClick,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return FlowyIconButton(
+      onPressed: onClick,
+      iconPadding: EdgeInsets.zero,
+      icon: svgWidget(
+        "home/add",
+        color: Theme.of(context).colorScheme.onSurface,
+      ),
+      width: 22,
+    );
+  }
+}
+
+class _DayBadge extends StatelessWidget {
+  final bool isToday;
+  final bool isInMonth;
+  final DateTime date;
+  const _DayBadge({
+    required this.isToday,
+    required this.isInMonth,
+    required this.date,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
     Color dayTextColor = Theme.of(context).colorScheme.onSurface;
-    Color cellBackgroundColor = Theme.of(context).colorScheme.surface;
     String dayString = date.day == 1
         ? DateFormat('MMM d', context.locale.toLanguageTag()).format(date)
         : date.day.toString();
@@ -137,8 +345,8 @@ class _CalendarContentState extends State<CalendarContent> {
     }
     if (!isInMonth) {
       dayTextColor = Theme.of(context).disabledColor;
-      cellBackgroundColor = AFThemeExtension.of(context).lightGreyHover;
     }
+
     Widget day = Container(
       decoration: BoxDecoration(
         color: isToday ? Theme.of(context).colorScheme.primary : null,
@@ -151,12 +359,21 @@ class _CalendarContentState extends State<CalendarContent> {
       ),
     );
 
-    return Container(
-      color: cellBackgroundColor,
-      child: Align(
-        alignment: Alignment.topRight,
-        child: day.padding(all: 6.0),
-      ),
-    );
+    return day;
   }
 }
+
+class _CardEnterNotifier extends ChangeNotifier {
+  bool _onEnter = false;
+
+  _CardEnterNotifier();
+
+  set onEnter(bool value) {
+    if (_onEnter != value) {
+      _onEnter = value;
+      notifyListeners();
+    }
+  }
+
+  bool get onEnter => _onEnter;
+}

+ 6 - 5
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart

@@ -9,7 +9,7 @@ import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import '../../application/field/field_controller.dart';
-import 'grid_data_controller.dart';
+import '../../application/database_controller.dart';
 import 'dart:collection';
 
 part 'grid_bloc.freezed.dart';
@@ -66,10 +66,10 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   }
 
   void _startListening() {
-    databaseController.addListener(
-      onGridChanged: (grid) {
+    final onDatabaseChanged = DatabaseCallbacks(
+      onDatabaseChanged: (database) {
         if (!isClosed) {
-          add(GridEvent.didReceiveGridUpdate(grid));
+          add(GridEvent.didReceiveGridUpdate(database));
         }
       },
       onRowsChanged: (rowInfos, reason) {
@@ -83,10 +83,11 @@ class GridBloc extends Bloc<GridEvent, GridState> {
         }
       },
     );
+    databaseController.addListener(onDatabaseChanged: onDatabaseChanged);
   }
 
   Future<void> _openGrid(Emitter<GridState> emit) async {
-    final result = await databaseController.openGrid();
+    final result = await databaseController.open();
     result.fold(
       (grid) {
         emit(

+ 0 - 83
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_data_controller.dart

@@ -1,83 +0,0 @@
-import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
-import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
-import 'package:collection/collection.dart';
-import 'dart:async';
-import 'package:dartz/dartz.dart';
-import '../../application/database_service.dart';
-import '../../application/defines.dart';
-import '../../application/row/row_cache.dart';
-
-typedef OnRowsChanged = void Function(
-  List<RowInfo> rowInfos,
-  RowsChangedReason,
-);
-typedef ListenOnRowChangedCondition = bool Function();
-
-class DatabaseController {
-  final String viewId;
-  final DatabaseBackendService _databaseBackendSvc;
-  final FieldController fieldController;
-  late DatabaseViewCache _viewCache;
-
-  OnRowsChanged? _onRowChanged;
-  OnDatabaseChanged? _onGridChanged;
-  List<RowInfo> get rowInfos => _viewCache.rowInfos;
-  RowCache get rowCache => _viewCache.rowCache;
-
-  DatabaseController({required ViewPB view})
-      : viewId = view.id,
-        _databaseBackendSvc = DatabaseBackendService(viewId: view.id),
-        fieldController = FieldController(viewId: view.id) {
-    _viewCache = DatabaseViewCache(
-      viewId: viewId,
-      fieldController: fieldController,
-    );
-    _viewCache.addListener(onRowsChanged: (reason) {
-      _onRowChanged?.call(rowInfos, reason);
-    });
-  }
-
-  void addListener({
-    OnDatabaseChanged? onGridChanged,
-    OnRowsChanged? onRowsChanged,
-    OnFieldsChanged? onFieldsChanged,
-    OnFiltersChanged? onFiltersChanged,
-  }) {
-    _onGridChanged = onGridChanged;
-    _onRowChanged = onRowsChanged;
-
-    fieldController.addListener(
-      onReceiveFields: (fields) {
-        onFieldsChanged?.call(UnmodifiableListView(fields));
-      },
-      onFilters: onFiltersChanged,
-    );
-  }
-
-  Future<Either<Unit, FlowyError>> openGrid() async {
-    return _databaseBackendSvc.openGrid().then((result) {
-      return result.fold(
-        (grid) async {
-          _onGridChanged?.call(grid);
-          _viewCache.rowCache.initializeRows(grid.rows);
-          final result = await fieldController.loadFields(
-            fieldIds: grid.fields,
-          );
-          return result;
-        },
-        (err) => right(err),
-      );
-    });
-  }
-
-  Future<void> createRow() async {
-    await _databaseBackendSvc.createRow();
-  }
-
-  Future<void> dispose() async {
-    await _databaseBackendSvc.closeView();
-    await fieldController.dispose();
-  }
-}

+ 2 - 2
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart

@@ -14,11 +14,11 @@ part 'row_bloc.freezed.dart';
 
 class RowBloc extends Bloc<RowEvent, RowState> {
   final RowBackendService _rowBackendSvc;
-  final RowDataController _dataController;
+  final RowController _dataController;
 
   RowBloc({
     required RowInfo rowInfo,
-    required RowDataController dataController,
+    required RowController dataController,
   })  : _rowBackendSvc = RowBackendService(viewId: rowInfo.viewId),
         _dataController = dataController,
         super(RowState.initial(rowInfo, dataController.loadData())) {

+ 2 - 2
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart

@@ -7,7 +7,7 @@ import '../../../application/row/row_data_controller.dart';
 part 'row_detail_bloc.freezed.dart';
 
 class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
-  final RowDataController dataController;
+  final RowController dataController;
 
   RowDetailBloc({
     required this.dataController,
@@ -27,7 +27,7 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
           },
           deleteField: (_DeleteField value) {
             final fieldService = FieldBackendService(
-              viewId: dataController.rowInfo.viewId,
+              viewId: dataController.viewId,
               fieldId: value.fieldId,
             );
             fieldService.deleteField();

+ 16 - 10
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart

@@ -1,4 +1,6 @@
 import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pbenum.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
@@ -16,17 +18,16 @@ import '../../application/row/row_data_controller.dart';
 import '../../application/setting/setting_bloc.dart';
 import '../application/filter/filter_menu_bloc.dart';
 import '../application/grid_bloc.dart';
-import '../application/grid_data_controller.dart';
+import '../../application/database_controller.dart';
 import '../application/sort/sort_menu_bloc.dart';
 import 'grid_scroll.dart';
 import 'layout/layout.dart';
 import 'layout/sizes.dart';
 import 'widgets/accessory_menu.dart';
-import 'widgets/cell/cell_builder.dart';
-import 'widgets/row/grid_row.dart';
+import 'widgets/row/row.dart';
 import 'widgets/footer/grid_footer.dart';
 import 'widgets/header/grid_header.dart';
-import 'widgets/row/row_detail.dart';
+import '../../widgets/row/row_detail.dart';
 import 'widgets/shortcuts.dart';
 import 'widgets/toolbar/grid_toolbar.dart';
 
@@ -35,7 +36,10 @@ class GridPage extends StatefulWidget {
     required this.view,
     this.onDeleted,
     Key? key,
-  })  : databaseController = DatabaseController(view: view),
+  })  : databaseController = DatabaseController(
+          view: view,
+          layoutType: LayoutTypePB.Grid,
+        ),
         super(key: key);
 
   final ViewPB view;
@@ -275,14 +279,15 @@ class _GridRowsState extends State<_GridRows> {
 
     final fieldController =
         context.read<GridBloc>().databaseController.fieldController;
-    final dataController = RowDataController(
-      rowInfo: rowInfo,
+    final dataController = RowController(
+      rowId: rowInfo.rowPB.id,
+      viewId: rowInfo.viewId,
       rowCache: rowCache,
     );
 
     return SizeTransition(
       sizeFactor: animation,
-      child: GridRowWidget(
+      child: GridRow(
         rowInfo: rowInfo,
         dataController: dataController,
         cellBuilder: GridCellBuilder(cellCache: dataController.cellCache),
@@ -307,8 +312,9 @@ class _GridRowsState extends State<_GridRows> {
     RowCache rowCache,
     GridCellBuilder cellBuilder,
   ) {
-    final dataController = RowDataController(
-      rowInfo: rowInfo,
+    final dataController = RowController(
+      viewId: rowInfo.viewId,
+      rowId: rowInfo.rowPB.id,
       rowCache: rowCache,
     );
 

+ 0 - 7
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/prelude.dart

@@ -1,7 +0,0 @@
-export 'cell_builder.dart';
-export 'text_cell.dart';
-export 'number_cell.dart';
-export 'date_cell/date_cell.dart';
-export 'checkbox_cell.dart';
-export 'select_option_cell/select_option_cell.dart';
-export 'url_cell/url_cell.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart

@@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/checkbox_filter.pbenum.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart

@@ -4,7 +4,6 @@ import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/checklist_filter.pbenum.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart

@@ -8,7 +8,7 @@ import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.d
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../cell/select_option_cell/extension.dart';
+import '../../../../../../widgets/row/cells/select_option_cell/extension.dart';
 import '../../filter_info.dart';
 import 'select_option_loader.dart';
 

+ 0 - 2
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart

@@ -1,8 +1,6 @@
 import 'package:appflowy/plugins/database_view/grid/application/filter/select_option_filter_bloc.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_option_filter.pb.dart';
 import 'package:flutter/material.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart

@@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/text_filter.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_menu.dart

@@ -5,7 +5,6 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart

@@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:flutter/material.dart';

+ 0 - 3
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart

@@ -1,9 +1,6 @@
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import '../../layout/sizes.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart

@@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:dartz/dartz.dart' show Either;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:flutter/material.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart

@@ -9,7 +9,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 0 - 2
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart

@@ -6,8 +6,6 @@ import 'package:easy_localization/easy_localization.dart' hide DateFormat;
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 0 - 2
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/number.dart

@@ -4,8 +4,6 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/typ
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 1 - 3
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart

@@ -2,8 +2,6 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/sel
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -11,7 +9,7 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 
 import '../../../layout/sizes.dart';
-import '../../cell/select_option_cell/extension.dart';
+import '../../../../../widgets/row/cells/select_option_cell/extension.dart';
 import '../../common/type_option_separator.dart';
 import 'select_option_editor.dart';
 

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart

@@ -1,5 +1,5 @@
 import 'package:appflowy/plugins/database_view/application/field/type_option/edit_select_option_bloc.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/extension.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';

+ 5 - 7
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row_action_sheet.dart → frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/action.dart

@@ -12,9 +12,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 import '../../layout/sizes.dart';
 
-class GridRowActionSheet extends StatelessWidget {
+class RowActions extends StatelessWidget {
   final RowInfo rowData;
-  const GridRowActionSheet({required this.rowData, Key? key}) : super(key: key);
+  const RowActions({required this.rowData, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -24,9 +24,7 @@ class GridRowActionSheet extends StatelessWidget {
         builder: (context, state) {
           final cells = _RowAction.values
               .where((value) => value.enable())
-              .map(
-                (action) => _RowActionCell(action: action),
-              )
+              .map((action) => _ActionCell(action: action))
               .toList();
 
           //
@@ -49,9 +47,9 @@ class GridRowActionSheet extends StatelessWidget {
   }
 }
 
-class _RowActionCell extends StatelessWidget {
+class _ActionCell extends StatelessWidget {
   final _RowAction action;
-  const _RowActionCell({required this.action, Key? key}) : super(key: key);
+  const _ActionCell({required this.action, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {

+ 10 - 12
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/grid_row.dart → frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart

@@ -2,30 +2,29 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_service.dar
 import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
 import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart';
 import 'package:appflowy/plugins/database_view/grid/application/row/row_bloc.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cell_builder.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
 
+import '../../../../widgets/row/accessory/cell_accessory.dart';
 import '../../layout/sizes.dart';
-import '../cell/cell_accessory.dart';
-import '../cell/cell_container.dart';
-import '../cell/prelude.dart';
-import 'row_action_sheet.dart';
+import '../../../../widgets/row/cells/cell_container.dart';
+import 'action.dart';
 import "package:appflowy/generated/locale_keys.g.dart";
 import 'package:easy_localization/easy_localization.dart';
 
-class GridRowWidget extends StatefulWidget {
+class GridRow extends StatefulWidget {
   final RowInfo rowInfo;
-  final RowDataController dataController;
+  final RowController dataController;
   final GridCellBuilder cellBuilder;
   final void Function(BuildContext, GridCellBuilder) openDetailPage;
 
-  const GridRowWidget({
+  const GridRow({
     required this.rowInfo,
     required this.dataController,
     required this.cellBuilder,
@@ -34,10 +33,10 @@ class GridRowWidget extends StatefulWidget {
   }) : super(key: key);
 
   @override
-  State<GridRowWidget> createState() => _GridRowWidgetState();
+  State<GridRow> createState() => _GridRowState();
 }
 
-class _GridRowWidgetState extends State<GridRowWidget> {
+class _GridRowState extends State<GridRow> {
   late RowBloc _rowBloc;
 
   @override
@@ -111,8 +110,7 @@ class _RowLeadingState extends State<_RowLeading> {
       direction: PopoverDirection.rightWithCenterAligned,
       margin: const EdgeInsets.all(6),
       popupBuilder: (BuildContext popoverContext) {
-        return GridRowActionSheet(
-            rowData: context.read<RowBloc>().state.rowInfo);
+        return RowActions(rowData: context.read<RowBloc>().state.rowInfo);
       },
       child: Consumer<RegionStateNotifier>(
         builder: (context, state, _) {

+ 0 - 3
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart

@@ -9,9 +9,6 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'dart:math' as math;

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart

@@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 0 - 3
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_property.dart

@@ -6,9 +6,6 @@ import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart

@@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.dart';

+ 0 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart

@@ -4,7 +4,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 16 - 16
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/checkbox_card_cell_bloc.dart

@@ -3,16 +3,16 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import '../../../application/cell/cell_controller_builder.dart';
 
-part 'board_checkbox_cell_bloc.freezed.dart';
+part 'checkbox_card_cell_bloc.freezed.dart';
 
-class BoardCheckboxCellBloc
-    extends Bloc<BoardCheckboxCellEvent, BoardCheckboxCellState> {
+class CheckboxCardCellBloc
+    extends Bloc<CheckboxCardCellEvent, CheckboxCardCellState> {
   final CheckboxCellController cellController;
   void Function()? _onCellChangedFn;
-  BoardCheckboxCellBloc({
+  CheckboxCardCellBloc({
     required this.cellController,
-  }) : super(BoardCheckboxCellState.initial(cellController)) {
-    on<BoardCheckboxCellEvent>(
+  }) : super(CheckboxCardCellState.initial(cellController)) {
+    on<CheckboxCardCellEvent>(
       (event, emit) async {
         await event.when(
           initial: () async {
@@ -43,7 +43,7 @@ class BoardCheckboxCellBloc
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((cellContent) {
         if (!isClosed) {
-          add(BoardCheckboxCellEvent.didReceiveCellUpdate(cellContent ?? ""));
+          add(CheckboxCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
         }
       }),
     );
@@ -51,21 +51,21 @@ class BoardCheckboxCellBloc
 }
 
 @freezed
-class BoardCheckboxCellEvent with _$BoardCheckboxCellEvent {
-  const factory BoardCheckboxCellEvent.initial() = _InitialCell;
-  const factory BoardCheckboxCellEvent.select() = _Selected;
-  const factory BoardCheckboxCellEvent.didReceiveCellUpdate(
-      String cellContent) = _DidReceiveCellUpdate;
+class CheckboxCardCellEvent with _$CheckboxCardCellEvent {
+  const factory CheckboxCardCellEvent.initial() = _InitialCell;
+  const factory CheckboxCardCellEvent.select() = _Selected;
+  const factory CheckboxCardCellEvent.didReceiveCellUpdate(String cellContent) =
+      _DidReceiveCellUpdate;
 }
 
 @freezed
-class BoardCheckboxCellState with _$BoardCheckboxCellState {
-  const factory BoardCheckboxCellState({
+class CheckboxCardCellState with _$CheckboxCardCellState {
+  const factory CheckboxCardCellState({
     required bool isSelected,
   }) = _CheckboxCellState;
 
-  factory BoardCheckboxCellState.initial(TextCellController context) {
-    return BoardCheckboxCellState(
+  factory CheckboxCardCellState.initial(TextCellController context) {
+    return CheckboxCardCellState(
         isSelected: _isSelected(context.getCellData()));
   }
 }

+ 14 - 14
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_date_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/date_card_cell_bloc.dart

@@ -5,15 +5,15 @@ import 'dart:async';
 
 import '../../../application/cell/cell_controller_builder.dart';
 import '../../../application/field/field_controller.dart';
-part 'board_date_cell_bloc.freezed.dart';
+part 'date_card_cell_bloc.freezed.dart';
 
-class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
+class DateCardCellBloc extends Bloc<DateCardCellEvent, DateCardCellState> {
   final DateCellController cellController;
   void Function()? _onCellChangedFn;
 
-  BoardDateCellBloc({required this.cellController})
-      : super(BoardDateCellState.initial(cellController)) {
-    on<BoardDateCellEvent>(
+  DateCardCellBloc({required this.cellController})
+      : super(DateCardCellState.initial(cellController)) {
+    on<DateCardCellEvent>(
       (event, emit) async {
         event.when(
           initial: () => _startListening(),
@@ -40,7 +40,7 @@ class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((data) {
         if (!isClosed) {
-          add(BoardDateCellEvent.didReceiveCellUpdate(data));
+          add(DateCardCellEvent.didReceiveCellUpdate(data));
         }
       }),
     );
@@ -48,24 +48,24 @@ class BoardDateCellBloc extends Bloc<BoardDateCellEvent, BoardDateCellState> {
 }
 
 @freezed
-class BoardDateCellEvent with _$BoardDateCellEvent {
-  const factory BoardDateCellEvent.initial() = _InitialCell;
-  const factory BoardDateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
+class DateCardCellEvent with _$DateCardCellEvent {
+  const factory DateCardCellEvent.initial() = _InitialCell;
+  const factory DateCardCellEvent.didReceiveCellUpdate(DateCellDataPB? data) =
       _DidReceiveCellUpdate;
 }
 
 @freezed
-class BoardDateCellState with _$BoardDateCellState {
-  const factory BoardDateCellState({
+class DateCardCellState with _$DateCardCellState {
+  const factory DateCardCellState({
     required DateCellDataPB? data,
     required String dateStr,
     required FieldInfo fieldInfo,
-  }) = _BoardDateCellState;
+  }) = _DateCardCellState;
 
-  factory BoardDateCellState.initial(DateCellController context) {
+  factory DateCardCellState.initial(DateCellController context) {
     final cellData = context.getCellData();
 
-    return BoardDateCellState(
+    return DateCardCellState(
       fieldInfo: context.fieldInfo,
       data: cellData,
       dateStr: _dateStrFromCellData(cellData),

+ 15 - 15
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_number_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/number_card_cell_bloc.dart

@@ -4,16 +4,16 @@ import 'dart:async';
 
 import '../../../application/cell/cell_controller_builder.dart';
 
-part 'board_number_cell_bloc.freezed.dart';
+part 'number_card_cell_bloc.freezed.dart';
 
-class BoardNumberCellBloc
-    extends Bloc<BoardNumberCellEvent, BoardNumberCellState> {
+class NumberCardCellBloc
+    extends Bloc<NumberCardCellEvent, NumberCardCellState> {
   final NumberCellController cellController;
   void Function()? _onCellChangedFn;
-  BoardNumberCellBloc({
+  NumberCardCellBloc({
     required this.cellController,
-  }) : super(BoardNumberCellState.initial(cellController)) {
-    on<BoardNumberCellEvent>(
+  }) : super(NumberCardCellState.initial(cellController)) {
+    on<NumberCardCellEvent>(
       (event, emit) async {
         await event.when(
           initial: () async {
@@ -41,7 +41,7 @@ class BoardNumberCellBloc
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((cellContent) {
         if (!isClosed) {
-          add(BoardNumberCellEvent.didReceiveCellUpdate(cellContent ?? ""));
+          add(NumberCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
         }
       }),
     );
@@ -49,20 +49,20 @@ class BoardNumberCellBloc
 }
 
 @freezed
-class BoardNumberCellEvent with _$BoardNumberCellEvent {
-  const factory BoardNumberCellEvent.initial() = _InitialCell;
-  const factory BoardNumberCellEvent.didReceiveCellUpdate(String cellContent) =
+class NumberCardCellEvent with _$NumberCardCellEvent {
+  const factory NumberCardCellEvent.initial() = _InitialCell;
+  const factory NumberCardCellEvent.didReceiveCellUpdate(String cellContent) =
       _DidReceiveCellUpdate;
 }
 
 @freezed
-class BoardNumberCellState with _$BoardNumberCellState {
-  const factory BoardNumberCellState({
+class NumberCardCellState with _$NumberCardCellState {
+  const factory NumberCardCellState({
     required String content,
-  }) = _BoardNumberCellState;
+  }) = _NumberCardCellState;
 
-  factory BoardNumberCellState.initial(TextCellController context) =>
-      BoardNumberCellState(
+  factory NumberCardCellState.initial(TextCellController context) =>
+      NumberCardCellState(
         content: context.getCellData() ?? "",
       );
 }

+ 15 - 15
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_select_option_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/select_option_card_cell_bloc.dart

@@ -4,17 +4,17 @@ import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.d
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
-part 'board_select_option_cell_bloc.freezed.dart';
+part 'select_option_card_cell_bloc.freezed.dart';
 
-class BoardSelectOptionCellBloc
-    extends Bloc<BoardSelectOptionCellEvent, BoardSelectOptionCellState> {
+class SelectOptionCardCellBloc
+    extends Bloc<SelectOptionCardCellEvent, SelectOptionCardCellState> {
   final SelectOptionCellController cellController;
   void Function()? _onCellChangedFn;
 
-  BoardSelectOptionCellBloc({
+  SelectOptionCardCellBloc({
     required this.cellController,
-  }) : super(BoardSelectOptionCellState.initial(cellController)) {
-    on<BoardSelectOptionCellEvent>(
+  }) : super(SelectOptionCardCellState.initial(cellController)) {
+    on<SelectOptionCardCellEvent>(
       (event, emit) async {
         await event.when(
           initial: () async {
@@ -42,7 +42,7 @@ class BoardSelectOptionCellBloc
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((selectOptionContext) {
         if (!isClosed) {
-          add(BoardSelectOptionCellEvent.didReceiveOptions(
+          add(SelectOptionCardCellEvent.didReceiveOptions(
             selectOptionContext?.selectOptions ?? [],
           ));
         }
@@ -52,23 +52,23 @@ class BoardSelectOptionCellBloc
 }
 
 @freezed
-class BoardSelectOptionCellEvent with _$BoardSelectOptionCellEvent {
-  const factory BoardSelectOptionCellEvent.initial() = _InitialCell;
-  const factory BoardSelectOptionCellEvent.didReceiveOptions(
+class SelectOptionCardCellEvent with _$SelectOptionCardCellEvent {
+  const factory SelectOptionCardCellEvent.initial() = _InitialCell;
+  const factory SelectOptionCardCellEvent.didReceiveOptions(
     List<SelectOptionPB> selectedOptions,
   ) = _DidReceiveOptions;
 }
 
 @freezed
-class BoardSelectOptionCellState with _$BoardSelectOptionCellState {
-  const factory BoardSelectOptionCellState({
+class SelectOptionCardCellState with _$SelectOptionCardCellState {
+  const factory SelectOptionCardCellState({
     required List<SelectOptionPB> selectedOptions,
-  }) = _BoardSelectOptionCellState;
+  }) = _SelectOptionCardCellState;
 
-  factory BoardSelectOptionCellState.initial(
+  factory SelectOptionCardCellState.initial(
       SelectOptionCellController context) {
     final data = context.getCellData();
-    return BoardSelectOptionCellState(
+    return SelectOptionCardCellState(
       selectedOptions: data?.selectOptions ?? [],
     );
   }

+ 16 - 16
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_text_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/text_card_cell_bloc.dart

@@ -3,15 +3,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 
-part 'board_text_cell_bloc.freezed.dart';
+part 'text_card_cell_bloc.freezed.dart';
 
-class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
+class TextCardCellBloc extends Bloc<TextCardCellEvent, TextCardCellState> {
   final TextCellController cellController;
   void Function()? _onCellChangedFn;
-  BoardTextCellBloc({
+  TextCardCellBloc({
     required this.cellController,
-  }) : super(BoardTextCellState.initial(cellController)) {
-    on<BoardTextCellEvent>(
+  }) : super(TextCardCellState.initial(cellController)) {
+    on<TextCardCellEvent>(
       (event, emit) async {
         await event.when(
           initial: () async {
@@ -48,7 +48,7 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((cellContent) {
         if (!isClosed) {
-          add(BoardTextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
+          add(TextCardCellEvent.didReceiveCellUpdate(cellContent ?? ""));
         }
       }),
     );
@@ -56,23 +56,23 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
 }
 
 @freezed
-class BoardTextCellEvent with _$BoardTextCellEvent {
-  const factory BoardTextCellEvent.initial() = _InitialCell;
-  const factory BoardTextCellEvent.updateText(String text) = _UpdateContent;
-  const factory BoardTextCellEvent.enableEdit(bool enabled) = _EnableEdit;
-  const factory BoardTextCellEvent.didReceiveCellUpdate(String cellContent) =
+class TextCardCellEvent with _$TextCardCellEvent {
+  const factory TextCardCellEvent.initial() = _InitialCell;
+  const factory TextCardCellEvent.updateText(String text) = _UpdateContent;
+  const factory TextCardCellEvent.enableEdit(bool enabled) = _EnableEdit;
+  const factory TextCardCellEvent.didReceiveCellUpdate(String cellContent) =
       _DidReceiveCellUpdate;
 }
 
 @freezed
-class BoardTextCellState with _$BoardTextCellState {
-  const factory BoardTextCellState({
+class TextCardCellState with _$TextCardCellState {
+  const factory TextCardCellState({
     required String content,
     required bool enableEdit,
-  }) = _BoardTextCellState;
+  }) = _TextCardCellState;
 
-  factory BoardTextCellState.initial(TextCellController context) =>
-      BoardTextCellState(
+  factory TextCardCellState.initial(TextCellController context) =>
+      TextCardCellState(
         content: context.getCellData() ?? "",
         enableEdit: false,
       );

+ 15 - 15
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_url_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/bloc/url_card_cell_bloc.dart

@@ -4,15 +4,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 
-part 'board_url_cell_bloc.freezed.dart';
+part 'url_card_cell_bloc.freezed.dart';
 
-class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
+class URLCardCellBloc extends Bloc<URLCardCellEvent, URLCardCellState> {
   final URLCellController cellController;
   void Function()? _onCellChangedFn;
-  BoardURLCellBloc({
+  URLCardCellBloc({
     required this.cellController,
-  }) : super(BoardURLCellState.initial(cellController)) {
-    on<BoardURLCellEvent>(
+  }) : super(URLCardCellState.initial(cellController)) {
+    on<URLCardCellEvent>(
       (event, emit) async {
         event.when(
           initial: () {
@@ -46,7 +46,7 @@ class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((cellData) {
         if (!isClosed) {
-          add(BoardURLCellEvent.didReceiveCellUpdate(cellData));
+          add(URLCardCellEvent.didReceiveCellUpdate(cellData));
         }
       }),
     );
@@ -54,23 +54,23 @@ class BoardURLCellBloc extends Bloc<BoardURLCellEvent, BoardURLCellState> {
 }
 
 @freezed
-class BoardURLCellEvent with _$BoardURLCellEvent {
-  const factory BoardURLCellEvent.initial() = _InitialCell;
-  const factory BoardURLCellEvent.updateURL(String url) = _UpdateURL;
-  const factory BoardURLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
+class URLCardCellEvent with _$URLCardCellEvent {
+  const factory URLCardCellEvent.initial() = _InitialCell;
+  const factory URLCardCellEvent.updateURL(String url) = _UpdateURL;
+  const factory URLCardCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) =
       _DidReceiveCellUpdate;
 }
 
 @freezed
-class BoardURLCellState with _$BoardURLCellState {
-  const factory BoardURLCellState({
+class URLCardCellState with _$URLCardCellState {
+  const factory URLCardCellState({
     required String content,
     required String url,
-  }) = _BoardURLCellState;
+  }) = _URLCardCellState;
 
-  factory BoardURLCellState.initial(URLCellController context) {
+  factory URLCardCellState.initial(URLCellController context) {
     final cellData = context.getCellData();
-    return BoardURLCellState(
+    return URLCardCellState(
       content: cellData?.content ?? "",
       url: cellData?.url ?? "",
     );

+ 8 - 8
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_number_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/board_number_cell.dart

@@ -2,7 +2,7 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../application/card/board_number_cell_bloc.dart';
+import 'bloc/number_card_cell_bloc.dart';
 import 'define.dart';
 
 class BoardNumberCell extends StatefulWidget {
@@ -16,19 +16,19 @@ class BoardNumberCell extends StatefulWidget {
   }) : super(key: key);
 
   @override
-  State<BoardNumberCell> createState() => _BoardNumberCellState();
+  State<BoardNumberCell> createState() => _NumberCardCellState();
 }
 
-class _BoardNumberCellState extends State<BoardNumberCell> {
-  late BoardNumberCellBloc _cellBloc;
+class _NumberCardCellState extends State<BoardNumberCell> {
+  late NumberCardCellBloc _cellBloc;
 
   @override
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as NumberCellController;
 
-    _cellBloc = BoardNumberCellBloc(cellController: cellController)
-      ..add(const BoardNumberCellEvent.initial());
+    _cellBloc = NumberCardCellBloc(cellController: cellController)
+      ..add(const NumberCardCellEvent.initial());
     super.initState();
   }
 
@@ -36,7 +36,7 @@ class _BoardNumberCellState extends State<BoardNumberCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<BoardNumberCellBloc, BoardNumberCellState>(
+      child: BlocBuilder<NumberCardCellBloc, NumberCardCellState>(
         buildWhen: (previous, current) => previous.content != current.content,
         builder: (context, state) {
           if (state.content.isEmpty) {
@@ -46,7 +46,7 @@ class _BoardNumberCellState extends State<BoardNumberCell> {
               alignment: Alignment.centerLeft,
               child: Padding(
                 padding: EdgeInsets.symmetric(
-                  vertical: BoardSizes.cardCellVPadding,
+                  vertical: CardSizes.cardCellVPadding,
                 ),
                 child: FlowyText.medium(
                   state.content,

+ 40 - 30
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart

@@ -1,47 +1,52 @@
-import 'package:appflowy/plugins/database_view/board/application/card/card_bloc.dart';
-import 'package:appflowy/plugins/database_view/board/application/card/card_data_controller.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row_action_sheet.dart';
+import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
+import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/action.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'board_cell.dart';
+import 'card_bloc.dart';
+import 'cells/card_cell.dart';
 import 'card_cell_builder.dart';
 import 'container/accessory.dart';
 import 'container/card_container.dart';
 
-class BoardCard extends StatefulWidget {
+class Card<CustomCardData> extends StatefulWidget {
+  final RowPB row;
   final String viewId;
-  final String groupId;
   final String fieldId;
+  final CustomCardData? cardData;
   final bool isEditing;
-  final CardDataController dataController;
-  final BoardCellBuilder cellBuilder;
+  final RowCache rowCache;
+  final CardCellBuilder<CustomCardData> cellBuilder;
   final void Function(BuildContext) openCard;
   final VoidCallback onStartEditing;
   final VoidCallback onEndEditing;
+  final CardConfiguration<CustomCardData>? configuration;
 
-  const BoardCard({
+  const Card({
+    required this.row,
     required this.viewId,
-    required this.groupId,
     required this.fieldId,
     required this.isEditing,
-    required this.dataController,
+    required this.rowCache,
     required this.cellBuilder,
     required this.openCard,
     required this.onStartEditing,
     required this.onEndEditing,
+    this.cardData,
+    this.configuration,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<BoardCard> createState() => _BoardCardState();
+  State<Card<CustomCardData>> createState() => _CardState<CustomCardData>();
 }
 
-class _BoardCardState extends State<BoardCard> {
-  late BoardCardBloc _cardBloc;
+class _CardState<T> extends State<Card<T>> {
+  late CardBloc _cardBloc;
   late EditableRowNotifier rowNotifier;
   late PopoverController popoverController;
   AccessoryType? accessoryType;
@@ -49,11 +54,12 @@ class _BoardCardState extends State<BoardCard> {
   @override
   void initState() {
     rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
-    _cardBloc = BoardCardBloc(
+    _cardBloc = CardBloc(
       viewId: widget.viewId,
       groupFieldId: widget.fieldId,
-      dataController: widget.dataController,
       isEditing: widget.isEditing,
+      row: widget.row,
+      rowCache: widget.rowCache,
     )..add(const BoardCardEvent.initial());
 
     rowNotifier.isEditing.addListener(() {
@@ -75,7 +81,7 @@ class _BoardCardState extends State<BoardCard> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cardBloc,
-      child: BlocBuilder<BoardCardBloc, BoardCardState>(
+      child: BlocBuilder<CardBloc, BoardCardState>(
         buildWhen: (previous, current) {
           // Rebuild when:
           // 1.If the length of the cells is not the same
@@ -110,11 +116,12 @@ class _BoardCardState extends State<BoardCard> {
               },
               openAccessory: _handleOpenAccessory,
               openCard: (context) => widget.openCard(context),
-              child: _CellColumn(
-                groupId: widget.groupId,
+              child: _CardContent<T>(
                 rowNotifier: rowNotifier,
                 cellBuilder: widget.cellBuilder,
                 cells: state.cells,
+                cardConfiguration: widget.configuration,
+                cardData: widget.cardData,
               ),
             ),
           );
@@ -142,8 +149,8 @@ class _BoardCardState extends State<BoardCard> {
       case AccessoryType.edit:
         throw UnimplementedError();
       case AccessoryType.more:
-        return GridRowActionSheet(
-          rowData: context.read<BoardCardBloc>().rowInfo(),
+        return RowActions(
+          rowData: context.read<CardBloc>().rowInfo(),
         );
     }
   }
@@ -156,16 +163,18 @@ class _BoardCardState extends State<BoardCard> {
   }
 }
 
-class _CellColumn extends StatelessWidget {
-  final String groupId;
-  final BoardCellBuilder cellBuilder;
+class _CardContent<CustomCardData> extends StatelessWidget {
+  final CardCellBuilder<CustomCardData> cellBuilder;
   final EditableRowNotifier rowNotifier;
   final List<BoardCellEquatable> cells;
-  const _CellColumn({
-    required this.groupId,
+  final CardConfiguration<CustomCardData>? cardConfiguration;
+  final CustomCardData? cardData;
+  const _CardContent({
     required this.rowNotifier,
     required this.cellBuilder,
     required this.cells,
+    required this.cardData,
+    this.cardConfiguration,
     Key? key,
   }) : super(key: key);
 
@@ -188,7 +197,7 @@ class _CellColumn extends StatelessWidget {
     cells.asMap().forEach(
       (int index, BoardCellEquatable cell) {
         final isEditing = index == 0 ? rowNotifier.isEditing.value : false;
-        final cellNotifier = EditableCellNotifier(isEditing: isEditing);
+        final cellNotifier = EditableCardNotifier(isEditing: isEditing);
 
         if (index == 0) {
           // Only use the first cell to receive user's input when click the edit
@@ -200,9 +209,10 @@ class _CellColumn extends StatelessWidget {
           key: cell.identifier.key(),
           padding: const EdgeInsets.only(left: 4, right: 4),
           child: cellBuilder.buildCell(
-            groupId,
-            cell.identifier,
-            cellNotifier,
+            cellId: cell.identifier,
+            cellNotifier: cellNotifier,
+            cardConfiguration: cardConfiguration,
+            cardData: cardData,
           ),
         );
 

+ 23 - 17
frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart

@@ -1,34 +1,36 @@
 import 'dart:collection';
 import 'package:equatable/equatable.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
+import 'package:flutter/foundation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import '../../../application/cell/cell_service.dart';
-import '../../../application/row/row_cache.dart';
-import '../../../application/row/row_service.dart';
-import 'card_data_controller.dart';
+
+import '../../application/cell/cell_service.dart';
+import '../../application/row/row_cache.dart';
+import '../../application/row/row_service.dart';
 
 part 'card_bloc.freezed.dart';
 
-class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
+class CardBloc extends Bloc<BoardCardEvent, BoardCardState> {
+  final RowPB row;
   final String groupFieldId;
   final RowBackendService _rowBackendSvc;
-  final CardDataController _dataController;
+  final RowCache _rowCache;
+  VoidCallback? _rowCallback;
 
-  BoardCardBloc({
+  CardBloc({
+    required this.row,
     required this.groupFieldId,
     required String viewId,
-    required CardDataController dataController,
+    required RowCache rowCache,
     required bool isEditing,
-  })  : _rowBackendSvc = RowBackendService(
-          viewId: viewId,
-        ),
-        _dataController = dataController,
+  })  : _rowBackendSvc = RowBackendService(viewId: viewId),
+        _rowCache = rowCache,
         super(
           BoardCardState.initial(
-            dataController.rowPB,
-            _makeCells(groupFieldId, dataController.loadData()),
+            row,
+            _makeCells(groupFieldId, rowCache.loadGridCells(row.id)),
             isEditing,
           ),
         ) {
@@ -54,7 +56,10 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
 
   @override
   Future<void> close() async {
-    _dataController.dispose();
+    if (_rowCallback != null) {
+      _rowCache.removeRowListener(_rowCallback!);
+      _rowCallback = null;
+    }
     return super.close();
   }
 
@@ -69,8 +74,9 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
   }
 
   Future<void> _startListening() async {
-    _dataController.addListener(
-      onRowChanged: (cellMap, reason) {
+    _rowCallback = _rowCache.addListener(
+      rowId: row.id,
+      onCellUpdated: (cellMap, reason) {
         if (!isClosed) {
           final cells = _makeCells(groupFieldId, cellMap);
           add(BoardCardEvent.didReceiveCells(cells, reason));

+ 32 - 37
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card_cell_builder.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart

@@ -2,83 +2,78 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
 import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 
-import '../../../application/cell/cell_service.dart';
-import 'board_cell.dart';
-import 'board_checkbox_cell.dart';
-import 'board_checklist_cell.dart';
-import 'board_date_cell.dart';
-import 'board_number_cell.dart';
-import 'board_select_option_cell.dart';
-import 'board_text_cell.dart';
-import 'board_url_cell.dart';
+import '../../application/cell/cell_service.dart';
+import 'cells/card_cell.dart';
+import 'cells/checkbox_card_cell.dart';
+import 'cells/checklist_card_cell.dart';
+import 'cells/date_card_cell.dart';
+import 'cells/number_card_cell.dart';
+import 'cells/select_option_card_cell.dart';
+import 'cells/text_card_cell.dart';
+import 'cells/url_card_cell.dart';
 
-abstract class BoardCellBuilderDelegate {
-  CellCache get cellCache;
-}
-
-class BoardCellBuilder {
-  final BoardCellBuilderDelegate delegate;
+// T represents as the Generic card data
+class CardCellBuilder<CustomCardData> {
+  final CellCache cellCache;
 
-  BoardCellBuilder(this.delegate);
+  CardCellBuilder(this.cellCache);
 
-  Widget buildCell(
-    String groupId,
-    CellIdentifier cellId,
-    EditableCellNotifier cellNotifier,
-  ) {
+  Widget buildCell({
+    CustomCardData? cardData,
+    required CellIdentifier cellId,
+    EditableCardNotifier? cellNotifier,
+    CardConfiguration<CustomCardData>? cardConfiguration,
+  }) {
     final cellControllerBuilder = CellControllerBuilder(
       cellId: cellId,
-      cellCache: delegate.cellCache,
+      cellCache: cellCache,
     );
 
     final key = cellId.key();
     switch (cellId.fieldType) {
       case FieldType.Checkbox:
-        return BoardCheckboxCell(
-          groupId: groupId,
+        return CheckboxCardCell(
           cellControllerBuilder: cellControllerBuilder,
           key: key,
         );
       case FieldType.DateTime:
-        return BoardDateCell(
-          groupId: groupId,
+        return DateCardCell(
           cellControllerBuilder: cellControllerBuilder,
           key: key,
         );
       case FieldType.SingleSelect:
-        return BoardSelectOptionCell(
-          groupId: groupId,
+        return SelectOptionCardCell<CustomCardData>(
+          renderHook: cardConfiguration?.renderHook[FieldType.SingleSelect],
           cellControllerBuilder: cellControllerBuilder,
+          cardData: cardData,
           key: key,
         );
       case FieldType.MultiSelect:
-        return BoardSelectOptionCell(
-          groupId: groupId,
+        return SelectOptionCardCell<CustomCardData>(
+          renderHook: cardConfiguration?.renderHook[FieldType.MultiSelect],
           cellControllerBuilder: cellControllerBuilder,
+          cardData: cardData,
           editableNotifier: cellNotifier,
           key: key,
         );
       case FieldType.Checklist:
-        return BoardChecklistCell(
+        return ChecklistCardCell(
           cellControllerBuilder: cellControllerBuilder,
           key: key,
         );
       case FieldType.Number:
-        return BoardNumberCell(
-          groupId: groupId,
+        return NumberCardCell(
           cellControllerBuilder: cellControllerBuilder,
           key: key,
         );
       case FieldType.RichText:
-        return BoardTextCell(
-          groupId: groupId,
+        return TextCardCell(
           cellControllerBuilder: cellControllerBuilder,
           editableNotifier: cellNotifier,
           key: key,
         );
       case FieldType.URL:
-        return BoardUrlCell(
-          groupId: groupId,
+        return URLCardCell(
           cellControllerBuilder: cellControllerBuilder,
           key: key,
         );

+ 32 - 7
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart

@@ -1,14 +1,39 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 
-abstract class FocusableBoardCell {
-  set becomeFocus(bool isFocus);
+typedef CellRenderHook<C, T> = Widget? Function(C cellData, T cardData);
+typedef RenderHookByFieldType<C> = Map<FieldType, CellRenderHook<dynamic, C>>;
+
+class CardConfiguration<CustomCardData> {
+  final RenderHookByFieldType<CustomCardData> renderHook = {};
+  CardConfiguration();
+
+  void addSelectOptionHook(
+    CellRenderHook<List<SelectOptionPB>, CustomCardData> hook,
+  ) {
+    selectOptionHook(cellData, cardData) {
+      if (cellData is List<SelectOptionPB>) {
+        hook(cellData, cardData);
+      }
+    }
+
+    renderHook[FieldType.SingleSelect] = selectOptionHook;
+    renderHook[FieldType.MultiSelect] = selectOptionHook;
+  }
+}
+
+abstract class CardCell<T> extends StatefulWidget {
+  final T? cardData;
+
+  const CardCell({super.key, this.cardData});
 }
 
-class EditableCellNotifier {
+class EditableCardNotifier {
   final ValueNotifier<bool> isCellEditing;
 
-  EditableCellNotifier({bool isEditing = false})
+  EditableCardNotifier({bool isEditing = false})
       : isCellEditing = ValueNotifier(isEditing);
 
   void dispose() {
@@ -17,7 +42,7 @@ class EditableCellNotifier {
 }
 
 class EditableRowNotifier {
-  final Map<EditableCellId, EditableCellNotifier> _cells = {};
+  final Map<EditableCellId, EditableCardNotifier> _cells = {};
   final ValueNotifier<bool> isEditing;
 
   EditableRowNotifier({required bool isEditing})
@@ -25,7 +50,7 @@ class EditableRowNotifier {
 
   void bindCell(
     CellIdentifier cellIdentifier,
-    EditableCellNotifier notifier,
+    EditableCardNotifier notifier,
   ) {
     assert(
       _cells.values.isEmpty,
@@ -80,7 +105,7 @@ abstract class EditableCell {
   // the row notifier receive its cells event. For example: begin editing the
   // cell or end editing the cell.
   //
-  EditableCellNotifier? get editableNotifier;
+  EditableCardNotifier? get editableNotifier;
 }
 
 class EditableCellId {

+ 13 - 13
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checkbox_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/checkbox_card_cell.dart

@@ -1,33 +1,33 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-class BoardCheckboxCell extends StatefulWidget {
-  final String groupId;
+import '../bloc/checkbox_card_cell_bloc.dart';
+import 'card_cell.dart';
+
+class CheckboxCardCell extends CardCell {
   final CellControllerBuilder cellControllerBuilder;
 
-  const BoardCheckboxCell({
-    required this.groupId,
+  const CheckboxCardCell({
     required this.cellControllerBuilder,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<BoardCheckboxCell> createState() => _BoardCheckboxCellState();
+  State<CheckboxCardCell> createState() => _CheckboxCardCellState();
 }
 
-class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
-  late BoardCheckboxCellBloc _cellBloc;
+class _CheckboxCardCellState extends State<CheckboxCardCell> {
+  late CheckboxCardCellBloc _cellBloc;
 
   @override
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as CheckboxCellController;
-    _cellBloc = BoardCheckboxCellBloc(cellController: cellController);
-    _cellBloc.add(const BoardCheckboxCellEvent.initial());
+    _cellBloc = CheckboxCardCellBloc(cellController: cellController);
+    _cellBloc.add(const CheckboxCardCellEvent.initial());
     super.initState();
   }
 
@@ -35,7 +35,7 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<BoardCheckboxCellBloc, BoardCheckboxCellState>(
+      child: BlocBuilder<CheckboxCardCellBloc, CheckboxCardCellState>(
         buildWhen: (previous, current) =>
             previous.isSelected != current.isSelected,
         builder: (context, state) {
@@ -49,8 +49,8 @@ class _BoardCheckboxCellState extends State<BoardCheckboxCell> {
               icon: icon,
               width: 20,
               onPressed: () => context
-                  .read<BoardCheckboxCellBloc>()
-                  .add(const BoardCheckboxCellEvent.select()),
+                  .read<CheckboxCardCellBloc>()
+                  .add(const CheckboxCardCellEvent.select()),
             ),
           );
         },

+ 10 - 9
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checklist_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/checklist_card_cell.dart

@@ -1,27 +1,28 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../grid/application/cell/checklist_cell_bloc.dart';
-import '../../../grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart';
+import '../../row/cells/checklist_cell/checklist_cell_bloc.dart';
+import 'card_cell.dart';
 
-class BoardChecklistCell extends StatefulWidget {
+class ChecklistCardCell extends CardCell {
   final CellControllerBuilder cellControllerBuilder;
-  const BoardChecklistCell({required this.cellControllerBuilder, Key? key})
+  const ChecklistCardCell({required this.cellControllerBuilder, Key? key})
       : super(key: key);
 
   @override
-  State<BoardChecklistCell> createState() => _BoardChecklistCellState();
+  State<ChecklistCardCell> createState() => _ChecklistCardCellState();
 }
 
-class _BoardChecklistCellState extends State<BoardChecklistCell> {
-  late ChecklistCellBloc _cellBloc;
+class _ChecklistCardCellState extends State<ChecklistCardCell> {
+  late ChecklistCardCellBloc _cellBloc;
 
   @override
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as ChecklistCellController;
-    _cellBloc = ChecklistCellBloc(cellController: cellController);
+    _cellBloc = ChecklistCardCellBloc(cellController: cellController);
     _cellBloc.add(const ChecklistCellEvent.initial());
     super.initState();
   }
@@ -30,7 +31,7 @@ class _BoardChecklistCellState extends State<BoardChecklistCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
+      child: BlocBuilder<ChecklistCardCellBloc, ChecklistCellState>(
         builder: (context, state) =>
             ChecklistProgressBar(percent: state.percent),
       ),

+ 12 - 13
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_date_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/date_card_cell.dart

@@ -1,35 +1,34 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/board/application/card/board_date_cell_bloc.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import 'define.dart';
+import '../bloc/date_card_cell_bloc.dart';
+import '../define.dart';
+import 'card_cell.dart';
 
-class BoardDateCell extends StatefulWidget {
-  final String groupId;
+class DateCardCell extends CardCell {
   final CellControllerBuilder cellControllerBuilder;
 
-  const BoardDateCell({
-    required this.groupId,
+  const DateCardCell({
     required this.cellControllerBuilder,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<BoardDateCell> createState() => _BoardDateCellState();
+  State<DateCardCell> createState() => _DateCardCellState();
 }
 
-class _BoardDateCellState extends State<BoardDateCell> {
-  late BoardDateCellBloc _cellBloc;
+class _DateCardCellState extends State<DateCardCell> {
+  late DateCardCellBloc _cellBloc;
 
   @override
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as DateCellController;
 
-    _cellBloc = BoardDateCellBloc(cellController: cellController)
-      ..add(const BoardDateCellEvent.initial());
+    _cellBloc = DateCardCellBloc(cellController: cellController)
+      ..add(const DateCardCellEvent.initial());
     super.initState();
   }
 
@@ -37,7 +36,7 @@ class _BoardDateCellState extends State<BoardDateCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<BoardDateCellBloc, BoardDateCellState>(
+      child: BlocBuilder<DateCardCellBloc, DateCardCellState>(
         buildWhen: (previous, current) => previous.dateStr != current.dateStr,
         builder: (context, state) {
           if (state.dateStr.isEmpty) {
@@ -47,7 +46,7 @@ class _BoardDateCellState extends State<BoardDateCell> {
               alignment: Alignment.centerLeft,
               child: Padding(
                 padding: EdgeInsets.symmetric(
-                  vertical: BoardSizes.cardCellVPadding,
+                  vertical: CardSizes.cardCellVPadding,
                 ),
                 child: FlowyText.regular(
                   state.dateStr,

+ 68 - 0
frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/number_card_cell.dart

@@ -0,0 +1,68 @@
+import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../bloc/number_card_cell_bloc.dart';
+import '../define.dart';
+import 'card_cell.dart';
+
+class NumberCardCell extends CardCell {
+  final CellControllerBuilder cellControllerBuilder;
+
+  const NumberCardCell({
+    required this.cellControllerBuilder,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  State<NumberCardCell> createState() => _NumberCardCellState();
+}
+
+class _NumberCardCellState extends State<NumberCardCell> {
+  late NumberCardCellBloc _cellBloc;
+
+  @override
+  void initState() {
+    final cellController =
+        widget.cellControllerBuilder.build() as NumberCellController;
+
+    _cellBloc = NumberCardCellBloc(cellController: cellController)
+      ..add(const NumberCardCellEvent.initial());
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider.value(
+      value: _cellBloc,
+      child: BlocBuilder<NumberCardCellBloc, NumberCardCellState>(
+        buildWhen: (previous, current) => previous.content != current.content,
+        builder: (context, state) {
+          if (state.content.isEmpty) {
+            return const SizedBox();
+          } else {
+            return Align(
+              alignment: Alignment.centerLeft,
+              child: Padding(
+                padding: EdgeInsets.symmetric(
+                  vertical: CardSizes.cardCellVPadding,
+                ),
+                child: FlowyText.medium(
+                  state.content,
+                  fontSize: 14,
+                ),
+              ),
+            );
+          }
+        },
+      ),
+    );
+  }
+
+  @override
+  Future<void> dispose() async {
+    _cellBloc.close();
+    super.dispose();
+  }
+}

+ 26 - 26
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_select_option_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/select_option_card_cell.dart

@@ -1,32 +1,35 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart';
+import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../../grid/presentation/widgets/cell/select_option_cell/extension.dart';
-import '../../../grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart';
-import '../../application/card/board_select_option_cell_bloc.dart';
-import 'board_cell.dart';
+import '../bloc/select_option_card_cell_bloc.dart';
+import 'card_cell.dart';
 
-class BoardSelectOptionCell extends StatefulWidget with EditableCell {
-  final String groupId;
+class SelectOptionCardCell<T> extends CardCell<T> with EditableCell {
   final CellControllerBuilder cellControllerBuilder;
+  final CellRenderHook<List<SelectOptionPB>, T>? renderHook;
+
   @override
-  final EditableCellNotifier? editableNotifier;
+  final EditableCardNotifier? editableNotifier;
 
-  const BoardSelectOptionCell({
-    required this.groupId,
+  SelectOptionCardCell({
     required this.cellControllerBuilder,
+    required T? cardData,
+    this.renderHook,
     this.editableNotifier,
     Key? key,
-  }) : super(key: key);
+  }) : super(key: key, cardData: cardData);
 
   @override
-  State<BoardSelectOptionCell> createState() => _BoardSelectOptionCellState();
+  State<SelectOptionCardCell> createState() => _SelectOptionCardCellState();
 }
 
-class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
-  late BoardSelectOptionCellBloc _cellBloc;
+class _SelectOptionCardCellState extends State<SelectOptionCardCell> {
+  late SelectOptionCardCellBloc _cellBloc;
   late PopoverController _popover;
 
   @override
@@ -34,8 +37,8 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
     _popover = PopoverController();
     final cellController =
         widget.cellControllerBuilder.build() as SelectOptionCellController;
-    _cellBloc = BoardSelectOptionCellBloc(cellController: cellController)
-      ..add(const BoardSelectOptionCellEvent.initial());
+    _cellBloc = SelectOptionCardCellBloc(cellController: cellController)
+      ..add(const SelectOptionCardCellEvent.initial());
     super.initState();
   }
 
@@ -43,12 +46,17 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<BoardSelectOptionCellBloc, BoardSelectOptionCellState>(
+      child: BlocBuilder<SelectOptionCardCellBloc, SelectOptionCardCellState>(
           buildWhen: (previous, current) {
         return previous.selectedOptions != current.selectedOptions;
       }, builder: (context, state) {
-        // Returns SizedBox if the content of the cell is empty
-        if (_isEmpty(state)) return const SizedBox();
+        Widget? custom = widget.renderHook?.call(
+          state.selectedOptions,
+          widget.cardData,
+        );
+        if (custom != null) {
+          return custom;
+        }
 
         final children = state.selectedOptions.map(
           (option) {
@@ -73,14 +81,6 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
     );
   }
 
-  bool _isEmpty(BoardSelectOptionCellState state) {
-    // The cell should hide if the option id is equal to the groupId.
-    final isInGroup = state.selectedOptions
-        .where((element) => element.id == widget.groupId)
-        .isNotEmpty;
-    return isInGroup || state.selectedOptions.isEmpty;
-  }
-
   Widget _wrapPopover(Widget child) {
     final constraints = BoxConstraints.loose(Size(
       SelectOptionCellEditor.editorPanelWidth,

+ 21 - 23
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_text_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/text_card_cell.dart

@@ -1,33 +1,31 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
-import '../../application/card/board_text_cell_bloc.dart';
-import 'board_cell.dart';
-import 'define.dart';
+import '../../row/cell_builder.dart';
+import '../bloc/text_card_cell_bloc.dart';
+import '../define.dart';
+import 'card_cell.dart';
 
-class BoardTextCell extends StatefulWidget with EditableCell {
-  final String groupId;
+class TextCardCell extends CardCell with EditableCell {
   @override
-  final EditableCellNotifier? editableNotifier;
+  final EditableCardNotifier? editableNotifier;
   final CellControllerBuilder cellControllerBuilder;
 
-  const BoardTextCell({
-    required this.groupId,
+  const TextCardCell({
     required this.cellControllerBuilder,
     this.editableNotifier,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<BoardTextCell> createState() => _BoardTextCellState();
+  State<TextCardCell> createState() => _TextCardCellState();
 }
 
-class _BoardTextCellState extends State<BoardTextCell> {
-  late BoardTextCellBloc _cellBloc;
+class _TextCardCellState extends State<TextCardCell> {
+  late TextCardCellBloc _cellBloc;
   late TextEditingController _controller;
   bool focusWhenInit = false;
   final focusNode = SingleListenerFocusNode();
@@ -36,8 +34,8 @@ class _BoardTextCellState extends State<BoardTextCell> {
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as TextCellController;
-    _cellBloc = BoardTextCellBloc(cellController: cellController)
-      ..add(const BoardTextCellEvent.initial());
+    _cellBloc = TextCardCellBloc(cellController: cellController)
+      ..add(const TextCardCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
     focusWhenInit = widget.editableNotifier?.isCellEditing.value ?? false;
     if (focusWhenInit) {
@@ -51,7 +49,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
       if (!focusNode.hasFocus) {
         focusWhenInit = false;
         widget.editableNotifier?.isCellEditing.value = false;
-        _cellBloc.add(const BoardTextCellEvent.enableEdit(false));
+        _cellBloc.add(const TextCardCellEvent.enableEdit(false));
       }
     });
     _bindEditableNotifier();
@@ -68,12 +66,12 @@ class _BoardTextCellState extends State<BoardTextCell> {
           focusNode.requestFocus();
         });
       }
-      _cellBloc.add(BoardTextCellEvent.enableEdit(isEditing));
+      _cellBloc.add(TextCardCellEvent.enableEdit(isEditing));
     });
   }
 
   @override
-  void didUpdateWidget(covariant BoardTextCell oldWidget) {
+  void didUpdateWidget(covariant TextCardCell oldWidget) {
     _bindEditableNotifier();
     super.didUpdateWidget(oldWidget);
   }
@@ -82,13 +80,13 @@ class _BoardTextCellState extends State<BoardTextCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocListener<BoardTextCellBloc, BoardTextCellState>(
+      child: BlocListener<TextCardCellBloc, TextCardCellState>(
         listener: (context, state) {
           if (_controller.text != state.content) {
             _controller.text = state.content;
           }
         },
-        child: BlocBuilder<BoardTextCellBloc, BoardTextCellState>(
+        child: BlocBuilder<TextCardCellBloc, TextCardCellState>(
           buildWhen: (previous, current) {
             if (previous.content != current.content &&
                 _controller.text == current.content &&
@@ -120,7 +118,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
   }
 
   Future<void> focusChanged() async {
-    _cellBloc.add(BoardTextCellEvent.updateText(_controller.text));
+    _cellBloc.add(TextCardCellEvent.updateText(_controller.text));
   }
 
   @override
@@ -131,10 +129,10 @@ class _BoardTextCellState extends State<BoardTextCell> {
     super.dispose();
   }
 
-  Widget _buildText(BoardTextCellState state) {
+  Widget _buildText(TextCardCellState state) {
     return Padding(
       padding: EdgeInsets.symmetric(
-        vertical: BoardSizes.cardCellVPadding,
+        vertical: CardSizes.cardCellVPadding,
       ),
       child: FlowyText.medium(
         state.content,
@@ -156,7 +154,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
         decoration: InputDecoration(
           // Magic number 4 makes the textField take up the same space as FlowyText
           contentPadding: EdgeInsets.symmetric(
-            vertical: BoardSizes.cardCellVPadding + 4,
+            vertical: CardSizes.cardCellVPadding + 4,
           ),
           border: InputBorder.none,
           isDense: true,

+ 12 - 13
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_url_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/url_card_cell.dart

@@ -4,32 +4,31 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
 
-import '../../application/card/board_url_cell_bloc.dart';
-import 'define.dart';
+import '../bloc/url_card_cell_bloc.dart';
+import '../define.dart';
+import 'card_cell.dart';
 
-class BoardUrlCell extends StatefulWidget {
-  final String groupId;
+class URLCardCell extends CardCell {
   final CellControllerBuilder cellControllerBuilder;
 
-  const BoardUrlCell({
-    required this.groupId,
+  const URLCardCell({
     required this.cellControllerBuilder,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<BoardUrlCell> createState() => _BoardUrlCellState();
+  State<URLCardCell> createState() => _URLCardCellState();
 }
 
-class _BoardUrlCellState extends State<BoardUrlCell> {
-  late BoardURLCellBloc _cellBloc;
+class _URLCardCellState extends State<URLCardCell> {
+  late URLCardCellBloc _cellBloc;
 
   @override
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as URLCellController;
-    _cellBloc = BoardURLCellBloc(cellController: cellController);
-    _cellBloc.add(const BoardURLCellEvent.initial());
+    _cellBloc = URLCardCellBloc(cellController: cellController);
+    _cellBloc.add(const URLCardCellEvent.initial());
     super.initState();
   }
 
@@ -37,7 +36,7 @@ class _BoardUrlCellState extends State<BoardUrlCell> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: _cellBloc,
-      child: BlocBuilder<BoardURLCellBloc, BoardURLCellState>(
+      child: BlocBuilder<URLCardCellBloc, URLCardCellState>(
         buildWhen: (previous, current) => previous.content != current.content,
         builder: (context, state) {
           if (state.content.isEmpty) {
@@ -47,7 +46,7 @@ class _BoardUrlCellState extends State<BoardUrlCell> {
               alignment: Alignment.centerLeft,
               child: Padding(
                 padding: EdgeInsets.symmetric(
-                  vertical: BoardSizes.cardCellVPadding,
+                  vertical: CardSizes.cardCellVPadding,
                 ),
                 child: RichText(
                   textAlign: TextAlign.left,

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/container/accessory.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/container/accessory.dart


+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/container/card_container.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/container/card_container.dart


+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/define.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/define.dart

@@ -1,3 +1,3 @@
-class BoardSizes {
+class CardSizes {
   static double get cardCellVPadding => 6;
 }

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_accessory.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_accessory.dart

@@ -9,7 +9,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
 
-import 'cell_builder.dart';
+import '../cell_builder.dart';
 
 class GridCellAccessoryBuildContext {
   final BuildContext anchorContext;

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_decoration.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_decoration.dart


+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_shortcuts.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/accessory/cell_shortcuts.dart


+ 10 - 10
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart

@@ -3,16 +3,16 @@ import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart'
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter/material.dart';
-import '../../../../application/cell/cell_service.dart';
-import 'cell_accessory.dart';
-import 'cell_shortcuts.dart';
-import 'checkbox_cell.dart';
-import 'checklist_cell/checklist_cell.dart';
-import 'date_cell/date_cell.dart';
-import 'number_cell.dart';
-import 'select_option_cell/select_option_cell.dart';
-import 'text_cell.dart';
-import 'url_cell/url_cell.dart';
+import '../../application/cell/cell_service.dart';
+import 'accessory/cell_accessory.dart';
+import 'accessory/cell_shortcuts.dart';
+import 'cells/checkbox_cell/checkbox_cell.dart';
+import 'cells/checklist_cell/checklist_cell.dart';
+import 'cells/date_cell/date_cell.dart';
+import 'cells/number_cell/number_cell.dart';
+import 'cells/select_option_cell/select_option_cell.dart';
+import 'cells/text_cell/text_cell.dart';
+import 'cells/url_cell/url_cell.dart';
 
 class GridCellBuilder {
   final CellCache cellCache;

+ 5 - 5
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_container.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/cell_container.dart

@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 
-import '../../layout/sizes.dart';
-import '../row/grid_row.dart';
-import 'cell_accessory.dart';
-import 'cell_builder.dart';
-import 'cell_shortcuts.dart';
+import '../../../grid/presentation/layout/sizes.dart';
+import '../../../grid/presentation/widgets/row/row.dart';
+import '../accessory/cell_accessory.dart';
+import '../accessory/cell_shortcuts.dart';
+import '../cell_builder.dart';
 
 class CellContainer extends StatelessWidget {
   final GridCellWidget child;

+ 6 - 5
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checkbox_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checkbox_cell/checkbox_cell.dart

@@ -1,12 +1,12 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../../application/cell/checkbox_cell_bloc.dart';
-import '../../layout/sizes.dart';
-import 'cell_builder.dart';
+import 'checkbox_cell_bloc.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../cell_builder.dart';
 
 class GridCheckboxCell extends GridCellWidget {
   final CellControllerBuilder cellControllerBuilder;
@@ -26,7 +26,8 @@ class _CheckboxCellState extends GridCellState<GridCheckboxCell> {
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as CheckboxCellController;
-    _cellBloc = getIt<CheckboxCellBloc>(param1: cellController)
+    _cellBloc = CheckboxCellBloc(
+        service: CellBackendService(), cellController: cellController)
       ..add(const CheckboxCellEvent.initial());
     super.initState();
   }

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checkbox_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checkbox_cell/checkbox_cell_bloc.dart


+ 5 - 5
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell.dart

@@ -1,12 +1,12 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../cell_builder.dart';
+import '../../cell_builder.dart';
+import 'checklist_cell_bloc.dart';
 import 'checklist_cell_editor.dart';
 import 'checklist_progress_bar.dart';
 
@@ -20,7 +20,7 @@ class GridChecklistCell extends GridCellWidget {
 }
 
 class GridChecklistCellState extends GridCellState<GridChecklistCell> {
-  late ChecklistCellBloc _cellBloc;
+  late ChecklistCardCellBloc _cellBloc;
   late final PopoverController _popover;
 
   @override
@@ -28,7 +28,7 @@ class GridChecklistCellState extends GridCellState<GridChecklistCell> {
     _popover = PopoverController();
     final cellController =
         widget.cellControllerBuilder.build() as ChecklistCellController;
-    _cellBloc = ChecklistCellBloc(cellController: cellController);
+    _cellBloc = ChecklistCardCellBloc(cellController: cellController);
     _cellBloc.add(const ChecklistCellEvent.initial());
     super.initState();
   }
@@ -54,7 +54,7 @@ class GridChecklistCellState extends GridCellState<GridChecklistCell> {
         onClose: () => widget.onCellEditing.value = false,
         child: Padding(
           padding: GridSize.cellContentInsets,
-          child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
+          child: BlocBuilder<ChecklistCardCellBloc, ChecklistCellState>(
             builder: (context, state) =>
                 ChecklistProgressBar(percent: state.percent),
           ),

+ 4 - 3
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart

@@ -1,18 +1,19 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart';
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'checklist_cell_editor_bloc.dart';
-import 'select_option_service.dart';
 part 'checklist_cell_bloc.freezed.dart';
 
-class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
+class ChecklistCardCellBloc
+    extends Bloc<ChecklistCellEvent, ChecklistCellState> {
   final ChecklistCellController cellController;
   final SelectOptionBackendService _selectOptionSvc;
   void Function()? _onCellChangedFn;
-  ChecklistCellBloc({
+  ChecklistCardCellBloc({
     required this.cellController,
   })  : _selectOptionSvc =
             SelectOptionBackendService(cellId: cellController.cellId),

+ 3 - 7
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart

@@ -2,16 +2,12 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_controller_
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
-import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../../application/cell/checklist_cell_editor_bloc.dart';
-import '../../../layout/sizes.dart';
-import '../../header/type_option/select_option_editor.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../../../grid/presentation/widgets/header/type_option/select_option_editor.dart';
+import 'checklist_cell_editor_bloc.dart';
 import 'checklist_progress_bar.dart';
 
 class GridChecklistCellEditor extends StatefulWidget {

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart

@@ -1,12 +1,12 @@
 import 'dart:async';
 
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'select_option_service.dart';
 
 part 'checklist_cell_editor_bloc.freezed.dart';
 

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart

@@ -1,6 +1,6 @@
 import 'package:appflowy/generated/locale_keys.g.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

+ 77 - 65
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cal_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cal_bloc.dart

@@ -15,19 +15,20 @@ import 'package:table_calendar/table_calendar.dart';
 import 'dart:async';
 import 'package:dartz/dartz.dart';
 import 'package:protobuf/protobuf.dart';
-import 'package:fixnum/fixnum.dart' as $fixnum;
+
 part 'date_cal_bloc.freezed.dart';
 
-class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
+class DateCellCalendarBloc
+    extends Bloc<DateCellCalendarEvent, DateCellCalendarState> {
   final DateCellController cellController;
   void Function()? _onCellChangedFn;
 
-  DateCalBloc({
+  DateCellCalendarBloc({
     required DateTypeOptionPB dateTypeOptionPB,
     required DateCellDataPB? cellData,
     required this.cellController,
-  }) : super(DateCalState.initial(dateTypeOptionPB, cellData)) {
-    on<DateCalEvent>(
+  }) : super(DateCellCalendarState.initial(dateTypeOptionPB, cellData)) {
+    on<DateCellCalendarEvent>(
       (event, emit) async {
         await event.when(
           initial: () async => _startListening(),
@@ -41,13 +42,13 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
             emit(state.copyWith(focusedDay: focusedDay));
           },
           didReceiveCellUpdate: (DateCellDataPB? cellData) {
-            final calData = calDataFromCellData(cellData);
-            final time = calData.foldRight(
+            final dateCellData = calDataFromCellData(cellData);
+            final time = dateCellData.foldRight(
                 "", (dateData, previous) => dateData.time ?? '');
-            emit(state.copyWith(calData: calData, time: time));
+            emit(state.copyWith(dateCellData: dateCellData, time: time));
           },
           setIncludeTime: (includeTime) async {
-            await _updateTypeOption(emit, includeTime: includeTime);
+            await _updateDateData(emit, includeTime: includeTime);
           },
           setDateFormat: (dateFormat) async {
             await _updateTypeOption(emit, dateFormat: dateFormat);
@@ -56,24 +57,28 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
             await _updateTypeOption(emit, timeFormat: timeFormat);
           },
           setTime: (time) async {
-            if (state.calData.isSome()) {
+            if (state.dateCellData.isSome()) {
               await _updateDateData(emit, time: time);
             }
           },
           didUpdateCalData:
-              (Option<CalendarData> data, Option<String> timeFormatError) {
+              (Option<DateCellData> data, Option<String> timeFormatError) {
             emit(state.copyWith(
-                calData: data, timeFormatError: timeFormatError));
+                dateCellData: data, timeFormatError: timeFormatError));
           },
         );
       },
     );
   }
 
-  Future<void> _updateDateData(Emitter<DateCalState> emit,
-      {DateTime? date, String? time}) {
-    final CalendarData newDateData = state.calData.fold(
-      () => CalendarData(date: date ?? DateTime.now(), time: time),
+  Future<void> _updateDateData(Emitter<DateCellCalendarState> emit,
+      {DateTime? date, String? time, bool? includeTime}) {
+    final DateCellData newDateData = state.dateCellData.fold(
+      () => DateCellData(
+        date: date ?? DateTime.now(),
+        time: time,
+        includeTime: includeTime ?? false,
+      ),
       (dateData) {
         var newDateData = dateData;
         if (date != null && !isSameDay(newDateData.date, date)) {
@@ -83,6 +88,11 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
         if (newDateData.time != time) {
           newDateData = newDateData.copyWith(time: time);
         }
+
+        if (includeTime != null && newDateData.includeTime != includeTime) {
+          newDateData = newDateData.copyWith(includeTime: includeTime);
+        }
+
         return newDateData;
       },
     );
@@ -91,15 +101,16 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
   }
 
   Future<void> _saveDateData(
-      Emitter<DateCalState> emit, CalendarData newCalData) async {
-    if (state.calData == Some(newCalData)) {
+      Emitter<DateCellCalendarState> emit, DateCellData newCalData) async {
+    if (state.dateCellData == Some(newCalData)) {
       return;
     }
 
     updateCalData(
-        Option<CalendarData> calData, Option<String> timeFormatError) {
+        Option<DateCellData> dateCellData, Option<String> timeFormatError) {
       if (!isClosed) {
-        add(DateCalEvent.didUpdateCalData(calData, timeFormatError));
+        add(DateCellCalendarEvent.didUpdateCalData(
+            dateCellData, timeFormatError));
       }
     }
 
@@ -109,7 +120,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
         (err) {
           switch (ErrorCode.valueOf(err.code)!) {
             case ErrorCode.InvalidDateTimeFormat:
-              updateCalData(state.calData, Some(timeFormatPrompt(err)));
+              updateCalData(state.dateCellData, Some(timeFormatPrompt(err)));
               break;
             default:
               Log.error(err);
@@ -148,17 +159,16 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
     _onCellChangedFn = cellController.startListening(
       onCellChanged: ((cell) {
         if (!isClosed) {
-          add(DateCalEvent.didReceiveCellUpdate(cell));
+          add(DateCellCalendarEvent.didReceiveCellUpdate(cell));
         }
       }),
     );
   }
 
   Future<void>? _updateTypeOption(
-    Emitter<DateCalState> emit, {
+    Emitter<DateCellCalendarState> emit, {
     DateFormat? dateFormat,
     TimeFormat? timeFormat,
-    bool? includeTime,
   }) async {
     state.dateTypeOptionPB.freeze();
     final newDateTypeOption = state.dateTypeOptionPB.rebuild((typeOption) {
@@ -169,10 +179,6 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
       if (timeFormat != null) {
         typeOption.timeFormat = timeFormat;
       }
-
-      if (includeTime != null) {
-        typeOption.includeTime = includeTime;
-      }
     });
 
     final result = await FieldBackendService.updateFieldTypeOption(
@@ -191,48 +197,51 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
 }
 
 @freezed
-class DateCalEvent with _$DateCalEvent {
-  const factory DateCalEvent.initial() = _Initial;
-  const factory DateCalEvent.selectDay(DateTime day) = _SelectDay;
-  const factory DateCalEvent.setCalFormat(CalendarFormat format) =
+class DateCellCalendarEvent with _$DateCellCalendarEvent {
+  const factory DateCellCalendarEvent.initial() = _Initial;
+  const factory DateCellCalendarEvent.selectDay(DateTime day) = _SelectDay;
+  const factory DateCellCalendarEvent.setCalFormat(CalendarFormat format) =
       _CalendarFormat;
-  const factory DateCalEvent.setFocusedDay(DateTime day) = _FocusedDay;
-  const factory DateCalEvent.setTimeFormat(TimeFormat timeFormat) = _TimeFormat;
-  const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
-  const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
-  const factory DateCalEvent.setTime(String time) = _Time;
-  const factory DateCalEvent.didReceiveCellUpdate(DateCellDataPB? data) =
-      _DidReceiveCellUpdate;
-  const factory DateCalEvent.didUpdateCalData(
-          Option<CalendarData> data, Option<String> timeFormatError) =
+  const factory DateCellCalendarEvent.setFocusedDay(DateTime day) = _FocusedDay;
+  const factory DateCellCalendarEvent.setTimeFormat(TimeFormat timeFormat) =
+      _TimeFormat;
+  const factory DateCellCalendarEvent.setDateFormat(DateFormat dateFormat) =
+      _DateFormat;
+  const factory DateCellCalendarEvent.setIncludeTime(bool includeTime) =
+      _IncludeTime;
+  const factory DateCellCalendarEvent.setTime(String time) = _Time;
+  const factory DateCellCalendarEvent.didReceiveCellUpdate(
+      DateCellDataPB? data) = _DidReceiveCellUpdate;
+  const factory DateCellCalendarEvent.didUpdateCalData(
+          Option<DateCellData> data, Option<String> timeFormatError) =
       _DidUpdateCalData;
 }
 
 @freezed
-class DateCalState with _$DateCalState {
-  const factory DateCalState({
+class DateCellCalendarState with _$DateCellCalendarState {
+  const factory DateCellCalendarState({
     required DateTypeOptionPB dateTypeOptionPB,
     required CalendarFormat format,
     required DateTime focusedDay,
     required Option<String> timeFormatError,
-    required Option<CalendarData> calData,
+    required Option<DateCellData> dateCellData,
     required String? time,
     required String timeHintText,
-  }) = _DateCalState;
+  }) = _DateCellCalendarState;
 
-  factory DateCalState.initial(
+  factory DateCellCalendarState.initial(
     DateTypeOptionPB dateTypeOptionPB,
     DateCellDataPB? cellData,
   ) {
-    Option<CalendarData> calData = calDataFromCellData(cellData);
+    Option<DateCellData> dateCellData = calDataFromCellData(cellData);
     final time =
-        calData.foldRight("", (dateData, previous) => dateData.time ?? '');
-    return DateCalState(
+        dateCellData.foldRight("", (dateData, previous) => dateData.time ?? '');
+    return DateCellCalendarState(
       dateTypeOptionPB: dateTypeOptionPB,
       format: CalendarFormat.month,
       focusedDay: DateTime.now(),
       time: time,
-      calData: calData,
+      dateCellData: dateCellData,
       timeFormatError: none(),
       timeHintText: _timeHintText(dateTypeOptionPB),
     );
@@ -245,30 +254,33 @@ String _timeHintText(DateTypeOptionPB typeOption) {
       return LocaleKeys.document_date_timeHintTextInTwelveHour.tr();
     case TimeFormat.TwentyFourHour:
       return LocaleKeys.document_date_timeHintTextInTwentyFourHour.tr();
+    default:
+      return "";
   }
-  return "";
 }
 
-Option<CalendarData> calDataFromCellData(DateCellDataPB? cellData) {
+Option<DateCellData> calDataFromCellData(DateCellDataPB? cellData) {
   String? time = timeFromCellData(cellData);
-  Option<CalendarData> calData = none();
+  Option<DateCellData> dateData = none();
   if (cellData != null) {
     final timestamp = cellData.timestamp * 1000;
-    final date = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt());
-    calData = Some(CalendarData(date: date, time: time));
+    final date = DateTime.fromMillisecondsSinceEpoch(
+      timestamp.toInt(),
+      isUtc: true,
+    );
+    dateData = Some(DateCellData(
+      date: date,
+      time: time,
+      includeTime: cellData.includeTime,
+    ));
   }
-  return calData;
-}
-
-$fixnum.Int64 timestampFromDateTime(DateTime dateTime) {
-  final timestamp = (dateTime.millisecondsSinceEpoch ~/ 1000);
-  return $fixnum.Int64(timestamp);
+  return dateData;
 }
 
 String? timeFromCellData(DateCellDataPB? cellData) {
-  String? time;
-  if (cellData?.hasTime() ?? false) {
-    time = cellData?.time;
+  if (cellData == null || !cellData.hasTime()) {
+    return null;
   }
-  return time;
+
+  return cellData.time;
 }

+ 4 - 5
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell.dart

@@ -1,13 +1,12 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/date_cell_bloc.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 
-import '../../../layout/sizes.dart';
-import '../cell_builder.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../cell_builder.dart';
+import 'date_cell_bloc.dart';
 import 'date_editor.dart';
 
 class DateCellStyle extends GridCellStyle {
@@ -50,7 +49,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
     _popover = PopoverController();
     final cellController =
         widget.cellControllerBuilder.build() as DateCellController;
-    _cellBloc = getIt<DateCellBloc>(param1: cellController)
+    _cellBloc = DateCellBloc(cellController: cellController)
       ..add(const DateCellEvent.initial());
     super.initState();
   }

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_cell_bloc.dart


+ 37 - 31
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_editor.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart

@@ -1,7 +1,6 @@
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
 import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/date_cal_bloc.dart';
 import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
 import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
@@ -11,9 +10,7 @@ import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/log.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/date_type_option.pb.dart';
@@ -21,9 +18,10 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:table_calendar/table_calendar.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
-import '../../../layout/sizes.dart';
-import '../../common/type_option_separator.dart';
-import '../../header/type_option/date.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../../../grid/presentation/widgets/common/type_option_separator.dart';
+import '../../../../grid/presentation/widgets/header/type_option/date.dart';
+import 'date_cal_bloc.dart';
 
 final kToday = DateTime.now();
 final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
@@ -92,17 +90,17 @@ class _CellCalendarWidget extends StatefulWidget {
 
 class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
   late PopoverMutex popoverMutex;
-  late DateCalBloc bloc;
+  late DateCellCalendarBloc bloc;
 
   @override
   void initState() {
     popoverMutex = PopoverMutex();
 
-    bloc = DateCalBloc(
+    bloc = DateCellCalendarBloc(
       dateTypeOptionPB: widget.dateTypeOptionPB,
       cellData: widget.cellContext.getCellData(),
       cellController: widget.cellContext,
-    )..add(const DateCalEvent.initial());
+    )..add(const DateCellCalendarEvent.initial());
     super.initState();
   }
 
@@ -110,18 +108,20 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
   Widget build(BuildContext context) {
     return BlocProvider.value(
       value: bloc,
-      child: BlocBuilder<DateCalBloc, DateCalState>(
+      child: BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
         buildWhen: (p, c) => p != c,
         builder: (context, state) {
+          bool includeTime = state.dateCellData
+              .fold(() => false, (dateData) => dateData.includeTime);
           List<Widget> children = [
             Padding(
               padding: const EdgeInsets.symmetric(horizontal: 12.0),
               child: _buildCalendar(context),
             ),
-            if (state.dateTypeOptionPB.includeTime) ...[
+            if (includeTime) ...[
               const VSpace(12.0),
               _TimeTextField(
-                bloc: context.read<DateCalBloc>(),
+                bloc: context.read<DateCellCalendarBloc>(),
                 popoverMutex: popoverMutex,
               ),
             ],
@@ -151,7 +151,7 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
   }
 
   Widget _buildCalendar(BuildContext context) {
-    return BlocBuilder<DateCalBloc, DateCalState>(
+    return BlocBuilder<DateCellCalendarBloc, DateCellCalendarState>(
       builder: (context, state) {
         final textStyle = Theme.of(context).textTheme.bodyMedium!;
         final boxDecoration = BoxDecoration(
@@ -208,23 +208,25 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> {
                 textStyle.textColor(Theme.of(context).disabledColor),
           ),
           selectedDayPredicate: (day) {
-            return state.calData.fold(
+            return state.dateCellData.fold(
               () => false,
               (dateData) => isSameDay(dateData.date, day),
             );
           },
           onDaySelected: (selectedDay, focusedDay) {
             context
-                .read<DateCalBloc>()
-                .add(DateCalEvent.selectDay(selectedDay));
+                .read<DateCellCalendarBloc>()
+                .add(DateCellCalendarEvent.selectDay(selectedDay));
           },
           onFormatChanged: (format) {
-            context.read<DateCalBloc>().add(DateCalEvent.setCalFormat(format));
+            context
+                .read<DateCellCalendarBloc>()
+                .add(DateCellCalendarEvent.setCalFormat(format));
           },
           onPageChanged: (focusedDay) {
             context
-                .read<DateCalBloc>()
-                .add(DateCalEvent.setFocusedDay(focusedDay));
+                .read<DateCellCalendarBloc>()
+                .add(DateCellCalendarEvent.setFocusedDay(focusedDay));
           },
         );
       },
@@ -237,8 +239,11 @@ class _IncludeTimeButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return BlocSelector<DateCalBloc, DateCalState, bool>(
-      selector: (state) => state.dateTypeOptionPB.includeTime,
+    return BlocSelector<DateCellCalendarBloc, DateCellCalendarState, bool>(
+      selector: (state) => state.dateCellData.fold(
+        () => false,
+        (dateData) => dateData.includeTime,
+      ),
       builder: (context, includeTime) {
         return Padding(
           padding: const EdgeInsets.symmetric(horizontal: 12.0),
@@ -258,8 +263,8 @@ class _IncludeTimeButton extends StatelessWidget {
                   Toggle(
                     value: includeTime,
                     onChanged: (value) => context
-                        .read<DateCalBloc>()
-                        .add(DateCalEvent.setIncludeTime(!value)),
+                        .read<DateCellCalendarBloc>()
+                        .add(DateCellCalendarEvent.setIncludeTime(!value)),
                     style: ToggleStyle.big,
                     padding: EdgeInsets.zero,
                   ),
@@ -274,7 +279,7 @@ class _IncludeTimeButton extends StatelessWidget {
 }
 
 class _TimeTextField extends StatefulWidget {
-  final DateCalBloc bloc;
+  final DateCellCalendarBloc bloc;
   final PopoverMutex popoverMutex;
 
   const _TimeTextField({
@@ -298,7 +303,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
 
     _focusNode.addListener(() {
       if (mounted) {
-        widget.bloc.add(DateCalEvent.setTime(_controller.text));
+        widget.bloc.add(DateCellCalendarEvent.setTime(_controller.text));
       }
     });
 
@@ -340,7 +345,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
           errorText: widget.bloc.state.timeFormatError
               .fold(() => "", (error) => error),
           onEditingComplete: (value) =>
-              widget.bloc.add(DateCalEvent.setTime(value)),
+              widget.bloc.add(DateCellCalendarEvent.setTime(value)),
         ),
       ),
     );
@@ -364,7 +369,8 @@ class _DateTypeOptionButton extends StatelessWidget {
   Widget build(BuildContext context) {
     final title =
         "${LocaleKeys.grid_field_dateFormat.tr()} & ${LocaleKeys.grid_field_timeFormat.tr()}";
-    return BlocSelector<DateCalBloc, DateCalState, DateTypeOptionPB>(
+    return BlocSelector<DateCellCalendarBloc, DateCellCalendarState,
+        DateTypeOptionPB>(
       selector: (state) => state.dateTypeOptionPB,
       builder: (context, dateTypeOptionPB) {
         return AppFlowyPopover(
@@ -391,7 +397,7 @@ class _DateTypeOptionButton extends StatelessWidget {
             return _CalDateTimeSetting(
               dateTypeOptionPB: dateTypeOptionPB,
               onEvent: (event) {
-                context.read<DateCalBloc>().add(event);
+                context.read<DateCellCalendarBloc>().add(event);
                 popoverMutex.close();
               },
             );
@@ -404,7 +410,7 @@ class _DateTypeOptionButton extends StatelessWidget {
 
 class _CalDateTimeSetting extends StatefulWidget {
   final DateTypeOptionPB dateTypeOptionPB;
-  final Function(DateCalEvent) onEvent;
+  final Function(DateCellCalendarEvent) onEvent;
   const _CalDateTimeSetting({
     required this.dateTypeOptionPB,
     required this.onEvent,
@@ -430,7 +436,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
           return DateFormatList(
             selectedFormat: widget.dateTypeOptionPB.dateFormat,
             onSelected: (format) {
-              widget.onEvent(DateCalEvent.setDateFormat(format));
+              widget.onEvent(DateCellCalendarEvent.setDateFormat(format));
               timeSettingPopoverMutex.close();
             },
           );
@@ -448,7 +454,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
           return TimeFormatList(
               selectedFormat: widget.dateTypeOptionPB.timeFormat,
               onSelected: (format) {
-                widget.onEvent(DateCalEvent.setTimeFormat(format));
+                widget.onEvent(DateCellCalendarEvent.setTimeFormat(format));
                 timeSettingPopoverMutex.close();
               });
         },

+ 6 - 6
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/number_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/number_cell/number_cell.dart

@@ -1,12 +1,11 @@
 import 'dart:async';
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/startup/startup.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../application/cell/number_cell_bloc.dart';
-import '../../layout/sizes.dart';
-import 'cell_builder.dart';
+import 'number_cell_bloc.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../cell_builder.dart';
 
 class GridNumberCell extends GridCellWidget {
   final CellControllerBuilder cellControllerBuilder;
@@ -26,8 +25,9 @@ class _NumberCellState extends GridFocusNodeCellState<GridNumberCell> {
 
   @override
   void initState() {
-    final cellController = widget.cellControllerBuilder.build();
-    _cellBloc = getIt<NumberCellBloc>(param1: cellController)
+    final cellController =
+        widget.cellControllerBuilder.build() as NumberCellController;
+    _cellBloc = NumberCellBloc(cellController: cellController)
       ..add(const NumberCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.cellContent);
     super.initState();

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/number_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/number_cell/number_cell_bloc.dart


+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/extension.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart


+ 5 - 6
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_cell.dart

@@ -1,15 +1,14 @@
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../../application/cell/select_option_cell_bloc.dart';
-import '../../../layout/sizes.dart';
-import '../cell_builder.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../cell_builder.dart';
 import 'extension.dart';
+import 'select_option_cell_bloc.dart';
 import 'select_option_editor.dart';
 
 class SelectOptionCellStyle extends GridCellStyle {
@@ -48,7 +47,7 @@ class _SingleSelectCellState extends GridCellState<GridSingleSelectCell> {
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as SelectOptionCellController;
-    _cellBloc = getIt<SelectOptionCellBloc>(param1: cellController)
+    _cellBloc = SelectOptionCellBloc(cellController: cellController)
       ..add(const SelectOptionCellEvent.initial());
     _popover = PopoverController();
     super.initState();
@@ -111,7 +110,7 @@ class _MultiSelectCellState extends GridCellState<GridMultiSelectCell> {
   void initState() {
     final cellController =
         widget.cellControllerBuilder.build() as SelectOptionCellController;
-    _cellBloc = getIt<SelectOptionCellBloc>(param1: cellController)
+    _cellBloc = SelectOptionCellBloc(cellController: cellController)
       ..add(const SelectOptionCellEvent.initial());
     _popover = PopoverController();
     super.initState();

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_cell_bloc.dart


+ 4 - 7
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart

@@ -1,14 +1,10 @@
 import 'dart:collection';
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/theme_extension.dart';
 
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
-import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -16,10 +12,11 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:textfield_tags/textfield_tags.dart';
 
-import '../../../layout/sizes.dart';
-import '../../common/type_option_separator.dart';
-import '../../header/type_option/select_option_editor.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../../../grid/presentation/widgets/common/type_option_separator.dart';
+import '../../../../grid/presentation/widgets/header/type_option/select_option_editor.dart';
 import 'extension.dart';
+import 'select_option_editor_bloc.dart';
 import 'text_field.dart';
 
 const double _editorPanelWidth = 300;

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart


+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_service.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_service.dart


+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/text_field.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart


+ 6 - 6
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/text_cell.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/text_cell/text_cell.dart

@@ -1,11 +1,10 @@
 import 'dart:async';
 import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
-import 'package:appflowy/plugins/database_view/grid/application/cell/text_cell_bloc.dart';
+import 'package:appflowy/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:appflowy/startup/startup.dart';
-import '../../layout/sizes.dart';
-import 'cell_builder.dart';
+import '../../../../grid/presentation/layout/sizes.dart';
+import '../../cell_builder.dart';
 
 class GridTextCellStyle extends GridCellStyle {
   String? placeholder;
@@ -40,8 +39,9 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
 
   @override
   void initState() {
-    final cellController = widget.cellControllerBuilder.build();
-    _cellBloc = getIt<TextCellBloc>(param1: cellController);
+    final cellController =
+        widget.cellControllerBuilder.build() as TextCellController;
+    _cellBloc = TextCellBloc(cellController: cellController);
     _cellBloc.add(const TextCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
     super.initState();

+ 0 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/text_cell_bloc.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart


+ 1 - 1
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/cell_editor.dart → frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/cell_editor.dart

@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
 import 'dart:async';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../../application/cell/url_cell_editor_bloc.dart';
+import 'url_cell_editor_bloc.dart';
 
 class URLCellEditor extends StatefulWidget {
   final VoidCallback onExit;

部分文件因为文件数量过多而无法显示