Преглед изворни кода

feat: field settings application domain (#3284)

* chore: default field settings if not exist

* chore: field settings listeners and services

* chore: don't need to updateFieldInfos

* chore: load default field settings if none found

* chore: update collab ref

* chore: fix remidner compile errors

* fix: fix tests

* chore: update tauri project setting

---------

Co-authored-by: nathan <[email protected]>
Richard Shiue пре 1 година
родитељ
комит
18498c0479
32 измењених фајлова са 907 додато и 544 уклоњено
  1. 265 212
      frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_controller.dart
  2. 5 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_info.dart
  3. 51 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/field_settings/field_settings_listener.dart
  4. 54 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/field_settings/field_settings_service.dart
  5. 3 0
      frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart
  6. 4 0
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart
  7. 2 0
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart
  8. 94 63
      frontend/appflowy_tauri/src-tauri/Cargo.lock
  9. 9 9
      frontend/appflowy_tauri/src-tauri/Cargo.toml
  10. 17 69
      frontend/rust-lib/Cargo.lock
  11. 8 9
      frontend/rust-lib/Cargo.toml
  12. 9 3
      frontend/rust-lib/flowy-database2/src/entities/field_settings_entities.rs
  13. 6 2
      frontend/rust-lib/flowy-database2/src/entities/setting_entities.rs
  14. 29 2
      frontend/rust-lib/flowy-database2/src/event_handler.rs
  15. 5 1
      frontend/rust-lib/flowy-database2/src/event_map.rs
  16. 6 11
      frontend/rust-lib/flowy-database2/src/manager.rs
  17. 101 46
      frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs
  18. 12 1
      frontend/rust-lib/flowy-database2/src/services/database/util.rs
  19. 12 10
      frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs
  20. 0 35
      frontend/rust-lib/flowy-database2/src/services/field_settings/field_settings.rs
  21. 81 10
      frontend/rust-lib/flowy-database2/src/services/field_settings/field_settings_builder.rs
  22. 0 3
      frontend/rust-lib/flowy-database2/src/services/field_settings/mod.rs
  23. 5 2
      frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs
  24. 22 7
      frontend/rust-lib/flowy-database2/src/template.rs
  25. 16 10
      frontend/rust-lib/flowy-database2/tests/database/field_settings_test/script.rs
  26. 64 23
      frontend/rust-lib/flowy-database2/tests/database/field_settings_test/test.rs
  27. 5 2
      frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs
  28. 4 3
      frontend/rust-lib/flowy-database2/tests/database/mock_data/calendar_mock_data.rs
  29. 9 3
      frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs
  30. 1 1
      frontend/rust-lib/flowy-user-deps/Cargo.toml
  31. 6 6
      frontend/rust-lib/flowy-user/src/entities/reminder.rs
  32. 2 1
      frontend/rust-lib/flowy-user/src/services/user_awareness.rs

+ 265 - 212
frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_controller.dart

@@ -1,24 +1,27 @@
-import 'dart:collection';
-import 'package:appflowy_backend/protobuf/flowy-database2/filter_changeset.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
-import 'package:dartz/dartz.dart';
+import 'package:appflowy/plugins/database_view/application/database_view_service.dart';
+import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_listener.dart';
+import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
+import 'package:appflowy/plugins/database_view/application/filter/filter_listener.dart';
+import 'package:appflowy/plugins/database_view/application/filter/filter_service.dart';
+import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
+import 'package:appflowy/plugins/database_view/application/setting/setting_listener.dart';
+import 'package:appflowy/plugins/database_view/application/sort/sort_listener.dart';
+import 'package:appflowy/plugins/database_view/application/sort/sort_service.dart';
+import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart';
+import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
 import 'package:appflowy_backend/log.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/filter_changeset.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:collection/collection.dart';
+import 'package:dartz/dartz.dart';
 import 'package:flutter/foundation.dart';
-import '../../grid/presentation/widgets/filter/filter_info.dart';
-import '../../grid/presentation/widgets/sort/sort_info.dart';
-import '../database_view_service.dart';
-import '../filter/filter_listener.dart';
-import '../filter/filter_service.dart';
-import '../row/row_cache.dart';
-import '../setting/setting_listener.dart';
-import '../setting/setting_service.dart';
-import '../sort/sort_listener.dart';
-import '../sort/sort_service.dart';
+
 import 'field_info.dart';
 import 'field_listener.dart';
 
@@ -72,20 +75,23 @@ typedef OnReceiveUpdateFields = void Function(List<FieldInfo>);
 typedef OnReceiveFields = void Function(List<FieldInfo>);
 typedef OnReceiveFilters = void Function(List<FilterInfo>);
 typedef OnReceiveSorts = void Function(List<SortInfo>);
+typedef OnReceiveFieldSettings = void Function(List<FieldInfo>);
 
 class FieldController {
   final String viewId;
+
   // Listeners
   final FieldsListener _fieldListener;
   final DatabaseSettingListener _settingListener;
   final FiltersListener _filtersListener;
   final SortsListener _sortsListener;
+  final FieldSettingsListener _fieldSettingsListener;
 
   // FFI services
   final DatabaseViewBackendService _databaseViewBackendSvc;
-  final SettingBackendService _settingBackendSvc;
   final FilterBackendService _filterBackendSvc;
   final SortBackendService _sortBackendSvc;
+  final FieldSettingsBackendService _fieldSettingsBackendSvc;
 
   bool _isDisposed = false;
 
@@ -97,18 +103,17 @@ class FieldController {
   final Map<OnReceiveUpdateFields, void Function(List<FieldInfo>)>
       _updatedFieldCallbacks = {};
 
-  // Group callbacks
-  final Map<String, GroupSettingPB> _groupConfigurationByFieldId = {};
-
   // Filter callbacks
   final Map<OnReceiveFilters, VoidCallback> _filterCallbacks = {};
   _GridFilterNotifier? _filterNotifier = _GridFilterNotifier();
-  final Map<String, FilterPB> _filterPBByFieldId = {};
 
   // Sort callbacks
   final Map<OnReceiveSorts, VoidCallback> _sortCallbacks = {};
   _GridSortNotifier? _sortNotifier = _GridSortNotifier();
-  final Map<String, SortPB> _sortPBByFieldId = {};
+
+  // Database settings temporary storage
+  final Map<String, GroupSettingPB> _groupConfigurationByFieldId = {};
+  final List<FieldSettingsPB> _fieldSettings = [];
 
   // Getters
   List<FieldInfo> get fieldInfos => [..._fieldNotifier.fieldInfos];
@@ -116,39 +121,28 @@ class FieldController {
   List<SortInfo> get sortInfos => [..._sortNotifier?.sorts ?? []];
 
   FieldInfo? getField(String fieldId) {
-    final fields = _fieldNotifier.fieldInfos
-        .where((element) => element.id == fieldId)
-        .toList();
+    return _fieldNotifier.fieldInfos
+        .firstWhereOrNull((element) => element.id == fieldId);
+  }
 
-    if (fields.isEmpty) {
-      return null;
-    }
-    assert(fields.length == 1);
-    return fields.first;
+  FilterInfo? getFilterByFilterId(String filterId) {
+    return _filterNotifier?.filters
+        .firstWhereOrNull((element) => element.filterId == filterId);
   }
 
-  FilterInfo? getFilter(String filterId) {
-    final filters = _filterNotifier?.filters
-            .where((element) => element.filter.id == filterId)
-            .toList() ??
-        [];
-    if (filters.isEmpty) {
-      return null;
-    }
-    assert(filters.length == 1);
-    return filters.first;
+  FilterInfo? getFilterByFieldId(String fieldId) {
+    return _filterNotifier?.filters
+        .firstWhereOrNull((element) => element.fieldId == fieldId);
   }
 
-  SortInfo? getSort(String sortId) {
-    final sorts = _sortNotifier?.sorts
-            .where((element) => element.sortId == sortId)
-            .toList() ??
-        [];
-    if (sorts.isEmpty) {
-      return null;
-    }
-    assert(sorts.length == 1);
-    return sorts.first;
+  SortInfo? getSortBySortId(String sortId) {
+    return _sortNotifier?.sorts
+        .firstWhereOrNull((element) => element.sortId == sortId);
+  }
+
+  SortInfo? getSortByFieldId(String fieldId) {
+    return _sortNotifier?.sorts
+        .firstWhereOrNull((element) => element.fieldId == fieldId);
   }
 
   FieldController({required this.viewId})
@@ -159,34 +153,18 @@ class FieldController {
         _databaseViewBackendSvc = DatabaseViewBackendService(viewId: viewId),
         _sortBackendSvc = SortBackendService(viewId: viewId),
         _sortsListener = SortsListener(viewId: viewId),
-        _settingBackendSvc = SettingBackendService(viewId: viewId) {
-    //Listen on field's changes
+        _fieldSettingsListener = FieldSettingsListener(viewId: viewId),
+        _fieldSettingsBackendSvc = FieldSettingsBackendService(viewId: viewId) {
+    // Start listeners
     _listenOnFieldChanges();
-
-    //Listen on setting changes
     _listenOnSettingChanges();
-
-    //Listen on the filter changes
     _listenOnFilterChanges();
-
-    //Listen on the sort changes
     _listenOnSortChanged();
-
-    _settingBackendSvc.getSetting().then((result) {
-      if (_isDisposed) {
-        return;
-      }
-
-      result.fold(
-        (setting) => _updateSetting(setting),
-        (err) => Log.error(err),
-      );
-    });
+    _listenOnFieldSettingsChanged();
   }
 
+  /// Listen for filter changes in the backend.
   void _listenOnFilterChanges() {
-    //Listen on the filter changes
-
     deleteFilterFromChangeset(
       List<FilterInfo> filters,
       FilterChangesetNotificationPB changeset,
@@ -196,9 +174,6 @@ class FieldController {
         filters.retainWhere(
           (element) => !deleteFilterIds.contains(element.filter.id),
         );
-
-        _filterPBByFieldId
-            .removeWhere((key, value) => deleteFilterIds.contains(value.id));
       }
     }
 
@@ -216,7 +191,6 @@ class FieldController {
             fieldType: newFilter.fieldType,
           );
           if (fieldInfo != null) {
-            _filterPBByFieldId[fieldInfo.id] = newFilter;
             filters.add(FilterInfo(viewId, newFilter, fieldInfo));
           }
         }
@@ -234,8 +208,6 @@ class FieldController {
         // Remove the old filter
         if (filterIndex != -1) {
           filters.removeAt(filterIndex);
-          _filterPBByFieldId
-              .removeWhere((key, value) => value.id == updatedFilter.filterId);
         }
 
         // Insert the filter if there is a filter and its field info is
@@ -257,7 +229,6 @@ class FieldController {
             } else {
               filters.add(filterInfo);
             }
-            _filterPBByFieldId[fieldInfo.id] = updatedFilter.filter;
           }
         }
       }
@@ -272,16 +243,17 @@ class FieldController {
         result.fold(
           (FilterChangesetNotificationPB changeset) {
             final List<FilterInfo> filters = filterInfos;
-            // Deletes the filters
+            // delete removed filters
             deleteFilterFromChangeset(filters, changeset);
 
-            // Inserts the new filter if it's not exist
+            // insert new filters
             insertFilterFromChangeset(filters, changeset);
 
+            // edit modified filters
             updateFilterFromChangeset(filters, changeset);
 
-            _updateFieldInfos();
             _filterNotifier?.filters = filters;
+            _updateFieldInfos();
           },
           (err) => Log.error(err),
         );
@@ -289,6 +261,7 @@ class FieldController {
     );
   }
 
+  /// Listen for sort changes in the backend.
   void _listenOnSortChanged() {
     deleteSortFromChangeset(
       List<SortInfo> newSortInfos,
@@ -299,9 +272,6 @@ class FieldController {
         newSortInfos.retainWhere(
           (element) => !deleteSortIds.contains(element.sortId),
         );
-
-        _sortPBByFieldId
-            .removeWhere((key, value) => deleteSortIds.contains(value.id));
       }
     }
 
@@ -320,7 +290,6 @@ class FieldController {
           );
 
           if (fieldInfo != null) {
-            _sortPBByFieldId[newSortPB.fieldId] = newSortPB;
             newSortInfos.add(SortInfo(sortPB: newSortPB, fieldInfo: fieldInfo));
           }
         }
@@ -356,7 +325,6 @@ class FieldController {
           } else {
             newSortInfos.add(newSortInfo);
           }
-          _sortPBByFieldId[updatedSort.fieldId] = updatedSort;
         }
       }
     }
@@ -373,7 +341,6 @@ class FieldController {
             insertSortFromChangeset(newSortInfos, changeset);
             updateSortFromChangeset(newSortInfos, changeset);
 
-            _updateFieldInfos();
             _sortNotifier?.sorts = newSortInfos;
           },
           (err) => Log.error(err),
@@ -382,8 +349,8 @@ class FieldController {
     );
   }
 
+  /// Listen for databse setting changes in the backend.
   void _listenOnSettingChanges() {
-    //Listen on setting changes
     _settingListener.start(
       onSettingUpdated: (result) {
         if (_isDisposed) {
@@ -398,8 +365,83 @@ class FieldController {
     );
   }
 
+  /// Listen for field changes in the backend.
   void _listenOnFieldChanges() {
-    //Listen on field's changes
+    void deleteFields(List<FieldIdPB> deletedFields) {
+      if (deletedFields.isEmpty) {
+        return;
+      }
+      final List<FieldInfo> newFields = fieldInfos;
+      final Map<String, FieldIdPB> deletedFieldMap = {
+        for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
+      };
+
+      newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
+      _fieldNotifier.fieldInfos = newFields;
+    }
+
+    Future<FieldInfo> attachFieldSettings(FieldInfo fieldInfo) async {
+      return _fieldSettingsBackendSvc
+          .getFieldSettings(fieldInfo.id)
+          .then((result) {
+        final fieldSettings = result.fold(
+          (fieldSettings) => fieldSettings,
+          (err) {
+            return null;
+          },
+        );
+        if (fieldSettings == null) {
+          return fieldInfo;
+        }
+        final updatedFieldInfo =
+            fieldInfo.copyWith(fieldSettings: fieldSettings);
+
+        return updatedFieldInfo;
+      });
+    }
+
+    Future<void> insertFields(List<IndexFieldPB> insertedFields) async {
+      if (insertedFields.isEmpty) {
+        return;
+      }
+      final List<FieldInfo> newFieldInfos = fieldInfos;
+      for (final indexField in insertedFields) {
+        final initial = FieldInfo.initial(indexField.field_1);
+        final fieldInfo = await attachFieldSettings(initial);
+        if (newFieldInfos.length > indexField.index) {
+          newFieldInfos.insert(indexField.index, fieldInfo);
+        } else {
+          newFieldInfos.add(fieldInfo);
+        }
+      }
+      _fieldNotifier.fieldInfos = newFieldInfos;
+    }
+
+    List<FieldInfo> updateFields(List<FieldPB> updatedFieldPBs) {
+      if (updatedFieldPBs.isEmpty) {
+        return [];
+      }
+
+      final List<FieldInfo> newFields = fieldInfos;
+      final List<FieldInfo> updatedFields = [];
+      for (final updatedFieldPB in updatedFieldPBs) {
+        final index =
+            newFields.indexWhere((field) => field.id == updatedFieldPB.id);
+        if (index != -1) {
+          newFields.removeAt(index);
+          final fieldInfo = FieldInfo.initial(updatedFieldPB);
+          newFields.insert(index, fieldInfo);
+          updatedFields.add(fieldInfo);
+        }
+      }
+
+      if (updatedFields.isNotEmpty) {
+        _fieldNotifier.fieldInfos = newFields;
+      }
+      return updatedFields;
+    }
+
+    // Listen on field's changes
     _fieldListener.start(
       onFieldsChanged: (result) {
         result.fold(
@@ -407,10 +449,10 @@ class FieldController {
             if (_isDisposed) {
               return;
             }
-            _deleteFields(changeset.deletedFields);
-            _insertFields(changeset.insertedFields);
+            deleteFields(changeset.deletedFields);
+            insertFields(changeset.insertedFields);
 
-            final updatedFields = _updateFields(changeset.updatedFields);
+            final updatedFields = updateFields(changeset.updatedFields);
             for (final listener in _updatedFieldCallbacks.values) {
               listener(updatedFields);
             }
@@ -421,31 +463,70 @@ class FieldController {
     );
   }
 
+  /// Listen for field setting changes in the backend.
+  void _listenOnFieldSettingsChanged() {
+    FieldInfo updateFieldSettings(FieldSettingsPB updatedFieldSettings) {
+      final List<FieldInfo> newFields = fieldInfos;
+      FieldInfo updatedField = newFields[0];
+
+      final index = newFields
+          .indexWhere((field) => field.id == updatedFieldSettings.fieldId);
+      if (index != -1) {
+        newFields[index] =
+            newFields[index].copyWith(fieldSettings: updatedFieldSettings);
+        updatedField = newFields[index];
+      }
+
+      _fieldNotifier.fieldInfos = newFields;
+      return updatedField;
+    }
+
+    _fieldSettingsListener.start(
+      onFieldSettingsChanged: (result) {
+        if (_isDisposed) {
+          return;
+        }
+        result.fold(
+          (fieldSettings) {
+            final updatedFieldInfo = updateFieldSettings(fieldSettings);
+            for (final listener in _updatedFieldCallbacks.values) {
+              listener([updatedFieldInfo]);
+            }
+          },
+          (err) => Log.error(err),
+        );
+      },
+    );
+  }
+
+  /// Updates sort, filter, group and field info from `DatabaseViewSettingPB`
   void _updateSetting(DatabaseViewSettingPB setting) {
     _groupConfigurationByFieldId.clear();
     for (final configuration in setting.groupSettings.items) {
       _groupConfigurationByFieldId[configuration.fieldId] = configuration;
     }
 
-    for (final filter in setting.filters.items) {
-      _filterPBByFieldId[filter.fieldId] = filter;
-    }
+    _filterNotifier?.filters = _filterInfoListFromPBs(setting.filters.items);
 
-    for (final sort in setting.sorts.items) {
-      _sortPBByFieldId[sort.fieldId] = sort;
-    }
+    _sortNotifier?.sorts = _sortInfoListFromPBs(setting.sorts.items);
+
+    _fieldSettings.clear();
+    _fieldSettings.addAll(setting.fieldSettings.items);
 
     _updateFieldInfos();
   }
 
+  /// Attach sort, filter, group information and field settings to `FieldInfo`
   void _updateFieldInfos() {
     final List<FieldInfo> newFieldInfos = [];
     for (final field in _fieldNotifier.fieldInfos) {
       newFieldInfos.add(
         field.copyWith(
+          fieldSettings: _fieldSettings
+              .firstWhereOrNull((setting) => setting.fieldId == field.id),
           isGroupField: _groupConfigurationByFieldId[field.id] != null,
-          hasFilter: _filterPBByFieldId[field.id] != null,
-          hasSort: _sortPBByFieldId[field.id] != null,
+          hasFilter: getFilterByFieldId(field.id) != null,
+          hasSort: getSortByFieldId(field.id) != null,
         ),
       );
     }
@@ -453,52 +534,27 @@ class FieldController {
     _fieldNotifier.fieldInfos = newFieldInfos;
   }
 
-  Future<void> dispose() async {
-    if (_isDisposed) {
-      Log.warn('FieldController is already disposed');
-      return;
-    }
-    _isDisposed = true;
-    await _fieldListener.stop();
-    await _filtersListener.stop();
-    await _settingListener.stop();
-    await _sortsListener.stop();
-
-    for (final callback in _fieldCallbacks.values) {
-      _fieldNotifier.removeListener(callback);
-    }
-    _fieldNotifier.dispose();
-
-    for (final callback in _filterCallbacks.values) {
-      _filterNotifier?.removeListener(callback);
-    }
-    for (final callback in _sortCallbacks.values) {
-      _sortNotifier?.removeListener(callback);
-    }
-
-    _filterNotifier?.dispose();
-    _filterNotifier = null;
-
-    _sortNotifier?.dispose();
-    _sortNotifier = null;
-  }
-
+  /// Load all of the fields. This is required when opening the database
   Future<Either<Unit, FlowyError>> loadFields({
     required List<FieldIdPB> fieldIds,
   }) async {
     final result = await _databaseViewBackendSvc.getFields(fieldIds: fieldIds);
     return Future(
       () => result.fold(
-        (newFields) {
+        (newFields) async {
           if (_isDisposed) {
             return left(unit);
           }
 
           _fieldNotifier.fieldInfos =
               newFields.map((field) => FieldInfo.initial(field)).toList();
-          _loadFilters();
-          _loadSorts();
+          Future.wait([
+            _loadFilters(),
+            _loadSorts(),
+            _loadAllFieldSettings(),
+          ]);
           _updateFieldInfos();
+
           return left(unit);
         },
         (err) => right(err),
@@ -506,24 +562,12 @@ class FieldController {
     );
   }
 
+  /// Load all the filters from the backend. Required by `loadFields`
   Future<Either<Unit, FlowyError>> _loadFilters() async {
     return _filterBackendSvc.getAllFilters().then((result) {
       return result.fold(
         (filterPBs) {
-          final List<FilterInfo> filters = [];
-          for (final filterPB in filterPBs) {
-            final fieldInfo = _findFieldInfo(
-              fieldInfos: fieldInfos,
-              fieldId: filterPB.fieldId,
-              fieldType: filterPB.fieldType,
-            );
-            if (fieldInfo != null) {
-              final filterInfo = FilterInfo(viewId, filterPB, fieldInfo);
-              filters.add(filterInfo);
-            }
-          }
-
-          _filterNotifier?.filters = filters;
+          _filterNotifier?.filters = _filterInfoListFromPBs(filterPBs);
           return left(unit);
         },
         (err) => right(err),
@@ -531,26 +575,26 @@ class FieldController {
     });
   }
 
+  /// Load all the sorts from the backend. Required by `loadFields`
   Future<Either<Unit, FlowyError>> _loadSorts() async {
     return _sortBackendSvc.getAllSorts().then((result) {
       return result.fold(
         (sortPBs) {
-          final List<SortInfo> sortInfos = [];
-          for (final sortPB in sortPBs) {
-            final fieldInfo = _findFieldInfo(
-              fieldInfos: fieldInfos,
-              fieldId: sortPB.fieldId,
-              fieldType: sortPB.fieldType,
-            );
-
-            if (fieldInfo != null) {
-              final sortInfo = SortInfo(sortPB: sortPB, fieldInfo: fieldInfo);
-              sortInfos.add(sortInfo);
-            }
-          }
+          _sortNotifier?.sorts = _sortInfoListFromPBs(sortPBs);
+          return left(unit);
+        },
+        (err) => right(err),
+      );
+    });
+  }
 
-          _updateFieldInfos();
-          _sortNotifier?.sorts = sortInfos;
+  /// Load all the field settings from the backend. Required by `loadFields`
+  Future<Either<Unit, FlowyError>> _loadAllFieldSettings() async {
+    return _fieldSettingsBackendSvc.getAllFieldSettings().then((result) {
+      return result.fold(
+        (fieldSettingsList) {
+          _fieldSettings.clear();
+          _fieldSettings.addAll(fieldSettingsList);
           return left(unit);
         },
         (err) => right(err),
@@ -558,6 +602,42 @@ class FieldController {
     });
   }
 
+  /// Attach corresponding `FieldInfo`s to the `FilterPB`s
+  List<FilterInfo> _filterInfoListFromPBs(List<FilterPB> filterPBs) {
+    FilterInfo? getFilterInfo(FilterPB filterPB) {
+      final fieldInfo = _findFieldInfo(
+        fieldInfos: fieldInfos,
+        fieldId: filterPB.fieldId,
+        fieldType: filterPB.fieldType,
+      );
+      return fieldInfo != null ? FilterInfo(viewId, filterPB, fieldInfo) : null;
+    }
+
+    return filterPBs
+        .map((filterPB) => getFilterInfo(filterPB))
+        .whereType<FilterInfo>()
+        .toList();
+  }
+
+  /// Attach corresponding `FieldInfo`s to the `SortPB`s
+  List<SortInfo> _sortInfoListFromPBs(List<SortPB> sortPBs) {
+    SortInfo? getSortInfo(SortPB sortPB) {
+      final fieldInfo = _findFieldInfo(
+        fieldInfos: fieldInfos,
+        fieldId: sortPB.fieldId,
+        fieldType: sortPB.fieldType,
+      );
+      return fieldInfo != null
+          ? SortInfo(sortPB: sortPB, fieldInfo: fieldInfo)
+          : null;
+    }
+
+    return sortPBs
+        .map((sortPB) => getSortInfo(sortPB))
+        .whereType<SortInfo>()
+        .toList();
+  }
+
   void addListener({
     OnReceiveFields? onReceiveFields,
     OnReceiveUpdateFields? onFieldsChanged,
@@ -640,57 +720,35 @@ class FieldController {
     }
   }
 
-  void _deleteFields(List<FieldIdPB> deletedFields) {
-    if (deletedFields.isEmpty) {
-      return;
-    }
-    final List<FieldInfo> newFields = fieldInfos;
-    final Map<String, FieldIdPB> deletedFieldMap = {
-      for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
-    };
-
-    newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
-    _fieldNotifier.fieldInfos = newFields;
-  }
-
-  void _insertFields(List<IndexFieldPB> insertedFields) {
-    if (insertedFields.isEmpty) {
+  /// Stop listeners, dispose notifiers and clear listener callbacks
+  Future<void> dispose() async {
+    if (_isDisposed) {
+      Log.warn('FieldController is already disposed');
       return;
     }
-    final List<FieldInfo> newFieldInfos = fieldInfos;
-    for (final indexField in insertedFields) {
-      final fieldInfo = FieldInfo.initial(indexField.field_1);
-      if (newFieldInfos.length > indexField.index) {
-        newFieldInfos.insert(indexField.index, fieldInfo);
-      } else {
-        newFieldInfos.add(fieldInfo);
-      }
-    }
-    _fieldNotifier.fieldInfos = newFieldInfos;
-  }
+    _isDisposed = true;
+    await _fieldListener.stop();
+    await _filtersListener.stop();
+    await _settingListener.stop();
+    await _sortsListener.stop();
+    await _fieldSettingsListener.stop();
 
-  List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) {
-    if (updatedFieldPBs.isEmpty) {
-      return [];
+    for (final callback in _fieldCallbacks.values) {
+      _fieldNotifier.removeListener(callback);
     }
+    _fieldNotifier.dispose();
 
-    final List<FieldInfo> newFields = fieldInfos;
-    final List<FieldInfo> updatedFields = [];
-    for (final updatedFieldPB in updatedFieldPBs) {
-      final index =
-          newFields.indexWhere((field) => field.id == updatedFieldPB.id);
-      if (index != -1) {
-        newFields.removeAt(index);
-        final fieldInfo = FieldInfo.initial(updatedFieldPB);
-        newFields.insert(index, fieldInfo);
-        updatedFields.add(fieldInfo);
-      }
+    for (final callback in _filterCallbacks.values) {
+      _filterNotifier?.removeListener(callback);
     }
+    _filterNotifier?.dispose();
+    _filterNotifier = null;
 
-    if (updatedFields.isNotEmpty) {
-      _fieldNotifier.fieldInfos = newFields;
+    for (final callback in _sortCallbacks.values) {
+      _sortNotifier?.removeListener(callback);
     }
-    return updatedFields;
+    _sortNotifier?.dispose();
+    _sortNotifier = null;
   }
 }
 
@@ -727,12 +785,7 @@ FieldInfo? _findFieldInfo({
   required String fieldId,
   required FieldType fieldType,
 }) {
-  final fieldIndex = fieldInfos.indexWhere((element) {
-    return element.id == fieldId && element.fieldType == fieldType;
-  });
-  if (fieldIndex != -1) {
-    return fieldInfos[fieldIndex];
-  } else {
-    return null;
-  }
+  return fieldInfos.firstWhereOrNull(
+    (element) => element.id == fieldId && element.fieldType == fieldType,
+  );
 }

+ 5 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_info.dart

@@ -1,4 +1,5 @@
 import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 part 'field_info.freezed.dart';
 
@@ -8,6 +9,7 @@ class FieldInfo with _$FieldInfo {
 
   const factory FieldInfo({
     required FieldPB field,
+    required FieldSettingsPB? fieldSettings,
     required bool isGroupField,
     required bool hasFilter,
     required bool hasSort,
@@ -21,8 +23,11 @@ class FieldInfo with _$FieldInfo {
 
   bool get isPrimary => field.isPrimary;
 
+  FieldVisibility? get visibility => fieldSettings?.visibility;
+
   factory FieldInfo.initial(FieldPB field) => FieldInfo(
         field: field,
+        fieldSettings: null,
         hasFilter: false,
         hasSort: false,
         isGroupField: false,

+ 51 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/field_settings/field_settings_listener.dart

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

+ 54 - 0
frontend/appflowy_flutter/lib/plugins/database_view/application/field_settings/field_settings_service.dart

@@ -0,0 +1,54 @@
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+
+class FieldSettingsBackendService {
+  final String viewId;
+  FieldSettingsBackendService({required this.viewId});
+
+  Future<Either<FieldSettingsPB, FlowyError>> getFieldSettings(
+    String fieldId,
+  ) {
+    final id = FieldIdPB(fieldId: fieldId);
+    final ids = RepeatedFieldIdPB()..items.add(id);
+    final payload = FieldIdsPB()
+      ..viewId = viewId
+      ..fieldIds = ids;
+
+    return DatabaseEventGetFieldSettings(payload).send().then((result) {
+      return result.fold(
+        (fieldSettings) => left(fieldSettings.items.first),
+        (r) => right(r),
+      );
+    });
+  }
+
+  Future<Either<List<FieldSettingsPB>, FlowyError>> getAllFieldSettings() {
+    final payload = DatabaseViewIdPB()..value = viewId;
+
+    return DatabaseEventGetAllFieldSettings(payload).send().then((result) {
+      return result.fold(
+        (fieldSettings) => left(fieldSettings.items),
+        (r) => right(r),
+      );
+    });
+  }
+
+  Future<Either<Unit, FlowyError>> updateFieldSettings({
+    required String fieldId,
+    FieldVisibility? fieldVisibility,
+  }) {
+    final FieldSettingsChangesetPB payload = FieldSettingsChangesetPB.create()
+      ..viewId = viewId
+      ..fieldId = fieldId;
+
+    if (fieldVisibility != null) {
+      payload.visibility = fieldVisibility;
+    }
+
+    return DatabaseEventUpdateFieldSettings(payload).send();
+  }
+}

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

@@ -74,6 +74,7 @@ class RowCache {
       final rowInfo = buildGridRow(row);
       _rowList.add(rowInfo);
     }
+    _changedNotifier.receive(const ChangedReason.setInitialRows());
   }
 
   Future<void> dispose() async {
@@ -281,6 +282,7 @@ class RowChangesetNotifier extends ChangeNotifier {
       initial: (_) {},
       reorderRows: (_) => notifyListeners(),
       reorderSingleRow: (_) => notifyListeners(),
+      setInitialRows: (_) => notifyListeners(),
     );
   }
 }
@@ -313,6 +315,7 @@ class ChangedReason with _$ChangedReason {
     ReorderSingleRowPB reorderRow,
     RowInfo rowInfo,
   ) = _ReorderSingleRow;
+  const factory ChangedReason.setInitialRows() = _SetInitialRows;
 }
 
 class InsertedIndex {

+ 4 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart

@@ -22,6 +22,10 @@ class FilterInfo {
     );
   }
 
+  String get filterId => filter.id;
+
+  String get fieldId => filter.fieldId;
+
   DateFilterPB? dateFilter() {
     if (![
       FieldType.DateTime,

+ 2 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart

@@ -8,4 +8,6 @@ class SortInfo {
   SortInfo({required this.sortPB, required this.fieldInfo});
 
   String get sortId => sortPB.id;
+
+  String get fieldId => sortPB.fieldId;
 }

+ 94 - 63
frontend/appflowy_tauri/src-tauri/Cargo.lock

@@ -140,7 +140,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 [[package]]
 name = "appflowy-integrate"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
@@ -203,18 +203,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
 name = "async-trait"
-version = "0.1.68"
+version = "0.1.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -307,7 +307,7 @@ dependencies = [
  "regex",
  "rustc-hash",
  "shlex",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -728,7 +728,7 @@ dependencies = [
 [[package]]
 name = "collab"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "bytes",
@@ -746,7 +746,7 @@ dependencies = [
 [[package]]
 name = "collab-client-ws"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bytes",
  "collab-sync",
@@ -764,7 +764,7 @@ dependencies = [
 [[package]]
 name = "collab-database"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -793,15 +793,19 @@ dependencies = [
 [[package]]
 name = "collab-define"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
+ "anyhow",
+ "collab",
+ "serde",
+ "serde_repr",
  "uuid",
 ]
 
 [[package]]
 name = "collab-derive"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -813,7 +817,7 @@ dependencies = [
 [[package]]
 name = "collab-document"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
@@ -832,7 +836,7 @@ dependencies = [
 [[package]]
 name = "collab-folder"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "chrono",
@@ -852,7 +856,7 @@ dependencies = [
 [[package]]
 name = "collab-persistence"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bincode",
  "chrono",
@@ -872,7 +876,7 @@ dependencies = [
 [[package]]
 name = "collab-plugins"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -884,7 +888,6 @@ dependencies = [
  "futures-util",
  "parking_lot 0.12.1",
  "rand 0.8.5",
- "rusoto_credential",
  "serde",
  "serde_json",
  "similar 2.2.1",
@@ -901,7 +904,7 @@ dependencies = [
 [[package]]
 name = "collab-sync"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bytes",
  "collab",
@@ -923,10 +926,11 @@ dependencies = [
 [[package]]
 name = "collab-user"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
+ "collab-define",
  "parking_lot 0.12.1",
  "serde",
  "serde_json",
@@ -1120,7 +1124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
 dependencies = [
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1184,7 +1188,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1195,7 +1199,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1808,6 +1812,7 @@ dependencies = [
  "flowy-error",
  "flowy-folder-deps",
  "flowy-server-config",
+ "flowy-storage",
  "flowy-user-deps",
  "futures",
  "futures-util",
@@ -1815,6 +1820,7 @@ dependencies = [
  "hyper",
  "lazy_static",
  "lib-infra",
+ "mime_guess",
  "parking_lot 0.12.1",
  "postgrest",
  "reqwest",
@@ -1824,7 +1830,9 @@ dependencies = [
  "thiserror",
  "tokio",
  "tokio-retry",
+ "tokio-util",
  "tracing",
+ "url",
  "uuid",
 ]
 
@@ -1855,6 +1863,18 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "flowy-storage"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "bytes",
+ "reqwest",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "flowy-task"
 version = "0.1.0"
@@ -2038,7 +2058,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -3183,6 +3203,16 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -3460,7 +3490,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -3647,7 +3677,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -3825,7 +3855,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -3942,7 +3972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282"
 dependencies = [
  "proc-macro2",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -4121,9 +4151,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
 dependencies = [
  "proc-macro2",
 ]
@@ -4351,6 +4381,7 @@ dependencies = [
  "js-sys",
  "log",
  "mime",
+ "mime_guess",
  "native-tls",
  "once_cell",
  "percent-encoding",
@@ -4363,10 +4394,12 @@ dependencies = [
  "tokio",
  "tokio-native-tls",
  "tokio-rustls",
+ "tokio-util",
  "tower-service",
  "url",
  "wasm-bindgen",
  "wasm-bindgen-futures",
+ "wasm-streams",
  "web-sys",
  "webpki-roots",
  "winreg 0.10.1",
@@ -4425,24 +4458,6 @@ dependencies = [
  "librocksdb-sys",
 ]
 
-[[package]]
-name = "rusoto_credential"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05"
-dependencies = [
- "async-trait",
- "chrono",
- "dirs-next",
- "futures",
- "hyper",
- "serde",
- "serde_json",
- "shlex",
- "tokio",
- "zeroize",
-]
-
 [[package]]
 name = "rust_decimal"
 version = "1.30.0"
@@ -4700,7 +4715,7 @@ checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -4722,7 +4737,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -4771,7 +4786,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5082,7 +5097,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5104,9 +5119,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.22"
+version = "2.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5485,7 +5500,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5588,7 +5603,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5765,7 +5780,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5961,6 +5976,15 @@ dependencies = [
  "unic-common",
 ]
 
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.13"
@@ -6161,7 +6185,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
  "wasm-bindgen-shared",
 ]
 
@@ -6195,7 +6219,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.29",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -6206,6 +6230,19 @@ version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
 
+[[package]]
+name = "wasm-streams"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
 [[package]]
 name = "web-sys"
 version = "0.3.64"
@@ -6709,12 +6746,6 @@ dependencies = [
  "thiserror",
 ]
 
-[[package]]
-name = "zeroize"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
-
 [[package]]
 name = "zstd-sys"
 version = "2.0.8+zstd.1.5.5"

+ 9 - 9
frontend/appflowy_tauri/src-tauri/Cargo.toml

@@ -34,15 +34,15 @@ default = ["custom-protocol"]
 custom-protocol = ["tauri/custom-protocol"]
 
 [patch.crates-io]
-collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
+collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
 
 #collab = { path = "../../../../AppFlowy-Collab/collab" }
 #collab-folder = { path = "../../../../AppFlowy-Collab/collab-folder" }

+ 17 - 69
frontend/rust-lib/Cargo.lock

@@ -120,7 +120,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 [[package]]
 name = "appflowy-integrate"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
@@ -611,7 +611,7 @@ dependencies = [
 [[package]]
 name = "collab"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "bytes",
@@ -629,7 +629,7 @@ dependencies = [
 [[package]]
 name = "collab-client-ws"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bytes",
  "collab-sync",
@@ -647,7 +647,7 @@ dependencies = [
 [[package]]
 name = "collab-database"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -676,15 +676,19 @@ dependencies = [
 [[package]]
 name = "collab-define"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
+ "anyhow",
+ "collab",
+ "serde",
+ "serde_repr",
  "uuid",
 ]
 
 [[package]]
 name = "collab-derive"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -696,7 +700,7 @@ dependencies = [
 [[package]]
 name = "collab-document"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
@@ -715,7 +719,7 @@ dependencies = [
 [[package]]
 name = "collab-folder"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "chrono",
@@ -735,7 +739,7 @@ dependencies = [
 [[package]]
 name = "collab-persistence"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bincode",
  "chrono",
@@ -755,7 +759,7 @@ dependencies = [
 [[package]]
 name = "collab-plugins"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -767,7 +771,6 @@ dependencies = [
  "futures-util",
  "parking_lot 0.12.1",
  "rand 0.8.5",
- "rusoto_credential",
  "serde",
  "serde_json",
  "similar 2.2.1",
@@ -784,7 +787,7 @@ dependencies = [
 [[package]]
 name = "collab-sync"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "bytes",
  "collab",
@@ -806,10 +809,11 @@ dependencies = [
 [[package]]
 name = "collab-user"
 version = "0.1.0"
-source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
+source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=223011#223011a4340d0d98e7e10ca6bb19b8d622b0f568"
 dependencies = [
  "anyhow",
  "collab",
+ "collab-define",
  "parking_lot 0.12.1",
  "serde",
  "serde_json",
@@ -1150,27 +1154,6 @@ dependencies = [
  "subtle",
 ]
 
-[[package]]
-name = "dirs-next"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
-dependencies = [
- "cfg-if",
- "dirs-sys-next",
-]
-
-[[package]]
-name = "dirs-sys-next"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
 [[package]]
 name = "dotenv"
 version = "0.15.0"
@@ -3681,17 +3664,6 @@ dependencies = [
  "bitflags",
 ]
 
-[[package]]
-name = "redox_users"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
-dependencies = [
- "getrandom 0.2.9",
- "redox_syscall 0.2.16",
- "thiserror",
-]
-
 [[package]]
 name = "regex"
 version = "1.7.3"
@@ -3831,24 +3803,6 @@ dependencies = [
  "librocksdb-sys",
 ]
 
-[[package]]
-name = "rusoto_credential"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05"
-dependencies = [
- "async-trait",
- "chrono",
- "dirs-next",
- "futures",
- "hyper",
- "serde",
- "serde_json",
- "shlex",
- "tokio",
- "zeroize",
-]
-
 [[package]]
 name = "rust_decimal"
 version = "1.29.1"
@@ -5408,12 +5362,6 @@ dependencies = [
  "thiserror",
 ]
 
-[[package]]
-name = "zeroize"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
-
 [[package]]
 name = "zip"
 version = "0.6.6"

+ 8 - 9
frontend/rust-lib/Cargo.toml

@@ -40,14 +40,14 @@ opt-level = 3
 incremental = false
 
 [patch.crates-io]
-collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
-collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "1b297c" }
+collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
+collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "223011" }
 
 #collab = { path = "../AppFlowy-Collab/collab" }
 #collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
@@ -57,4 +57,3 @@ collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev =
 #appflowy-integrate = { path = "../AppFlowy-Collab/appflowy-integrate" }
 #collab-user = { path = "../AppFlowy-Collab/collab-user" }
 #collab-define = { path = "../AppFlowy-Collab/collab-define" }
-

+ 9 - 3
frontend/rust-lib/flowy-database2/src/entities/field_settings_entities.rs

@@ -8,7 +8,7 @@ use crate::impl_into_field_visibility;
 use crate::services::field_settings::{FieldSettings, FieldSettingsChangesetParams};
 
 /// Defines the field settings for a field in a view.
-#[derive(Debug, Default, Clone, ProtoBuf)]
+#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
 pub struct FieldSettingsPB {
   #[pb(index = 1)]
   pub field_id: String,
@@ -27,7 +27,7 @@ impl From<FieldSettings> for FieldSettingsPB {
 }
 
 #[repr(u8)]
-#[derive(Debug, Default, Clone, ProtoBuf_Enum, PartialEq)]
+#[derive(Debug, Default, Clone, ProtoBuf_Enum, Eq, PartialEq)]
 pub enum FieldVisibility {
   #[default]
   AlwaysShown = 0,
@@ -77,12 +77,18 @@ impl TryInto<(String, Vec<String>)> for FieldIdsPB {
   }
 }
 
-#[derive(Debug, Default, Clone, ProtoBuf)]
+#[derive(Debug, Default, Clone, ProtoBuf, Eq, PartialEq)]
 pub struct RepeatedFieldSettingsPB {
   #[pb(index = 1)]
   pub items: Vec<FieldSettingsPB>,
 }
 
+impl std::convert::From<Vec<FieldSettingsPB>> for RepeatedFieldSettingsPB {
+  fn from(items: Vec<FieldSettingsPB>) -> Self {
+    Self { items }
+  }
+}
+
 #[derive(Debug, Default, Clone, ProtoBuf)]
 pub struct FieldSettingsChangesetPB {
   #[pb(index = 1)]

+ 6 - 2
frontend/rust-lib/flowy-database2/src/entities/setting_entities.rs

@@ -9,8 +9,9 @@ use flowy_error::ErrorCode;
 use crate::entities::parser::NotEmptyStr;
 use crate::entities::{
   CalendarLayoutSettingPB, DeleteFilterParams, DeleteFilterPayloadPB, DeleteSortParams,
-  DeleteSortPayloadPB, RepeatedFilterPB, RepeatedGroupSettingPB, RepeatedSortPB,
-  UpdateFilterParams, UpdateFilterPayloadPB, UpdateGroupPB, UpdateSortParams, UpdateSortPayloadPB,
+  DeleteSortPayloadPB, RepeatedFieldSettingsPB, RepeatedFilterPB, RepeatedGroupSettingPB,
+  RepeatedSortPB, UpdateFilterParams, UpdateFilterPayloadPB, UpdateGroupPB, UpdateSortParams,
+  UpdateSortPayloadPB,
 };
 use crate::services::setting::CalendarLayoutSetting;
 
@@ -31,6 +32,9 @@ pub struct DatabaseViewSettingPB {
 
   #[pb(index = 5)]
   pub sorts: RepeatedSortPB,
+
+  #[pb(index = 6)]
+  pub field_settings: RepeatedFieldSettingsPB,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)]

+ 29 - 2
frontend/rust-lib/flowy-database2/src/event_handler.rs

@@ -901,12 +901,39 @@ pub(crate) async fn get_field_settings_handler(
   let manager = upgrade_manager(manager)?;
   let (view_id, field_ids) = data.into_inner().try_into()?;
   let database_editor = manager.get_database_with_view_id(&view_id).await?;
+
+  let layout_ty = database_editor.get_layout_type(view_id.as_ref()).await;
+
   let field_settings = database_editor
-    .get_field_settings(&view_id, field_ids)
+    .get_field_settings(&view_id, layout_ty, field_ids.clone())
     .await?
     .into_iter()
     .map(FieldSettingsPB::from)
-    .collect::<Vec<FieldSettingsPB>>();
+    .collect();
+
+  data_result_ok(RepeatedFieldSettingsPB {
+    items: field_settings,
+  })
+}
+
+#[tracing::instrument(level = "debug", skip_all, err)]
+pub(crate) async fn get_all_field_settings_handler(
+  data: AFPluginData<DatabaseViewIdPB>,
+  manager: AFPluginState<Weak<DatabaseManager>>,
+) -> DataResult<RepeatedFieldSettingsPB, FlowyError> {
+  let manager = upgrade_manager(manager)?;
+  let view_id = data.into_inner();
+  let database_editor = manager.get_database_with_view_id(view_id.as_ref()).await?;
+
+  let layout_ty = database_editor.get_layout_type(view_id.as_ref()).await;
+
+  let field_settings = database_editor
+    .get_all_field_settings(view_id.as_ref(), layout_ty)
+    .await?
+    .into_iter()
+    .map(FieldSettingsPB::from)
+    .collect();
+
   data_result_ok(RepeatedFieldSettingsPB {
     items: field_settings,
   })

+ 5 - 1
frontend/rust-lib/flowy-database2/src/event_map.rs

@@ -77,6 +77,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
         .event(DatabaseEvent::GetDatabaseSnapshots, get_snapshots_handler)
         // Field settings
         .event(DatabaseEvent::GetFieldSettings, get_field_settings_handler)
+        .event(DatabaseEvent::GetAllFieldSettings, get_all_field_settings_handler)
         .event(DatabaseEvent::UpdateFieldSettings, update_field_settings_handler)
 }
 
@@ -324,7 +325,10 @@ pub enum DatabaseEvent {
   #[event(input = "FieldIdsPB", output = "RepeatedFieldSettingsPB")]
   GetFieldSettings = 160,
 
+  #[event(input = "DatabaseViewIdPB", output = "RepeatedFieldSettingsPB")]
+  GetAllFieldSettings = 161,
+
   /// Updates the field settings for a field in the given view
   #[event(input = "FieldSettingsChangesetPB")]
-  UpdateFieldSettings = 161,
+  UpdateFieldSettings = 162,
 }

+ 6 - 11
frontend/rust-lib/flowy-database2/src/manager.rs

@@ -25,9 +25,7 @@ use crate::entities::{
 use crate::notification::{send_notification, DatabaseNotification};
 use crate::services::database::DatabaseEditor;
 use crate::services::database_view::DatabaseLayoutDepsResolver;
-use crate::services::field_settings::{
-  default_field_settings_by_layout, default_field_settings_by_layout_map,
-};
+use crate::services::field_settings::default_field_settings_by_layout_map;
 use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
 
 pub trait DatabaseUser: Send + Sync {
@@ -252,18 +250,15 @@ impl DatabaseManager {
     database_view_id: String,
   ) -> FlowyResult<()> {
     let wdb = self.get_workspace_database().await?;
-    let mut params = CreateViewParams::new(
-      database_id.clone(),
-      database_view_id,
-      name,
-      layout,
-      default_field_settings_by_layout(layout),
-    );
+    let mut params = CreateViewParams::new(database_id.clone(), database_view_id, name, layout);
     if let Some(database) = wdb.get_database(&database_id).await {
       let (field, layout_setting) = DatabaseLayoutDepsResolver::new(database, layout)
         .resolve_deps_when_create_database_linked_view();
       if let Some(field) = field {
-        params = params.with_deps_fields(vec![field], default_field_settings_by_layout_map())
+        params = params.with_deps_fields(
+          vec![field.clone()],
+          vec![default_field_settings_by_layout_map()],
+        );
       }
       if let Some(layout_setting) = layout_setting {
         params = params.with_layout_setting(layout_setting);

+ 101 - 46
frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs

@@ -28,8 +28,7 @@ use crate::services::field::{
   SelectOptionIds, TypeOptionCellDataHandler, TypeOptionCellExt,
 };
 use crate::services::field_settings::{
-  default_field_settings_by_layout, default_field_settings_by_layout_map, FieldSettings,
-  FieldSettingsChangesetParams,
+  default_field_settings_by_layout_map, FieldSettings, FieldSettingsChangesetParams,
 };
 use crate::services::filter::Filter;
 use crate::services::group::{
@@ -109,6 +108,15 @@ impl DatabaseEditor {
 
   pub async fn close(&self) {}
 
+  pub async fn get_layout_type(&self, view_id: &str) -> DatabaseLayout {
+    let view = self.database_views.get_view_editor(view_id).await.ok();
+    if let Some(editor) = view {
+      editor.v_get_layout_type().await
+    } else {
+      DatabaseLayout::default()
+    }
+  }
+
   pub async fn update_view_layout(
     &self,
     view_id: &str,
@@ -1107,18 +1115,57 @@ impl DatabaseEditor {
   pub async fn get_field_settings(
     &self,
     view_id: &str,
+    layout_ty: DatabaseLayout,
     field_ids: Vec<String>,
-  ) -> Result<Vec<FieldSettings>, anyhow::Error> {
+  ) -> FlowyResult<Vec<FieldSettings>> {
     let view = self.database_views.get_view_editor(view_id).await?;
-    view.v_get_field_settings(field_ids).await
+    let default_field_settings = default_field_settings_by_layout_map()
+      .get(&layout_ty)
+      .unwrap()
+      .to_owned();
+
+    let found_field_settings = view.v_get_field_settings(&field_ids).await;
+
+    let field_settings = field_ids
+      .into_iter()
+      .map(|field_id| {
+        if let Some(field_settings) = found_field_settings.get(&field_id) {
+          field_settings.to_owned()
+        } else {
+          FieldSettings::try_from_anymap(field_id, default_field_settings.clone()).unwrap()
+        }
+      })
+      .collect();
+
+    Ok(field_settings)
   }
 
   pub async fn get_all_field_settings(
     &self,
     view_id: &str,
-  ) -> Result<Vec<FieldSettings>, anyhow::Error> {
+    layout_ty: DatabaseLayout,
+  ) -> FlowyResult<Vec<FieldSettings>> {
     let view = self.database_views.get_view_editor(view_id).await?;
-    view.v_get_all_field_settings().await
+    let default_field_settings = default_field_settings_by_layout_map()
+      .get(&layout_ty)
+      .unwrap()
+      .to_owned();
+    let fields = self.get_fields(view_id, None);
+
+    let found_field_settings = view.v_get_all_field_settings().await;
+
+    let field_settings = fields
+      .into_iter()
+      .map(|field| {
+        if let Some(field_settings) = found_field_settings.get(&field.id) {
+          field_settings.to_owned()
+        } else {
+          FieldSettings::try_from_anymap(field.id, default_field_settings.clone()).unwrap()
+        }
+      })
+      .collect();
+
+    Ok(field_settings)
   }
 
   pub async fn update_field_settings_with_changeset(
@@ -1352,10 +1399,6 @@ impl DatabaseViewData for DatabaseViewDataImpl {
       .insert_layout_setting(view_id, layout_ty, layout_setting);
   }
 
-  fn get_layout_type(&self, view_id: &str) -> DatabaseLayout {
-    self.database.lock().views.get_database_view_layout(view_id)
-  }
-
   fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout) {
     self
       .database
@@ -1379,30 +1422,40 @@ impl DatabaseViewData for DatabaseViewDataImpl {
   fn get_field_settings(
     &self,
     view_id: &str,
-    field_ids: Vec<String>,
-  ) -> Result<Vec<FieldSettings>, anyhow::Error> {
+    field_ids: &Vec<String>,
+  ) -> HashMap<String, FieldSettings> {
     let field_settings_map = self
       .database
       .lock()
-      .get_field_settings(view_id, Some(field_ids));
+      .get_field_settings(view_id, Some(&field_ids));
 
-    let field_settings: Result<Vec<FieldSettings>, anyhow::Error> = field_settings_map
+    field_settings_map
       .into_iter()
-      .map(|(field_id, field_settings)| FieldSettings::try_from_anymap(field_id, field_settings))
-      .collect();
-
-    field_settings
+      .filter_map(|(field_id, field_settings)| {
+        let field_settings = FieldSettings::try_from_anymap(field_id.clone(), field_settings);
+        if let Ok(settings) = field_settings {
+          Some((field_id, settings))
+        } else {
+          None
+        }
+      })
+      .collect()
   }
 
-  fn get_all_field_settings(&self, view_id: &str) -> Result<Vec<FieldSettings>, anyhow::Error> {
+  fn get_all_field_settings(&self, view_id: &str) -> HashMap<String, FieldSettings> {
     let field_settings_map = self.database.lock().get_field_settings(view_id, None);
 
-    let field_settings: Result<Vec<FieldSettings>, anyhow::Error> = field_settings_map
+    field_settings_map
       .into_iter()
-      .map(|(field_id, field_settings)| FieldSettings::try_from_anymap(field_id, field_settings))
-      .collect();
-
-    field_settings
+      .filter_map(|(field_id, field_settings)| {
+        let field_settings = FieldSettings::try_from_anymap(field_id.clone(), field_settings);
+        if let Ok(settings) = field_settings {
+          Some((field_id, settings))
+        } else {
+          None
+        }
+      })
+      .collect()
   }
 
   fn update_field_settings(
@@ -1411,32 +1464,34 @@ impl DatabaseViewData for DatabaseViewDataImpl {
     field_id: &str,
     visibility: Option<FieldVisibility>,
   ) {
-    let field_settings = self
-      .get_field_settings(view_id, vec![field_id.to_string()])
-      .ok();
-
-    let new_field_settings = match field_settings {
-      Some(field_settings) => {
-        let mut field_settings = field_settings.first().unwrap().clone();
-        field_settings.visibility = visibility.unwrap_or(field_settings.visibility);
-        field_settings
-      },
-      None => {
-        let layout_ty = self.get_layout_for_view(view_id);
-        let mut field_settings = FieldSettings::try_from_anymap(
-          field_id.to_string(),
-          default_field_settings_by_layout(layout_ty),
-        )
-        .unwrap();
-        field_settings.visibility = visibility.unwrap_or(field_settings.visibility);
-        field_settings
-      },
+    let field_settings_map = self.get_field_settings(view_id, &vec![field_id.to_string()]);
+
+    let new_field_settings = if let Some(field_settings) = field_settings_map.get(field_id) {
+      let mut field_settings = field_settings.to_owned();
+      field_settings.visibility = visibility.unwrap_or(field_settings.visibility);
+      field_settings
+    } else {
+      let layout_ty = self.get_layout_for_view(view_id);
+      let mut field_settings = FieldSettings::try_from_anymap(
+        field_id.to_string(),
+        default_field_settings_by_layout_map()
+          .get(&layout_ty)
+          .unwrap()
+          .to_owned(),
+      )
+      .unwrap();
+      field_settings.visibility = visibility.unwrap_or(field_settings.visibility);
+      field_settings
     };
 
     self.database.lock().update_field_settings(
       view_id,
       Some(vec![field_id.to_string()]),
-      new_field_settings,
-    )
+      new_field_settings.clone(),
+    );
+
+    send_notification(view_id, DatabaseNotification::DidUpdateFieldSettings)
+      .payload(FieldSettingsPB::from(new_field_settings))
+      .send()
   }
 }

+ 12 - 1
frontend/rust-lib/flowy-database2/src/services/database/util.rs

@@ -2,8 +2,9 @@ use collab_database::views::DatabaseView;
 
 use crate::entities::{
   CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseLayoutSettingPB, DatabaseViewSettingPB,
-  FilterPB, GroupSettingPB, SortPB,
+  FieldSettingsPB, FilterPB, GroupSettingPB, SortPB,
 };
+use crate::services::field_settings::FieldSettings;
 use crate::services::filter::Filter;
 use crate::services::group::GroupSetting;
 use crate::services::setting::CalendarLayoutSetting;
@@ -30,6 +31,7 @@ pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> Database
       Err(_) => None,
     })
     .collect::<Vec<FilterPB>>();
+
   let group_settings = view
     .group_settings
     .into_iter()
@@ -48,11 +50,20 @@ pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> Database
     })
     .collect::<Vec<SortPB>>();
 
+  let field_settings = view
+    .field_settings
+    .into_inner()
+    .into_iter()
+    .flat_map(|(field_id, field_settings)| FieldSettings::try_from_anymap(field_id, field_settings))
+    .map(FieldSettingsPB::from)
+    .collect::<Vec<FieldSettingsPB>>();
+
   DatabaseViewSettingPB {
     layout_type,
     filters: filters.into(),
     group_settings: group_settings.into(),
     sorts: sorts.into(),
+    field_settings: field_settings.into(),
     layout_setting,
   }
 }

+ 12 - 10
frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs

@@ -75,6 +75,8 @@ pub trait DatabaseViewData: Send + Sync + 'static {
 
   fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Fut<Arc<RowCell>>;
 
+  /// Return the database layout type for the view with given view_id
+  /// The default layout type is [DatabaseLayout::Grid]
   fn get_layout_for_view(&self, view_id: &str) -> DatabaseLayout;
 
   fn get_group_setting(&self, view_id: &str) -> Vec<GroupSetting>;
@@ -110,10 +112,6 @@ pub trait DatabaseViewData: Send + Sync + 'static {
     layout_setting: LayoutSetting,
   );
 
-  /// Return the database layout type for the view with given view_id
-  /// The default layout type is [DatabaseLayout::Grid]
-  fn get_layout_type(&self, view_id: &str) -> DatabaseLayout;
-
   fn update_layout_type(&self, view_id: &str, layout_type: &DatabaseLayout);
 
   /// Returns a `TaskDispatcher` used to poll a `Task`
@@ -128,10 +126,10 @@ pub trait DatabaseViewData: Send + Sync + 'static {
   fn get_field_settings(
     &self,
     view_id: &str,
-    field_ids: Vec<String>,
-  ) -> Result<Vec<FieldSettings>, anyhow::Error>;
+    field_ids: &Vec<String>,
+  ) -> HashMap<String, FieldSettings>;
 
-  fn get_all_field_settings(&self, view_id: &str) -> Result<Vec<FieldSettings>, anyhow::Error>;
+  fn get_all_field_settings(&self, view_id: &str) -> HashMap<String, FieldSettings>;
 
   fn update_field_settings(
     &self,
@@ -864,6 +862,10 @@ impl DatabaseViewEditor {
     Some(events)
   }
 
+  pub async fn v_get_layout_type(&self) -> DatabaseLayout {
+    self.delegate.get_layout_for_view(&self.view_id)
+  }
+
   #[tracing::instrument(level = "trace", skip_all)]
   pub async fn v_update_layout_type(&self, new_layout_type: DatabaseLayout) -> FlowyResult<()> {
     self
@@ -910,12 +912,12 @@ impl DatabaseViewEditor {
 
   pub async fn v_get_field_settings(
     &self,
-    field_ids: Vec<String>,
-  ) -> Result<Vec<FieldSettings>, anyhow::Error> {
+    field_ids: &Vec<String>,
+  ) -> HashMap<String, FieldSettings> {
     self.delegate.get_field_settings(&self.view_id, field_ids)
   }
 
-  pub async fn v_get_all_field_settings(&self) -> Result<Vec<FieldSettings>, anyhow::Error> {
+  pub async fn v_get_all_field_settings(&self) -> HashMap<String, FieldSettings> {
     self.delegate.get_all_field_settings(&self.view_id)
   }
 

+ 0 - 35
frontend/rust-lib/flowy-database2/src/services/field_settings/field_settings.rs

@@ -1,35 +0,0 @@
-use std::collections::HashMap;
-
-use strum::IntoEnumIterator;
-
-use collab_database::views::{DatabaseLayout, FieldSettingsMap, FieldSettingsMapBuilder};
-
-use crate::{entities::FieldVisibility, services::field_settings::VISIBILITY};
-
-/// Creates a map of the database layout and the default field settings for fields
-/// in a view of that database layout
-pub fn default_field_settings_by_layout_map() -> HashMap<DatabaseLayout, FieldSettingsMap> {
-  let mut template = HashMap::new();
-  for layout_ty in DatabaseLayout::iter() {
-    template.insert(layout_ty, default_field_settings_by_layout(layout_ty));
-  }
-
-  template
-}
-
-/// Returns the default FieldSettingsMap for the given database layout
-pub fn default_field_settings_by_layout(layout_ty: DatabaseLayout) -> FieldSettingsMap {
-  let visibility = default_visibility(layout_ty);
-  FieldSettingsMapBuilder::new()
-    .insert_i64_value(VISIBILITY, visibility.into())
-    .build()
-}
-
-/// Returns the default visibility of a field for the given database layout
-pub fn default_visibility(layout_ty: DatabaseLayout) -> FieldVisibility {
-  match layout_ty {
-    DatabaseLayout::Grid => FieldVisibility::AlwaysShown,
-    DatabaseLayout::Board => FieldVisibility::HideWhenEmpty,
-    DatabaseLayout::Calendar => FieldVisibility::HideWhenEmpty,
-  }
-}

+ 81 - 10
frontend/rust-lib/flowy-database2/src/services/field_settings/field_settings_builder.rs

@@ -1,8 +1,16 @@
-use collab_database::views::DatabaseLayout;
+use std::collections::HashMap;
+use std::sync::Arc;
+
+use collab_database::database::MutexDatabase;
+use collab_database::fields::Field;
+use collab_database::views::{
+  DatabaseLayout, FieldSettingsByFieldIdMap, FieldSettingsMap, FieldSettingsMapBuilder,
+};
+use strum::IntoEnumIterator;
 
 use crate::entities::FieldVisibility;
 
-use crate::services::field_settings::{default_visibility, FieldSettings};
+use crate::services::field_settings::{FieldSettings, VISIBILITY};
 
 /// Helper struct to create a new field setting
 pub struct FieldSettingsBuilder {
@@ -18,14 +26,6 @@ impl FieldSettingsBuilder {
     Self { field_settings }
   }
 
-  pub fn from_layout_type(field_id: &str, layout_ty: DatabaseLayout) -> Self {
-    let field_settings = FieldSettings {
-      field_id: field_id.to_string(),
-      visibility: default_visibility(layout_ty),
-    };
-    Self { field_settings }
-  }
-
   pub fn field_id(mut self, field_id: &str) -> Self {
     self.field_settings.field_id = field_id.to_string();
     self
@@ -40,3 +40,74 @@ impl FieldSettingsBuilder {
     self.field_settings
   }
 }
+
+pub struct DatabaseFieldSettingsMapBuilder {
+  pub fields: Vec<Field>,
+  pub database_layout: DatabaseLayout,
+}
+
+impl DatabaseFieldSettingsMapBuilder {
+  pub fn new(fields: Vec<Field>, database_layout: DatabaseLayout) -> Self {
+    Self {
+      fields,
+      database_layout,
+    }
+  }
+
+  pub fn from_database(database: Arc<MutexDatabase>, database_layout: DatabaseLayout) -> Self {
+    let fields = database.lock().get_fields(None);
+    Self {
+      fields,
+      database_layout,
+    }
+  }
+
+  pub fn build(self) -> FieldSettingsByFieldIdMap {
+    self
+      .fields
+      .into_iter()
+      .map(|field| {
+        let field_settings = field_settings_for_field(self.database_layout, &field);
+        (field.id, field_settings)
+      })
+      .collect::<HashMap<String, FieldSettingsMap>>()
+      .into()
+  }
+}
+
+pub fn field_settings_for_field(
+  database_layout: DatabaseLayout,
+  field: &Field,
+) -> FieldSettingsMap {
+  let visibility = if field.is_primary {
+    FieldVisibility::AlwaysShown
+  } else {
+    match database_layout {
+      DatabaseLayout::Grid => FieldVisibility::AlwaysShown,
+      DatabaseLayout::Board => FieldVisibility::HideWhenEmpty,
+      DatabaseLayout::Calendar => FieldVisibility::HideWhenEmpty,
+    }
+  };
+
+  FieldSettingsBuilder::new(&field.id)
+    .visibility(visibility)
+    .build()
+    .into()
+}
+
+pub fn default_field_settings_by_layout_map() -> HashMap<DatabaseLayout, FieldSettingsMap> {
+  let mut map = HashMap::new();
+  for layout_ty in DatabaseLayout::iter() {
+    let visibility = match layout_ty {
+      DatabaseLayout::Grid => FieldVisibility::AlwaysShown,
+      DatabaseLayout::Board => FieldVisibility::HideWhenEmpty,
+      DatabaseLayout::Calendar => FieldVisibility::HideWhenEmpty,
+    };
+    let field_settings = FieldSettingsMapBuilder::new()
+      .insert_i64_value(VISIBILITY, visibility.into())
+      .build();
+    map.insert(layout_ty, field_settings);
+  }
+
+  map
+}

+ 0 - 3
frontend/rust-lib/flowy-database2/src/services/field_settings/mod.rs

@@ -1,8 +1,5 @@
 pub use entities::*;
-pub use field_settings::*;
 pub use field_settings_builder::*;
 
 mod entities;
-#[allow(clippy::module_inception)]
-mod field_settings;
 mod field_settings_builder;

+ 5 - 2
frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs

@@ -9,7 +9,7 @@ use flowy_error::{FlowyError, FlowyResult};
 
 use crate::entities::FieldType;
 use crate::services::field::{default_type_option_data_from_type, CELL_DATA};
-use crate::services::field_settings::default_field_settings_by_layout;
+use crate::services::field_settings::DatabaseFieldSettingsMapBuilder;
 use crate::services::share::csv::CSVFormat;
 
 #[derive(Default)]
@@ -97,6 +97,9 @@ fn database_from_fields_and_rows(
     })
     .collect::<Vec<Field>>();
 
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Grid).build();
+
   let created_rows = rows
     .iter()
     .map(|cells| {
@@ -135,7 +138,7 @@ fn database_from_fields_and_rows(
     sorts: vec![],
     created_rows,
     fields,
-    field_settings: default_field_settings_by_layout(DatabaseLayout::Grid),
+    field_settings,
   }
 }
 

+ 22 - 7
frontend/rust-lib/flowy-database2/src/template.rs

@@ -7,7 +7,7 @@ use crate::services::cell::{insert_select_option_cell, insert_text_cell};
 use crate::services::field::{
   FieldBuilder, SelectOption, SelectOptionColor, SingleSelectTypeOption,
 };
-use crate::services::field_settings::default_field_settings_by_layout;
+use crate::services::field_settings::DatabaseFieldSettingsMapBuilder;
 use crate::services::setting::CalendarLayoutSetting;
 
 pub fn make_default_grid(view_id: &str, name: &str) -> CreateDatabaseParams {
@@ -27,6 +27,11 @@ pub fn make_default_grid(view_id: &str, name: &str) -> CreateDatabaseParams {
     .visibility(true)
     .build();
 
+  let fields = vec![text_field, single_select, checkbox_field];
+
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Grid).build();
+
   CreateDatabaseParams {
     database_id: gen_database_id(),
     view_id: view_id.to_string(),
@@ -41,8 +46,8 @@ pub fn make_default_grid(view_id: &str, name: &str) -> CreateDatabaseParams {
       CreateRowParams::new(gen_row_id()),
       CreateRowParams::new(gen_row_id()),
     ],
-    fields: vec![text_field, single_select, checkbox_field],
-    field_settings: default_field_settings_by_layout(DatabaseLayout::Grid),
+    fields,
+    field_settings,
   }
 }
 
@@ -83,6 +88,11 @@ pub fn make_default_board(view_id: &str, name: &str) -> CreateDatabaseParams {
     rows.push(row);
   }
 
+  let fields = vec![text_field, single_select];
+
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Board).build();
+
   CreateDatabaseParams {
     database_id: gen_database_id(),
     view_id: view_id.to_string(),
@@ -93,8 +103,8 @@ pub fn make_default_board(view_id: &str, name: &str) -> CreateDatabaseParams {
     groups: vec![],
     sorts: vec![],
     created_rows: rows,
-    fields: vec![text_field, single_select],
-    field_settings: default_field_settings_by_layout(DatabaseLayout::Board),
+    fields,
+    field_settings,
   }
 }
 
@@ -119,6 +129,11 @@ pub fn make_default_calendar(view_id: &str, name: &str) -> CreateDatabaseParams
     .visibility(true)
     .build();
 
+  let fields = vec![text_field, date_field, multi_select_field];
+
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Calendar).build();
+
   let mut layout_settings = LayoutSettings::default();
   layout_settings.insert(
     DatabaseLayout::Calendar,
@@ -135,7 +150,7 @@ pub fn make_default_calendar(view_id: &str, name: &str) -> CreateDatabaseParams
     groups: vec![],
     sorts: vec![],
     created_rows: vec![],
-    fields: vec![text_field, date_field, multi_select_field],
-    field_settings: default_field_settings_by_layout(DatabaseLayout::Calendar),
+    fields,
+    field_settings,
   }
 }

+ 16 - 10
frontend/rust-lib/flowy-database2/tests/database/field_settings_test/script.rs

@@ -1,3 +1,4 @@
+use collab_database::views::DatabaseLayout;
 use flowy_database2::entities::FieldVisibility;
 use flowy_database2::services::field_settings::FieldSettingsChangesetParams;
 
@@ -6,10 +7,12 @@ use crate::database::database_editor::DatabaseEditorTest;
 #[allow(clippy::enum_variant_names)]
 pub enum FieldSettingsScript {
   AssertFieldSettings {
-    field_id: String,
+    field_ids: Vec<String>,
+    layout_ty: DatabaseLayout,
     visibility: FieldVisibility,
   },
   AssertAllFieldSettings {
+    layout_ty: DatabaseLayout,
     visibility: FieldVisibility,
   },
   UpdateFieldSettings {
@@ -47,24 +50,27 @@ impl FieldSettingsTest {
   pub async fn run_script(&mut self, script: FieldSettingsScript) {
     match script {
       FieldSettingsScript::AssertFieldSettings {
-        field_id,
+        field_ids,
+        layout_ty,
         visibility,
       } => {
         let field_settings = self
           .editor
-          .get_field_settings(&self.view_id, vec![field_id])
+          .get_field_settings(&self.view_id, layout_ty, field_ids)
           .await
-          .unwrap()
-          .first()
-          .unwrap()
-          .to_owned();
+          .unwrap();
 
-        assert_eq!(field_settings.visibility, visibility)
+        for field_settings in field_settings.into_iter() {
+          assert_eq!(field_settings.visibility, visibility)
+        }
       },
-      FieldSettingsScript::AssertAllFieldSettings { visibility } => {
+      FieldSettingsScript::AssertAllFieldSettings {
+        layout_ty,
+        visibility,
+      } => {
         let field_settings = self
           .editor
-          .get_all_field_settings(&self.view_id)
+          .get_all_field_settings(&self.view_id, layout_ty)
           .await
           .unwrap();
 

+ 64 - 23
frontend/rust-lib/flowy-database2/tests/database/field_settings_test/test.rs

@@ -1,7 +1,6 @@
 use collab_database::views::DatabaseLayout;
 use flowy_database2::entities::FieldType;
 use flowy_database2::entities::FieldVisibility;
-use flowy_database2::services::field_settings::default_visibility;
 
 use crate::database::field_settings_test::script::FieldSettingsScript::*;
 use crate::database::field_settings_test::script::FieldSettingsTest;
@@ -10,45 +9,87 @@ use crate::database::field_settings_test::script::FieldSettingsTest;
 #[tokio::test]
 async fn get_default_field_settings() {
   let mut test = FieldSettingsTest::new_grid().await;
-  let visibility = default_visibility(DatabaseLayout::Grid);
-  let scripts = vec![AssertAllFieldSettings { visibility }];
+  let scripts = vec![AssertAllFieldSettings {
+    layout_ty: DatabaseLayout::Grid,
+    visibility: FieldVisibility::AlwaysShown,
+  }];
   test.run_scripts(scripts).await;
 
   let mut test = FieldSettingsTest::new_board().await;
-  let visibility = default_visibility(DatabaseLayout::Board);
-  let scripts = vec![AssertAllFieldSettings { visibility }];
+  let non_primary_field_ids: Vec<String> = test
+    .get_fields()
+    .into_iter()
+    .filter(|field| !field.is_primary)
+    .map(|field| field.id)
+    .collect();
+  let primary_field_id = test.get_first_field(FieldType::RichText).id;
+  let scripts = vec![
+    AssertFieldSettings {
+      field_ids: non_primary_field_ids.clone(),
+      layout_ty: DatabaseLayout::Board,
+      visibility: FieldVisibility::HideWhenEmpty,
+    },
+    AssertFieldSettings {
+      field_ids: vec![primary_field_id.clone()],
+      layout_ty: DatabaseLayout::Board,
+      visibility: FieldVisibility::AlwaysShown,
+    },
+  ];
   test.run_scripts(scripts).await;
 
   let mut test = FieldSettingsTest::new_calendar().await;
-  let visibility = default_visibility(DatabaseLayout::Calendar);
-  let scripts = vec![AssertAllFieldSettings { visibility }];
+  let non_primary_field_ids: Vec<String> = test
+    .get_fields()
+    .into_iter()
+    .filter(|field| !field.is_primary)
+    .map(|field| field.id)
+    .collect();
+  let primary_field_id = test.get_first_field(FieldType::RichText).id;
+  let scripts = vec![
+    AssertFieldSettings {
+      field_ids: non_primary_field_ids.clone(),
+      layout_ty: DatabaseLayout::Calendar,
+      visibility: FieldVisibility::HideWhenEmpty,
+    },
+    AssertFieldSettings {
+      field_ids: vec![primary_field_id.clone()],
+      layout_ty: DatabaseLayout::Calendar,
+      visibility: FieldVisibility::AlwaysShown,
+    },
+  ];
   test.run_scripts(scripts).await;
 }
 
 /// Update field settings for a field
 #[tokio::test]
 async fn update_field_settings_test() {
-  let mut test = FieldSettingsTest::new_grid().await;
-  let checkbox_field = test.get_first_field(FieldType::Checkbox);
-  let text_field = test.get_first_field(FieldType::RichText);
-  let visibility = default_visibility(DatabaseLayout::Grid);
-  let new_visibility = FieldVisibility::AlwaysHidden;
+  let mut test = FieldSettingsTest::new_board().await;
+  let non_primary_field_ids: Vec<String> = test
+    .get_fields()
+    .into_iter()
+    .filter(|field| !field.is_primary)
+    .map(|field| field.id)
+    .collect();
+  let primary_field_id = test.get_first_field(FieldType::RichText).id;
 
   let scripts = vec![
-    AssertAllFieldSettings {
-      visibility: visibility.clone(),
-    },
-    UpdateFieldSettings {
-      field_id: checkbox_field.id.clone(),
-      visibility: Some(new_visibility.clone()),
-    },
     AssertFieldSettings {
-      field_id: checkbox_field.id,
-      visibility: new_visibility,
+      field_ids: non_primary_field_ids,
+      layout_ty: DatabaseLayout::Board,
+      visibility: FieldVisibility::HideWhenEmpty,
     },
     AssertFieldSettings {
-      field_id: text_field.id,
-      visibility,
+      field_ids: vec![primary_field_id.clone()],
+      layout_ty: DatabaseLayout::Board,
+      visibility: FieldVisibility::AlwaysShown,
+    },
+    UpdateFieldSettings {
+      field_id: primary_field_id,
+      visibility: Some(FieldVisibility::HideWhenEmpty),
+    },
+    AssertAllFieldSettings {
+      layout_ty: DatabaseLayout::Board,
+      visibility: FieldVisibility::HideWhenEmpty,
     },
   ];
   test.run_scripts(scripts).await;

+ 5 - 2
frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs

@@ -1,6 +1,6 @@
 use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
 use collab_database::views::{DatabaseLayout, DatabaseView};
-use flowy_database2::services::field_settings::default_field_settings_by_layout;
+use flowy_database2::services::field_settings::DatabaseFieldSettingsMapBuilder;
 use strum::IntoEnumIterator;
 
 use flowy_database2::entities::FieldType;
@@ -17,8 +17,8 @@ use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, T
 pub fn make_test_board() -> DatabaseData {
   let mut fields = vec![];
   let mut rows = vec![];
+
   // Iterate through the FieldType to create the corresponding Field.
-  let field_settings = default_field_settings_by_layout(DatabaseLayout::Board);
   for field_type in FieldType::iter() {
     match field_type {
       FieldType::RichText => {
@@ -116,6 +116,9 @@ pub fn make_test_board() -> DatabaseData {
     }
   }
 
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Board).build();
+
   // We have many assumptions base on the number of the rows, so do not change the number of the loop.
   for i in 0..5 {
     let mut row_builder = TestRowBuilder::new(gen_row_id(), &fields);

+ 4 - 3
frontend/rust-lib/flowy-database2/tests/database/mock_data/calendar_mock_data.rs

@@ -1,6 +1,6 @@
 use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
 use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, LayoutSettings};
-use flowy_database2::services::field_settings::default_field_settings_by_layout;
+use flowy_database2::services::field_settings::DatabaseFieldSettingsMapBuilder;
 use strum::IntoEnumIterator;
 
 use flowy_database2::entities::FieldType;
@@ -13,7 +13,6 @@ use crate::database::database_editor::TestRowBuilder;
 pub fn make_test_calendar() -> DatabaseData {
   let mut fields = vec![];
   let mut rows = vec![];
-  let field_settings = default_field_settings_by_layout(DatabaseLayout::Calendar);
 
   // text
   let text_field = FieldBuilder::from_field_type(FieldType::RichText)
@@ -32,7 +31,6 @@ pub fn make_test_calendar() -> DatabaseData {
   fields.push(date_field);
 
   // multi select
-
   let type_option = MultiSelectTypeOption::default();
   let multi_select_field = FieldBuilder::new(FieldType::MultiSelect, type_option)
     .name("Tags")
@@ -42,6 +40,9 @@ pub fn make_test_calendar() -> DatabaseData {
 
   let calendar_setting: LayoutSetting = CalendarLayoutSetting::new(date_field_id).into();
 
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Calendar).build();
+
   for i in 0..5 {
     let mut row_builder = TestRowBuilder::new(gen_row_id(), &fields);
     match i {

+ 9 - 3
frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs

@@ -1,6 +1,6 @@
 use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
 use collab_database::views::{DatabaseLayout, DatabaseView};
-use flowy_database2::services::field_settings::default_field_settings_by_layout;
+use flowy_database2::services::field_settings::DatabaseFieldSettingsMapBuilder;
 use strum::IntoEnumIterator;
 
 use flowy_database2::entities::FieldType;
@@ -16,7 +16,7 @@ use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, T
 pub fn make_test_grid() -> DatabaseData {
   let mut fields = vec![];
   let mut rows = vec![];
-  let field_settings = default_field_settings_by_layout(DatabaseLayout::Grid);
+
   // Iterate through the FieldType to create the corresponding Field.
   for field_type in FieldType::iter() {
     match field_type {
@@ -118,6 +118,9 @@ pub fn make_test_grid() -> DatabaseData {
     }
   }
 
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Grid).build();
+
   for i in 0..7 {
     let mut row_builder = TestRowBuilder::new(gen_row_id(), &fields);
     match i {
@@ -254,7 +257,7 @@ pub fn make_test_grid() -> DatabaseData {
 pub fn make_no_date_test_grid() -> DatabaseData {
   let mut fields = vec![];
   let mut rows = vec![];
-  let field_settings = default_field_settings_by_layout(DatabaseLayout::Grid);
+
   // Iterate through the FieldType to create the corresponding Field.
   for field_type in FieldType::iter() {
     match field_type {
@@ -281,6 +284,9 @@ pub fn make_no_date_test_grid() -> DatabaseData {
     }
   }
 
+  let field_settings =
+    DatabaseFieldSettingsMapBuilder::new(fields.clone(), DatabaseLayout::Grid).build();
+
   for i in 0..3 {
     let mut row_builder = TestRowBuilder::new(gen_row_id(), &fields);
     match i {

+ 1 - 1
frontend/rust-lib/flowy-user-deps/Cargo.toml

@@ -13,6 +13,6 @@ serde = { version = "1.0", features = ["derive"] }
 collab-define = { version = "0.1.0" }
 serde_json = {version = "1.0"}
 serde_repr = "0.1"
-chrono = { version = "0.4.22", default-features = false, features = ["clock"] }
+chrono = { version = "0.4.22", default-features = false, features = ["clock", "serde"] }
 anyhow = "1.0.71"
 tokio = { version = "1.26", features = ["sync"] }

+ 6 - 6
frontend/rust-lib/flowy-user/src/entities/reminder.rs

@@ -1,5 +1,4 @@
-use collab_user::core::Reminder;
-
+use appflowy_integrate::reminder::{ObjectType, Reminder};
 use flowy_derive::ProtoBuf;
 
 #[derive(ProtoBuf, Default, Clone)]
@@ -38,10 +37,11 @@ impl From<ReminderPB> for Reminder {
       id: value.id,
       scheduled_at: value.scheduled_at,
       is_ack: value.is_ack,
-      ty: value.ty,
+      ty: ObjectType::Document,
       title: value.title,
       message: value.message,
-      reminder_object_id: value.reminder_object_id,
+      meta: Default::default(),
+      object_id: value.reminder_object_id,
     }
   }
 }
@@ -52,10 +52,10 @@ impl From<Reminder> for ReminderPB {
       id: value.id,
       scheduled_at: value.scheduled_at,
       is_ack: value.is_ack,
-      ty: value.ty,
+      ty: value.ty as i64,
       title: value.title,
       message: value.message,
-      reminder_object_id: value.reminder_object_id,
+      reminder_object_id: value.object_id,
     }
   }
 }

+ 2 - 1
frontend/rust-lib/flowy-user/src/services/user_awareness.rs

@@ -1,8 +1,9 @@
 use std::sync::{Arc, Weak};
 
+use appflowy_integrate::reminder::Reminder;
 use appflowy_integrate::{CollabType, RocksCollabDB};
 use collab::core::collab::{CollabRawData, MutexCollab};
-use collab_user::core::{MutexUserAwareness, Reminder, UserAwareness};
+use collab_user::core::{MutexUserAwareness, UserAwareness};
 
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};