mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: insert a new field to the left or right of an existing one (#4022)
* feat: allow inserting fields before or after a certain field * fix: tauri build * chore: implement frontend * test: rust-lib tests * test: integration test * chore: point to temp collab rev * chore: bump collab rev * chore: fix tauri build * chore: fix the tauri build, for real this time * fix: new field editor show detail not general --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
38e3947b2d
commit
20b485bcfe
@ -10,8 +10,8 @@ import '../util/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid cell', () {
|
||||
testWidgets('edit text cell', (tester) async {
|
||||
group('edit grid cell:', () {
|
||||
testWidgets('text', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -34,7 +34,7 @@ void main() {
|
||||
|
||||
// Make sure the text cells are filled with the right content when there are
|
||||
// multiple text cell
|
||||
testWidgets('edit multiple text cells', (tester) async {
|
||||
testWidgets('multiple text cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(
|
||||
@ -73,7 +73,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit number cell', (tester) async {
|
||||
testWidgets('number', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -131,7 +131,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit checkbox cell', (tester) async {
|
||||
testWidgets('checkbox', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -149,7 +149,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit create time cell', (tester) async {
|
||||
testWidgets('created time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -167,7 +167,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit last time cell', (tester) async {
|
||||
testWidgets('last modified time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -185,7 +185,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit date time cell', (tester) async {
|
||||
testWidgets('date time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -275,7 +275,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit single select cell', (tester) async {
|
||||
testWidgets('single select', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -347,7 +347,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit multi select cell', (tester) async {
|
||||
testWidgets('multi select', (tester) async {
|
||||
final tags = [
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
@ -436,107 +436,107 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('edit checklist cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
testWidgets('checklist', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// assert that there is no progress bar in the grid
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
// assert that there is no progress bar in the grid
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
|
||||
// tap on the first checklist cell
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
// tap on the first checklist cell
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// assert that the checklist editor is shown
|
||||
tester.assertChecklistEditorVisible(visible: true);
|
||||
// assert that the checklist editor is shown
|
||||
tester.assertChecklistEditorVisible(visible: true);
|
||||
|
||||
// create a new task with enter
|
||||
await tester.createNewChecklistTask(name: "task 0", enter: true);
|
||||
// create a new task with enter
|
||||
await tester.createNewChecklistTask(name: "task 0", enter: true);
|
||||
|
||||
// assert that the task is displayed
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 0",
|
||||
isChecked: false,
|
||||
);
|
||||
// assert that the task is displayed
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 0",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// update the task's name
|
||||
await tester.renameChecklistTask(index: 0, name: "task 1");
|
||||
// update the task's name
|
||||
await tester.renameChecklistTask(index: 0, name: "task 1");
|
||||
|
||||
// assert that the task's name is updated
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 1",
|
||||
isChecked: false,
|
||||
);
|
||||
// assert that the task's name is updated
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 1",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// dismiss new task editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss new task editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// dismiss checklist cell editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss checklist cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progress bar is shown in grid at 0%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0);
|
||||
// assert that progress bar is shown in grid at 0%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0);
|
||||
|
||||
// start editing the first checklist cell again
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
// start editing the first checklist cell again
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// create another task with the create button
|
||||
await tester.createNewChecklistTask(name: "task 2", button: true);
|
||||
// create another task with the create button
|
||||
await tester.createNewChecklistTask(name: "task 2", button: true);
|
||||
|
||||
// assert that the task was inserted
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: false,
|
||||
);
|
||||
// assert that the task was inserted
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// mark it as complete
|
||||
await tester.checkChecklistTask(index: 1);
|
||||
// mark it as complete
|
||||
await tester.checkChecklistTask(index: 1);
|
||||
|
||||
// assert that the task was checked in the editor
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: true,
|
||||
);
|
||||
// assert that the task was checked in the editor
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: true,
|
||||
);
|
||||
|
||||
// dismiss checklist editor
|
||||
await tester.dismissCellEditor();
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss checklist editor
|
||||
await tester.dismissCellEditor();
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progressbar is shown in grid at 50%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0.5);
|
||||
// assert that progressbar is shown in grid at 50%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0.5);
|
||||
|
||||
// re-open the cell editor
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
// re-open the cell editor
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// hover over first task and delete it
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
// hover over first task and delete it
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss cell editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progressbar is shown in grid at 100%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 1);
|
||||
// assert that progressbar is shown in grid at 100%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 1);
|
||||
|
||||
// re-open the cell edior
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
// re-open the cell edior
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// delete the remaining task
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
// delete the remaining task
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// check that the progress bar is not viisble
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
// check that the progress bar is not viisble
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import '../util/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid page', () {
|
||||
group('grid field editor:', () {
|
||||
testWidgets('rename existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
@ -98,7 +98,7 @@ void main() {
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Delete the field
|
||||
// duplicate the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDuplicatePropertyButton();
|
||||
|
||||
@ -106,6 +106,29 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('insert field on either side of a field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: false, name: 'Right');
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.findFieldWithName('Right');
|
||||
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: true, name: "Left");
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.findFieldWithName('Left');
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create checklist field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -765,6 +765,20 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(field);
|
||||
}
|
||||
|
||||
Future<void> tapInsertFieldButton({
|
||||
required bool left,
|
||||
required String name,
|
||||
}) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell &&
|
||||
(left && widget.action == FieldAction.insertLeft ||
|
||||
!left && widget.action == FieldAction.insertRight),
|
||||
);
|
||||
await tapButton(field);
|
||||
await renameField(name);
|
||||
}
|
||||
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapHidePropertyButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field_settings/field_settings_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
@ -25,11 +26,13 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
final FieldBackendService fieldService;
|
||||
final FieldSettingsBackendService fieldSettingsService;
|
||||
final TypeOptionController typeOptionController;
|
||||
final void Function(String newFieldId)? onFieldInserted;
|
||||
|
||||
FieldEditorBloc({
|
||||
required this.viewId,
|
||||
required this.field,
|
||||
required this.fieldController,
|
||||
this.onFieldInserted,
|
||||
required FieldTypeOptionLoader loader,
|
||||
}) : typeOptionController = TypeOptionController(
|
||||
field: field,
|
||||
@ -73,6 +76,28 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
final result = await fieldService.updateField(name: newName);
|
||||
_logIfError(result);
|
||||
},
|
||||
insertLeft: () async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
viewId: viewId,
|
||||
position: CreateFieldPosition.Before,
|
||||
targetFieldId: field.id,
|
||||
);
|
||||
result.fold(
|
||||
(typeOptionPB) => onFieldInserted?.call(typeOptionPB.field_2.id),
|
||||
(err) => Log.error("Failed creating field $err"),
|
||||
);
|
||||
},
|
||||
insertRight: () async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
viewId: viewId,
|
||||
position: CreateFieldPosition.After,
|
||||
targetFieldId: field.id,
|
||||
);
|
||||
result.fold(
|
||||
(typeOptionPB) => onFieldInserted?.call(typeOptionPB.field_2.id),
|
||||
(err) => Log.error("Failed creating field $err"),
|
||||
);
|
||||
},
|
||||
toggleFieldVisibility: () async {
|
||||
final currentVisibility =
|
||||
state.field.visibility ?? FieldVisibility.AlwaysShown;
|
||||
@ -122,6 +147,8 @@ class FieldEditorEvent with _$FieldEditorEvent {
|
||||
const factory FieldEditorEvent.switchFieldType(final FieldType fieldType) =
|
||||
_SwitchFieldType;
|
||||
const factory FieldEditorEvent.renameField(final String name) = _RenameField;
|
||||
const factory FieldEditorEvent.insertLeft() = _InsertLeft;
|
||||
const factory FieldEditorEvent.insertRight() = _InsertRight;
|
||||
const factory FieldEditorEvent.toggleFieldVisibility() =
|
||||
_ToggleFieldVisiblity;
|
||||
const factory FieldEditorEvent.deleteField() = _DeleteField;
|
||||
|
@ -26,11 +26,20 @@ class TypeOptionBackendService {
|
||||
static Future<Either<TypeOptionPB, FlowyError>> createFieldTypeOption({
|
||||
required String viewId,
|
||||
FieldType fieldType = FieldType.RichText,
|
||||
CreateFieldPosition position = CreateFieldPosition.End,
|
||||
String? targetFieldId,
|
||||
}) {
|
||||
final payload = CreateFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldType = FieldType.RichText;
|
||||
|
||||
return DatabaseEventCreateTypeOption(payload).send();
|
||||
if (position == CreateFieldPosition.Before ||
|
||||
position == CreateFieldPosition.After && targetFieldId != null) {
|
||||
payload.targetFieldId = targetFieldId!;
|
||||
}
|
||||
|
||||
payload.fieldPosition = position;
|
||||
|
||||
return DatabaseEventCreateField(payload).send();
|
||||
}
|
||||
}
|
||||
|
@ -20,17 +20,17 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
}) : super(GridHeaderState.initial()) {
|
||||
on<GridHeaderEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialHeader value) {
|
||||
await event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
add(
|
||||
GridHeaderEvent.didReceiveFieldUpdate(fieldController.fieldInfos),
|
||||
);
|
||||
},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
didReceiveFieldUpdate: (List<FieldInfo> fields) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
fields: value.fields
|
||||
fields: fields
|
||||
.where(
|
||||
(element) =>
|
||||
element.visibility != null &&
|
||||
@ -40,8 +40,17 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
),
|
||||
);
|
||||
},
|
||||
moveField: (_MoveField value) async {
|
||||
await _moveField(value, emit);
|
||||
startEditingField: (fieldId) {
|
||||
emit(state.copyWith(editingFieldId: fieldId));
|
||||
},
|
||||
startEditingNewField: (fieldId) {
|
||||
emit(state.copyWith(editingFieldId: fieldId, newFieldId: fieldId));
|
||||
},
|
||||
endEditingField: () {
|
||||
emit(state.copyWith(editingFieldId: null, newFieldId: null));
|
||||
},
|
||||
moveField: (field, fromIndex, toIndex) async {
|
||||
await _moveField(field, fromIndex, toIndex, emit);
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -49,19 +58,17 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
}
|
||||
|
||||
Future<void> _moveField(
|
||||
_MoveField value,
|
||||
FieldPB field,
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
Emitter<GridHeaderState> emit,
|
||||
) async {
|
||||
final fields = List<FieldInfo>.from(state.fields);
|
||||
fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
|
||||
fields.insert(toIndex, fields.removeAt(fromIndex));
|
||||
emit(state.copyWith(fields: fields));
|
||||
|
||||
final fieldService =
|
||||
FieldBackendService(viewId: viewId, fieldId: value.field.id);
|
||||
final result = await fieldService.moveField(
|
||||
value.fromIndex,
|
||||
value.toIndex,
|
||||
);
|
||||
final fieldService = FieldBackendService(viewId: viewId, fieldId: field.id);
|
||||
final result = await fieldService.moveField(fromIndex, toIndex);
|
||||
result.fold((l) {}, (err) => Log.error(err));
|
||||
}
|
||||
|
||||
@ -79,6 +86,11 @@ class GridHeaderEvent with _$GridHeaderEvent {
|
||||
const factory GridHeaderEvent.initial() = _InitialHeader;
|
||||
const factory GridHeaderEvent.didReceiveFieldUpdate(List<FieldInfo> fields) =
|
||||
_DidReceiveFieldUpdate;
|
||||
const factory GridHeaderEvent.startEditingField(String fieldId) =
|
||||
_StartEditingField;
|
||||
const factory GridHeaderEvent.startEditingNewField(String fieldId) =
|
||||
_StartEditingNewField;
|
||||
const factory GridHeaderEvent.endEditingField() = _EndEditingField;
|
||||
const factory GridHeaderEvent.moveField(
|
||||
FieldPB field,
|
||||
int fromIndex,
|
||||
@ -88,8 +100,12 @@ class GridHeaderEvent with _$GridHeaderEvent {
|
||||
|
||||
@freezed
|
||||
class GridHeaderState with _$GridHeaderState {
|
||||
const factory GridHeaderState({required List<FieldInfo> fields}) =
|
||||
_GridHeaderState;
|
||||
const factory GridHeaderState({
|
||||
required List<FieldInfo> fields,
|
||||
required String? editingFieldId,
|
||||
required String? newFieldId,
|
||||
}) = _GridHeaderState;
|
||||
|
||||
factory GridHeaderState.initial() => const GridHeaderState(fields: []);
|
||||
factory GridHeaderState.initial() =>
|
||||
const GridHeaderState(fields: [], editingFieldId: null, newFieldId: null);
|
||||
}
|
||||
|
@ -16,16 +16,27 @@ import 'field_editor.dart';
|
||||
import 'field_type_extension.dart';
|
||||
|
||||
class GridFieldCell extends StatefulWidget {
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
final FieldInfo fieldInfo;
|
||||
const GridFieldCell({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.fieldController,
|
||||
required this.fieldInfo,
|
||||
required this.onTap,
|
||||
required this.onEditorOpened,
|
||||
required this.onFieldInsertedOnEitherSide,
|
||||
required this.isEditing,
|
||||
required this.isNew,
|
||||
});
|
||||
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
final FieldInfo fieldInfo;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback onEditorOpened;
|
||||
final void Function(String fieldId) onFieldInsertedOnEitherSide;
|
||||
final bool isEditing;
|
||||
final bool isNew;
|
||||
|
||||
@override
|
||||
State<GridFieldCell> createState() => _GridFieldCellState();
|
||||
}
|
||||
@ -39,6 +50,11 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
super.initState();
|
||||
popoverController = PopoverController();
|
||||
_bloc = FieldCellBloc(viewId: widget.viewId, fieldInfo: widget.fieldInfo);
|
||||
if (widget.isEditing) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
popoverController.show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -46,6 +62,11 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
if (widget.fieldInfo != oldWidget.fieldInfo && !_bloc.isClosed) {
|
||||
_bloc.add(FieldCellEvent.onFieldChanged(widget.fieldInfo));
|
||||
}
|
||||
if (widget.isEditing) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
popoverController.show();
|
||||
});
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@ -62,16 +83,20 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
controller: popoverController,
|
||||
popupBuilder: (BuildContext context) {
|
||||
widget.onEditorOpened();
|
||||
return FieldEditor(
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
field: widget.fieldInfo.field,
|
||||
initialPage: FieldEditorPage.general,
|
||||
initialPage: widget.isNew
|
||||
? FieldEditorPage.details
|
||||
: FieldEditorPage.general,
|
||||
onFieldInserted: widget.onFieldInsertedOnEitherSide,
|
||||
);
|
||||
},
|
||||
child: FieldCellButton(
|
||||
field: widget.fieldInfo.field,
|
||||
onTap: () => popoverController.show(),
|
||||
onTap: widget.onTap,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -30,6 +30,7 @@ class FieldEditor extends StatefulWidget {
|
||||
final FieldController fieldController;
|
||||
final FieldPB field;
|
||||
final FieldEditorPage initialPage;
|
||||
final void Function(String fieldId)? onFieldInserted;
|
||||
|
||||
const FieldEditor({
|
||||
super.key,
|
||||
@ -37,6 +38,7 @@ class FieldEditor extends StatefulWidget {
|
||||
required this.field,
|
||||
required this.fieldController,
|
||||
this.initialPage = FieldEditorPage.details,
|
||||
this.onFieldInserted,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -61,6 +63,7 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
viewId: widget.viewId,
|
||||
field: widget.field,
|
||||
fieldController: widget.fieldController,
|
||||
onFieldInserted: widget.onFieldInserted,
|
||||
loader: FieldTypeOptionLoader(
|
||||
viewId: widget.viewId,
|
||||
field: widget.field,
|
||||
@ -89,6 +92,10 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
},
|
||||
),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_actionCell(FieldAction.insertLeft),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_actionCell(FieldAction.insertRight),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_actionCell(FieldAction.toggleVisibility),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_actionCell(FieldAction.duplicate),
|
||||
@ -170,40 +177,56 @@ class FieldActionCell extends StatelessWidget {
|
||||
),
|
||||
onHover: (_) => popoverMutex?.close(),
|
||||
onTap: () => action.run(context, viewId, fieldInfo),
|
||||
leftIcon: FlowySvg(
|
||||
action.icon(fieldInfo),
|
||||
size: const Size.square(16),
|
||||
color: enable ? null : Theme.of(context).disabledColor,
|
||||
leftIcon: action.icon(
|
||||
fieldInfo,
|
||||
enable ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum FieldAction {
|
||||
insertLeft,
|
||||
insertRight,
|
||||
toggleVisibility,
|
||||
duplicate,
|
||||
delete,
|
||||
}
|
||||
delete;
|
||||
|
||||
extension _FieldActionExtension on FieldAction {
|
||||
FlowySvgData icon(FieldInfo fieldInfo) {
|
||||
Widget icon(FieldInfo fieldInfo, Color? color) {
|
||||
late final FlowySvgData svgData;
|
||||
switch (this) {
|
||||
case FieldAction.insertLeft:
|
||||
svgData = FlowySvgs.arrow_s;
|
||||
case FieldAction.insertRight:
|
||||
svgData = FlowySvgs.arrow_s;
|
||||
case FieldAction.toggleVisibility:
|
||||
if (fieldInfo.visibility != null &&
|
||||
fieldInfo.visibility == FieldVisibility.AlwaysHidden) {
|
||||
return FlowySvgs.show_m;
|
||||
svgData = FlowySvgs.show_m;
|
||||
} else {
|
||||
return FlowySvgs.hide_s;
|
||||
svgData = FlowySvgs.hide_s;
|
||||
}
|
||||
case FieldAction.duplicate:
|
||||
return FlowySvgs.copy_s;
|
||||
svgData = FlowySvgs.copy_s;
|
||||
case FieldAction.delete:
|
||||
return FlowySvgs.delete_s;
|
||||
svgData = FlowySvgs.delete_s;
|
||||
}
|
||||
final icon = FlowySvg(
|
||||
svgData,
|
||||
size: const Size.square(16),
|
||||
color: color,
|
||||
);
|
||||
return this == FieldAction.insertRight
|
||||
? Transform.flip(flipX: true, child: icon)
|
||||
: icon;
|
||||
}
|
||||
|
||||
String title(FieldInfo fieldInfo) {
|
||||
switch (this) {
|
||||
case FieldAction.insertLeft:
|
||||
return LocaleKeys.grid_field_insertLeft.tr();
|
||||
case FieldAction.insertRight:
|
||||
return LocaleKeys.grid_field_insertRight.tr();
|
||||
case FieldAction.toggleVisibility:
|
||||
if (fieldInfo.visibility != null &&
|
||||
fieldInfo.visibility == FieldVisibility.AlwaysHidden) {
|
||||
@ -220,6 +243,18 @@ extension _FieldActionExtension on FieldAction {
|
||||
|
||||
void run(BuildContext context, String viewId, FieldInfo fieldInfo) {
|
||||
switch (this) {
|
||||
case FieldAction.insertLeft:
|
||||
PopoverContainer.of(context).close();
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.insertLeft());
|
||||
break;
|
||||
case FieldAction.insertRight:
|
||||
PopoverContainer.of(context).close();
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.insertRight());
|
||||
break;
|
||||
case FieldAction.toggleVisibility:
|
||||
PopoverContainer.of(context).close();
|
||||
context
|
||||
|
@ -7,17 +7,14 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
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';
|
||||
|
||||
class GridHeaderSliverAdaptor extends StatefulWidget {
|
||||
@ -93,7 +90,6 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GridHeaderBloc, GridHeaderState>(
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
builder: (context, state) {
|
||||
final cells = state.fields
|
||||
.map(
|
||||
@ -103,6 +99,17 @@ class _GridHeaderState extends State<_GridHeader> {
|
||||
viewId: widget.viewId,
|
||||
fieldInfo: fieldInfo,
|
||||
fieldController: widget.fieldController,
|
||||
onTap: () => context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.startEditingField(fieldInfo.id)),
|
||||
onFieldInsertedOnEitherSide: (fieldId) => context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.startEditingNewField(fieldId)),
|
||||
onEditorOpened: () => context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(const GridHeaderEvent.endEditingField()),
|
||||
isEditing: state.editingFieldId == fieldInfo.id,
|
||||
isNew: state.newFieldId == fieldInfo.id,
|
||||
)
|
||||
: MobileFieldButton(
|
||||
key: _getKeyById(fieldInfo.id),
|
||||
@ -184,74 +191,57 @@ class _CellTrailing extends StatelessWidget {
|
||||
)
|
||||
: null,
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: CreateFieldButton(viewId: viewId),
|
||||
child: CreateFieldButton(
|
||||
viewId: viewId,
|
||||
onFieldCreated: (fieldId) => context
|
||||
.read<GridHeaderBloc>()
|
||||
.add(GridHeaderEvent.startEditingNewField(fieldId)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateFieldButton extends StatefulWidget {
|
||||
final String viewId;
|
||||
const CreateFieldButton({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.onFieldCreated,
|
||||
});
|
||||
|
||||
final String viewId;
|
||||
final void Function(String fieldId) onFieldCreated;
|
||||
|
||||
@override
|
||||
State<CreateFieldButton> createState() => _CreateFieldButtonState();
|
||||
}
|
||||
|
||||
class _CreateFieldButtonState extends State<CreateFieldButton> {
|
||||
final popoverController = PopoverController();
|
||||
late TypeOptionPB typeOption;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fieldController =
|
||||
context.read<GridBloc>().databaseController.fieldController;
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
direction: PopoverDirection.bottomWithRightAligned,
|
||||
asBarrier: true,
|
||||
margin: EdgeInsets.zero,
|
||||
constraints: BoxConstraints.loose(const Size(240, 600)),
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
child: FlowyButton(
|
||||
margin: PlatformExtension.isDesktop
|
||||
? GridSize.cellContentInsets
|
||||
: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
radius: BorderRadius.zero,
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.grid_field_newProperty.tr(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color:
|
||||
PlatformExtension.isDesktop ? null : Theme.of(context).hintColor,
|
||||
),
|
||||
hoverColor: AFThemeExtension.of(context).greyHover,
|
||||
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: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color:
|
||||
PlatformExtension.isDesktop ? null : Theme.of(context).hintColor,
|
||||
),
|
||||
return FlowyButton(
|
||||
margin: PlatformExtension.isDesktop
|
||||
? GridSize.cellContentInsets
|
||||
: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
radius: BorderRadius.zero,
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.grid_field_newProperty.tr(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: PlatformExtension.isDesktop ? null : Theme.of(context).hintColor,
|
||||
),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return FieldEditor(
|
||||
hoverColor: AFThemeExtension.of(context).greyHover,
|
||||
onTap: () async {
|
||||
final result = await TypeOptionBackendService.createFieldTypeOption(
|
||||
viewId: widget.viewId,
|
||||
fieldController: fieldController,
|
||||
field: typeOption.field_2,
|
||||
);
|
||||
result.fold(
|
||||
(typeOptionPB) => widget.onFieldCreated(typeOptionPB.field_2.id),
|
||||
(err) => Log.error("Failed to create field type option: $err"),
|
||||
);
|
||||
},
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color: PlatformExtension.isDesktop ? null : Theme.of(context).hintColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
18
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
18
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -880,7 +880,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -900,7 +900,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -930,7 +930,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -942,7 +942,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -962,7 +962,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -976,7 +976,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -1018,7 +1018,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1040,7 +1040,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1067,7 +1067,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
|
@ -67,15 +67,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "509
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
|
||||
|
||||
|
||||
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
DatabaseEventMoveField,
|
||||
DatabaseEventGetFields,
|
||||
DatabaseEventDeleteField,
|
||||
DatabaseEventCreateTypeOption,
|
||||
DatabaseEventCreateField,
|
||||
DatabaseEventUpdateFieldSettings,
|
||||
DatabaseEventGetAllFieldSettings,
|
||||
} from '@/services/backend/events/flowy-database2';
|
||||
@ -73,7 +73,7 @@ export async function createField(viewId: string, fieldType?: FieldType, data?:
|
||||
type_option_data: data,
|
||||
});
|
||||
|
||||
const result = await DatabaseEventCreateTypeOption(payload);
|
||||
const result = await DatabaseEventCreateField(payload);
|
||||
|
||||
if (result.ok === false) {
|
||||
return Promise.reject('Failed to create field');
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CreateFieldPayloadPB, FieldType, TypeOptionPathPB, UpdateFieldTypePayloadPB } from '@/services/backend';
|
||||
import {
|
||||
DatabaseEventCreateTypeOption,
|
||||
DatabaseEventCreateField,
|
||||
DatabaseEventGetTypeOption,
|
||||
DatabaseEventUpdateFieldType,
|
||||
} from '@/services/backend/events/flowy-database2';
|
||||
@ -11,7 +11,7 @@ export class TypeOptionBackendService {
|
||||
createTypeOption = (fieldType: FieldType) => {
|
||||
const payload = CreateFieldPayloadPB.fromObject({ view_id: this.viewId, field_type: fieldType });
|
||||
|
||||
return DatabaseEventCreateTypeOption(payload);
|
||||
return DatabaseEventCreateField(payload);
|
||||
};
|
||||
|
||||
getTypeOption = (fieldId: string, fieldType: FieldType) => {
|
||||
|
3
frontend/resources/flowy_icons/16x/arrow.svg
Normal file
3
frontend/resources/flowy_icons/16x/arrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 5.5L3.5 8.5M3.5 8.5L6 11.5M3.5 8.5H12.5" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 222 B |
18
frontend/rust-lib/Cargo.lock
generated
18
frontend/rust-lib/Cargo.lock
generated
@ -730,7 +730,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -750,7 +750,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -780,7 +780,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -792,7 +792,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -812,7 +812,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -826,7 +826,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -868,7 +868,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -890,7 +890,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -917,7 +917,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a4f8a08544f6b113fb7b26a0c953e1c1979cf22c#a4f8a08544f6b113fb7b26a0c953e1c1979cf22c"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a462a49facbf3682717c3074b14fd29f19276e28#a462a49facbf3682717c3074b14fd29f19276e28"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
|
@ -67,7 +67,7 @@ futures = "0.3.29"
|
||||
tokio = "1.34.0"
|
||||
tokio-stream = "0.1.14"
|
||||
async-trait = "0.1.74"
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
lru = "0.12.0"
|
||||
|
||||
[profile.dev]
|
||||
@ -109,11 +109,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "509
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a4f8a08544f6b113fb7b26a0c953e1c1979cf22c" }
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "a462a49facbf3682717c3074b14fd29f19276e28" }
|
||||
|
@ -112,11 +112,11 @@ impl EventIntegrationTest {
|
||||
|
||||
pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateTypeOption)
|
||||
.event(DatabaseEvent::CreateField)
|
||||
.payload(CreateFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_type,
|
||||
type_option_data: None,
|
||||
..Default::default()
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
|
@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::views::FieldOrder;
|
||||
use collab_database::views::{FieldOrder, OrderObjectPosition};
|
||||
use serde_repr::*;
|
||||
use strum_macros::{EnumCount as EnumCountMacro, EnumIter};
|
||||
|
||||
@ -155,30 +155,94 @@ pub struct CreateFieldPayloadPB {
|
||||
#[pb(index = 2)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
#[pb(index = 3, one_of)]
|
||||
pub field_name: Option<String>,
|
||||
|
||||
/// If the type_option_data is not empty, it will be used to create the field.
|
||||
/// Otherwise, the default value will be used.
|
||||
#[pb(index = 3, one_of)]
|
||||
#[pb(index = 4, one_of)]
|
||||
pub type_option_data: Option<Vec<u8>>,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub field_position: CreateFieldPosition,
|
||||
|
||||
#[pb(index = 6, one_of)]
|
||||
pub target_field_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
pub enum CreateFieldPosition {
|
||||
#[default]
|
||||
End = 0,
|
||||
Start = 1,
|
||||
Before = 2,
|
||||
After = 3,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateFieldParams {
|
||||
pub view_id: String,
|
||||
pub field_name: Option<String>,
|
||||
pub field_type: FieldType,
|
||||
/// If the type_option_data is not empty, it will be used to create the field.
|
||||
/// Otherwise, the default value will be used.
|
||||
pub type_option_data: Option<Vec<u8>>,
|
||||
pub position: OrderObjectPosition,
|
||||
}
|
||||
|
||||
impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateFieldParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
|
||||
|
||||
let field_name = match self.field_name {
|
||||
Some(name) => Some(
|
||||
NotEmptyStr::parse(name)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let position = match &self.field_position {
|
||||
CreateFieldPosition::Start => {
|
||||
if self.target_field_id.is_some() {
|
||||
return Err(ErrorCode::InvalidParams);
|
||||
}
|
||||
OrderObjectPosition::Start
|
||||
},
|
||||
CreateFieldPosition::End => {
|
||||
if self.target_field_id.is_some() {
|
||||
return Err(ErrorCode::InvalidParams);
|
||||
}
|
||||
OrderObjectPosition::End
|
||||
},
|
||||
CreateFieldPosition::Before => {
|
||||
let field_id = self
|
||||
.target_field_id
|
||||
.ok_or_else(|| ErrorCode::InvalidParams)?;
|
||||
let field_id = NotEmptyStr::parse(field_id)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0;
|
||||
OrderObjectPosition::Before(field_id)
|
||||
},
|
||||
CreateFieldPosition::After => {
|
||||
let field_id = self
|
||||
.target_field_id
|
||||
.ok_or_else(|| ErrorCode::InvalidParams)?;
|
||||
let field_id = NotEmptyStr::parse(field_id)
|
||||
.map_err(|_| ErrorCode::InvalidParams)?
|
||||
.0;
|
||||
OrderObjectPosition::After(field_id)
|
||||
},
|
||||
};
|
||||
|
||||
Ok(CreateFieldParams {
|
||||
view_id: view_id.0,
|
||||
field_name,
|
||||
field_type: self.field_type,
|
||||
type_option_data: self.type_option_data,
|
||||
position,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -471,6 +535,7 @@ pub struct FieldChangesetParams {
|
||||
/// it would be better to append it to the end of the list.
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Hash,
|
||||
|
@ -2,6 +2,7 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
use collab_database::database::gen_row_id;
|
||||
use collab_database::rows::RowId;
|
||||
use collab_database::views::OrderObjectPosition;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
@ -319,16 +320,14 @@ pub(crate) async fn get_field_type_option_data_handler(
|
||||
|
||||
/// Create TypeOptionPB and save it. Return the FieldTypeOptionData.
|
||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||
pub(crate) async fn create_field_type_option_data_handler(
|
||||
pub(crate) async fn create_field_handler(
|
||||
data: AFPluginData<CreateFieldPayloadPB>,
|
||||
manager: AFPluginState<Weak<DatabaseManager>>,
|
||||
) -> DataResult<TypeOptionPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let params: CreateFieldParams = data.into_inner().try_into()?;
|
||||
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||
let (field, data) = database_editor
|
||||
.create_field_with_type_option(¶ms.view_id, ¶ms.field_type, params.type_option_data)
|
||||
.await;
|
||||
let (field, data) = database_editor.create_field_with_type_option(¶ms).await;
|
||||
|
||||
let data = TypeOptionPB {
|
||||
view_id: params.view_id,
|
||||
@ -449,12 +448,16 @@ pub(crate) async fn create_row_handler(
|
||||
CellBuilder::with_cells(params.cell_data_by_field_id.unwrap_or_default(), &fields).build();
|
||||
let view_id = params.view_id;
|
||||
let group_id = params.group_id;
|
||||
let position = match params.start_row_id {
|
||||
Some(row_id) => OrderObjectPosition::After(row_id.into()),
|
||||
None => OrderObjectPosition::Start,
|
||||
};
|
||||
let params = collab_database::rows::CreateRowParams {
|
||||
id: gen_row_id(),
|
||||
cells,
|
||||
height: 60,
|
||||
visibility: true,
|
||||
prev_row_id: params.start_row_id,
|
||||
row_position: position,
|
||||
timestamp: timestamp(),
|
||||
};
|
||||
match database_editor
|
||||
|
@ -31,7 +31,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
|
||||
.event(DatabaseEvent::DuplicateField, duplicate_field_handler)
|
||||
.event(DatabaseEvent::MoveField, move_field_handler)
|
||||
.event(DatabaseEvent::GetTypeOption, get_field_type_option_data_handler)
|
||||
.event(DatabaseEvent::CreateTypeOption, create_field_type_option_data_handler)
|
||||
.event(DatabaseEvent::CreateField, create_field_handler)
|
||||
// Row
|
||||
.event(DatabaseEvent::CreateRow, create_row_handler)
|
||||
.event(DatabaseEvent::GetRow, get_row_handler)
|
||||
@ -182,9 +182,10 @@ pub enum DatabaseEvent {
|
||||
#[event(input = "TypeOptionPathPB", output = "TypeOptionPB")]
|
||||
GetTypeOption = 23,
|
||||
|
||||
/// [CreateTypeOption] event is used to create a new FieldTypeOptionData.
|
||||
/// [CreateField] event is used to create a new field with an optional
|
||||
/// TypeOptionData.
|
||||
#[event(input = "CreateFieldPayloadPB", output = "TypeOptionPB")]
|
||||
CreateTypeOption = 24,
|
||||
CreateField = 24,
|
||||
|
||||
#[event(input = "DatabaseViewIdPB", output = "FieldPB")]
|
||||
GetPrimaryField = 25,
|
||||
|
@ -5,7 +5,7 @@ use bytes::Bytes;
|
||||
use collab_database::database::MutexDatabase;
|
||||
use collab_database::fields::{Field, TypeOptionData};
|
||||
use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowDetail, RowId};
|
||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
||||
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, OrderObjectPosition};
|
||||
use futures::StreamExt;
|
||||
use tokio::sync::{broadcast, RwLock};
|
||||
use tracing::{event, warn};
|
||||
@ -470,25 +470,26 @@ impl DatabaseEditor {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_field_with_type_option(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_type: &FieldType,
|
||||
type_option_data: Option<Vec<u8>>,
|
||||
) -> (Field, Bytes) {
|
||||
let name = field_type.default_name();
|
||||
let type_option_data = match type_option_data {
|
||||
None => default_type_option_data_from_type(field_type),
|
||||
Some(type_option_data) => type_option_data_from_pb_or_default(type_option_data, field_type),
|
||||
pub async fn create_field_with_type_option(&self, params: &CreateFieldParams) -> (Field, Bytes) {
|
||||
let name = params
|
||||
.field_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| params.field_type.default_name());
|
||||
let type_option_data = match ¶ms.type_option_data {
|
||||
None => default_type_option_data_from_type(¶ms.field_type),
|
||||
Some(type_option_data) => {
|
||||
type_option_data_from_pb_or_default(type_option_data.clone(), ¶ms.field_type)
|
||||
},
|
||||
};
|
||||
let (index, field) = self.database.lock().create_field_with_mut(
|
||||
view_id,
|
||||
¶ms.view_id,
|
||||
name,
|
||||
field_type.into(),
|
||||
params.field_type.into(),
|
||||
¶ms.position,
|
||||
|field| {
|
||||
field
|
||||
.type_options
|
||||
.insert(field_type.to_string(), type_option_data.clone());
|
||||
.insert(params.field_type.to_string(), type_option_data.clone());
|
||||
},
|
||||
default_field_settings_by_layout_map(),
|
||||
);
|
||||
@ -497,7 +498,10 @@ impl DatabaseEditor {
|
||||
.notify_did_insert_database_field(field.clone(), index)
|
||||
.await;
|
||||
|
||||
(field, type_option_to_pb(type_option_data, field_type))
|
||||
(
|
||||
field,
|
||||
type_option_to_pb(type_option_data, ¶ms.field_type),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn move_field(
|
||||
@ -1223,6 +1227,7 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
|
||||
view_id,
|
||||
name.to_string(),
|
||||
field_type.clone().into(),
|
||||
&OrderObjectPosition::default(),
|
||||
|field| {
|
||||
field
|
||||
.type_options
|
||||
|
@ -1,6 +1,6 @@
|
||||
use collab_database::database::{gen_field_id, MutexDatabase};
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::views::{DatabaseLayout, LayoutSetting};
|
||||
use collab_database::views::{DatabaseLayout, LayoutSetting, OrderObjectPosition};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entities::FieldType;
|
||||
@ -87,10 +87,12 @@ impl DatabaseLayoutDepsResolver {
|
||||
tracing::trace!("Create a new date field after layout type change");
|
||||
let field = self.create_date_field();
|
||||
let field_id = field.id.clone();
|
||||
self
|
||||
.database
|
||||
.lock()
|
||||
.create_field(field, default_field_settings_by_layout_map());
|
||||
self.database.lock().create_field(
|
||||
None,
|
||||
field,
|
||||
&OrderObjectPosition::End,
|
||||
default_field_settings_by_layout_map(),
|
||||
);
|
||||
field_id
|
||||
},
|
||||
Some(date_field) => date_field.id,
|
||||
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||
use collab_database::database::{gen_database_view_id, timestamp};
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::rows::{CreateRowParams, RowDetail, RowId};
|
||||
use collab_database::views::OrderObjectPosition;
|
||||
use strum::EnumCount;
|
||||
|
||||
use event_integration::folder_event::ViewTest;
|
||||
@ -410,7 +411,7 @@ impl<'a> TestRowBuilder<'a> {
|
||||
cells: self.cell_build.build(),
|
||||
height: 60,
|
||||
visibility: true,
|
||||
prev_row_id: None,
|
||||
row_position: OrderObjectPosition::End,
|
||||
timestamp: timestamp(),
|
||||
}
|
||||
}
|
||||
|
@ -64,10 +64,7 @@ impl DatabaseFieldTest {
|
||||
match script {
|
||||
FieldScript::CreateField { params } => {
|
||||
self.field_count += 1;
|
||||
self
|
||||
.editor
|
||||
.create_field_with_type_option(&self.view_id, ¶ms.field_type, params.type_option_data)
|
||||
.await;
|
||||
self.editor.create_field_with_type_option(¶ms).await;
|
||||
let fields = self.editor.get_fields(&self.view_id, None);
|
||||
assert_eq!(self.field_count, fields.len());
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::views::OrderObjectPosition;
|
||||
use flowy_database2::entities::{CreateFieldParams, FieldType};
|
||||
use flowy_database2::services::field::{
|
||||
type_option_to_pb, DateCellChangeset, DateFormat, DateTypeOption, FieldBuilder,
|
||||
@ -19,6 +20,8 @@ pub fn create_text_field(grid_id: &str) -> (CreateFieldParams, Field) {
|
||||
view_id: grid_id.to_owned(),
|
||||
field_type,
|
||||
type_option_data: Some(type_option_data),
|
||||
field_name: None,
|
||||
position: OrderObjectPosition::default(),
|
||||
};
|
||||
(params, text_field)
|
||||
}
|
||||
@ -38,6 +41,8 @@ pub fn create_single_select_field(grid_id: &str) -> (CreateFieldParams, Field) {
|
||||
view_id: grid_id.to_owned(),
|
||||
field_type,
|
||||
type_option_data: Some(type_option_data),
|
||||
field_name: None,
|
||||
position: OrderObjectPosition::default(),
|
||||
};
|
||||
(params, single_select_field)
|
||||
}
|
||||
@ -60,6 +65,8 @@ pub fn create_date_field(grid_id: &str) -> (CreateFieldParams, Field) {
|
||||
view_id: grid_id.to_owned(),
|
||||
field_type: FieldType::DateTime,
|
||||
type_option_data: Some(type_option_data),
|
||||
field_name: None,
|
||||
position: OrderObjectPosition::default(),
|
||||
};
|
||||
(params, field)
|
||||
}
|
||||
@ -92,6 +99,8 @@ pub fn create_timestamp_field(grid_id: &str, field_type: FieldType) -> (CreateFi
|
||||
view_id: grid_id.to_owned(),
|
||||
field_type,
|
||||
type_option_data: Some(type_option_data),
|
||||
field_name: None,
|
||||
position: OrderObjectPosition::default(),
|
||||
};
|
||||
(params, field)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use chrono::{offset, Duration};
|
||||
use collab_database::database::gen_row_id;
|
||||
use collab_database::rows::CreateRowParams;
|
||||
|
||||
use collab_database::views::OrderObjectPosition;
|
||||
use flowy_database2::entities::FieldType;
|
||||
use flowy_database2::services::cell::CellBuilder;
|
||||
use flowy_database2::services::field::DateCellData;
|
||||
@ -34,7 +35,7 @@ async fn group_by_date_test() {
|
||||
cells,
|
||||
height: 60,
|
||||
visibility: true,
|
||||
prev_row_id: None,
|
||||
row_position: OrderObjectPosition::default(),
|
||||
timestamp: 0,
|
||||
};
|
||||
let res = test.editor.create_row(&test.view_id, None, params).await;
|
||||
|
Loading…
Reference in New Issue
Block a user