diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart index 0955559ce4..9aa72bcb88 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_cache.dart @@ -1,6 +1,6 @@ part of 'cell_service.dart'; -typedef CellByFieldId = LinkedHashMap; +typedef CellContextByFieldId = LinkedHashMap; class DatabaseCell { dynamic object; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart index 3f7d9e2f7a..cac1857281 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart @@ -22,7 +22,7 @@ import 'cell_service.dart'; /// // ignore: must_be_immutable class CellController extends Equatable { - final CellIdentifier cellId; + final DatabaseCellContext cellContext; final CellCache _cellCache; final CellCacheKey _cacheKey; final FieldBackendService _fieldBackendSvc; @@ -37,37 +37,37 @@ class CellController extends Equatable { Timer? _loadDataOperation; Timer? _saveDataOperation; - String get viewId => cellId.viewId; + String get viewId => cellContext.viewId; - RowId get rowId => cellId.rowId; + RowId get rowId => cellContext.rowId; - String get fieldId => cellId.fieldInfo.id; + String get fieldId => cellContext.fieldInfo.id; - FieldInfo get fieldInfo => cellId.fieldInfo; + FieldInfo get fieldInfo => cellContext.fieldInfo; - FieldType get fieldType => cellId.fieldInfo.fieldType; + FieldType get fieldType => cellContext.fieldInfo.fieldType; CellController({ - required this.cellId, + required this.cellContext, required CellCache cellCache, required CellDataLoader cellDataLoader, required CellDataPersistence cellDataPersistence, }) : _cellCache = cellCache, _cellDataLoader = cellDataLoader, _cellDataPersistence = cellDataPersistence, - _fieldListener = SingleFieldListener(fieldId: cellId.fieldId), + _fieldListener = SingleFieldListener(fieldId: cellContext.fieldId), _fieldBackendSvc = FieldBackendService( - viewId: cellId.viewId, - fieldId: cellId.fieldInfo.id, + viewId: cellContext.viewId, + fieldId: cellContext.fieldInfo.id, ), _cacheKey = CellCacheKey( - rowId: cellId.rowId, - fieldId: cellId.fieldInfo.id, + rowId: cellContext.rowId, + fieldId: cellContext.fieldInfo.id, ) { _cellDataNotifier = CellDataNotifier(value: _cellCache.get(_cacheKey)); _cellListener = CellListener( - rowId: cellId.rowId, - fieldId: cellId.fieldInfo.id, + rowId: cellContext.rowId, + fieldId: cellContext.fieldInfo.id, ); /// 1.Listen on user edit event and load the new cell data if needed. @@ -195,8 +195,10 @@ class CellController extends Equatable { } @override - List get props => - [_cellCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id]; + List get props => [ + _cellCache.get(_cacheKey) ?? "", + cellContext.rowId + cellContext.fieldInfo.id + ]; } class CellDataNotifier extends ChangeNotifier { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart index c4a30d2361..70fee21e35 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart @@ -17,104 +17,111 @@ typedef DateCellController = CellController; typedef URLCellController = CellController; class CellControllerBuilder { - final CellIdentifier _cellId; + final DatabaseCellContext _cellContext; final CellCache _cellCache; CellControllerBuilder({ - required CellIdentifier cellId, + required DatabaseCellContext cellContext, required CellCache cellCache, }) : _cellCache = cellCache, - _cellId = cellId; + _cellContext = cellContext; CellController build() { - switch (_cellId.fieldType) { + switch (_cellContext.fieldType) { case FieldType.Checkbox: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: StringCellDataParser(), ); return TextCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); case FieldType.DateTime: case FieldType.LastEditedTime: case FieldType.CreatedTime: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: DateCellDataParser(), reloadOnFieldChanged: true, ); return DateCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: DateCellDataPersistence(cellId: _cellId), + cellDataPersistence: + DateCellDataPersistence(cellContext: _cellContext), ); case FieldType.Number: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: NumberCellDataParser(), reloadOnFieldChanged: true, ); return NumberCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); case FieldType.RichText: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: StringCellDataParser(), ); return TextCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); case FieldType.MultiSelect: case FieldType.SingleSelect: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: SelectOptionCellDataParser(), reloadOnFieldChanged: true, ); return SelectOptionCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); case FieldType.Checklist: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: ChecklistCellDataParser(), reloadOnFieldChanged: true, ); return ChecklistCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); case FieldType.URL: final cellDataLoader = CellDataLoader( - cellId: _cellId, + cellContext: _cellContext, parser: URLCellDataParser(), ); return URLCellController( - cellId: _cellId, + cellContext: _cellContext, cellCache: _cellCache, cellDataLoader: cellDataLoader, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + cellDataPersistence: + TextCellDataPersistence(cellContext: _cellContext), ); } throw UnimplementedError; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_loader.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_loader.dart index ab1deabd1c..f8be693e6b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_loader.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_loader.dart @@ -11,18 +11,18 @@ abstract class CellDataParser { class CellDataLoader { final CellBackendService service = CellBackendService(); - final CellIdentifier cellId; + final DatabaseCellContext cellContext; final CellDataParser parser; final bool reloadOnFieldChanged; CellDataLoader({ - required this.cellId, + required this.cellContext, required this.parser, this.reloadOnFieldChanged = false, }); Future loadData() { - final fut = service.getCell(cellId: cellId); + final fut = service.getCell(cellContext: cellContext); return fut.then( (result) => result.fold( (CellPB cell) { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart index 2604187381..8b039bd31f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_data_persistence.dart @@ -7,16 +7,17 @@ abstract class CellDataPersistence { } class TextCellDataPersistence implements CellDataPersistence { - final CellIdentifier cellId; + final DatabaseCellContext cellContext; final _cellBackendSvc = CellBackendService(); TextCellDataPersistence({ - required this.cellId, + required this.cellContext, }); @override Future> save(String data) async { - final fut = _cellBackendSvc.updateCell(cellId: cellId, data: data); + final fut = + _cellBackendSvc.updateCell(cellContext: cellContext, data: data); return fut.then((result) { return result.fold( (l) => none(), @@ -36,14 +37,15 @@ class DateCellData with _$DateCellData { } class DateCellDataPersistence implements CellDataPersistence { - final CellIdentifier cellId; + final DatabaseCellContext cellContext; DateCellDataPersistence({ - required this.cellId, + required this.cellContext, }); @override Future> save(DateCellData data) { - var payload = DateChangesetPB.create()..cellPath = _makeCellPath(cellId); + var payload = DateChangesetPB.create() + ..cellPath = _makeCellPath(cellContext); if (data.dateTime != null) { final date = (data.dateTime!.millisecondsSinceEpoch ~/ 1000).toString(); payload.date = date; @@ -62,7 +64,7 @@ class DateCellDataPersistence implements CellDataPersistence { } } -CellIdPB _makeCellPath(CellIdentifier cellId) { +CellIdPB _makeCellPath(DatabaseCellContext cellId) { return CellIdPB.create() ..viewId = cellId.viewId ..fieldId = cellId.fieldId diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart index 4edaf42c89..501fa8c2b1 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart @@ -25,40 +25,39 @@ class CellBackendService { CellBackendService(); Future> updateCell({ - required CellIdentifier cellId, + required DatabaseCellContext cellContext, required String data, }) { final payload = CellChangesetPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldId - ..rowId = cellId.rowId + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldId + ..rowId = cellContext.rowId ..cellChangeset = data; return DatabaseEventUpdateCell(payload).send(); } Future> getCell({ - required CellIdentifier cellId, + required DatabaseCellContext cellContext, }) { final payload = CellIdPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldId - ..rowId = cellId.rowId; + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldId + ..rowId = cellContext.rowId; return DatabaseEventGetCell(payload).send(); } } -/// Id of the cell /// We can locate the cell by using database + rowId + field.id. @freezed -class CellIdentifier with _$CellIdentifier { - const factory CellIdentifier({ +class DatabaseCellContext with _$DatabaseCellContext { + const factory DatabaseCellContext({ required String viewId, required RowId rowId, required FieldInfo fieldInfo, - }) = _CellIdentifier; + }) = _DatabaseCellContext; // ignore: unused_element - const CellIdentifier._(); + const DatabaseCellContext._(); String get fieldId => fieldInfo.id; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/checklist_cell_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/checklist_cell_service.dart index 0613e751c5..d836d188be 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/checklist_cell_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/checklist_cell_service.dart @@ -7,17 +7,17 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:dartz/dartz.dart'; class ChecklistCellBackendService { - final CellIdentifier cellId; + final DatabaseCellContext cellContext; - ChecklistCellBackendService({required this.cellId}); + ChecklistCellBackendService({required this.cellContext}); Future> create({ required String name, }) { final payload = ChecklistCellDataChangesetPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldInfo.id - ..rowId = cellId.rowId + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldInfo.id + ..rowId = cellContext.rowId ..insertOptions.add(name); return DatabaseEventUpdateChecklistCell(payload).send(); @@ -27,9 +27,9 @@ class ChecklistCellBackendService { required List optionIds, }) { final payload = ChecklistCellDataChangesetPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldInfo.id - ..rowId = cellId.rowId + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldInfo.id + ..rowId = cellContext.rowId ..deleteOptionIds.addAll(optionIds); return DatabaseEventUpdateChecklistCell(payload).send(); @@ -39,9 +39,9 @@ class ChecklistCellBackendService { required String optionId, }) { final payload = ChecklistCellDataChangesetPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldInfo.id - ..rowId = cellId.rowId + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldInfo.id + ..rowId = cellContext.rowId ..selectedOptionIds.add(optionId); return DatabaseEventUpdateChecklistCell(payload).send(); @@ -51,9 +51,9 @@ class ChecklistCellBackendService { required SelectOptionPB option, }) { final payload = ChecklistCellDataChangesetPB.create() - ..viewId = cellId.viewId - ..fieldId = cellId.fieldInfo.id - ..rowId = cellId.rowId + ..viewId = cellContext.viewId + ..fieldId = cellContext.fieldInfo.id + ..rowId = cellContext.rowId ..updateOptions.add(option); return DatabaseEventUpdateChecklistCell(payload).send(); @@ -61,10 +61,10 @@ class ChecklistCellBackendService { Future> getCellData() { final payload = CellIdPB.create() - ..fieldId = cellId.fieldInfo.id - ..viewId = cellId.viewId - ..rowId = cellId.rowId - ..rowId = cellId.rowId; + ..fieldId = cellContext.fieldInfo.id + ..viewId = cellContext.viewId + ..rowId = cellContext.rowId + ..rowId = cellContext.rowId; return DatabaseEventGetChecklistCellData(payload).send(); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/select_option_cell_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/select_option_cell_service.dart index a42f711d64..c95bc33251 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/select_option_cell_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/select_option_cell_service.dart @@ -8,12 +8,12 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/cell_entities.pb.dart'; class SelectOptionCellBackendService { - final CellIdentifier cellId; - SelectOptionCellBackendService({required this.cellId}); + final DatabaseCellContext cellContext; + SelectOptionCellBackendService({required this.cellContext}); - String get viewId => cellId.viewId; - String get fieldId => cellId.fieldInfo.id; - RowId get rowId => cellId.rowId; + String get viewId => cellContext.viewId; + String get fieldId => cellContext.fieldInfo.id; + RowId get rowId => cellContext.rowId; Future> create({ required String name, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_action_sheet_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_action_sheet_bloc.dart index cdcac04bed..7b50c44417 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_action_sheet_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_action_sheet_bloc.dart @@ -10,7 +10,7 @@ class FieldActionSheetBloc extends Bloc { final FieldBackendService fieldService; - FieldActionSheetBloc({required FieldCellContext fieldCellContext}) + FieldActionSheetBloc({required FieldContext fieldCellContext}) : fieldService = FieldBackendService( viewId: fieldCellContext.viewId, fieldId: fieldCellContext.field.id, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_cell_bloc.dart index 2fd72eea99..40980a9c51 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_cell_bloc.dart @@ -14,7 +14,7 @@ class FieldCellBloc extends Bloc { final FieldBackendService _fieldBackendSvc; FieldCellBloc({ - required FieldCellContext cellContext, + required FieldContext cellContext, }) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id), _fieldBackendSvc = FieldBackendService( viewId: cellContext.viewId, @@ -83,8 +83,7 @@ class FieldCellState with _$FieldCellState { required double width, }) = _FieldCellState; - factory FieldCellState.initial(FieldCellContext cellContext) => - FieldCellState( + factory FieldCellState.initial(FieldContext cellContext) => FieldCellState( viewId: cellContext.viewId, field: cellContext.field, width: cellContext.field.width.toDouble(), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart index adff9ae532..32cada4ff1 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart @@ -12,12 +12,20 @@ class FieldEditorBloc extends Bloc { final TypeOptionController dataController; FieldEditorBloc({ - required String viewId, - required String fieldName, required bool isGroupField, - required ITypeOptionLoader loader, - }) : dataController = TypeOptionController(viewId: viewId, loader: loader), - super(FieldEditorState.initial(viewId, fieldName, isGroupField)) { + required FieldPB field, + required FieldTypeOptionLoader loader, + }) : dataController = TypeOptionController( + field: field, + loader: loader, + ), + super( + FieldEditorState.initial( + loader.viewId, + loader.field.name, + isGroupField, + ), + ) { on( (event, emit) async { await event.when( @@ -27,7 +35,7 @@ class FieldEditorBloc extends Bloc { add(FieldEditorEvent.didReceiveFieldChanged(field)); } }); - await dataController.loadTypeOptionData(); + await dataController.reloadTypeOption(); add(FieldEditorEvent.didReceiveFieldChanged(dataController.field)); }, updateName: (name) { @@ -50,7 +58,7 @@ class FieldEditorBloc extends Bloc { () => null, (field) { final fieldService = FieldBackendService( - viewId: viewId, + viewId: loader.viewId, fieldId: field.id, ); fieldService.deleteField(); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart index 91bdad3e88..83bfcf218e 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_service.dart @@ -107,8 +107,8 @@ class FieldBackendService { } @freezed -class FieldCellContext with _$FieldCellContext { - const factory FieldCellContext({ +class FieldContext with _$FieldContext { + const factory FieldContext({ required String viewId, required FieldPB field, }) = _FieldCellContext; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart index 00cc1f50dd..a79de8ee61 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart @@ -113,7 +113,7 @@ class TypeOptionContext { required TypeOptionController dataController, }) : _dataController = dataController; - String get viewId => _dataController.viewId; + String get viewId => _dataController.loader.viewId; String get fieldId => _dataController.field.id; @@ -121,7 +121,7 @@ class TypeOptionContext { void Function(T)? onCompleted, required void Function(FlowyError) onError, }) async { - await _dataController.loadTypeOptionData().then((result) { + await _dataController.reloadTypeOption().then((result) { result.fold((l) => null, (err) => onError(err)); }); @@ -153,54 +153,12 @@ abstract class TypeOptionFieldDelegate { abstract class ITypeOptionLoader { String get viewId; String get fieldName; + Future> initialize(); } -/// Uses when creating a new field -class NewFieldTypeOptionLoader extends ITypeOptionLoader { - TypeOptionPB? fieldTypeOption; - - @override - final String viewId; - NewFieldTypeOptionLoader({ - required this.viewId, - }); - - /// Creates the field type option if the fieldTypeOption is null. - /// Otherwise, it loads the type option data from the backend. - @override - Future> initialize() { - if (fieldTypeOption != null) { - final payload = TypeOptionPathPB.create() - ..viewId = viewId - ..fieldId = fieldTypeOption!.field_2.id - ..fieldType = fieldTypeOption!.field_2.fieldType; - - return DatabaseEventGetTypeOption(payload).send(); - } else { - final payload = CreateFieldPayloadPB.create() - ..viewId = viewId - ..fieldType = FieldType.RichText; - - return DatabaseEventCreateTypeOption(payload).send().then((result) { - return result.fold( - (newFieldTypeOption) { - fieldTypeOption = newFieldTypeOption; - return left(newFieldTypeOption); - }, - (err) => right(err), - ); - }); - } - } - - @override - String get fieldName => fieldTypeOption?.field_2.name ?? ''; -} - /// Uses when editing a existing field -class FieldTypeOptionLoader extends ITypeOptionLoader { - @override +class FieldTypeOptionLoader { final String viewId; final FieldPB field; @@ -209,8 +167,7 @@ class FieldTypeOptionLoader extends ITypeOptionLoader { required this.field, }); - @override - Future> initialize() { + Future> load() { final payload = TypeOptionPathPB.create() ..viewId = viewId ..fieldId = field.id @@ -218,7 +175,4 @@ class FieldTypeOptionLoader extends ITypeOptionLoader { return DatabaseEventGetTypeOption(payload).send(); } - - @override - String get fieldName => field.name; } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart index e116baee02..e793b72f8e 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart @@ -11,31 +11,27 @@ import '../field_service.dart'; import 'type_option_context.dart'; class TypeOptionController { - final String viewId; late TypeOptionPB _typeOption; - final ITypeOptionLoader loader; + final FieldTypeOptionLoader loader; final PublishNotifier _fieldNotifier = PublishNotifier(); /// Returns a [TypeOptionController] used to modify the specified /// [FieldPB]'s data /// - /// Should call [loadTypeOptionData] if the passed-in [FieldInfo] + /// Should call [reloadTypeOption] if the passed-in [FieldInfo] /// is null /// TypeOptionController({ - required this.viewId, required this.loader, - FieldInfo? fieldInfo, + required FieldPB field, }) { - if (fieldInfo != null) { - _typeOption = TypeOptionPB.create() - ..viewId = viewId - ..field_2 = fieldInfo.field; - } + _typeOption = TypeOptionPB.create() + ..viewId = loader.viewId + ..field_2 = field; } - Future> loadTypeOptionData() async { - final result = await loader.initialize(); + Future> reloadTypeOption() async { + final result = await loader.load(); return result.fold( (data) { data.freeze(); @@ -67,7 +63,7 @@ class TypeOptionController { _fieldNotifier.value = _typeOption.field_2; - FieldBackendService(viewId: viewId, fieldId: field.id) + FieldBackendService(viewId: loader.viewId, fieldId: field.id) .updateField(name: name); } @@ -79,7 +75,7 @@ class TypeOptionController { }); FieldBackendService.updateFieldTypeOption( - viewId: viewId, + viewId: loader.viewId, fieldId: field.id, typeOptionData: typeOptionData, ); @@ -87,7 +83,7 @@ class TypeOptionController { Future switchToField(FieldType newFieldType) async { final payload = UpdateFieldTypePayloadPB.create() - ..viewId = viewId + ..viewId = loader.viewId ..fieldId = field.id ..fieldType = newFieldType; @@ -97,7 +93,7 @@ class TypeOptionController { // Should load the type-option data after switching to a new field. // After loading the type-option data, the editor widget that uses // the type-option data will be rebuild. - loadTypeOptionData(); + reloadTypeOption(); }, (err) => Future(() => Log.error(err)), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_service.dart index 79c65ba910..7c1fd9a71c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_service.dart @@ -1,8 +1,7 @@ -import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:dartz/dartz.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/cell_entities.pb.dart'; class TypeOptionBackendService { final String viewId; @@ -23,4 +22,15 @@ class TypeOptionBackendService { return DatabaseEventCreateSelectOption(payload).send(); } + + static Future> createFieldTypeOption({ + required String viewId, + FieldType fieldType = FieldType.RichText, + }) { + final payload = CreateFieldPayloadPB.create() + ..viewId = viewId + ..fieldType = FieldType.RichText; + + return DatabaseEventCreateTypeOption(payload).send(); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart index 7ec3b11db8..1ddca215f8 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_cache.dart @@ -185,7 +185,7 @@ class RowCache { RowUpdateCallback addListener({ required RowId rowId, - void Function(CellByFieldId, RowsChangedReason)? onCellUpdated, + void Function(CellContextByFieldId, RowsChangedReason)? onCellUpdated, bool Function()? listenWhen, }) { listenerHandler() async { @@ -197,7 +197,7 @@ class RowCache { if (onCellUpdated != null) { final rowInfo = _rowList.get(rowId); if (rowInfo != null) { - final CellByFieldId cellDataMap = + final CellContextByFieldId cellDataMap = _makeGridCells(rowId, rowInfo.rowPB); onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason); } @@ -220,7 +220,7 @@ class RowCache { _rowChangeReasonNotifier.removeListener(callback); } - CellByFieldId loadGridCells(RowId rowId) { + CellContextByFieldId loadGridCells(RowId rowId) { final RowPB? data = _rowList.get(rowId)?.rowPB; if (data == null) { _loadRow(rowId); @@ -240,12 +240,12 @@ class RowCache { ); } - CellByFieldId _makeGridCells(RowId rowId, RowPB? row) { + CellContextByFieldId _makeGridCells(RowId rowId, RowPB? row) { // ignore: prefer_collection_literals - var cellDataMap = CellByFieldId(); + var cellDataMap = CellContextByFieldId(); for (final field in _delegate.fields) { if (field.visibility) { - cellDataMap[field.id] = CellIdentifier( + cellDataMap[field.id] = DatabaseCellContext( rowId: rowId, viewId: viewId, fieldInfo: field, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart index edee0d24f7..24fd809633 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart @@ -3,7 +3,7 @@ import '../cell/cell_service.dart'; import 'row_cache.dart'; import 'row_service.dart'; -typedef OnRowChanged = void Function(CellByFieldId, RowsChangedReason); +typedef OnRowChanged = void Function(CellContextByFieldId, RowsChangedReason); class RowController { final RowId rowId; @@ -21,7 +21,7 @@ class RowController { this.groupId, }) : _rowCache = rowCache; - CellByFieldId loadData() { + CellContextByFieldId loadData() { return _rowCache.loadGridCells(rowId); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart index 650c905c8e..bdc25fac78 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_bloc.dart @@ -70,7 +70,7 @@ class RowEvent with _$RowEvent { const factory RowEvent.initial() = _InitialRow; const factory RowEvent.createRow() = _CreateRow; const factory RowEvent.didReceiveCells( - CellByFieldId cellsByFieldId, + CellContextByFieldId cellsByFieldId, RowsChangedReason reason, ) = _DidReceiveCells; } @@ -79,12 +79,15 @@ class RowEvent with _$RowEvent { class RowState with _$RowState { const factory RowState({ required RowInfo rowInfo, - required CellByFieldId cellByFieldId, + required CellContextByFieldId cellByFieldId, required UnmodifiableListView cells, RowsChangedReason? changeReason, }) = _RowState; - factory RowState.initial(RowInfo rowInfo, CellByFieldId cellByFieldId) => + factory RowState.initial( + RowInfo rowInfo, + CellContextByFieldId cellByFieldId, + ) => RowState( rowInfo: rowInfo, cellByFieldId: cellByFieldId, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart index a04124b43f..15d2c7644b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/row/row_detail_bloc.dart @@ -88,14 +88,14 @@ class RowDetailEvent with _$RowDetailEvent { const factory RowDetailEvent.duplicateRow(String rowId, String? groupId) = _DuplicateRow; const factory RowDetailEvent.didReceiveCellDatas( - List gridCells, + List gridCells, ) = _DidReceiveCellDatas; } @freezed class RowDetailState with _$RowDetailState { const factory RowDetailState({ - required List gridCells, + required List gridCells, }) = _RowDetailState; factory RowDetailState.initial() => RowDetailState( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart index 93e042373f..d2562cdb84 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell.dart @@ -14,7 +14,7 @@ import 'field_cell_action_sheet.dart'; import 'field_type_extension.dart'; class GridFieldCell extends StatefulWidget { - final FieldCellContext cellContext; + final FieldContext cellContext; const GridFieldCell({ Key? key, required this.cellContext, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart index 68f7246717..e54c578a5a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart @@ -19,7 +19,7 @@ import '../../layout/sizes.dart'; import 'field_editor.dart'; class GridFieldCellActionSheet extends StatefulWidget { - final FieldCellContext cellContext; + final FieldContext cellContext; const GridFieldCellActionSheet({required this.cellContext, Key? key}) : super(key: key); @@ -69,7 +69,7 @@ class _GridFieldCellActionSheetState extends State { } class _EditFieldButton extends StatelessWidget { - final FieldCellContext cellContext; + final FieldContext cellContext; final void Function()? onTap; const _EditFieldButton({required this.cellContext, Key? key, this.onTap}) : super(key: key); @@ -95,7 +95,7 @@ class _EditFieldButton extends StatelessWidget { } class _FieldOperationList extends StatelessWidget { - final FieldCellContext fieldInfo; + final FieldContext fieldInfo; const _FieldOperationList(this.fieldInfo, {Key? key}) : super(key: key); @override @@ -138,7 +138,7 @@ class _FieldOperationList extends StatelessWidget { } class FieldActionCell extends StatelessWidget { - final FieldCellContext fieldInfo; + final FieldContext fieldInfo; final FieldAction action; final bool enable; @@ -203,7 +203,7 @@ extension _FieldActionExtension on FieldAction { } } - void run(BuildContext context, FieldCellContext fieldInfo) { + void run(BuildContext context, FieldContext fieldInfo) { switch (this) { case FieldAction.hide: context diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart index 7f7e85e6b8..60eff2b44c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_editor.dart @@ -19,7 +19,7 @@ class FieldEditor extends StatefulWidget { final bool isGroupingField; final Function(String)? onDeleted; final Function(String)? onHidden; - final ITypeOptionLoader typeOptionLoader; + final FieldTypeOptionLoader typeOptionLoader; const FieldEditor({ required this.viewId, @@ -53,22 +53,25 @@ class _FieldEditorState extends State { Widget build(BuildContext context) { List children = [ _FieldNameTextField(popoverMutex: popoverMutex), - const VSpace(10), if (widget.onDeleted != null) _addDeleteFieldButton(), if (widget.onHidden != null) _addHideFieldButton(), - _FieldTypeOptionCell(popoverMutex: popoverMutex), + if (!widget.typeOptionLoader.field.isPrimary) + _FieldTypeOptionCell(popoverMutex: popoverMutex), ]; return BlocProvider( - create: (context) => FieldEditorBloc( - viewId: widget.viewId, - fieldName: widget.typeOptionLoader.fieldName, - isGroupField: widget.isGroupingField, - loader: widget.typeOptionLoader, - )..add(const FieldEditorEvent.initial()), - child: ListView.builder( + create: (context) { + return FieldEditorBloc( + isGroupField: widget.isGroupingField, + loader: widget.typeOptionLoader, + field: widget.typeOptionLoader.field, + )..add(const FieldEditorEvent.initial()); + }, + child: ListView.separated( shrinkWrap: true, itemCount: children.length, itemBuilder: (context, index) => children[index], + separatorBuilder: (context, index) => + VSpace(GridSize.typeOptionSeparatorHeight), padding: const EdgeInsets.symmetric(vertical: 12.0), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart index d6ccae1944..df3237119e 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/grid_header.dart @@ -4,6 +4,7 @@ import 'package:appflowy/plugins/database_view/application/field/field_service.d import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/grid/application/grid_header_bloc.dart'; import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/theme_extension.dart'; @@ -13,6 +14,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:reorderables/reorderables.dart'; +import '../../../../application/field/type_option/type_option_service.dart'; import '../../layout/sizes.dart'; import 'field_editor.dart'; import 'field_cell.dart'; @@ -101,8 +103,10 @@ class _GridHeaderState extends State<_GridHeader> { final cells = state.fields .where((field) => field.visibility) .map( - (field) => - FieldCellContext(viewId: widget.viewId, field: field.field), + (field) => FieldContext( + viewId: widget.viewId, + field: field.field, + ), ) .map( (ctx) => GridFieldCell( @@ -177,28 +181,52 @@ class _CellTrailing extends StatelessWidget { } } -class CreateFieldButton extends StatelessWidget { +class CreateFieldButton extends StatefulWidget { final String viewId; const CreateFieldButton({required this.viewId, Key? key}) : super(key: key); + @override + State createState() => _CreateFieldButtonState(); +} + +class _CreateFieldButtonState extends State { + final popoverController = PopoverController(); + late TypeOptionPB typeOption; + @override Widget build(BuildContext context) { return AppFlowyPopover( + controller: popoverController, direction: PopoverDirection.bottomWithRightAligned, asBarrier: true, margin: EdgeInsets.zero, constraints: BoxConstraints.loose(const Size(240, 600)), + triggerActions: PopoverTriggerFlags.none, child: FlowyButton( radius: BorderRadius.zero, text: FlowyText.medium(LocaleKeys.grid_field_newProperty.tr()), hoverColor: AFThemeExtension.of(context).greyHover, - onTap: () {}, + onTap: () async { + final result = await TypeOptionBackendService.createFieldTypeOption( + viewId: widget.viewId, + ); + result.fold( + (l) { + typeOption = l; + popoverController.show(); + }, + (r) => Log.error("Failed to create field type option: $r"), + ); + }, leftIcon: const FlowySvg(name: 'home/add'), ), popupBuilder: (BuildContext popover) { return FieldEditor( - viewId: viewId, - typeOptionLoader: NewFieldTypeOptionLoader(viewId: viewId), + viewId: widget.viewId, + typeOptionLoader: FieldTypeOptionLoader( + viewId: widget.viewId, + field: typeOption.field_2, + ), ); }, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart index fe61042135..1047031a4f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart @@ -60,7 +60,7 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({ required TypeOptionController dataController, required PopoverMutex popoverMutex, }) { - final viewId = dataController.viewId; + final viewId = dataController.loader.viewId; final fieldType = dataController.field.fieldType; switch (dataController.field.fieldType) { @@ -146,9 +146,8 @@ TypeOptionContext makeTypeOptionContext({ }) { final loader = FieldTypeOptionLoader(viewId: viewId, field: fieldInfo.field); final dataController = TypeOptionController( - viewId: viewId, loader: loader, - fieldInfo: fieldInfo, + field: fieldInfo.field, ); return makeTypeOptionContextWithDataController( viewId: viewId, @@ -180,8 +179,8 @@ TypeOptionContext makeSelectTypeOptionContext({ field: fieldPB, ); final dataController = TypeOptionController( - viewId: viewId, loader: loader, + field: fieldPB, ); final typeOptionContext = makeTypeOptionContextWithDataController( viewId: viewId, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart index 5fd0169c39..6ee6198f54 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/row/row.dart @@ -255,7 +255,10 @@ class RowContent extends StatelessWidget { ); } - List _makeCells(BuildContext context, CellByFieldId cellByFieldId) { + List _makeCells( + BuildContext context, + CellContextByFieldId cellByFieldId, + ) { return cellByFieldId.values.map( (cellId) { final GridCellWidget child = builder.build(cellId); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart index 2f4f30fe7e..17af488de4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card.dart @@ -194,7 +194,7 @@ class _RowCardState extends State> { class _CardContent extends StatelessWidget { final CardCellBuilder cellBuilder; final EditableRowNotifier rowNotifier; - final List cells; + final List cells; final RowCardRenderHook? renderHook; final CustomCardData? cardData; final RowCardStyleConfiguration styleConfiguration; @@ -233,28 +233,28 @@ class _CardContent extends StatelessWidget { List _makeCells( BuildContext context, - List cells, + List cells, ) { final List children = []; // Remove all the cell listeners. rowNotifier.unbind(); cells.asMap().forEach( - (int index, CellIdentifier cell) { + (int index, DatabaseCellContext cellContext) { final isEditing = index == 0 ? rowNotifier.isEditing.value : false; final cellNotifier = EditableCardNotifier(isEditing: isEditing); if (index == 0) { // Only use the first cell to receive user's input when click the edit // button - rowNotifier.bindCell(cell, cellNotifier); + rowNotifier.bindCell(cellContext, cellNotifier); } final child = Padding( - key: cell.key(), + key: cellContext.key(), padding: styleConfiguration.cellPadding, child: cellBuilder.buildCell( - cellId: cell, + cellContext: cellContext, cellNotifier: cellNotifier, renderHook: renderHook, cardData: cardData, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart index b33d0a9a9d..58c4a59c32 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_bloc.dart @@ -87,11 +87,11 @@ class CardBloc extends Bloc { } } -List _makeCells( +List _makeCells( String? groupFieldId, - CellByFieldId originalCellMap, + CellContextByFieldId originalCellMap, ) { - List cells = []; + List cells = []; for (final entry in originalCellMap.entries) { // Filter out the cell if it's fieldId equal to the groupFieldId if (groupFieldId != null) { @@ -110,7 +110,7 @@ class RowCardEvent with _$RowCardEvent { const factory RowCardEvent.initial() = _InitialRow; const factory RowCardEvent.setIsEditing(bool isEditing) = _IsEditing; const factory RowCardEvent.didReceiveCells( - List cells, + List cells, RowsChangedReason reason, ) = _DidReceiveCells; } @@ -119,14 +119,14 @@ class RowCardEvent with _$RowCardEvent { class RowCardState with _$RowCardState { const factory RowCardState({ required RowPB rowPB, - required List cells, + required List cells, required bool isEditing, RowsChangedReason? changeReason, }) = _RowCardState; factory RowCardState.initial( RowPB rowPB, - List cells, + List cells, bool isEditing, ) => RowCardState( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart index d21ea1036f..676e5e5cc4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/card_cell_builder.dart @@ -21,18 +21,18 @@ class CardCellBuilder { Widget buildCell({ CustomCardData? cardData, - required CellIdentifier cellId, + required DatabaseCellContext cellContext, EditableCardNotifier? cellNotifier, RowCardRenderHook? renderHook, }) { final cellControllerBuilder = CellControllerBuilder( - cellId: cellId, + cellContext: cellContext, cellCache: cellCache, ); - final key = cellId.key(); - final style = styles?[cellId.fieldType]; - switch (cellId.fieldType) { + final key = cellContext.key(); + final style = styles?[cellContext.fieldType]; + switch (cellContext.fieldType) { case FieldType.Checkbox: return CheckboxCardCell( cellControllerBuilder: cellControllerBuilder, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart index 624f0bf74a..db71945acd 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/card/cells/card_cell.dart @@ -106,7 +106,7 @@ class EditableRowNotifier { : isEditing = ValueNotifier(isEditing); void bindCell( - CellIdentifier cellIdentifier, + DatabaseCellContext cellIdentifier, EditableCardNotifier notifier, ) { assert( @@ -171,7 +171,8 @@ class EditableCellId { EditableCellId(this.rowId, this.fieldId); - factory EditableCellId.from(CellIdentifier cellIdentifier) => EditableCellId( + factory EditableCellId.from(DatabaseCellContext cellIdentifier) => + EditableCellId( cellIdentifier.rowId, cellIdentifier.fieldId, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart index 02c654d332..ad427be38d 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cell_builder.dart @@ -20,14 +20,17 @@ class GridCellBuilder { required this.cellCache, }); - GridCellWidget build(CellIdentifier cellId, {GridCellStyle? style}) { + GridCellWidget build( + DatabaseCellContext cellContext, { + GridCellStyle? style, + }) { final cellControllerBuilder = CellControllerBuilder( - cellId: cellId, + cellContext: cellContext, cellCache: cellCache, ); - final key = cellId.key(); - switch (cellId.fieldType) { + final key = cellContext.key(); + switch (cellContext.fieldType) { case FieldType.Checkbox: return GridCheckboxCell( cellControllerBuilder: cellControllerBuilder, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart index 2e3852f2a5..e0913008a9 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_bloc.dart @@ -15,8 +15,9 @@ class ChecklistCardCellBloc void Function()? _onCellChangedFn; ChecklistCardCellBloc({ required this.cellController, - }) : _checklistCellSvc = - ChecklistCellBackendService(cellId: cellController.cellId), + }) : _checklistCellSvc = ChecklistCellBackendService( + cellContext: cellController.cellContext, + ), super(ChecklistCellState.initial(cellController)) { on( (event, emit) async { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart index 2f100299ea..f60fa887b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor_bloc.dart @@ -18,8 +18,9 @@ class ChecklistCellEditorBloc ChecklistCellEditorBloc({ required this.cellController, - }) : _checklistCellService = - ChecklistCellBackendService(cellId: cellController.cellId), + }) : _checklistCellService = ChecklistCellBackendService( + cellContext: cellController.cellContext, + ), super(ChecklistCellEditorState.initial(cellController)) { on( (event, emit) async { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart index b8b652e569..667e66d913 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor_bloc.dart @@ -16,8 +16,9 @@ class SelectOptionCellEditorBloc SelectOptionCellEditorBloc({ required this.cellController, - }) : _selectOptionService = - SelectOptionCellBackendService(cellId: cellController.cellId), + }) : _selectOptionService = SelectOptionCellBackendService( + cellContext: cellController.cellContext, + ), super(SelectOptionEditorState.initial(cellController)) { on( (event, emit) async { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart index c8f1cf5d91..b3897942bd 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_detail.dart @@ -1,8 +1,10 @@ import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; +import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart'; import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart'; import 'package:appflowy/plugins/database_view/grid/application/row/row_detail_bloc.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; +import 'package:appflowy_backend/log.dart'; import 'package:collection/collection.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/image.dart'; @@ -134,7 +136,7 @@ class _PropertyColumn extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _RowTitle( - cellId: state.gridCells + cellContext: state.gridCells .firstWhereOrNull((e) => e.fieldInfo.isPrimary), cellBuilder: cellBuilder, ), @@ -145,7 +147,7 @@ class _PropertyColumn extends StatelessWidget { (cell) => Padding( padding: const EdgeInsets.only(bottom: 4.0), child: _PropertyCell( - cellId: cell, + cellContext: cell, cellBuilder: cellBuilder, ), ), @@ -161,14 +163,14 @@ class _PropertyColumn extends StatelessWidget { } class _RowTitle extends StatelessWidget { - final CellIdentifier? cellId; + final DatabaseCellContext? cellContext; final GridCellBuilder cellBuilder; - const _RowTitle({this.cellId, required this.cellBuilder, Key? key}) + const _RowTitle({this.cellContext, required this.cellBuilder, Key? key}) : super(key: key); @override Widget build(BuildContext context) { - if (cellId == null) { + if (cellContext == null) { return const SizedBox(); } final style = GridTextCellStyle( @@ -176,7 +178,7 @@ class _RowTitle extends StatelessWidget { textStyle: Theme.of(context).textTheme.titleLarge, autofocus: true, ); - return cellBuilder.build(cellId!, style: style); + return cellBuilder.build(cellContext!, style: style); } } @@ -194,6 +196,7 @@ class _CreatePropertyButton extends StatefulWidget { class _CreatePropertyButtonState extends State<_CreatePropertyButton> { late PopoverController popoverController; + late TypeOptionPB typeOption; @override void initState() { @@ -207,6 +210,7 @@ class _CreatePropertyButtonState extends State<_CreatePropertyButton> { constraints: BoxConstraints.loose(const Size(240, 200)), controller: popoverController, direction: PopoverDirection.topWithLeftAligned, + triggerActions: PopoverTriggerFlags.none, margin: EdgeInsets.zero, child: SizedBox( height: 40, @@ -216,7 +220,18 @@ class _CreatePropertyButtonState extends State<_CreatePropertyButton> { color: AFThemeExtension.of(context).textColor, ), hoverColor: AFThemeExtension.of(context).lightGreyHover, - onTap: () {}, + onTap: () async { + final result = await TypeOptionBackendService.createFieldTypeOption( + viewId: widget.viewId, + ); + result.fold( + (l) { + typeOption = l; + popoverController.show(); + }, + (r) => Log.error("Failed to create field type option: $r"), + ); + }, leftIcon: svgWidget( "home/add", color: AFThemeExtension.of(context).textColor, @@ -226,7 +241,10 @@ class _CreatePropertyButtonState extends State<_CreatePropertyButton> { popupBuilder: (BuildContext popOverContext) { return FieldEditor( viewId: widget.viewId, - typeOptionLoader: NewFieldTypeOptionLoader(viewId: widget.viewId), + typeOptionLoader: FieldTypeOptionLoader( + viewId: widget.viewId, + field: typeOption.field_2, + ), onDeleted: (fieldId) { popoverController.close(); NavigatorAlertDialog( @@ -245,10 +263,10 @@ class _CreatePropertyButtonState extends State<_CreatePropertyButton> { } class _PropertyCell extends StatefulWidget { - final CellIdentifier cellId; + final DatabaseCellContext cellContext; final GridCellBuilder cellBuilder; const _PropertyCell({ - required this.cellId, + required this.cellContext, required this.cellBuilder, Key? key, }) : super(key: key); @@ -262,8 +280,8 @@ class _PropertyCellState extends State<_PropertyCell> { @override Widget build(BuildContext context) { - final style = _customCellStyle(widget.cellId.fieldType); - final cell = widget.cellBuilder.build(widget.cellId, style: style); + final style = _customCellStyle(widget.cellContext.fieldType); + final cell = widget.cellBuilder.build(widget.cellContext, style: style); final gesture = GestureDetector( behavior: HitTestBehavior.translucent, @@ -290,7 +308,7 @@ class _PropertyCellState extends State<_PropertyCell> { child: SizedBox( width: 150, child: FieldCellButton( - field: widget.cellId.fieldInfo.field, + field: widget.cellContext.fieldInfo.field, onTap: () => popover.show(), radius: BorderRadius.circular(6), ), @@ -306,11 +324,11 @@ class _PropertyCellState extends State<_PropertyCell> { Widget buildFieldEditor() { return FieldEditor( - viewId: widget.cellId.viewId, - isGroupingField: widget.cellId.fieldInfo.isGroupField, + viewId: widget.cellContext.viewId, + isGroupingField: widget.cellContext.fieldInfo.isGroupField, typeOptionLoader: FieldTypeOptionLoader( - viewId: widget.cellId.viewId, - field: widget.cellId.fieldInfo.field, + viewId: widget.cellContext.viewId, + field: widget.cellContext.fieldInfo.field, ), onHidden: (fieldId) { popover.close(); diff --git a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart index 5186646f53..bdd5129c2d 100644 --- a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart +++ b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart @@ -171,7 +171,7 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( + getIt.registerFactoryParam( (data, _) => FieldActionSheetBloc(fieldCellContext: data), ); diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/create_or_edit_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/create_or_edit_field_test.dart index dfc3a072b5..f9d6af8911 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/create_or_edit_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/create_or_edit_field_test.dart @@ -35,10 +35,9 @@ void main() { ); final editorBloc = FieldEditorBloc( - viewId: context.gridView.id, - fieldName: fieldInfo.name, isGroupField: fieldInfo.isGroupField, loader: loader, + field: fieldInfo.field, )..add(const FieldEditorEvent.initial()); await boardResponseFuture(); diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_unsupport_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_unsupport_field_test.dart index adede9d1e1..5bf03f12ec 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_unsupport_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_unsupport_field_test.dart @@ -15,7 +15,7 @@ void main() { boardTest = await AppFlowyBoardTest.ensureInitialized(); context = await boardTest.createTestBoard(); final fieldInfo = context.singleSelectFieldContext(); - editorBloc = context.createFieldEditor( + editorBloc = context.makeFieldEditor( fieldInfo: fieldInfo, )..add(const FieldEditorEvent.initial()); diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart index 347b603faf..9c7391b53c 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart @@ -76,22 +76,18 @@ class BoardTestContext { return _boardDataController.fieldController; } - FieldEditorBloc createFieldEditor({ - FieldInfo? fieldInfo, + FieldEditorBloc makeFieldEditor({ + required FieldInfo fieldInfo, }) { - ITypeOptionLoader loader; - if (fieldInfo == null) { - loader = NewFieldTypeOptionLoader(viewId: gridView.id); - } else { - loader = - FieldTypeOptionLoader(viewId: gridView.id, field: fieldInfo.field); - } + final loader = FieldTypeOptionLoader( + viewId: gridView.id, + field: fieldInfo.field, + ); final editorBloc = FieldEditorBloc( - fieldName: fieldInfo?.name ?? '', - isGroupField: fieldInfo?.isGroupField ?? false, + isGroupField: fieldInfo.isGroupField, loader: loader, - viewId: gridView.id, + field: fieldInfo.field, ); return editorBloc; } @@ -120,13 +116,13 @@ class BoardTestContext { await gridResponseFuture(); return CellControllerBuilder( - cellId: rowBloc.state.cellByFieldId[fieldId]!, + cellContext: rowBloc.state.cellByFieldId[fieldId]!, cellCache: rowCache.cellCache, ); } Future createField(FieldType fieldType) async { - final editorBloc = createFieldEditor() + final editorBloc = await createFieldEditor(viewId: gridView.id) ..add(const FieldEditorEvent.initial()); await gridResponseFuture(); editorBloc.add(FieldEditorEvent.switchToField(fieldType)); @@ -140,9 +136,9 @@ class BoardTestContext { return fieldInfo; } - FieldCellContext singleSelectFieldCellContext() { + FieldContext singleSelectFieldCellContext() { final field = singleSelectFieldContext().field; - return FieldCellContext(viewId: gridView.id, field: field); + return FieldContext(viewId: gridView.id, field: field); } FieldInfo textFieldContext() { diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/field/edit_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/field/edit_field_test.dart index 2d23614c07..f47e5181ec 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/field/edit_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/field/edit_field_test.dart @@ -13,10 +13,9 @@ Future createEditorBloc(AppFlowyGridTest gridTest) async { ); return FieldEditorBloc( - viewId: context.gridView.id, - fieldName: fieldInfo.name, isGroupField: fieldInfo.isGroupField, loader: loader, + field: fieldInfo.field, )..add(const FieldEditorEvent.initial()); } @@ -83,10 +82,9 @@ Future makeEditorBloc(AppFlowyGridTest gridTest) async { ); final editorBloc = FieldEditorBloc( - viewId: context.gridView.id, - fieldName: fieldInfo.name, isGroupField: fieldInfo.isGroupField, loader: loader, + field: fieldInfo.field, )..add(const FieldEditorEvent.initial()); await gridResponseFuture(); diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/edit_filter_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/edit_filter_field_test.dart index 6c949c3985..075677f0cf 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/edit_filter_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/edit_filter_field_test.dart @@ -41,10 +41,9 @@ void main() { ); final editorBloc = FieldEditorBloc( - viewId: context.gridView.id, - fieldName: textField.field.name, isGroupField: false, loader: loader, + field: textField.field, )..add(const FieldEditorEvent.initial()); await gridResponseFuture(); diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart index 30535402c3..c2de80c67a 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart @@ -4,6 +4,7 @@ import 'package:appflowy/plugins/database_view/application/field/field_controlle import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; +import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart'; import 'package:appflowy/plugins/database_view/application/row/row_cache.dart'; import 'package:appflowy/plugins/database_view/application/row/row_data_controller.dart'; import 'package:appflowy/plugins/database_view/application/database_controller.dart'; @@ -38,26 +39,6 @@ class GridTestContext { return gridController.createRow(); } - FieldEditorBloc createFieldEditor({ - FieldInfo? fieldInfo, - }) { - ITypeOptionLoader loader; - if (fieldInfo == null) { - loader = NewFieldTypeOptionLoader(viewId: gridView.id); - } else { - loader = - FieldTypeOptionLoader(viewId: gridView.id, field: fieldInfo.field); - } - - final editorBloc = FieldEditorBloc( - fieldName: fieldInfo?.name ?? '', - isGroupField: fieldInfo?.isGroupField ?? false, - loader: loader, - viewId: gridView.id, - ); - return editorBloc; - } - Future makeCellController( String fieldId, int rowIndex, @@ -86,13 +67,13 @@ class GridTestContext { await gridResponseFuture(); return CellControllerBuilder( - cellId: rowBloc.state.cellByFieldId[fieldId]!, + cellContext: rowBloc.state.cellByFieldId[fieldId]!, cellCache: rowCache.cellCache, ); } Future createField(FieldType fieldType) async { - final editorBloc = createFieldEditor() + final editorBloc = await createFieldEditor(viewId: gridView.id) ..add(const FieldEditorEvent.initial()); await gridResponseFuture(); editorBloc.add(FieldEditorEvent.switchToField(fieldType)); @@ -106,9 +87,9 @@ class GridTestContext { return fieldInfo; } - FieldCellContext singleSelectFieldCellContext() { + FieldContext singleSelectFieldCellContext() { final field = singleSelectFieldContext().field; - return FieldCellContext(viewId: gridView.id, field: field); + return FieldContext(viewId: gridView.id, field: field); } FieldInfo textFieldContext() { @@ -155,6 +136,28 @@ class GridTestContext { } } +Future createFieldEditor({ + required String viewId, +}) async { + final result = await TypeOptionBackendService.createFieldTypeOption( + viewId: viewId, + ); + return result.fold( + (data) { + final loader = FieldTypeOptionLoader( + viewId: viewId, + field: data.field_2, + ); + return FieldEditorBloc( + isGroupField: FieldInfo(field: data.field_2).isGroupField, + loader: loader, + field: data.field_2, + ); + }, + (err) => throw Exception(err), + ); +} + /// Create a empty Grid for test class AppFlowyGridTest { final AppFlowyUnitTest unitTest; diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 8e626106e5..9f1c5085af 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -196,16 +196,27 @@ impl DatabaseEditor { } pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> { + let is_primary = self + .database + .lock() + .fields + .get_field(¶ms.field_id) + .map(|field| field.is_primary) + .unwrap_or(false); self .database .lock() .fields - .update_field(¶ms.field_id, |update| { - update + .update_field(¶ms.field_id, |mut update| { + update = update .set_name_if_not_none(params.name) - .set_field_type_if_not_none(params.field_type.map(|field_type| field_type.into())) .set_width_at_if_not_none(params.width.map(|value| value as i64)) .set_visibility_if_not_none(params.visibility); + if is_primary { + tracing::warn!("Cannot update primary field type"); + } else { + update.set_field_type_if_not_none(params.field_type.map(|field_type| field_type.into())); + } }); self .notify_did_update_database_field(¶ms.field_id) @@ -238,10 +249,15 @@ impl DatabaseEditor { .lock() .fields .update_field(field_id, |update| { - update.update_type_options(|type_options_update| { - type_options_update.insert(&field_type.to_string(), type_option_data); - }); + if old_field.is_primary { + tracing::warn!("Cannot update primary field type"); + } else { + update.update_type_options(|type_options_update| { + type_options_update.insert(&field_type.to_string(), type_option_data); + }); + } }); + self .database_views .did_update_field_type_option(view_id, field_id, &old_field) diff --git a/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs b/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs index cf90396249..6892b07b5c 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs @@ -43,7 +43,7 @@ async fn update_at_field_test() { .unwrap(); let old_updated_at = DateCellData::from(&cell).timestamp.unwrap(); - tokio::time::sleep(Duration::from_millis(500)).await; + tokio::time::sleep(Duration::from_millis(1000)).await; test .run_script(UpdateTextCell { row_id: row.id.clone(),