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