fix: disable edit primary field (#2695)

* refactor: field editor

* chore: disable edit type option of primary field
This commit is contained in:
Nathan.fooo 2023-06-04 09:28:13 +08:00 committed by GitHub
parent edd58ede45
commit bec8122178
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 362 additions and 313 deletions

View File

@ -1,6 +1,6 @@
part of 'cell_service.dart';
typedef CellByFieldId = LinkedHashMap<String, CellIdentifier>;
typedef CellContextByFieldId = LinkedHashMap<String, DatabaseCellContext>;
class DatabaseCell {
dynamic object;

View File

@ -22,7 +22,7 @@ import 'cell_service.dart';
///
// ignore: must_be_immutable
class CellController<T, D> extends Equatable {
final CellIdentifier cellId;
final DatabaseCellContext cellContext;
final CellCache _cellCache;
final CellCacheKey _cacheKey;
final FieldBackendService _fieldBackendSvc;
@ -37,37 +37,37 @@ class CellController<T, D> 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<T> cellDataLoader,
required CellDataPersistence<D> 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<T, D> extends Equatable {
}
@override
List<Object> get props =>
[_cellCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id];
List<Object> get props => [
_cellCache.get(_cacheKey) ?? "",
cellContext.rowId + cellContext.fieldInfo.id
];
}
class CellDataNotifier<T> extends ChangeNotifier {

View File

@ -17,104 +17,111 @@ typedef DateCellController = CellController<DateCellDataPB, DateCellData>;
typedef URLCellController = CellController<URLCellDataPB, String>;
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;

View File

@ -11,18 +11,18 @@ abstract class CellDataParser<T> {
class CellDataLoader<T> {
final CellBackendService service = CellBackendService();
final CellIdentifier cellId;
final DatabaseCellContext cellContext;
final CellDataParser<T> parser;
final bool reloadOnFieldChanged;
CellDataLoader({
required this.cellId,
required this.cellContext,
required this.parser,
this.reloadOnFieldChanged = false,
});
Future<T?> loadData() {
final fut = service.getCell(cellId: cellId);
final fut = service.getCell(cellContext: cellContext);
return fut.then(
(result) => result.fold(
(CellPB cell) {

View File

@ -7,16 +7,17 @@ abstract class CellDataPersistence<D> {
}
class TextCellDataPersistence implements CellDataPersistence<String> {
final CellIdentifier cellId;
final DatabaseCellContext cellContext;
final _cellBackendSvc = CellBackendService();
TextCellDataPersistence({
required this.cellId,
required this.cellContext,
});
@override
Future<Option<FlowyError>> 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<DateCellData> {
final CellIdentifier cellId;
final DatabaseCellContext cellContext;
DateCellDataPersistence({
required this.cellId,
required this.cellContext,
});
@override
Future<Option<FlowyError>> 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<DateCellData> {
}
}
CellIdPB _makeCellPath(CellIdentifier cellId) {
CellIdPB _makeCellPath(DatabaseCellContext cellId) {
return CellIdPB.create()
..viewId = cellId.viewId
..fieldId = cellId.fieldId

View File

@ -25,40 +25,39 @@ class CellBackendService {
CellBackendService();
Future<Either<void, FlowyError>> 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<Either<CellPB, FlowyError>> 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;

View File

@ -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<Either<Unit, FlowyError>> 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<String> 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<Either<ChecklistCellDataPB, FlowyError>> 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();
}

View File

@ -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<Either<Unit, FlowyError>> create({
required String name,

View File

@ -10,7 +10,7 @@ class FieldActionSheetBloc
extends Bloc<FieldActionSheetEvent, FieldActionSheetState> {
final FieldBackendService fieldService;
FieldActionSheetBloc({required FieldCellContext fieldCellContext})
FieldActionSheetBloc({required FieldContext fieldCellContext})
: fieldService = FieldBackendService(
viewId: fieldCellContext.viewId,
fieldId: fieldCellContext.field.id,

View File

@ -14,7 +14,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
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(),

View File

@ -12,12 +12,20 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
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<FieldEditorEvent>(
(event, emit) async {
await event.when(
@ -27,7 +35,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
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<FieldEditorEvent, FieldEditorState> {
() => null,
(field) {
final fieldService = FieldBackendService(
viewId: viewId,
viewId: loader.viewId,
fieldId: field.id,
);
fieldService.deleteField();

View File

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

View File

@ -113,7 +113,7 @@ class TypeOptionContext<T extends GeneratedMessage> {
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<T extends GeneratedMessage> {
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<Either<TypeOptionPB, FlowyError>> 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<Either<TypeOptionPB, FlowyError>> 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<Either<TypeOptionPB, FlowyError>> initialize() {
Future<Either<TypeOptionPB, FlowyError>> 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;
}

View File

@ -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<FieldPB> _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<Either<TypeOptionPB, FlowyError>> loadTypeOptionData() async {
final result = await loader.initialize();
Future<Either<TypeOptionPB, FlowyError>> 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<void> 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)),
);

View File

@ -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<Either<TypeOptionPB, FlowyError>> createFieldTypeOption({
required String viewId,
FieldType fieldType = FieldType.RichText,
}) {
final payload = CreateFieldPayloadPB.create()
..viewId = viewId
..fieldType = FieldType.RichText;
return DatabaseEventCreateTypeOption(payload).send();
}
}

View File

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

View File

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

View File

@ -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<GridCellEquatable> cells,
RowsChangedReason? changeReason,
}) = _RowState;
factory RowState.initial(RowInfo rowInfo, CellByFieldId cellByFieldId) =>
factory RowState.initial(
RowInfo rowInfo,
CellContextByFieldId cellByFieldId,
) =>
RowState(
rowInfo: rowInfo,
cellByFieldId: cellByFieldId,

View File

@ -88,14 +88,14 @@ class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.duplicateRow(String rowId, String? groupId) =
_DuplicateRow;
const factory RowDetailEvent.didReceiveCellDatas(
List<CellIdentifier> gridCells,
List<DatabaseCellContext> gridCells,
) = _DidReceiveCellDatas;
}
@freezed
class RowDetailState with _$RowDetailState {
const factory RowDetailState({
required List<CellIdentifier> gridCells,
required List<DatabaseCellContext> gridCells,
}) = _RowDetailState;
factory RowDetailState.initial() => RowDetailState(

View File

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

View File

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

View File

@ -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<FieldEditor> {
Widget build(BuildContext context) {
List<Widget> 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),
),
);

View File

@ -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<CreateFieldButton> createState() => _CreateFieldButtonState();
}
class _CreateFieldButtonState extends State<CreateFieldButton> {
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,
),
);
},
);

View File

@ -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<T> makeTypeOptionContext<T extends GeneratedMessage>({
}) {
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<T> makeSelectTypeOptionContext<T extends GeneratedMessage>({
field: fieldPB,
);
final dataController = TypeOptionController(
viewId: viewId,
loader: loader,
field: fieldPB,
);
final typeOptionContext = makeTypeOptionContextWithDataController<T>(
viewId: viewId,

View File

@ -255,7 +255,10 @@ class RowContent extends StatelessWidget {
);
}
List<Widget> _makeCells(BuildContext context, CellByFieldId cellByFieldId) {
List<Widget> _makeCells(
BuildContext context,
CellContextByFieldId cellByFieldId,
) {
return cellByFieldId.values.map(
(cellId) {
final GridCellWidget child = builder.build(cellId);

View File

@ -194,7 +194,7 @@ class _RowCardState<T> extends State<RowCard<T>> {
class _CardContent<CustomCardData> extends StatelessWidget {
final CardCellBuilder<CustomCardData> cellBuilder;
final EditableRowNotifier rowNotifier;
final List<CellIdentifier> cells;
final List<DatabaseCellContext> cells;
final RowCardRenderHook<CustomCardData>? renderHook;
final CustomCardData? cardData;
final RowCardStyleConfiguration styleConfiguration;
@ -233,28 +233,28 @@ class _CardContent<CustomCardData> extends StatelessWidget {
List<Widget> _makeCells(
BuildContext context,
List<CellIdentifier> cells,
List<DatabaseCellContext> cells,
) {
final List<Widget> 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,

View File

@ -87,11 +87,11 @@ class CardBloc extends Bloc<RowCardEvent, RowCardState> {
}
}
List<CellIdentifier> _makeCells(
List<DatabaseCellContext> _makeCells(
String? groupFieldId,
CellByFieldId originalCellMap,
CellContextByFieldId originalCellMap,
) {
List<CellIdentifier> cells = [];
List<DatabaseCellContext> 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<CellIdentifier> cells,
List<DatabaseCellContext> cells,
RowsChangedReason reason,
) = _DidReceiveCells;
}
@ -119,14 +119,14 @@ class RowCardEvent with _$RowCardEvent {
class RowCardState with _$RowCardState {
const factory RowCardState({
required RowPB rowPB,
required List<CellIdentifier> cells,
required List<DatabaseCellContext> cells,
required bool isEditing,
RowsChangedReason? changeReason,
}) = _RowCardState;
factory RowCardState.initial(
RowPB rowPB,
List<CellIdentifier> cells,
List<DatabaseCellContext> cells,
bool isEditing,
) =>
RowCardState(

View File

@ -21,18 +21,18 @@ class CardCellBuilder<CustomCardData> {
Widget buildCell({
CustomCardData? cardData,
required CellIdentifier cellId,
required DatabaseCellContext cellContext,
EditableCardNotifier? cellNotifier,
RowCardRenderHook<CustomCardData>? 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,

View File

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

View File

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

View File

@ -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<ChecklistCellEvent>(
(event, emit) async {

View File

@ -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<ChecklistCellEditorEvent>(
(event, emit) async {

View File

@ -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<SelectOptionEditorEvent>(
(event, emit) async {

View File

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

View File

@ -171,7 +171,7 @@ void _resolveGridDeps(GetIt getIt) {
),
);
getIt.registerFactoryParam<FieldActionSheetBloc, FieldCellContext, void>(
getIt.registerFactoryParam<FieldActionSheetBloc, FieldContext, void>(
(data, _) => FieldActionSheetBloc(fieldCellContext: data),
);

View File

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

View File

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

View File

@ -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<FieldEditorBloc> 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() {

View File

@ -13,10 +13,9 @@ Future<FieldEditorBloc> 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<FieldEditorBloc> 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();

View File

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

View File

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

View File

@ -196,16 +196,27 @@ impl DatabaseEditor {
}
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
let is_primary = self
.database
.lock()
.fields
.get_field(&params.field_id)
.map(|field| field.is_primary)
.unwrap_or(false);
self
.database
.lock()
.fields
.update_field(&params.field_id, |update| {
update
.update_field(&params.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(&params.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)

View File

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