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 <nathan@appflowy.io>
This commit is contained in:
Richard Shiue 2023-09-01 22:40:17 +08:00 committed by GitHub
parent c652c32575
commit 18498c0479
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 909 additions and 546 deletions

View File

@ -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();
if (fields.isEmpty) {
return null;
}
assert(fields.length == 1);
return fields.first;
return _fieldNotifier.fieldInfos
.firstWhereOrNull((element) => element.id == fieldId);
}
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? getFilterByFilterId(String filterId) {
return _filterNotifier?.filters
.firstWhereOrNull((element) => element.filterId == filterId);
}
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;
FilterInfo? getFilterByFieldId(String fieldId) {
return _filterNotifier?.filters
.firstWhereOrNull((element) => element.fieldId == fieldId);
}
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,12 @@ 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);
}
}
_updateFieldInfos();
_sortNotifier?.sorts = sortInfos;
_sortNotifier?.sorts = _sortInfoListFromPBs(sortPBs);
return left(unit);
},
(err) => right(err),
@ -558,6 +588,56 @@ class FieldController {
});
}
/// 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),
);
});
}
/// 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) {
/// Stop listeners, dispose notifiers and clear listener callbacks
Future<void> dispose() async {
if (_isDisposed) {
Log.warn('FieldController is already disposed');
return;
}
final List<FieldInfo> newFields = fieldInfos;
final Map<String, FieldIdPB> deletedFieldMap = {
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
};
_isDisposed = true;
await _fieldListener.stop();
await _filtersListener.stop();
await _settingListener.stop();
await _sortsListener.stop();
await _fieldSettingsListener.stop();
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
_fieldNotifier.fieldInfos = newFields;
}
for (final callback in _fieldCallbacks.values) {
_fieldNotifier.removeListener(callback);
}
_fieldNotifier.dispose();
void _insertFields(List<IndexFieldPB> insertedFields) {
if (insertedFields.isEmpty) {
return;
for (final callback in _filterCallbacks.values) {
_filterNotifier?.removeListener(callback);
}
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;
}
_filterNotifier?.dispose();
_filterNotifier = null;
List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) {
if (updatedFieldPBs.isEmpty) {
return [];
for (final callback in _sortCallbacks.values) {
_sortNotifier?.removeListener(callback);
}
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;
_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,
);
}

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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 {

View File

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

View File

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

View File

@ -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"

View File

@ -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" }

View File

@ -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"

View File

@ -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" }

View File

@ -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)]

View File

@ -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)]

View File

@ -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,
})

View File

@ -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,
}

View File

@ -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);

View File

@ -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 field_settings_map = self.get_field_settings(view_id, &vec![field_id.to_string()]);
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 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()
}
}

View File

@ -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,
}
}

View File

@ -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)
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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();

View File

@ -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(),
AssertFieldSettings {
field_ids: non_primary_field_ids,
layout_ty: DatabaseLayout::Board,
visibility: FieldVisibility::HideWhenEmpty,
},
AssertFieldSettings {
field_ids: vec![primary_field_id.clone()],
layout_ty: DatabaseLayout::Board,
visibility: FieldVisibility::AlwaysShown,
},
UpdateFieldSettings {
field_id: checkbox_field.id.clone(),
visibility: Some(new_visibility.clone()),
field_id: primary_field_id,
visibility: Some(FieldVisibility::HideWhenEmpty),
},
AssertFieldSettings {
field_id: checkbox_field.id,
visibility: new_visibility,
},
AssertFieldSettings {
field_id: text_field.id,
visibility,
AssertAllFieldSettings {
layout_ty: DatabaseLayout::Board,
visibility: FieldVisibility::HideWhenEmpty,
},
];
test.run_scripts(scripts).await;

View File

@ -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);

View File

@ -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 {

View File

@ -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 {

View File

@ -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"] }

View File

@ -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,
}
}
}

View File

@ -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};